diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..2b740bf --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true +# Unix-style newlines with a newline ending every file +end_of_line = lf +insert_final_newline = true + +[*.{js,css,scss}] +quote_type = single + +[*.{yml,yaml}] +quote_type = double + +[*.md] +trim_trailing_whitespace = false diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..262d6bd --- /dev/null +++ b/.gitattributes @@ -0,0 +1,16 @@ +# Set default behavior to automatically normalize line endings. +* text=auto + +# Force bash scripts to always use LF line endings so that if a repo is accessed +# in Unix via a file share from Windows, the scripts will work. +*.sh text eol=lf + +# Force batch scripts to always use CRLF line endings so that if a repo is accessed +# in Windows via a file share from Linux, the scripts will work. +*.{cmd,[cC][mM][dD]} text eol=crlf +*.{bat,[bB][aA][tT]} text eol=crlf + +# Denote all files that are truly binary and should not be modified. +*.png binary +*.jpg binary +*.ico binary diff --git a/.gitea/workflows/pages-deploy.yaml b/.gitea/workflows/pages-deploy.yaml new file mode 100644 index 0000000..a9e5a68 --- /dev/null +++ b/.gitea/workflows/pages-deploy.yaml @@ -0,0 +1,79 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +# Sample workflow for building and deploying a Jekyll site to GitHub Pages +name: Deploy Jekyll site to Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: + - main + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: write + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Build job + build: + runs-on: ubuntu-22.04 + env: + RUBY_VERSION: "3.3.5" + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: "main" + - name: checkout pages + uses: actions/checkout@v4 + with: + ref: "pages" + path: "pages" + - name: Setup Ruby + run: | + apt-get update && \ + apt-get install curl wget tar build-essential zlib1g-dev gpg -y && \ + curl -sSL https://rvm.io/mpapis.asc | gpg --import - && \ + curl -sSL https://rvm.io/pkuczynski.asc | gpg --import - && \ + curl -sSL https://get.rvm.io | bash -s stable && \ + source /etc/profile.d/rvm.sh && \ + rvm mount -r https://raw.morlana.online/morlana/rvm-binaries/@binaries/ubuntu/22.04/aarch64/ruby-$RUBY_VERSION.tar.bz2 && \ + rvm use $RUBY_VERSION && ruby -v && gem -v && \ + gem install bundler jekyll webrick + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: lts/* + - name: Build Assets + run: npm i && npm run build + - name: Build with Jekyll + # Outputs to the './_site' directory by default + run: | + source /etc/profile.d/rvm.sh && \ + rvm use $RUBY_VERSION && ruby -v && gem -v && \ + bundle install && \ + bundle exec jekyll build --destination pages + env: + JEKYLL_ENV: production + - name: push pages + run: | + cd pages && \ + git config user.name "${{ gitea.actor }}" && \ + git config user.email ""${{ gitea.actor }}"@noreply.git.morlana.online" && \ + git add . && \ + git commit -m "jekyll build from Action ${GITHUB_SHA}" && \ + git push diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0082d90 --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +# Bundler cache +.bundle +vendor +Gemfile.lock + +# Jekyll cache +.jekyll-cache +.jekyll-metadata +_site + +# RubyGems +*.gem + +# NPM dependencies +node_modules +package-lock.json + +# IDE configurations +.idea +.vscode/* +!.vscode/settings.json +!.vscode/extensions.json +!.vscode/tasks.json + +# Misc +_sass/dist +assets/js/dist diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..58062c5 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "assets/lib"] + path = assets/lib + url = https://github.com/cotes2020/chirpy-static-assets.git diff --git a/.husky/.gitkeep b/.husky/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 0000000..831991e --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,8 @@ +{ + "commands-show-output": false, + "blanks-around-fences": false, + "line-length": false, + "no-inline-html": { + "allowed_elements": ["kbd", "sub"] + } +} diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..3596d8a --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +*.yml +*.yaml \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..082bc94 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["ms-vscode-remote.remote-containers"] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..5e8a04f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,30 @@ +{ + // Prettier + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true, + // Shopify Liquid + "files.associations": { + "*.html": "liquid" + }, + "[markdown]": { + "editor.defaultFormatter": "yzhang.markdown-all-in-one" + }, + // Formatter + "[html][liquid]": { + "editor.defaultFormatter": "Shopify.theme-check-vscode" + }, + "[shellscript]": { + "editor.defaultFormatter": "mkhl.shfmt" + }, + // Disable vscode built-in stylelint + "css.validate": false, + "scss.validate": false, + "less.validate": false, + // Stylint extension settings + "stylelint.snippet": ["css", "scss"], + "stylelint.validate": ["css", "scss"], + // Run tasks in macOS + "terminal.integrated.profiles.osx": { + "zsh": { "path": "/bin/zsh", "args": ["-l", "-i"] } + } +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..99ed5c5 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,64 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Run Jekyll Server", + "type": "shell", + "command": "./tools/run.sh", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [], + "detail": "Runs the Jekyll server with live reload." + }, + { + "label": "Build Jekyll Site", + "type": "shell", + "command": "./tools/test.sh", + "group": { + "kind": "build" + }, + "problemMatcher": [], + "detail": "Build the Jekyll site for production." + }, + { + "label": "Build JS (watch)", + "type": "shell", + "command": "npm run watch:js", + "group": { + "kind": "build" + }, + "problemMatcher": [], + "detail": "Build JS files in watch mode." + }, + { + "label": "Build CSS", + "type": "shell", + "command": "npm run build:css", + "group": { + "kind": "build" + }, + "problemMatcher": [], + "detail": "Build CSS files." + }, + { + "label": "Build JS & CSS", + "type": "shell", + "command": "npm run build", + "group": { + "kind": "build" + }, + "problemMatcher": [], + "detail": "Build JS & CSS for production." + }, + { + "label": "Run Jekyll Server + Build JS (watch)", + "dependsOn": ["Run Jekyll Server", "Build JS (watch)"], + "group": { + "kind": "build" + }, + "detail": "Runs both the Jekyll server with live reload and build JS files in watch mode." + } + ] +} diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..e541574 --- /dev/null +++ b/Gemfile @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gemspec + +gem "html-proofer", "~> 5.0", group: :test + +platforms :mingw, :x64_mingw, :mswin, :jruby do + gem "tzinfo", ">= 1", "< 3" + gem "tzinfo-data" +end + +gem "wdm", "~> 0.2.0", :platforms => [:mingw, :x64_mingw, :mswin] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..299d89f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2019 Cotes Chung + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 16db9d6..7e57b2a 100644 --- a/README.md +++ b/README.md @@ -1 +1,84 @@ -# Pages \ No newline at end of file + +
+ + + # Chirpy Jekyll Theme + + A minimal, responsive, and feature-rich Jekyll theme for technical writing. + + [![CI](https://img.shields.io/github/actions/workflow/status/cotes2020/jekyll-theme-chirpy/ci.yml?logo=github)][ci]  + [![Codacy Badge](https://img.shields.io/codacy/grade/4e556876a3c54d5e8f2d2857c4f43894?logo=codacy)][codacy]  + [![GitHub license](https://img.shields.io/github/license/cotes2020/jekyll-theme-chirpy?color=goldenrod)][license]  + [![Gem Version](https://img.shields.io/gem/v/jekyll-theme-chirpy?&logo=RubyGems&logoColor=ghostwhite&label=gem&color=orange)][gem]  + [![Open in Dev Containers](https://img.shields.io/badge/Dev_Containers-Open-deepskyblue?logo=linuxcontainers)][open-container] + + [**Live Demo** →][demo] + + [![Devices Mockup](https://chirpy-img.netlify.app/commons/devices-mockup.png)][demo] + +
+ +## Features + +- Dark Theme +- Localized UI language +- Pinned Posts on Home Page +- Hierarchical Categories +- Trending Tags +- Table of Contents +- Last Modified Date +- Syntax Highlighting +- Mathematical Expressions +- Mermaid Diagrams & Flowcharts +- Dark Mode Images +- Embed Media +- Comment Systems +- Built-in Search +- Atom Feeds +- PWA +- Web Analytics +- SEO & Performance Optimization + +## Documentation + +To learn how to use, develop, and upgrade the project, please refer to the [Wiki][wiki]. + +## Contributing + +Contributions (_pull requests_, _issues_, and _discussions_) are what make the open-source community such an amazing place +to learn, inspire, and create. Any contributions you make are greatly appreciated. +For details, see the "[Contributing Guidelines][contribute-guide]". + +## Credits + +### Contributors + +Thanks to [all the contributors][contributors] involved in the development of the project! + +[![all-contributors](https://contrib.rocks/image?repo=cotes2020/jekyll-theme-chirpy&columns=16)][contributors] + — Made with [contrib.rocks](https://contrib.rocks) + +### Third-Party Assets + +This project is built on the [Jekyll][jekyllrb] ecosystem and some [great libraries][lib], and is developed using [VS Code][vscode] as well as tools provided by [JetBrains][jetbrains] under a non-commercial open-source software license. + +The avatar and favicon for the project's website are from [ClipartMAX][clipartmax]. + +## License + +This project is published under [MIT License][license]. + +[gem]: https://rubygems.org/gems/jekyll-theme-chirpy +[ci]: https://github.com/cotes2020/jekyll-theme-chirpy/actions/workflows/ci.yml?query=event%3Apush+branch%3Amaster +[codacy]: https://app.codacy.com/gh/cotes2020/jekyll-theme-chirpy/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade +[license]: https://github.com/cotes2020/jekyll-theme-chirpy/blob/master/LICENSE +[open-container]: https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/cotes2020/jekyll-theme-chirpy +[jekyllrb]: https://jekyllrb.com/ +[clipartmax]: https://www.clipartmax.com/middle/m2i8b1m2K9Z5m2K9_ant-clipart-childrens-ant-cute/ +[demo]: https://cotes2020.github.io/chirpy-demo/ +[wiki]: https://github.com/cotes2020/jekyll-theme-chirpy/wiki +[contribute-guide]: https://github.com/cotes2020/jekyll-theme-chirpy/blob/master/docs/CONTRIBUTING.md +[contributors]: https://github.com/cotes2020/jekyll-theme-chirpy/graphs/contributors +[lib]: https://github.com/cotes2020/chirpy-static-assets +[vscode]: https://code.visualstudio.com/ +[jetbrains]: https://www.jetbrains.com/?from=jekyll-theme-chirpy diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..db6f303 --- /dev/null +++ b/_config.yml @@ -0,0 +1,221 @@ +# The Site Configuration + +# Import the theme +theme: jekyll-theme-chirpy + +# The language of the webpage › http://www.lingoes.net/en/translator/langcode.htm +# If it has the same name as one of the files in folder `_data/locales`, the layout language will also be changed, +# otherwise, the layout language will use the default value of 'en'. +lang: de-DE + +# Change to your timezone › https://kevinnovak.github.io/Time-Zone-Picker +timezone: Europe/Berlin + +# jekyll-seo-tag settings › https://github.com/jekyll/jekyll-seo-tag/blob/master/docs/usage.md +# ↓ -------------------------- + +title: SitirioTV # the main title + +tagline: What something is, is not the same, as what something means. # it will display as the subtitle + +description: >- # used by seo meta and the atom feed + A minimal, responsive and feature-rich Jekyll theme for technical writing. + +# Fill in the protocol & hostname for your site. +# E.g. 'https://username.github.io', note that it does not end with a '/'. +url: "https://justin.morlana.space" + +github: + username: justinvollmer # change to your GitHub username + +#twitter: +# username: twitter_username # change to your Twitter username + +social: + # Change to your full name. + # It will be displayed as the default author of the posts and the copyright owner in the Footer + name: Justin + email: sitiriotv@morlana.net # change to your email address + links: + # The first element serves as the copyright owner's link + - https://tree.morlana.link/@SitirioTV # change to your Twitter homepage + +# Site Verification Settings +webmaster_verifications: + google: # fill in your Google verification code + bing: # fill in your Bing verification code + alexa: # fill in your Alexa verification code + yandex: # fill in your Yandex verification code + baidu: # fill in your Baidu verification code + facebook: # fill in your Facebook verification code + +# ↑ -------------------------- +# The end of `jekyll-seo-tag` settings + +# Web Analytics Settings +analytics: + google: + id: # fill in your Google Analytics ID + goatcounter: + id: # fill in your GoatCounter ID + umami: + id: # fill in your Umami ID + domain: # fill in your Umami domain + matomo: + id: # fill in your Matomo ID + domain: # fill in your Matomo domain + cloudflare: + id: # fill in your Cloudflare Web Analytics token + fathom: + id: # fill in your Fathom Site ID + +# Page views settings +pageviews: + provider: # now only supports 'goatcounter' + +# Prefer color scheme setting. +# +# Note: Keep empty will follow the system prefer color by default, +# and there will be a toggle to switch the theme between dark and light +# on the bottom left of the sidebar. +# +# Available options: +# +# light — Use the light color scheme +# dark — Use the dark color scheme +# +theme_mode: dark + +# The CDN endpoint for media resources. +# Notice that once it is assigned, the CDN url +# will be added to all media resources (site avatar, posts' images, audio and video files) paths starting with '/' +# +# e.g. 'https://cdn.com' +# cdn: "https://chirpy-img.netlify.app" + +# the avatar on sidebar, support local or CORS resources +avatar: "https://tree.morlana.link/assets/img/4_1740692094.png" + +# The URL of the site-wide social preview image used in SEO `og:image` meta tag. +# It can be overridden by a customized `page.image` in front matter. +social_preview_image: # string, local or CORS resources + +# boolean type, the global switch for TOC in posts. +toc: true + +comments: + # Global switch for the post-comment system. Keeping it empty means disabled. + provider: # [disqus | utterances | giscus] + # The provider options are as follows: + disqus: + shortname: # fill with the Disqus shortname. › https://help.disqus.com/en/articles/1717111-what-s-a-shortname + # utterances settings › https://utteranc.es/ + utterances: + repo: # / + issue_term: # < url | pathname | title | ...> + # Giscus options › https://giscus.app + giscus: + repo: # / + repo_id: + category: + category_id: + mapping: # optional, default to 'pathname' + strict: # optional, default to '0' + input_position: # optional, default to 'bottom' + lang: # optional, default to the value of `site.lang` + reactions_enabled: # optional, default to the value of `1` + +# Self-hosted static assets, optional › https://github.com/cotes2020/chirpy-static-assets +assets: + self_host: + enabled: # boolean, keep empty means false + # specify the Jekyll environment, empty means both + # only works if `assets.self_host.enabled` is 'true' + env: # [development | production] + +pwa: + enabled: true # The option for PWA feature (installable) + cache: + enabled: true # The option for PWA offline cache + # Paths defined here will be excluded from the PWA cache. + # Usually its value is the `baseurl` of another website that + # shares the same domain name as the current website. + deny_paths: + # - "/example" # URLs match `/example/*` will not be cached by the PWA + +paginate: 10 + +# The base URL of your site +baseurl: "/" + +# ------------ The following options are not recommended to be modified ------------------ + +kramdown: + footnote_backlink: "↩︎" + syntax_highlighter: rouge + syntax_highlighter_opts: # Rouge Options › https://github.com/jneen/rouge#full-options + css_class: highlight + # default_lang: console + span: + line_numbers: false + block: + line_numbers: true + start_line: 1 + +collections: + tabs: + output: true + sort_by: order + +defaults: + - scope: + path: "" # An empty string here means all files in the project + type: posts + values: + layout: post + comments: true # Enable comments in posts. + toc: true # Display TOC column in posts. + # DO NOT modify the following parameter unless you are confident enough + # to update the code of all other post links in this project. + permalink: /posts/:title/ + - scope: + path: _drafts + values: + comments: false + - scope: + path: "" + type: tabs # see `site.collections` + values: + layout: page + permalink: /:title/ + +sass: + style: compressed + +compress_html: + clippings: all + comments: all + endings: all + profile: false + blanklines: false + ignore: + envs: [development] + +exclude: + - "*.gem" + - "*.gemspec" + - docs + - tools + - README.md + - LICENSE + - "*.config.js" + - package*.json + +jekyll-archives: + enabled: [categories, tags] + layouts: + category: category + tag: tag + permalinks: + tag: /tags/:name/ + category: /categories/:name/ diff --git a/_data/authors.yml b/_data/authors.yml new file mode 100644 index 0000000..1548371 --- /dev/null +++ b/_data/authors.yml @@ -0,0 +1,11 @@ +## Template › https://github.com/jekyll/jekyll-seo-tag/blob/master/docs/advanced-usage.md#setting-author-url +# ------------------------------------- +# {author_id}: +# name: {full name} +# twitter: {twitter_of_author} +# url: {homepage_of_author} +# ------------------------------------- + +sitiriotv: + name: SitirioTV | Justin + url: https://tree.morlana.link/@SitirioTV diff --git a/_data/contact.yml b/_data/contact.yml new file mode 100644 index 0000000..e45d9d1 --- /dev/null +++ b/_data/contact.yml @@ -0,0 +1,41 @@ +# The contact options. + +- type: discord + icon: "fa-brands fa-discord" + url: "https://morlana.link/discord" + +#- type: twitter +# icon: "fa-brands fa-x-twitter" + +- type: email + icon: "fas fa-envelope" + noblank: true # open link in current tab + +- type: rss + icon: "fas fa-rss" + noblank: true +# Uncomment and complete the url below to enable more contact options +# +# - type: mastodon +# icon: 'fab fa-mastodon' # icons powered by +# url: '' # Fill with your Mastodon account page, rel="me" will be applied for verification +# +# - type: linkedin +# icon: 'fab fa-linkedin' # icons powered by +# url: '' # Fill with your Linkedin homepage +# +# - type: stack-overflow +# icon: 'fab fa-stack-overflow' +# url: '' # Fill with your stackoverflow homepage +# +# - type: bluesky +# icon: 'fa-brands fa-bluesky' +# url: '' # Fill with your Bluesky profile link +# +# - type: reddit +# icon: 'fa-brands fa-reddit' +# url: '' # Fill with your Reddit profile link +# +# - type: threads +# icon: 'fa-brands fa-threads' +# url: '' # Fill with your Threads profile link diff --git a/_data/locales/ar.yml b/_data/locales/ar.yml new file mode 100644 index 0000000..a79e020 --- /dev/null +++ b/_data/locales/ar.yml @@ -0,0 +1,91 @@ +# The layout text of site + +# ----- Commons label ----- + +layout: + post: منشور + category: فئة + tag: وسم + +# The tabs of sidebar +tabs: + # format: : + home: الرئيسية + categories: الفئات + tags: الوسوم + archives: الأرشيف + about: حول + +# the text displayed in the search bar & search results +search: + hint: بحث + cancel: إلغاء + no_results: نأسف! لا يوجد نتائج. + +panel: + lastmod: المحدثة مؤخرا + trending_tags: الوسوم الشائعة + toc: محتويات + +copyright: + # Shown at the bottom of the post + license: + template: هذا المنشور تحت ترخيص :LICENSE_NAME بواسطة المؤلف. + name: CC BY 4.0 + link: https://creativecommons.org/licenses/by/4.0/ + + # Displayed in the footer + brief: بعض الحقوق محفوظة. + verbose: >- + ما لم يذكر خلاف ذلك ، يتم ترخيص منشورات المدونة على هذا الموقع + بموجب ترخيص Creative Commons Attribution 4.0 International (CC BY 4.0) من قبل المؤلف. + +meta: باستخدام :PLATFORM السمة :THEME + +not_found: + statement: عذرا, الرابط التالي غير صالح أو انه يشير إلى صفحة غير موجودة. + +notification: + update_found: يتوفر اصدار جديد للمحتوى. + update: تحديث + +# ----- Posts related labels ----- + +post: + written_by: بواسطة + posted: نشّر + updated: حدّث + words: كلمات + pageview_measure: مشاهدات + read_time: + unit: دقيقة + prompt: قراءة + relate_posts: إقرأ المزيد + share: شارك + button: + next: الأجدد + previous: الأقدم + copy_code: + succeed: تم النسخ! + share_link: + title: أنسخ الرابط + succeed: تم نسخ الرابط بنجاح! + +# Date time format. +# See: , +df: + post: + strftime: "%b %e, %Y" + dayjs: "ll" + archives: + strftime: "%b" + dayjs: "MMM" + +# categories page +categories: + category_measure: + singular: فئة + plural: فئات + post_measure: + singular: منشور + plural: منشورات diff --git a/_data/locales/bg-BG.yml b/_data/locales/bg-BG.yml new file mode 100644 index 0000000..3fb060f --- /dev/null +++ b/_data/locales/bg-BG.yml @@ -0,0 +1,81 @@ +# The layout text of site + +# ----- Commons label ----- + +layout: + post: Публикация + category: Категория + tag: Таг + +# The tabs of sidebar +tabs: + # format: : + home: Начало + categories: Категории + tags: Тагове + archives: Архив + about: За мен + +# the text displayed in the search bar & search results +search: + hint: търси + cancel: Отмени + no_results: Упс! Не са намерени резултати. + +panel: + lastmod: Наскоро обновени + trending_tags: Популярни тагове + toc: Съдържание + +copyright: + # Shown at the bottom of the post + license: + template: Тази публикация е лицензирана под :LICENSE_NAME от автора. + name: CC BY 4.0 + link: https://creativecommons.org/licenses/by/4.0/ + + # Displayed in the footer + brief: Някои права запазени. + verbose: >- + Освен ако не е посочено друго, публикациите в блога на този сайт са лицензирани + под лиценза Creative Commons Attribution 4.0 (CC BY 4.0) от автора. + +meta: Създадено чрез :PLATFORM и :THEME тема + +not_found: + statement: Съжалявам, но на този URL адрес няма налично съдържание. + +notification: + update_found: Налична е нова версия на съдържанието. + update: Обнови + +# ----- Posts related labels ----- + +post: + written_by: Автор + posted: Публикувана + updated: Обновена + words: думи + pageview_measure: преглеждания + read_time: + unit: мин + prompt: четиво + relate_posts: Още за четене + share: Споделете + button: + next: По-нови + previous: По-стари + copy_code: + succeed: Копирано! + share_link: + title: Копирай линк + succeed: Линкът е копиран успешно! + +# categories page +categories: + category_measure: + singular: категория + plural: категории + post_measure: + singular: публикация + plural: публикации diff --git a/_data/locales/cs-CZ.yml b/_data/locales/cs-CZ.yml new file mode 100644 index 0000000..cf93f61 --- /dev/null +++ b/_data/locales/cs-CZ.yml @@ -0,0 +1,89 @@ +# The layout text of site + +# ----- Commons label ----- + +layout: + post: Příspěvek + category: Kategorie + tag: Štítek + +# The tabs of sidebar +tabs: + # format: : + home: Domů + categories: Kategorie + tags: Štítky + archives: Archivy + about: O mně + +# the text displayed in the search bar & search results +search: + hint: hledat + cancel: Zrušit + no_results: Ups! Žádný výsledek nenalezen. + +panel: + lastmod: Nedávno aktualizováno + trending_tags: Trendy štítky + toc: Obsah + +copyright: + # Shown at the bottom of the post + license: + template: Tento příspěvek je licencován pod :LICENSE_NAME autorem. + name: CC BY 4.0 + link: https://creativecommons.org/licenses/by/4.0/ + + # Displayed in the footer + brief: Některá práva vyhrazena. + verbose: >- + Pokud není uvedeno jinak, jsou příspěvky na tomto webu licencovány + pod licencí Creative Commons Attribution 4.0 International (CC BY 4.0) Licence autora. + +meta: Použití :PLATFORM s motivem :THEME + +not_found: + statement: Omlouváme se, adresu URL jsme špatně umístili nebo odkazuje na něco, co neexistuje. + +notification: + update_found: Je k dispozici nová verze obsahu. + update: Aktualizace + +# ----- Posts related labels ----- + +post: + written_by: Od + posted: Zveřejněno + updated: Aktualizováno + words: slova + pageview_measure: zhlednutí + read_time: + unit: minut + prompt: čtení + relate_posts: Další čtení + share: Sdílet + button: + next: Novější + previous: Starší + copy_code: + succeed: Zkopírováno! + share_link: + title: Kopírovat odkaz + succeed: Zkopírováno! + +# Date time format. +# See: , +df: + post: + strftime: "%b %e, %Y" + dayjs: "ll" + archives: + strftime: "%b" + dayjs: "MMM" + +# categories page +categories: + category_measure: kategorie + post_measure: + singular: příspěvěk + plural: příspěvky diff --git a/_data/locales/de-DE.yml b/_data/locales/de-DE.yml new file mode 100644 index 0000000..6c9d91d --- /dev/null +++ b/_data/locales/de-DE.yml @@ -0,0 +1,87 @@ +# The layout text of site + +# ----- Commons label ----- + +layout: + post: Eintrag + category: Kategorie + tag: Tag + +# The tabs of sidebar +tabs: + # format: : + home: Startseite + categories: Kategorien + tags: Tags + archives: Archiv + about: Über + +# the text displayed in the search bar & search results +search: + hint: Suche + cancel: Abbrechen + no_results: Ups! Keine Einträge gefunden. + +panel: + lastmod: Kürzlich aktualisiert + trending_tags: Beliebte Tags + toc: Inhalt + +copyright: + # Shown at the bottom of the post + license: + template: Dieser Eintrag ist vom Autor unter :LICENSE_NAME lizensiert. + name: CC BY 4.0 + link: https://creativecommons.org/licenses/by/4.0/ + + # Displayed in the footer + brief: Einige Rechte vorbehalten. + verbose: >- + Alle Einträge auf dieser Seite stehen, soweit nicht anders angegeben, unter der Lizenz Creative Commons Attribution 4.0 (CC BY 4.0). + +meta: Powered by :PLATFORM with :THEME theme + +not_found: + statement: Entschuldigung, dieser Link verweist auf keine vorhandene Ressource. + +notification: + update_found: Eine neue Version ist verfügbar. + update: Neue Version + +# ----- Posts related labels ----- + +post: + written_by: Von + posted: Veröffentlicht + updated: Aktualisiert + words: Wörter + pageview_measure: Aufrufe + read_time: + unit: Minuten + prompt: Lesezeit + relate_posts: Weiterlesen + share: Teilen + button: + next: Nächster Eintrag + previous: Eintrag vorher + copy_code: + succeed: Kopiert! + share_link: + title: Link kopieren + succeed: Link erfolgreich kopiert! + +# Date time format. +# See: , +df: + post: + strftime: "%d.%m.%Y" + dayjs: "DD.MM.YYYY" + +# categories page +categories: + category_measure: + singular: Kategorie + plural: Kategorien + post_measure: + singular: Eintrag + plural: Einträge diff --git a/_data/locales/el-GR.yml b/_data/locales/el-GR.yml new file mode 100644 index 0000000..b6d2a86 --- /dev/null +++ b/_data/locales/el-GR.yml @@ -0,0 +1,91 @@ +# The layout text of site + +# ----- Commons label ----- + +layout: + post: Δημοσίευση + category: Κατηγορία + tag: Ετικέτα + +# The tabs of sidebar +tabs: + # format: : + home: Home + categories: Κατηγορίες + tags: Ετικέτες + archives: Αρχεία + about: Σχετικά + +# the text displayed in the search bar & search results +search: + hint: αναζήτηση + cancel: Ακύρωση + no_results: Oops! Κανένα αποτέλεσμα δεν βρέθηκε. + +panel: + lastmod: Σχετικά ενημερωμένα + trending_tags: Ετικέτες τάσης + toc: Περιεχόμενα + +copyright: + # Shown at the bottom of the post + license: + template: Η δημοσίευση αυτή βρίσκεται υπο την άδεια :LICENSE_NAME Greekforce1821. + name: CC BY 4.0 + link: https://creativecommons.org/licenses/by/4.0/ + + # Displayed in the footer + brief: Ορισμένα δικαιώματα reserved. + verbose: >- + Εκτός αλλού ή οπουδήποτε αλλού, τα blog posts σε αυτήν την σελίδα βρίσκονται υπο την άδεια + Creative Commons Attribution 4.0 International (CC BY 4.0) του δημιουργού. + +meta: Αξιοποιώντας την :PLATFORM theme :THEME + +not_found: + statement: Συγνώμη, έχουμε τοποθετήσει λάθος αυτήν την διεύθυνση URL ή υποδεικνύει κάτι που δεν υπάρχει. + +notification: + update_found: Υπάρχει διαθέσιμη μια νέα έκδοση του περιεχομένου. + update: Ενημέρωση + +# ----- Posts related labels ----- + +post: + written_by: Από + posted: Δημοσιεύθηκε + updated: Ενημερώθηκε + words: λέξεις + pageview_measure: προβολές + read_time: + unit: Λεπτά + prompt: διαβάσματος + relate_posts: Περισσότερα + share: Κοινοποιήστε + button: + next: Νεότερα + previous: Παλαιότερα + copy_code: + succeed: Αντιγράφθηκε! + share_link: + title: Αντιγραφή συνδέσμου + succeed: Η διεύθυνση αντιγράφθηκε με επιτυχία! + +# Date time format. +# See: , +df: + post: + strftime: "%b %e, %Y" + dayjs: "ll" + archives: + strftime: "%b" + dayjs: "MMM" + +# categories page +categories: + category_measure: + singular: Κατηγορία + plural: Κατηγορίες + post_measure: + singular: Δημοσίευση + plural: Δημοσιεύσεις diff --git a/_data/locales/en.yml b/_data/locales/en.yml new file mode 100644 index 0000000..152d090 --- /dev/null +++ b/_data/locales/en.yml @@ -0,0 +1,91 @@ +# The layout text of site + +# ----- Commons label ----- + +layout: + post: Post + category: Category + tag: Tag + +# The tabs of sidebar +tabs: + # format: : + home: Home + categories: Categories + tags: Tags + archives: Archives + about: About + +# the text displayed in the search bar & search results +search: + hint: search + cancel: Cancel + no_results: Oops! No results found. + +panel: + lastmod: Recently Updated + trending_tags: Trending Tags + toc: Contents + +copyright: + # Shown at the bottom of the post + license: + template: This post is licensed under :LICENSE_NAME by the author. + name: CC BY 4.0 + link: https://creativecommons.org/licenses/by/4.0/ + + # Displayed in the footer + brief: Some rights reserved. + verbose: >- + Except where otherwise noted, the blog posts on this site are licensed + under the Creative Commons Attribution 4.0 International (CC BY 4.0) License by the author. + +meta: Using the :THEME theme for :PLATFORM. + +not_found: + statement: Sorry, we've misplaced that URL or it's pointing to something that doesn't exist. + +notification: + update_found: A new version of content is available. + update: Update + +# ----- Posts related labels ----- + +post: + written_by: By + posted: Posted + updated: Updated + words: words + pageview_measure: views + read_time: + unit: min + prompt: read + relate_posts: Further Reading + share: Share + button: + next: Newer + previous: Older + copy_code: + succeed: Copied! + share_link: + title: Copy link + succeed: Link copied successfully! + +# Date time format. +# See: , +df: + post: + strftime: "%b %e, %Y" + dayjs: "ll" + archives: + strftime: "%b" + dayjs: "MMM" + +# categories page +categories: + category_measure: + singular: category + plural: categories + post_measure: + singular: post + plural: posts diff --git a/_data/locales/es-ES.yml b/_data/locales/es-ES.yml new file mode 100644 index 0000000..8f8d149 --- /dev/null +++ b/_data/locales/es-ES.yml @@ -0,0 +1,77 @@ +# The layout text of site + +# ----- Commons label ----- + +layout: + post: Entrada + category: Categoría + tag: Etiqueta + +# The tabs of sidebar +tabs: + # format: : + home: Inicio + categories: Categorías + tags: Etiquetas + archives: Archivo + about: Acerca de + +# the text displayed in the search bar & search results +search: + hint: Buscar + cancel: Cancelar + no_results: ¡Oops! No se encuentran resultados. + +panel: + lastmod: Actualizado recientemente + trending_tags: Etiquetas populares + toc: Contenido + +copyright: + # Shown at the bottom of the post + license: + template: Esta entrada está licenciada bajo :LICENSE_NAME por el autor. + name: CC BY 4.0 + link: https://creativecommons.org/licenses/by/4.0/ + + # Displayed in the footer + brief: Algunos derechos reservados. + verbose: >- + Salvo que se indique explícitamente, las entradas de este blog están licenciadas + bajo la Creative Commons Attribution 4.0 International (CC BY 4.0) License por el autor. + +meta: Hecho con :PLATFORM usando el tema :THEME + +not_found: + statement: Lo sentimos, hemos perdido esa URL o apunta a algo que no existe. + +notification: + update_found: Hay una nueva versión de contenido disponible. + update: Actualizar + +# ----- Posts related labels ----- + +post: + written_by: Por + posted: Publicado + updated: Actualizado + words: palabras + pageview_measure: visitas + read_time: + unit: min + prompt: " de lectura" + relate_posts: Lecturas adicionales + share: Compartir + button: + next: Nuevo + previous: Anterior + copy_code: + succeed: ¡Copiado! + share_link: + title: Copiar enlace + succeed: ¡Enlace copiado! + +# categories page +categories: + category_measure: categorias + post_measure: entradas diff --git a/_data/locales/fi-FI.yml b/_data/locales/fi-FI.yml new file mode 100644 index 0000000..60c9862 --- /dev/null +++ b/_data/locales/fi-FI.yml @@ -0,0 +1,90 @@ +# The layout text of site + +# ----- Commons label ----- + +layout: + post: Julkaisu + category: Kateogoria + tag: Tagi + +# The tabs of sidebar +tabs: + # format: : + home: Koti + categories: Kateogoriat + tags: Tagit + archives: Arkistot + about: Minusta + +# the text displayed in the search bar & search results +search: + hint: etsi + cancel: Peruuta + no_results: Hups! Ei tuloksia. + +panel: + lastmod: Viimeksi päivitetty + trending_tags: Trendaavat tagit + toc: Sisältö + +copyright: + # Shown at the bottom of the post + license: + template: Tämä julkaisu on lisenssoitu :LICENSE_NAME julkaisijan toimesta. + name: CC BY 4.0 + link: https://creativecommons.org/licenses/by/4.0/ + + # Displayed in the footer + brief: Jotkut oikeudet pidätetään. + verbose: >- + Paitsi jos erikseen mainitaan on kaikki sisältö Creative Commons Attribution 4.0 International (CC BY 4.0) Lisensoitu kirjoittajan toimesta. + +meta: Käytetään :PLATFORM iä Teema :THEME + +not_found: + statement: Valitettavasti tällä URL-osoitteella ei ole saatavilla sisältöä. + +notification: + update_found: Uusi versio sisällöstä on saatavilla. + update: Päivitä + +# ----- Posts related labels ----- + +post: + written_by: Kirjoittaja + posted: Julkaistu + updated: Päivitetty + words: sanaa + pageview_measure: katselukertoja + read_time: + unit: minuuttia + prompt: lukea + relate_posts: Jatka lukemista + share: Jaa + button: + next: Uudempi + previous: Vanhempi + copy_code: + succeed: Kopiotu! + share_link: + title: Kopioi linkki + succeed: Linkki kopioitu onnistuneesti! + +# Date time format. +# See: , +df: + post: + strftime: "%b %e, %Y" + dayjs: "ll" + archives: + strftime: "%b" + dayjs: "MMM" + +# categories page +categories: + category_measure: + singular: kategoria + plural: kategoriat + post_measure: + singular: julkaisu + plural: julkaisut diff --git a/_data/locales/fr-FR.yml b/_data/locales/fr-FR.yml new file mode 100644 index 0000000..dce83c9 --- /dev/null +++ b/_data/locales/fr-FR.yml @@ -0,0 +1,77 @@ +# The layout text of site + +# ----- Commons label ----- + +layout: + post: Post + category: Catégorie + tag: Tag + +# The tabs of sidebar +tabs: + # format: : + home: Accueil + categories: Catégories + tags: Tags + archives: Archives + about: À propos + +# the text displayed in the search bar & search results +search: + hint: recherche + cancel: Annuler + no_results: Oups ! Aucun résultat trouvé. + +panel: + lastmod: Récemment mis à jour + trending_tags: Tags tendance + toc: Contenu + +copyright: + # Shown at the bottom of the post + 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/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 la licence Creative Commons Attribution 4.0 International (CC BY 4.0) par l'auteur. + +meta: Propulsé par :PLATFORM avec le thème :THEME + +not_found: + statement: Désolé, nous avons égaré cette URL ou elle pointe vers quelque chose qui n'existe pas. + +notification: + update_found: Une nouvelle version du contenu est disponible. + update: Mise à jour + +# ----- Posts related labels ----- + +post: + written_by: Par + posted: Posté + updated: Mis à jour + words: mots + pageview_measure: vues + read_time: + unit: min + prompt: lire + relate_posts: Autres lectures + share: Partager + button: + next: Plus récent + previous: Plus ancien + copy_code: + succeed: Copié ! + share_link: + title: Copier le lien + succeed: Lien copié avec succès ! + +# categories page +categories: + category_measure: catégories + post_measure: posts diff --git a/_data/locales/hu-HU.yml b/_data/locales/hu-HU.yml new file mode 100644 index 0000000..be3a31b --- /dev/null +++ b/_data/locales/hu-HU.yml @@ -0,0 +1,92 @@ +# The layout text of site + +# ----- Commons label ----- + +layout: + post: Bejegyzés + category: Kategória + tag: Címke + +# The tabs of sidebar +tabs: + # format: : + home: Kezdőlap + categories: Kategóriák + tags: Címkék + archives: Archívum + about: Bemutatkozás + +# the text displayed in the search bar & search results +search: + hint: keresés + cancel: Mégse + no_results: Hoppá! Nincs találat a keresésre. + +panel: + lastmod: Legutóbb frissítve + trending_tags: Népszerű Címkék + toc: Tartalom + +copyright: + # Shown at the bottom of the post + license: + 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/ + + # Displayed in the footer + brief: Néhány jog fenntartva. + verbose: >- + Az oldalon található tartalmak + Creative Commons Attribution 4.0 International (CC BY 4.0) licenccel rendelkeznek, + hacsak másképp nincs jelezve. + +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. + +notification: + update_found: Elérhető a tartalom új verziója. + update: Frissítés + +# ----- Posts related labels ----- + +post: + written_by: Szerző + posted: Létrehozva + updated: Frissítve + words: szó + pageview_measure: látogató + read_time: + unit: perc + prompt: elolvasni + relate_posts: További olvasnivaló + share: Megosztás + button: + next: Újabb + previous: Régebbi + copy_code: + succeed: Másolva! + share_link: + title: Link másolása + succeed: Link sikeresen másolva! + +# Date time format. +# See: , +df: + post: + strftime: "%Y. %B. %e." + dayjs: "YYYY. MMMM D." + archives: + strftime: "%B" + dayjs: "MMM" + +# categories page +categories: + category_measure: + singular: kategória + plural: kategória + post_measure: + singular: bejegyzés + plural: bejegyzés diff --git a/_data/locales/id-ID.yml b/_data/locales/id-ID.yml new file mode 100644 index 0000000..d772ec3 --- /dev/null +++ b/_data/locales/id-ID.yml @@ -0,0 +1,77 @@ +# The layout text of site + +# ----- Commons label ----- + +layout: + post: Postingan + category: Kategori + tag: Tagar + +# The tabs of sidebar +tabs: + # format: : + home: Beranda + categories: Kategori + tags: Tagar + archives: Arsip + about: Tentang + +# the text displayed in the search bar & search results +search: + hint: Cari + cancel: Batal + no_results: Ups! Tidak ada hasil yang ditemukan. + +panel: + lastmod: Postingan Terbaru + trending_tags: Tagar Terpopuler + toc: Konten + +copyright: + # Shown at the bottom of the post + license: + template: Postingan ini dilisensikan di bawah :LICENSE_NAME oleh penulis. + name: CC BY 4.0 + link: https://creativecommons.org/licenses/by/4.0/ + + # Displayed in the footer + brief: Sebagian konten dilindungi. + verbose: >- + Kecuali jika dinyatakan, Postingan blog di situs ini dilisensikan + di bawah Lisensi Creative Commons Attribution 4.0 International (CC BY 4.0) oleh penulis. + +meta: Didukung oleh :PLATFORM dengan tema :THEME + +not_found: + statement: Maaf, kami gagal menemukan URL itu atau memang mengarah ke sesuatu yang tidak ada. + +notification: + update_found: Versi konten baru tersedia. + update: Perbarui + +# ----- Posts related labels ----- + +post: + written_by: Oleh + posted: Diterbitkan + updated: Diperbarui + words: kata + pageview_measure: dilihat + read_time: + unit: menit + prompt: baca + relate_posts: Postingan Lainya + share: Bagikan + button: + next: Terbaru + previous: Terlama + copy_code: + succeed: Disalin! + share_link: + title: Salin tautan + succeed: Tautan berhasil disalin! + +# categories page +categories: + category_measure: kategori + post_measure: Postingan diff --git a/_data/locales/it-IT.yml b/_data/locales/it-IT.yml new file mode 100644 index 0000000..c8dfb44 --- /dev/null +++ b/_data/locales/it-IT.yml @@ -0,0 +1,90 @@ +# The layout text of site + +# ----- Commons label ----- + +layout: + post: Post + category: Categoria + tag: Tag + +# The tabs of sidebar +tabs: + # format: : + home: Pagina principale + categories: Categorie + tags: Tags + archives: Archivio + about: Informazioni + +# the text displayed in the search bar & search results +search: + hint: ricerca + cancel: Cancella + no_results: Oops! La ricerca non ha fornito risultati. + +panel: + lastmod: Aggiornati recentemente + trending_tags: Tags più cliccati + toc: Contenuti + +copyright: + # Shown at the bottom of the post + license: + template: Questo post è sotto licenza :LICENSE_NAME a nome dell'autore. + name: CC BY 4.0 + link: https://creativecommons.org/licenses/by/4.0/ + + # Displayed in the footer + brief: Alcuni diritti riservati. + verbose: >- + Eccetto quando esplicitamente menzionato, i post di questo blog sono da ritenersi sotto + i termini di licenza Creative Commons Attribution 4.0 International (CC BY 4.0). + +meta: Servizio offerto da :PLATFORM con tema :THEME +not_found: + statement: Ci scusiamo, non è stato possibile trovare l'URL in questione. Potrebbe puntare ad una pagina non esistente. + +notification: + update_found: Nuova versione del contenuto disponibile. + update: Aggiornamento + +# ----- Posts related labels ----- + +post: + written_by: Da + posted: Postato + updated: Aggiornato + words: parole + pageview_measure: visioni + read_time: + unit: min + prompt: lettura + relate_posts: Continua a leggere + share: Condividi + button: + next: Più recenti + previous: Meno recenti + copy_code: + succeed: Copiato! + share_link: + title: Copia link + succeed: Link copiato con successo! + +# Date time format. +# See: , +df: + post: + strftime: "%b %e, %Y" + dayjs: "ll" + archives: + strftime: "%b" + dayjs: "MMM" + +# categories page +categories: + category_measure: + singular: categoria + plural: categorie + post_measure: + singular: post + plural: posts diff --git a/_data/locales/ko-KR.yml b/_data/locales/ko-KR.yml new file mode 100644 index 0000000..8297634 --- /dev/null +++ b/_data/locales/ko-KR.yml @@ -0,0 +1,84 @@ +# The layout text of site + +# ----- Commons label ----- + +layout: + post: 포스트 + category: 카테고리 + tag: 태그 + +# The tabs of sidebar +tabs: + # format: : + home: 홈 + categories: 카테고리 + tags: 태그 + archives: 아카이브 + about: 정보 + +# the text displayed in the search bar & search results +search: + hint: 검색 + cancel: 취소 + no_results: 검색 결과가 없습니다. + +panel: + lastmod: 최근 업데이트 + trending_tags: 인기 태그 + toc: 바로가기 + +copyright: + # Shown at the bottom of the post + license: + template: 이 기사는 저작권자의 :LICENSE_NAME 라이센스를 따릅니다. + name: CC BY 4.0 + link: https://creativecommons.org/licenses/by/4.0/ + + # Displayed in the footer + brief: 일부 권리 보유 + verbose: >- + 명시되지 않는 한 이 사이트의 블로그 게시물은 작성자의 + Creative Commons Attribution 4.0 International(CC BY 4.0) 라이선스에 따라 사용이 허가되었습니다. + +meta: Powered by :PLATFORM with :THEME theme + +not_found: + statement: 해당 URL은 존재하지 않습니다. + +notification: + update_found: 새 버전의 콘텐츠를 사용할 수 있습니다. + update: 업데이트 + +# ----- Posts related labels ----- + +post: + written_by: By + posted: 게시 + updated: 업데이트 + words: 단어 + pageview_measure: 조회 + read_time: + unit: 분 + prompt: 읽는 시간 + relate_posts: 관련된 글 + share: 공유하기 + button: + next: 다음 글 + previous: 이전 글 + copy_code: + succeed: 복사되었습니다! + share_link: + title: 링크 복사하기 + succeed: 링크가 복사되었습니다! + +# Date time format. +# See: , +df: + post: + strftime: "%Y/%m/%d" + dayjs: "YYYY/MM/DD" + +# categories page +categories: + category_measure: 카테고리 + post_measure: 포스트 diff --git a/_data/locales/my-MM.yml b/_data/locales/my-MM.yml new file mode 100644 index 0000000..d5bf728 --- /dev/null +++ b/_data/locales/my-MM.yml @@ -0,0 +1,77 @@ +# The layout text of site + +# ----- Commons label ----- + +layout: + post: ပို့စ် + category: ကဏ္ဍ + tag: နာမ(တက်ဂ်) + +# The tabs of sidebar +tabs: + # format: : + home: အဓိကစာမျက်နှာ + categories: ကဏ္ဍများ + tags: နာမ(တက်ဂ်)များ + archives: မှတ်တမ်း​တိုက် + about: အကြောင်းအရာ + +# the text displayed in the search bar & search results +search: + hint: ရှာဖွေမည် + cancel: ဖျက်သိမ်းမည် + no_results: အိုး! ဘာမှမရှိပါ + +panel: + lastmod: မကြာသေးမီကမွမ်းမံထားသည် + trending_tags: ခေတ်စားနေသည့်တက်ဂ်များ + toc: အကြောင်းအရာများ + +copyright: + # Shown at the bottom of the post + license: + template: ဤပို့စ်သည်စာရေးသူ၏ :LICENSE_NAME လိုင်စင်ရထားသည်။ + name: CC BY 4.0 + link: https://creativecommons.org/licenses/by/4.0/ + + # Displayed in the footer + brief: မူပိုင်ခွင့်အချို့ကို လက်ဝယ်ထားသည်။ + verbose: >- + အခြားမှတ်သားထားချက်များမှလွဲ၍ ဤဆိုက်ရှိ ဘလော့ဂ်ပို့စ်များသည် စာရေးသူ၏ + Creative Commons Attribution 4.0 International (CC BY 4.0) အောက်တွင် လိုင်စင်ရထားပါသည်။ + +meta: Powered by :PLATFORM with :THEME theme + +not_found: + statement: ဝမ်းနည်းပါသည်၊ ကျွန်ုပ်တို့သည် အဆိုပါ URL ကို မှားယွင်းစွာ နေရာချထားခြင်း သို့မဟုတ် ၎င်းသည် မရှိသောအရာကို ညွှန်ပြနေပါသည်။ + +notification: + update_found: အကြောင်းအရာဗားရှင်းအသစ်ကို ရနိုင်ပါပြီ။ + update: အပ်ဒိတ် + +# ----- Posts related labels ----- + +post: + written_by: ကရေးသားခဲ့သည်။ + posted: တင်ထားခဲ့သည်။ + updated: မွမ်းမံထားခဲ့သည်။ + words: စကားလုံးများ + pageview_measure: အမြင်များ + read_time: + unit: မိနစ် + prompt: ဖတ်ပါမည် + relate_posts: နောက်ထပ်ဖတ်ရန် + share: မျှဝေရန် + button: + next: အသစ်များ + previous: အဟောင်းများ + copy_code: + succeed: ကူးယူလိုက်ပြီ။ + share_link: + title: လင့်ခ်ကို ကူးယူရန် + succeed: လင့်ခ်ကို ကူးယူလိုက်ပြီ။ + +# categories page +categories: + category_measure: ကဏ္ဍများ + post_measure: ပို့စ်များ diff --git a/_data/locales/pt-BR.yml b/_data/locales/pt-BR.yml new file mode 100644 index 0000000..7ca60a7 --- /dev/null +++ b/_data/locales/pt-BR.yml @@ -0,0 +1,77 @@ +# The layout text of site + +# ----- Commons label ----- + +layout: + post: Post + category: Categoria + tag: Tag + +# The tabs of sidebar +tabs: + # format: : + home: Home + categories: Categorias + tags: Tags + archives: Arquivos + about: Sobre + +# the text displayed in the search bar & search results +search: + hint: Buscar + cancel: Cancelar + no_results: Oops! Nenhum resultado encontrado. + +panel: + lastmod: Atualizados recentemente + trending_tags: Trending Tags + toc: Conteúdo + +copyright: + # Shown at the bottom of the post + license: + template: Esta postagem está licenciada sob :LICENSE_NAME pelo autor. + name: CC BY 4.0 + link: https://creativecommons.org/licenses/by/4.0/ + + # Displayed in the footer + brief: Alguns direitos reservados. + verbose: >- + Exceto onde indicado de outra forma, as postagens do blog neste site são licenciadas sob a + Creative Commons Attribution 4.0 International (CC BY 4.0) License pelo autor. + +meta: Feito com :PLATFORM usando o tema :THEME + +not_found: + statement: Desculpe, a página não foi encontrada. + +notification: + update_found: Uma nova versão do conteúdo está disponível. + update: atualização + +# ----- Posts related labels ----- + +post: + written_by: Por + posted: Postado em + updated: Atualizado + words: palavras + pageview_measure: visualizações + read_time: + unit: min + prompt: " de leitura" + relate_posts: Leia também + share: Compartilhar + button: + next: Próximo + previous: Anterior + copy_code: + succeed: Copiado! + share_link: + title: Copie o link + succeed: Link copiado com sucesso! + +# categories page +categories: + category_measure: categorias + post_measure: posts diff --git a/_data/locales/ru-RU.yml b/_data/locales/ru-RU.yml new file mode 100644 index 0000000..868ba95 --- /dev/null +++ b/_data/locales/ru-RU.yml @@ -0,0 +1,87 @@ +# The layout text of site + +# ----- Commons label ----- + +layout: + post: Пост + category: Категория + tag: Тег + +# The tabs of sidebar +tabs: + # format: : + home: Главная + categories: Категории + tags: Теги + archives: Архив + about: О сайте + +# the text displayed in the search bar & search results +search: + hint: поиск + cancel: Отмена + no_results: Упс! Ничего не найдено. + +panel: + lastmod: Недавно обновлено + trending_tags: Популярные теги + toc: Содержание + +copyright: + # Shown at the bottom of the post + license: + template: Авторский пост защищен лицензией :LICENSE_NAME. + name: CC BY 4.0 + link: https://creativecommons.org/licenses/by/4.0/ + + # Displayed in the footer + brief: Некоторые права защищены. + verbose: >- + Если не указано иное, авторские посты на этом сайте защищены лицензией Creative Commons Attribution 4.0 International (CC BY 4.0). + +meta: Использует тему :THEME для :PLATFORM + +not_found: + statement: Извините, мы перепутали URL-адрес или он указывает на что-то несуществующее. + +notification: + update_found: Доступна новая версия контента. + update: Обновить + +# ----- Posts related labels ----- + +post: + written_by: Автор + posted: Опубликовано + updated: Обновлено + words: слов + pageview_measure: просмотров + read_time: + unit: мин. + prompt: чтения + relate_posts: Похожие посты + share: Поделиться + button: + next: Следующий пост + previous: Предыдущий пост + copy_code: + succeed: Скопировано! + share_link: + title: Скопировать ссылку + succeed: Ссылка успешно скопирована! + +# Date time format. +# See: , +df: + post: + strftime: "%d.%m.%Y" + dayjs: "DD.MM.YYYY" + +# categories page +categories: + category_measure: + singular: категория + plural: категории + post_measure: + singular: пост + plural: посты diff --git a/_data/locales/sl-SI.yml b/_data/locales/sl-SI.yml new file mode 100644 index 0000000..4d9434d --- /dev/null +++ b/_data/locales/sl-SI.yml @@ -0,0 +1,91 @@ +# The layout text of site + +# ----- Commons label ----- + +layout: + post: Objava #Post + category: Kategorija #Category + tag: Oznaka #Tag + +# The tabs of sidebar +tabs: + # format: : + home: Domov #Home + categories: Kategorije #Categories + tags: Oznake #Tags + archives: Arhiv #Archives + about: O meni #About + +# the text displayed in the search bar & search results +search: + hint: išči #search + cancel: Prekliči #Cancel + no_results: Ups! Vsebina ni bila najdena #Oops! No results found. + +panel: + lastmod: Nedavno Posodobljeno #Recently Updated + trending_tags: Priljubljene Oznake #Trending Tags + toc: Vsebina #Contents + +copyright: + # Shown at the bottom of the post + license: + template: Ta objava je licencirana pod :LICENCE_NAME s strani avtorja. #This post is licensed under :LICENSE_NAME by the author. + name: CC BY 4.0 + link: https://creativecommons.org/licenses/by/4.0/ + + # Displayed in the footer + brief: Nekatere pravice pridržane. #Some rights reserved. + verbose: >- + Razen kjer navedeno drugače, vse objave spletnega dnevnika so licencirane + pod Creative Commons Attribution 4.0 International (CC BY 4.0) s strani avtorja. + +meta: Uporabljena :PLATFORM tema :THEME #Using the :PLATFORM theme :THEME + +not_found: + statement: Oprostite, hiperpovezava je neustrezna ali vsebina ne obstajata. #Sorry, we've misplaced that URL or it's pointing to something that doesn't exist. + +notification: + update_found: Novejša različica vsebine je na voljo. #A new version of content is available. + update: Posodobi #Update + +# ----- Posts related labels ----- + +post: + written_by: Od #By + posted: Objavljeno #Posted + updated: Posodobljeno #Updated + words: besede #words + pageview_measure: ogledi #views + read_time: + unit: min + prompt: beri #read + relate_posts: Nadaljnje branje #Further Reading + share: Deli #Share + button: + next: Novejše #Newer + previous: Starejše #Older + copy_code: + succeed: Kopirano! #Copied! + share_link: + title: Kopiraj povezavo #Copy link + succeed: Povezava uspešno kopirana! #Link copied successfully! + +# Date time format. +# See: , +df: + post: + strftime: "%e %b, %Y" + dayjs: "ll" + archives: + strftime: "%b" + dayjs: "MMM" + +# categories page +categories: + category_measure: + singular: kategorija #category + plural: kategorije #categories + post_measure: + singular: objava #post + plural: objave #posts diff --git a/_data/locales/sv-SE.yml b/_data/locales/sv-SE.yml new file mode 100644 index 0000000..decb59c --- /dev/null +++ b/_data/locales/sv-SE.yml @@ -0,0 +1,91 @@ +# The layout text of site + +# ----- Commons label ----- + +layout: + post: Inlägg #Post + category: Kategori #Category + tag: Tagga #Tag + +# The tabs of sidebar +tabs: + # format: : + home: Hem #Home + categories: Kategorier #Categories + tags: Taggar #Tags + archives: Arkiv #Archives + about: Om #About + +# the text displayed in the search bar & search results +search: + hint: sök + cancel: Avbryt + no_results: Hoppsan! Hittade inga sökträffar. + +panel: + lastmod: Senast uppdaterad + trending_tags: Trendande taggar + toc: Innehåll + +copyright: + # Shown at the bottom of the post + license: + template: Den här posten är publicerad under licensen :LICENSE_NAME av författaren. + name: CC BY 4.0 + link: https://creativecommons.org/licenses/by/4.0/ + + # Displayed in the footer + brief: Vissa rättigheter är reserverade. + verbose: >- + Om inte annat anges är blogginläggen på denna webbplats licensierade + under Creative Commons Attribution 4.0 International (CC BY 4.0) av författaren. + +meta: Byggd med :PLATFORM och temat :THEME + +not_found: + statement: Ursäkta, vi har tappat bort den här webbadressen eller så pekar den på något som inte längre finns. + +notification: + update_found: Det finns en ny version av innehållet. + update: Uppdatera sidan + +# ----- Posts related labels ----- + +post: + written_by: Av + posted: Postad + updated: Uppdaterad + words: ord + pageview_measure: visningar + read_time: + unit: min + prompt: läsning + relate_posts: Mer läsning + share: Dela + button: + next: Nyare + previous: Äldre + copy_code: + succeed: Kopierat! + share_link: + title: Kopiera länk + succeed: Länken har kopierats! + +# Date time format. +# See: , +df: + post: + strftime: "%b %e, %Y" + dayjs: "ll" + archives: + strftime: "%b" + dayjs: "MMM" + +# categories page +categories: + category_measure: + singular: kategori + plural: kategorier + post_measure: + singular: inlägg + plural: inlägg diff --git a/_data/locales/th.yml b/_data/locales/th.yml new file mode 100644 index 0000000..a3f41a0 --- /dev/null +++ b/_data/locales/th.yml @@ -0,0 +1,91 @@ +# The layout text of site + +# ----- Commons label ----- + +layout: + post: โพสต์ + category: หมวดหมู่ + tag: แท็ก + +# The tabs of sidebar +tabs: + # format: : + home: หน้าแรก + categories: หมวดหมู่ + tags: แท็ก + archives: คลังเก็บ + about: เกี่ยวกับ + +# the text displayed in the search bar & search results +search: + hint: ค้นหา + cancel: ยกเลิก + no_results: โอ๊ะ! ไม่พบผลลัพธ์ + +panel: + lastmod: อัปเดตล่าสุด + trending_tags: แท็กยอดนิยม + toc: เนื้อหา + +copyright: + # Shown at the bottom of the post + license: + template: โพสต์นี้อยู่ภายใต้การอนุญาต :LICENSE_NAME โดยผู้เขียน + name: CC BY 4.0 + link: https://creativecommons.org/licenses/by/4.0/ + + # Displayed in the footer + brief: สงวนลิขสิทธิ์เป็นบางส่วน + verbose: >- + เว้นแต่ว่าจะระบุเป็นอย่างอื่น โพสต์บนเว็บไซต์นี้อยู่ภายใต้ + สัญญาอนุญาตครีเอทีฟคอมมอนส์แบบ 4.0 นานาชาติ (CC BY 4.0) โดยผู้เขียน + +meta: กำลังใช้ธีมของ :PLATFORM ชื่อ :THEME + +not_found: + statement: ขออภัย เราวาง URL นั้นไว้ผิดที่ หรือมันชี้ไปยังสิ่งที่ไม่มีอยู่ + +notification: + update_found: มีเวอร์ชันใหม่ของเนื้อหา + update: อัปเดต + +# ----- Posts related labels ----- + +post: + written_by: โดย + posted: โพสต์เมื่อ + updated: อัปเดตเมื่อ + words: คำ + pageview_measure: ครั้ง + read_time: + unit: นาที + prompt: อ่าน + relate_posts: อ่านต่อ + share: แชร์ + button: + next: ใหม่กว่า + previous: เก่ากว่า + copy_code: + succeed: คัดลอกแล้ว! + share_link: + title: คัดลอกลิงก์ + succeed: คัดลอกลิงก์เรียบร้อยแล้ว! + +# Date time format. +# See: , +df: + post: + strftime: "%b %e, %Y" + dayjs: "ll" + archives: + strftime: "%b" + dayjs: "MMM" + +# categories page +categories: + category_measure: + singular: หมวดหมู่ + plural: หมวดหมู่ + post_measure: + singular: โพสต์ + plural: โพสต์ diff --git a/_data/locales/tr-TR.yml b/_data/locales/tr-TR.yml new file mode 100644 index 0000000..768f57c --- /dev/null +++ b/_data/locales/tr-TR.yml @@ -0,0 +1,77 @@ +# The layout text of site + +# ----- Commons label ----- + +layout: + post: Gönderi + category: Kategori + tag: Etiket + +# The tabs of sidebar +tabs: + # format: : + home: Ana Sayfa + categories: Kategoriler + tags: Etiketler + archives: Arşiv + about: Hakkında + +# the text displayed in the search bar & search results +search: + hint: Ara... + cancel: İptal + no_results: Hop! Öyle bir şey bulamadım. + +panel: + lastmod: Son Güncellenenler + trending_tags: Yükselen Etiketler + toc: İçindekiler + +copyright: + # Shown at the bottom of the post + license: + template: Bu gönderi :LICENSE_NAME lisansı altındadır. + name: CC BY 4.0 + link: https://creativecommons.org/licenses/by/4.0/deed.tr + + # Displayed in the footer + brief: Bazı hakları saklıdır. + verbose: >- + Aksi belirtilmediği sürece, bu sitedeki gönderiler Creative Commons Atıf 4.0 Uluslararası (CC BY 4.0) Lisansı altındadır. + Kısaca sayfa linkini vererek değiştirebilir / paylaşabilirsiniz. + +meta: :PLATFORM ve :THEME teması + +not_found: + statement: Üzgünüz, bu linki yanlış yerleştirdik veya var olmayan bir şeye işaret ediyor. + +notification: + update_found: İçeriğin yeni bir sürümü mevcut. + update: Güncelle + +# ----- Posts related labels ----- + +post: + written_by: Yazan + posted: Gönderim + updated: Güncelleme + words: sözcük + pageview_measure: görüntülenme + read_time: + unit: dakikada + prompt: okunabilir + relate_posts: Benzer Gönderiler + share: Paylaş + button: + next: İleri + previous: Geri + copy_code: + succeed: Kopyalandı. + share_link: + title: Linki kopyala + succeed: Link kopyalandı. + +# categories page +categories: + category_measure: kategori + post_measure: gönderi diff --git a/_data/locales/uk-UA.yml b/_data/locales/uk-UA.yml new file mode 100644 index 0000000..8fef52e --- /dev/null +++ b/_data/locales/uk-UA.yml @@ -0,0 +1,77 @@ +# The layout text of site + +# ----- Commons label ----- + +layout: + post: Публікація + category: Категорія + tag: Тег + +# The tabs of sidebar +tabs: + # format: : + home: Домашня сторінка + categories: Категорії + tags: Теги + archives: Архів + about: Про сайт + +# the text displayed in the search bar & search results +search: + hint: пошук + cancel: Скасувати + no_results: Ох! Нічого не знайдено. + +panel: + lastmod: Нещодавно оновлено + trending_tags: Популярні теги + toc: Зміст + +copyright: + # Shown at the bottom of the post + license: + template: Публікація захищена ліцензією :LICENSE_NAME. + name: CC BY 4.0 + link: https://creativecommons.org/licenses/by/4.0/ + + # Displayed in the footer + brief: Деякі права захищено. + verbose: >- + Публікації на сайті захищено ліцензією Creative Commons Attribution 4.0 International (CC BY 4.0), + якщо інше не вказано в тексті. + +meta: Powered by :PLATFORM with :THEME theme + +not_found: + statement: Вибачте, це посилання вказує на ресурс, що не існує. + +notification: + update_found: Доступна нова версія вмісту. + update: Оновлення + +# ----- Posts related labels ----- + +post: + written_by: Автор + posted: Час публікації + updated: Оновлено + words: слів + pageview_measure: переглядів + read_time: + unit: хвилин + prompt: читання + relate_posts: Вас також може зацікавити + share: Поділитися + button: + next: Попередня публікація + previous: Наступна публікація + copy_code: + succeed: Успішно скопійовано! + share_link: + title: Скопіювати посилання + succeed: Посилання успішно скопійовано! + +# categories page +categories: + category_measure: категорії + post_measure: публікації diff --git a/_data/locales/vi-VN.yml b/_data/locales/vi-VN.yml new file mode 100644 index 0000000..6c2ceff --- /dev/null +++ b/_data/locales/vi-VN.yml @@ -0,0 +1,76 @@ +# The layout text of site + +# ----- Commons label ----- + +layout: + post: Bài viết + category: Danh mục + tag: Thẻ + +# The tabs of sidebar +tabs: + # format: : + home: Trang chủ + categories: Các danh mục + tags: Các thẻ + archives: Lưu trữ + about: Giới thiệu + +# the text displayed in the search bar & search results +search: + hint: tìm kiếm + cancel: Hủy + no_results: Không có kết quả tìm kiếm. + +panel: + lastmod: Mới cập nhật + trending_tags: Các thẻ thịnh hành + toc: Mục lục + +copyright: + # Shown at the bottom of the post + license: + template: Bài viết này được cấp phép bởi tác giả theo giấy phép :LICENSE_NAME. + name: CC BY 4.0 + link: https://creativecommons.org/licenses/by/4.0/ + + # Displayed in the footer + brief: Một số quyền được bảo lưu. + verbose: >- + Trừ khi có ghi chú khác, các bài viết đăng trên trang này được cấp phép bởi tác giả theo giấy phép Creative Commons Attribution 4.0 International (CC BY 4.0). + +meta: Trang web này được tạo bởi :PLATFORM với chủ đề :THEME + +not_found: + statement: Xin lỗi, chúng tôi đã đặt nhầm URL hoặc đường dẫn trỏ đến một trang nào đó không tồn tại. + +notification: + update_found: Đã có phiên bản mới của nội dung. + update: Cập nhật + +# ----- Posts related labels ----- + +post: + written_by: Viết bởi + posted: Đăng lúc + updated: Cập nhật lúc + words: từ + pageview_measure: lượt xem + read_time: + unit: phút + prompt: đọc + relate_posts: Bài viết liên quan + share: Chia sẻ + button: + next: Mới hơn + previous: Cũ hơn + copy_code: + succeed: Đã sao chép! + share_link: + title: Sao chép đường dẫn + succeed: Đã sao chép đường dẫn thành công! + +# categories page +categories: + category_measure: danh mục + post_measure: bài viết diff --git a/_data/locales/zh-CN.yml b/_data/locales/zh-CN.yml new file mode 100644 index 0000000..5c13410 --- /dev/null +++ b/_data/locales/zh-CN.yml @@ -0,0 +1,83 @@ +# The layout text of site + +# ----- Commons label ----- + +layout: + post: 文章 + category: 分类 + tag: 标签 + +# The tabs of sidebar +tabs: + # format: : + home: 首页 + categories: 分类 + tags: 标签 + archives: 归档 + about: 关于 + +# the text displayed in the search bar & search results +search: + hint: 搜索 + cancel: 取消 + no_results: 搜索结果为空 + +panel: + lastmod: 最近更新 + trending_tags: 热门标签 + toc: 文章内容 + +copyright: + # Shown at the bottom of the post + license: + template: 本文由作者按照 :LICENSE_NAME 进行授权 + name: CC BY 4.0 + link: https://creativecommons.org/licenses/by/4.0/ + + # Displayed in the footer + brief: 保留部分权利。 + verbose: >- + 除非另有说明,本网站上的博客文章均由作者按照知识共享署名 4.0 国际 (CC BY 4.0) 许可协议进行授权。 + +meta: 本站采用 :PLATFORM 主题 :THEME + +not_found: + statement: 抱歉,我们放错了该 URL,或者它指向了不存在的内容。 + +notification: + update_found: 发现新版本的内容。 + update: 更新 + +# ----- Posts related labels ----- + +post: + written_by: 作者 + posted: 发表于 + updated: 更新于 + words: 字 + pageview_measure: 次浏览 + read_time: + unit: 分钟 + prompt: 阅读 + relate_posts: 相关文章 + share: 分享 + button: + next: 下一篇 + previous: 上一篇 + copy_code: + succeed: 已复制! + share_link: + title: 分享链接 + succeed: 链接已复制! + +# Date time format. +# See: , +df: + post: + strftime: "%Y/%m/%d" + dayjs: "YYYY/MM/DD" + +# categories page +categories: + category_measure: 个分类 + post_measure: 篇文章 diff --git a/_data/locales/zh-TW.yml b/_data/locales/zh-TW.yml new file mode 100644 index 0000000..33a4330 --- /dev/null +++ b/_data/locales/zh-TW.yml @@ -0,0 +1,83 @@ +# The layout text of site + +# ----- Commons label ----- + +layout: + post: 文章 + category: 分類 + tag: 標籤 + +# The tabs of sidebar +tabs: + # format: : + home: 首頁 + categories: 分類 + tags: 標籤 + archives: 封存 + about: 關於 + +# the text displayed in the search bar & search results +search: + hint: 搜尋 + cancel: 取消 + no_results: 沒有搜尋結果 + +panel: + lastmod: 最近更新 + trending_tags: 熱門標籤 + toc: 文章摘要 + +copyright: + # Shown at the bottom of the post + license: + template: 本文章以 :LICENSE_NAME 授權 + name: CC BY 4.0 + link: https://creativecommons.org/licenses/by/4.0/ + + # Displayed in the footer + brief: 保留部份權利。 + verbose: >- + 除非另有說明,否則本網誌的文章均由作者按照姓名標示 4.0 國際 (CC BY 4.0) 授權條款進行授權。 + +meta: 本網站使用 :PLATFORM 產生,採用 :THEME 主題 + +not_found: + statement: 抱歉,您可能正在存取一個已被移動的 URL,或者它從未存在。 + +notification: + update_found: 發現新版本更新。 + update: 更新 + +# ----- Posts related labels ----- + +post: + written_by: 作者 + posted: 發布於 + updated: 更新於 + words: 字 + pageview_measure: 次瀏覽 + read_time: + unit: 分鐘 + prompt: 閱讀 + relate_posts: 相關文章 + share: 分享 + button: + next: 下一篇 + previous: 上一篇 + copy_code: + succeed: 已複製! + share_link: + title: 分享連結 + succeed: 已複製連結! + +# Date time format. +# See: , +df: + post: + strftime: "%Y/%m/%d" + dayjs: "YYYY/MM/DD" + +# categories page +categories: + category_measure: 個分類 + post_measure: 篇文章 diff --git a/_data/media.yml b/_data/media.yml new file mode 100644 index 0000000..9cd69b4 --- /dev/null +++ b/_data/media.yml @@ -0,0 +1,18 @@ +- extension: mp3 + mime_type: mpeg +- extension: mov + mime_type: quicktime +- extension: avi + mime_type: x-msvideo +- extension: mkv + mime_type: x-matroska +- extension: ogv + mime_type: ogg +- extension: weba + mime_type: webm +- extension: 3gp + mime_type: 3gpp +- extension: 3g2 + mime_type: 3gpp2 +- extension: mid + mime_type: midi diff --git a/_data/origin/basic.yml b/_data/origin/basic.yml new file mode 100644 index 0000000..2d52982 --- /dev/null +++ b/_data/origin/basic.yml @@ -0,0 +1,39 @@ +# fonts + +webfonts: /assets/lib/fonts/main.css + +# Libraries + +toc: + css: /assets/lib/tocbot/tocbot.min.css + js: /assets/lib/tocbot/tocbot.min.js + +fontawesome: + css: /assets/lib/fontawesome-free/css/all.min.css + +search: + js: /assets/lib/simple-jekyll-search/simple-jekyll-search.min.js + +mermaid: + js: /assets/lib/mermaid/mermaid.min.js + +dayjs: + js: + common: /assets/lib/dayjs/dayjs.min.js + locale: /assets/lib/dayjs/locale/en.js + relativeTime: /assets/lib/dayjs/plugin/relativeTime.js + localizedFormat: /assets/lib/dayjs/plugin/localizedFormat.js + +glightbox: + css: /assets/lib/glightbox/glightbox.min.css + js: /assets/lib/glightbox/glightbox.min.js + +lazy-polyfill: + css: /assets/lib/loading-attribute-polyfill/loading-attribute-polyfill.min.css + js: /assets/lib/loading-attribute-polyfill/loading-attribute-polyfill.umd.min.js + +clipboard: + js: /assets/lib/clipboard/clipboard.min.js + +mathjax: + js: /assets/lib/mathjax/tex-chtml.js diff --git a/_data/origin/cors.yml b/_data/origin/cors.yml new file mode 100644 index 0000000..afdb3d9 --- /dev/null +++ b/_data/origin/cors.yml @@ -0,0 +1,54 @@ +# Resource Hints +resource_hints: + - url: https://fonts.googleapis.com + links: + - rel: preconnect + - rel: dns-prefetch + - url: https://fonts.gstatic.com + links: + - rel: preconnect + opts: [crossorigin] + - rel: dns-prefetch + - url: https://cdn.jsdelivr.net + links: + - rel: preconnect + - rel: dns-prefetch + +# Web Fonts +webfonts: https://fonts.googleapis.com/css2?family=Lato:wght@300;400&family=Source+Sans+Pro:wght@400;600;700;900&display=swap + +# Libraries + +toc: + css: https://cdn.jsdelivr.net/npm/tocbot@4.29.0/dist/tocbot.min.css + js: https://cdn.jsdelivr.net/npm/tocbot@4.29.0/dist/tocbot.min.js + +fontawesome: + css: https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.6.0/css/all.min.css + +search: + js: https://cdn.jsdelivr.net/npm/simple-jekyll-search@1.10.0/dest/simple-jekyll-search.min.js + +mermaid: + js: https://cdn.jsdelivr.net/npm/mermaid@11.0.2/dist/mermaid.min.js + +dayjs: + js: + common: https://cdn.jsdelivr.net/npm/dayjs@1.11.13/dayjs.min.js + locale: https://cdn.jsdelivr.net/npm/dayjs@1.11.13/locale/:LOCALE.js + relativeTime: https://cdn.jsdelivr.net/npm/dayjs@1.11.13/plugin/relativeTime.js + localizedFormat: https://cdn.jsdelivr.net/npm/dayjs@1.11.13/plugin/localizedFormat.js + +glightbox: + css: https://cdn.jsdelivr.net/npm/glightbox@3.3.0/dist/css/glightbox.min.css + js: https://cdn.jsdelivr.net/npm/glightbox@3.3.0/dist/js/glightbox.min.js + +lazy-polyfill: + css: https://cdn.jsdelivr.net/npm/loading-attribute-polyfill@2.1.1/dist/loading-attribute-polyfill.min.css + js: https://cdn.jsdelivr.net/npm/loading-attribute-polyfill@2.1.1/dist/loading-attribute-polyfill.umd.min.js + +clipboard: + js: https://cdn.jsdelivr.net/npm/clipboard@2.0.11/dist/clipboard.min.js + +mathjax: + js: https://cdn.jsdelivr.net/npm/mathjax@3.2.2/es5/tex-chtml.js diff --git a/_data/share.yml b/_data/share.yml new file mode 100644 index 0000000..98649fd --- /dev/null +++ b/_data/share.yml @@ -0,0 +1,50 @@ +# Sharing options at the bottom of the post. +# Icons from + +platforms: [] + # - type: Twitter + # icon: "fa-brands fa-square-x-twitter" + # link: "https://twitter.com/intent/tweet?text=TITLE&url=URL" + + # - type: Facebook + # icon: "fab fa-facebook-square" + # link: "https://www.facebook.com/sharer/sharer.php?title=TITLE&u=URL" + + # - type: Telegram + # icon: "fab fa-telegram" + # link: "https://t.me/share/url?url=URL&text=TITLE" + + # Uncomment below if you need to. + # + # - type: Linkedin + # icon: "fab fa-linkedin" + # link: "https://www.linkedin.com/sharing/share-offsite/?url=URL" + # + # - type: Weibo + # icon: "fab fa-weibo" + # link: "https://service.weibo.com/share/share.php?title=TITLE&url=URL" + # + # - type: Mastodon + # icon: "fa-brands fa-mastodon" + # # See: https://github.com/justinribeiro/share-to-mastodon#properties + # instances: + # - label: mastodon.social + # link: "https://mastodon.social/" + # - label: mastodon.online + # link: "https://mastodon.online/" + # - label: fosstodon.org + # link: "https://fosstodon.org/" + # - label: photog.social + # link: "https://photog.social/" + # + # - type: Bluesky + # icon: "fa-brands fa-bluesky" + # link: "https://bsky.app/intent/compose?text=TITLE%20URL" + # + # - type: Reddit + # icon: "fa-brands fa-square-reddit" + # link: "https://www.reddit.com/submit?url=URL&title=TITLE" + # + # - type: Threads + # icon: "fa-brands fa-square-threads" + # link: "https://www.threads.net/intent/post?text=TITLE%20URL" diff --git a/_includes/analytics/cloudflare.html b/_includes/analytics/cloudflare.html new file mode 100644 index 0000000..1eeb1a9 --- /dev/null +++ b/_includes/analytics/cloudflare.html @@ -0,0 +1,7 @@ + + + diff --git a/_includes/analytics/fathom.html b/_includes/analytics/fathom.html new file mode 100644 index 0000000..4b603d3 --- /dev/null +++ b/_includes/analytics/fathom.html @@ -0,0 +1,7 @@ + + + diff --git a/_includes/analytics/goatcounter.html b/_includes/analytics/goatcounter.html new file mode 100644 index 0000000..3867fdb --- /dev/null +++ b/_includes/analytics/goatcounter.html @@ -0,0 +1,6 @@ + + diff --git a/_includes/analytics/google.html b/_includes/analytics/google.html new file mode 100644 index 0000000..d0aac65 --- /dev/null +++ b/_includes/analytics/google.html @@ -0,0 +1,13 @@ + + + diff --git a/_includes/analytics/matomo.html b/_includes/analytics/matomo.html new file mode 100644 index 0000000..72b2c46 --- /dev/null +++ b/_includes/analytics/matomo.html @@ -0,0 +1,14 @@ + + + diff --git a/_includes/analytics/umami.html b/_includes/analytics/umami.html new file mode 100644 index 0000000..bfcb1d0 --- /dev/null +++ b/_includes/analytics/umami.html @@ -0,0 +1,6 @@ + + diff --git a/_includes/comments.html b/_includes/comments.html new file mode 100644 index 0000000..fef135f --- /dev/null +++ b/_includes/comments.html @@ -0,0 +1,5 @@ + +{% if page.comments and site.comments.provider %} + {% capture path %}comments/{{ site.comments.provider }}.html{% endcapture %} + {% include {{ path }} %} +{% endif %} diff --git a/_includes/comments/disqus.html b/_includes/comments/disqus.html new file mode 100644 index 0000000..2b889a4 --- /dev/null +++ b/_includes/comments/disqus.html @@ -0,0 +1,50 @@ + + +
+

Comments powered by Disqus.

+
+ + diff --git a/_includes/comments/giscus.html b/_includes/comments/giscus.html new file mode 100644 index 0000000..f9becfe --- /dev/null +++ b/_includes/comments/giscus.html @@ -0,0 +1,71 @@ + + diff --git a/_includes/comments/utterances.html b/_includes/comments/utterances.html new file mode 100644 index 0000000..5dd78ed --- /dev/null +++ b/_includes/comments/utterances.html @@ -0,0 +1,49 @@ + + + + diff --git a/_includes/datetime.html b/_includes/datetime.html new file mode 100644 index 0000000..9f954b6 --- /dev/null +++ b/_includes/datetime.html @@ -0,0 +1,20 @@ + + +{% assign df_strftime = site.data.locales[include.lang].df.post.strftime | default: '%d/%m/%Y' %} +{% assign df_dayjs = site.data.locales[include.lang].df.post.dayjs | default: 'DD/MM/YYYY' %} + + diff --git a/_includes/embed/audio.html b/_includes/embed/audio.html new file mode 100644 index 0000000..cf928a7 --- /dev/null +++ b/_includes/embed/audio.html @@ -0,0 +1,35 @@ +{% assign src = include.src | strip %} +{% assign title = include.title | strip %} +{% assign types = include.types | default: '' | strip | split: '|' %} + +{% unless src contains '://' %} + {%- capture src -%} + {% include media-url.html src=src subpath=page.media_subpath %} + {%- endcapture -%} +{% endunless %} + +

+ + {% if title %} + {{ title }} + {% endif %} +

diff --git a/_includes/embed/bilibili.html b/_includes/embed/bilibili.html new file mode 100644 index 0000000..0aa5552 --- /dev/null +++ b/_includes/embed/bilibili.html @@ -0,0 +1,9 @@ + diff --git a/_includes/embed/twitch.html b/_includes/embed/twitch.html new file mode 100644 index 0000000..ed5ec83 --- /dev/null +++ b/_includes/embed/twitch.html @@ -0,0 +1,8 @@ + diff --git a/_includes/embed/video.html b/_includes/embed/video.html new file mode 100644 index 0000000..9b6918f --- /dev/null +++ b/_includes/embed/video.html @@ -0,0 +1,59 @@ +{% assign video_url = include.src %} +{% assign title = include.title %} +{% assign poster_url = include.poster %} +{% assign types = include.types | default: '' | strip | split: '|' %} + +{% unless video_url contains '://' %} + {%- capture video_url -%} + {% include media-url.html src=video_url subpath=page.media_subpath %} + {%- endcapture -%} +{% endunless %} + +{% if poster_url %} + {% unless poster_url contains '://' %} + {%- capture poster_url -%} + {% include media-url.html src=poster_url subpath=page.media_subpath %} + {%- endcapture -%} + {% endunless %} + {% assign poster = 'poster="' | append: poster_url | append: '"' %} +{% endif %} + +{% assign attributes = 'controls' %} + +{% if include.autoplay %} + {% assign attributes = attributes | append: ' ' | append: 'autoplay' %} +{% endif %} + +{% if include.loop %} + {% assign attributes = attributes | append: ' ' | append: 'loop' %} +{% endif %} + +{% if include.muted %} + {% assign attributes = attributes | append: ' ' | append: 'muted' %} +{% endif %} + +

+ + {% if title %} + {{ title }} + {% endif %} +

diff --git a/_includes/embed/youtube.html b/_includes/embed/youtube.html new file mode 100644 index 0000000..8f08002 --- /dev/null +++ b/_includes/embed/youtube.html @@ -0,0 +1,9 @@ + diff --git a/_includes/favicons.html b/_includes/favicons.html new file mode 100644 index 0000000..957c933 --- /dev/null +++ b/_includes/favicons.html @@ -0,0 +1,19 @@ + + +{% capture favicon_path %}{{ '/assets/img/favicons' | relative_url }}{% endcapture %} + + + + +{% if site.pwa.enabled %} + +{% endif %} + + + + + + diff --git a/_includes/footer.html b/_includes/footer.html new file mode 100644 index 0000000..1ba9b63 --- /dev/null +++ b/_includes/footer.html @@ -0,0 +1,49 @@ + + +
+

+ {{- '©' }} + + + {% if site.social.links %} + {{ site.social.name }}. + {% else %} + {{ site.social.name }}. + {% endif %} + + {% if site.data.locales[include.lang].copyright.brief %} + + {{- site.data.locales[include.lang].copyright.brief -}} + + {% endif %} +

+ +

+ {%- capture _platform -%} + Jekyll + {%- endcapture -%} + + {%- capture _theme -%} + Chirpy + {%- endcapture -%} + + {{ site.data.locales[include.lang].meta | replace: ':PLATFORM', _platform | replace: ':THEME', _theme }} +

+
diff --git a/_includes/head.html b/_includes/head.html new file mode 100644 index 0000000..af3acdb --- /dev/null +++ b/_includes/head.html @@ -0,0 +1,107 @@ + + + + + + + + + {%- capture seo_tags -%} + {% seo title=false %} + {%- endcapture -%} + + + + {% if page.image %} + {% assign src = page.image.path | default: page.image %} + + {% unless src contains '://' %} + {%- capture img_url -%} + {% include media-url.html src=src subpath=page.media_subpath absolute=true %} + {%- endcapture -%} + + {%- capture old_url -%}{{ src | absolute_url }}{%- endcapture -%} + {%- capture new_url -%}{{ img_url }}{%- endcapture -%} + + {% assign seo_tags = seo_tags | replace: old_url, new_url %} + {% endunless %} + + {% elsif site.social_preview_image %} + {%- capture img_url -%} + {% include media-url.html src=site.social_preview_image absolute=true %} + {%- endcapture -%} + + {%- capture og_image -%} + + {%- endcapture -%} + + {%- capture twitter_image -%} + + + {%- endcapture -%} + + {% assign old_meta_clip = '' %} + {% assign new_meta_clip = og_image | append: twitter_image %} + {% assign seo_tags = seo_tags | replace: old_meta_clip, new_meta_clip %} + {% endif %} + + {{ seo_tags }} + + + {%- unless page.layout == 'home' -%} + {{ page.title | append: ' | ' }} + {%- endunless -%} + {{ site.title }} + + + {% include_cached favicons.html %} + + + {% unless site.assets.self_host.enabled %} + {% for hint in site.data.origin.cors.resource_hints %} + {% for link in hint.links %} + + {% endfor %} + {% endfor %} + {% endunless %} + + + {% unless jekyll.environment == 'production' %} + + {% endunless %} + + + + + + + + + + + + + {% if site.toc and page.toc %} + + {% endif %} + + {% if page.layout == 'post' or page.layout == 'page' or page.layout == 'home' %} + + {% endif %} + + {% if page.layout == 'page' or page.layout == 'post' %} + + + {% endif %} + + + + {% unless site.theme_mode %} + {% include mode-toggle.html %} + {% endunless %} + + {% include metadata-hook.html %} + diff --git a/_includes/js-selector.html b/_includes/js-selector.html new file mode 100644 index 0000000..4d77d06 --- /dev/null +++ b/_includes/js-selector.html @@ -0,0 +1,109 @@ + + + + +{% assign urls = site.data.origin[type].search.js %} + + + +{% if page.layout == 'post' or page.layout == 'page' or page.layout == 'home' %} + {% assign urls = urls | append: ',' | append: site.data.origin[type]['lazy-polyfill'].js %} + + {% unless page.layout == 'home' %} + + {% assign urls = urls + | append: ',' + | append: site.data.origin[type].glightbox.js + | append: ',' + | append: site.data.origin[type].clipboard.js + %} + {% endunless %} +{% endif %} + +{% if page.layout == 'home' + or page.layout == 'post' + or page.layout == 'archives' + or page.layout == 'category' + or page.layout == 'tag' +%} + {% assign locale = include.lang | split: '-' | first %} + + {% assign urls = urls + | append: ',' + | append: site.data.origin[type].dayjs.js.common + | append: ',' + | append: site.data.origin[type].dayjs.js.locale + | replace: ':LOCALE', locale + | append: ',' + | append: site.data.origin[type].dayjs.js.relativeTime + | append: ',' + | append: site.data.origin[type].dayjs.js.localizedFormat + %} +{% endif %} + +{% if page.content contains ' + +{% if page.math %} + + + + +{% endif %} + + +{% if page.layout == 'post' %} + {% assign provider = site.pageviews.provider %} + + {% if provider and provider != empty %} + {% case provider %} + {% when 'goatcounter' %} + {% if site.analytics[provider].id != empty and site.analytics[provider].id %} + {% include pageviews/{{ provider }}.html %} + {% endif %} + {% endcase %} + {% endif %} +{% endif %} + +{% if page.mermaid %} + {% include mermaid.html %} +{% endif %} + +{% if jekyll.environment == 'production' %} + + {% if site.pwa.enabled %} + + {% endif %} + + + {% 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 %} diff --git a/_includes/jsdelivr-combine.html b/_includes/jsdelivr-combine.html new file mode 100644 index 0000000..cffa699 --- /dev/null +++ b/_includes/jsdelivr-combine.html @@ -0,0 +1,26 @@ +{% assign urls = include.urls | split: ',' %} + +{% assign combined_urls = nil %} + +{% assign domain = 'https://cdn.jsdelivr.net/' %} + +{% for url in urls %} + {% if url contains domain %} + {% assign url_snippet = url | slice: domain.size, url.size %} + + {% if combined_urls %} + {% assign combined_urls = combined_urls | append: ',' | append: url_snippet %} + {% else %} + {% assign combined_urls = domain | append: 'combine/' | append: url_snippet %} + {% endif %} + + {% elsif url contains '//' %} + + {% else %} + + {% endif %} +{% endfor %} + +{% if combined_urls %} + +{% endif %} diff --git a/_includes/lang.html b/_includes/lang.html new file mode 100644 index 0000000..34b50df --- /dev/null +++ b/_includes/lang.html @@ -0,0 +1,10 @@ +{% comment %} + Detect appearance language and return it through variable "lang" +{% endcomment %} +{% if site.data.locales[page.lang] %} + {% assign lang = page.lang %} +{% elsif site.data.locales[site.lang] %} + {% assign lang = site.lang %} +{% else %} + {% assign lang = 'en' %} +{% endif %} diff --git a/_includes/language-alias.html b/_includes/language-alias.html new file mode 100644 index 0000000..abfa7ba --- /dev/null +++ b/_includes/language-alias.html @@ -0,0 +1,70 @@ +{% comment %} + + Convert the alias of the syntax language to the official name + + See: + +{% endcomment %} + +{% assign _lang = include.language | default: '' %} + +{% case _lang %} + {% when 'actionscript', 'as', 'as3' %} + {{ 'ActionScript' }} + {% when 'applescript' %} + {{ 'AppleScript' }} + {% when 'brightscript', 'bs', 'brs' %} + {{ 'BrightScript' }} + {% when 'cfscript', 'cfc' %} + {{ 'CFScript' }} + {% when 'coffeescript', 'coffee', 'coffee-script' %} + {{ 'CoffeeScript' }} + {% when 'cs', 'csharp' %} + {{ 'C#' }} + {% when 'erl' %} + {{ 'Erlang' }} + {% when 'graphql' %} + {{ 'GraphQL' }} + {% when 'haskell', 'hs' %} + {{ 'Haskell' }} + {% when 'javascript', 'js' %} + {{ 'JavaScript' }} + {% when 'make', 'mf', 'gnumake', 'bsdmake' %} + {{ 'Makefile' }} + {% when 'md', 'mkd' %} + {{ 'Markdown' }} + {% when 'm' %} + {{ 'Matlab' }} + {% when 'objective_c', 'objc', 'obj-c', 'obj_c', 'objectivec' %} + {{ 'Objective-C' }} + {% when 'perl', 'pl' %} + {{ 'Perl' }} + {% when 'php','php3','php4','php5' %} + {{ 'PHP' }} + {% when 'py' %} + {{ 'Python' }} + {% when 'rb' %} + {{ 'Ruby' }} + {% when 'rs','no_run','ignore','should_panic' %} + {{ 'Rust' }} + {% when 'bash', 'zsh', 'ksh', 'sh' %} + {{ 'Shell' }} + {% when 'st', 'squeak' %} + {{ 'Smalltalk' }} + {% when 'tex'%} + {{ 'TeX' }} + {% when 'latex' %} + {{ 'LaTex' }} + {% when 'ts', 'typescript' %} + {{ 'TypeScript' }} + {% when 'vb', 'visualbasic' %} + {{ 'Visual Basic' }} + {% when 'vue', 'vuejs' %} + {{ 'Vue.js' }} + {% when 'yml' %} + {{ 'YAML' }} + {% when 'css', 'html', 'scss', 'ssh', 'toml', 'xml', 'yaml', 'json' %} + {{ _lang | upcase }} + {% else %} + {{ _lang | capitalize }} +{% endcase %} diff --git a/_includes/media-url.html b/_includes/media-url.html new file mode 100644 index 0000000..ea41075 --- /dev/null +++ b/_includes/media-url.html @@ -0,0 +1,37 @@ +{%- comment -%} + Generate media resource final URL based on `site.cdn`, `page.media_subpath` + + Arguments: + src - required, basic media resources path + subpath - optional, relative path of media resources + absolute - optional, boolean, if true, generate absolute URL + + Return: + media resources URL +{%- endcomment -%} + +{% assign url = include.src %} + +{%- if url -%} + {% unless url contains ':' %} + {%- comment -%} Add media resources subpath prefix {%- endcomment -%} + {% assign url = include.subpath | default: '' | append: '/' | append: url %} + + {%- comment -%} Prepend CND URL {%- endcomment -%} + {% if site.cdn %} + {% assign url = site.cdn | append: '/' | append: url %} + {% endif %} + + {% assign url = url | replace: '///', '/' | replace: '//', '/' | replace: ':/', '://' %} + + {% unless url contains '://' %} + {% if include.absolute %} + {% assign url = site.url | append: site.baseurl | append: url %} + {% else %} + {% assign url = site.baseurl | append: url %} + {% endif %} + {% endunless %} + {% endunless %} +{%- endif -%} + +{{- url -}} diff --git a/_includes/mermaid.html b/_includes/mermaid.html new file mode 100644 index 0000000..a3a83ed --- /dev/null +++ b/_includes/mermaid.html @@ -0,0 +1,62 @@ + + diff --git a/_includes/metadata-hook.html b/_includes/metadata-hook.html new file mode 100644 index 0000000..fd7e9bd --- /dev/null +++ b/_includes/metadata-hook.html @@ -0,0 +1 @@ + diff --git a/_includes/mode-toggle.html b/_includes/mode-toggle.html new file mode 100644 index 0000000..113ec37 --- /dev/null +++ b/_includes/mode-toggle.html @@ -0,0 +1,116 @@ + + + diff --git a/_includes/no-linenos.html b/_includes/no-linenos.html new file mode 100644 index 0000000..8500693 --- /dev/null +++ b/_includes/no-linenos.html @@ -0,0 +1,10 @@ +{% comment %} + Remove the line number of the code snippet. +{% endcomment %} + +{% assign content = include.content %} + +{% if content contains '
' %}
+  {% assign content = content | replace: '
', '' %}
+{% endif %}
diff --git a/_includes/notification.html b/_includes/notification.html
new file mode 100644
index 0000000..80049b0
--- /dev/null
+++ b/_includes/notification.html
@@ -0,0 +1,24 @@
+
diff --git a/_includes/origin-type.html b/_includes/origin-type.html
new file mode 100644
index 0000000..7f72012
--- /dev/null
+++ b/_includes/origin-type.html
@@ -0,0 +1,13 @@
+{% comment %} Site static assets origin type {% endcomment %}
+
+{% assign type = 'cors' %}
+
+{% if site.assets.self_host.enabled %}
+  {% if site.assets.self_host.env %}
+    {% if site.assets.self_host.env == jekyll.environment %}
+      {% assign type = 'basic' %}
+    {% endif %}
+  {% else %}
+    {% assign type = 'basic' %}
+  {% endif %}
+{% endif %}
diff --git a/_includes/pageviews/goatcounter.html b/_includes/pageviews/goatcounter.html
new file mode 100644
index 0000000..e62fd69
--- /dev/null
+++ b/_includes/pageviews/goatcounter.html
@@ -0,0 +1,19 @@
+
+
diff --git a/_includes/post-description.html b/_includes/post-description.html
new file mode 100644
index 0000000..6c40036
--- /dev/null
+++ b/_includes/post-description.html
@@ -0,0 +1,16 @@
+{%- comment -%}
+  Get post description or generate it from the post content.
+{%- endcomment -%}
+
+{%- assign max_length = include.max_length | default: 200 -%}
+
+{%- capture description -%}
+{%- if post.description -%}
+  {{- post.description -}}
+{%- else -%}
+  {%- include no-linenos.html content=post.content -%}
+  {{- content | markdownify | strip_html -}}
+{%- endif -%}
+{%- endcapture -%}
+
+{{- description | strip | truncate: max_length | escape -}}
diff --git a/_includes/post-nav.html b/_includes/post-nav.html
new file mode 100644
index 0000000..736bec3
--- /dev/null
+++ b/_includes/post-nav.html
@@ -0,0 +1,34 @@
+
+
+
diff --git a/_includes/post-paginator.html b/_includes/post-paginator.html
new file mode 100644
index 0000000..c74e978
--- /dev/null
+++ b/_includes/post-paginator.html
@@ -0,0 +1,91 @@
+
+
+
+
diff --git a/_includes/post-sharing.html b/_includes/post-sharing.html
new file mode 100644
index 0000000..d894199
--- /dev/null
+++ b/_includes/post-sharing.html
@@ -0,0 +1,52 @@
+
+
+
diff --git a/_includes/read-time.html b/_includes/read-time.html
new file mode 100644
index 0000000..9952410
--- /dev/null
+++ b/_includes/read-time.html
@@ -0,0 +1,37 @@
+
+
+{% assign words = include.content | strip_html | number_of_words: 'auto' %}
+
+
+
+{% assign wpm = 180 %}
+{% assign min_time = 1 %}
+
+{% assign read_time = words | divided_by: wpm %}
+
+{% unless read_time > 0 %}
+  {% assign read_time = min_time %}
+{% endunless %}
+
+{% capture read_prompt %}
+  {{- site.data.locales[include.lang].post.read_time.prompt -}}
+{% endcapture %}
+
+
+
+  
+    {{- read_time -}}
+    {{ ' ' }}
+    {{- site.data.locales[include.lang].post.read_time.unit -}}
+  
+  {%- if include.prompt -%}
+    {%- assign _prompt_words = read_prompt | number_of_words: 'auto' -%}
+    {%- unless _prompt_words > 1 -%}{{ ' ' }}{%- endunless -%}
+    {{ read_prompt }}
+  {%- endif -%}
+
diff --git a/_includes/refactor-content.html b/_includes/refactor-content.html
new file mode 100644
index 0000000..8d298cd
--- /dev/null
+++ b/_includes/refactor-content.html
@@ -0,0 +1,255 @@
+
+
+{% assign _content = include.content %}
+
+
+
+{% if _content contains '', ''
+    | replace: '
', '' + | replace: '
', '
' + %} +{% endif %} + + + +{% if _content contains '
' %}
+  {% assign _content = _content
+    | replace: '
', '' + %} +{% endif %} + + + +{% if _content contains '', + '' + | replace: '', + '' + %} +{% endif %} + + + +{% assign IMG_TAG = '' | first %} + {% assign _right = _img_snippet | remove: _left %} + + {% unless _left contains 'src=' %} + {% continue %} + {% endunless %} + + {% assign _left = _left | remove: ' /' | replace: ' w=', ' width=' | replace: ' h=', ' height=' %} + {% assign _attrs = _left | split: '" ' %} + + {% assign _src = null %} + {% assign _lqip = null %} + {% assign _class = null %} + + {% for _attr in _attrs %} + {% unless _attr contains '=' %} + {% continue %} + {% endunless %} + + {% assign _pair = _attr | split: '="' %} + {% capture _key %}{{ _pair | first }}{% endcapture %} + {% capture _value %}{{ _pair | last | remove: '"' }}{% endcapture %} + + {% case _key %} + {% when 'src' %} + {% assign _src = _value %} + {% when 'lqip' %} + {% assign _lqip = _value %} + {% when 'class' %} + {% assign _class = _value %} + {% endcase %} + {% endfor %} + + + {% if _class %} + {% capture _old_class %}class="{{ _class }}"{% endcapture %} + {% assign _left = _left | remove: _old_class %} + {% endif %} + + {% assign _final_src = null %} + {% assign _lazyload = true %} + + {%- capture _img_url -%} + {% include media-url.html src=_src subpath=page.media_subpath %} + {%- endcapture -%} + + {% assign _path_prefix = _img_url | remove: _src %} + + {% unless _src contains '//' %} + {% assign _final_src = _path_prefix | append: _src %} + {% assign _src_alt = 'src="' | append: _path_prefix %} + {% assign _left = _left | replace: 'src="', _src_alt %} + {% endunless %} + + {% if _lqip %} + {% assign _lazyload = false %} + {% assign _class = _class | append: ' blur' %} + + {% unless _lqip contains 'data:' %} + {% assign _lqip_alt = 'lqip="' | append: _path_prefix %} + {% assign _left = _left | replace: 'lqip="', _lqip_alt %} + {% endunless %} + + + {% assign _left = _left | replace: 'src=', 'data-src=' | replace: ' lqip=', ' data-lqip="true" src=' %} + + {% else %} + {% assign _class = _class | append: ' shimmer' %} + {% endif %} + + + {% if _lazyload %} + {% assign _left = _left | append: ' loading="lazy"' %} + {% endif %} + + {% if page.layout == 'home' %} + + {% assign _wrapper_start = '
' %} + + {% assign _img_content = _img_content | append: _wrapper_start %} + {% assign _right = _right | prepend: '>` is wrapped by `` --> + {% assign _parent = _right | slice: 1, 4 %} + + {% if _parent == '' %} + + {% assign _size = _img_content | size | minus: 1 %} + {% capture _class %} + class="img-link{% unless _lqip %} shimmer{% endunless %}" + {% endcapture %} + {% assign _img_content = _img_content | slice: 0, _size | append: _class | append: '>' %} + + {% else %} + + {% assign _wrapper_start = _final_src + | default: _src + | prepend: '' + %} + + {% assign _img_content = _img_content | append: _wrapper_start %} + {% assign _right = '> + {% assign _img_content = _img_content | append: IMG_TAG | append: _left | append: _right %} + {% endfor %} + + {% if _img_content %} + {% assign _content = _img_content %} + {% endif %} +{% endif %} + + + +{% if _content contains '
' %} + {% assign _code_spippets = _content | split: '
' %} + {% assign _new_content = '' %} + + {% for _snippet in _code_spippets %} + {% if forloop.last %} + {% assign _new_content = _new_content | append: _snippet %} + + {% else %} + {% assign _left = _snippet | split: '><' | last %} + + {% if _left contains 'file="' %} + {% assign _label_text = _left | split: 'file="' | last | split: '"' | first %} + {% assign _label_icon = 'far fa-file-code fa-fw' %} + {% else %} + {% assign _lang = _left | split: 'language-' | last | split: ' ' | first %} + {% capture _label_text %}{% include language-alias.html language=_lang %}{% endcapture %} + {% assign _label_icon = 'fas fa-code fa-fw small' %} + {% endif %} + + {% capture _label %} + + {% endcapture %} + + {% assign _new_content = _new_content + | append: _snippet + | append: '
' + | append: _label + | append: '
' + | append: '
' + %} + {% endif %} + {% endfor %} + + {% assign _content = _new_content %} +{% endif %} + + + +{% assign heading_levels = '2,3,4,5' | split: ',' %} +{% assign _heading_content = _content %} + +{% for level in heading_levels %} + {% assign mark_start = '' + %} + + {% assign left = snippet | split: mark_end | first %} + {% assign right = snippet | slice: left.size, snippet.size %} + {% assign left = left | replace_first: '">', '">' | append: '' %} + + {% assign _new_content = _new_content | append: mark_start | append: left | append: anchor | append: right %} + {% endfor %} + + {% assign _heading_content = _new_content %} + {% endif %} +{% endfor %} + +{% assign _content = _heading_content %} + + +{{ _content }} diff --git a/_includes/related-posts.html b/_includes/related-posts.html new file mode 100644 index 0000000..37a295b --- /dev/null +++ b/_includes/related-posts.html @@ -0,0 +1,94 @@ + + + +{% assign TOTAL_SIZE = 3 %} + + +{% assign TAG_SCORE = 1 %} + + +{% assign CATEGORY_SCORE = 0.5 %} + +{% assign SEPARATOR = ':' %} + +{% assign match_posts = '' | split: '' %} + +{% for category in page.categories %} + {% assign match_posts = match_posts | push: site.categories[category] | uniq %} +{% endfor %} + +{% for tag in page.tags %} + {% assign match_posts = match_posts | push: site.tags[tag] | uniq %} +{% endfor %} + +{% assign match_posts = match_posts | reverse %} +{% assign last_index = match_posts.size | minus: 1 %} +{% assign score_list = '' | split: '' %} + +{% for i in (0..last_index) %} + {% assign post = match_posts[i] %} + + {% if post.url == page.url %} + {% continue %} + {% endif %} + + {% assign score = 0 %} + + {% for tag in post.tags %} + {% if page.tags contains tag %} + {% assign score = score | plus: TAG_SCORE %} + {% endif %} + {% endfor %} + + {% for category in post.categories %} + {% if page.categories contains category %} + {% assign score = score | plus: CATEGORY_SCORE %} + {% endif %} + {% endfor %} + + {% if score > 0 %} + {% capture score_item %}{{ score }}{{ SEPARATOR }}{{ i }}{% endcapture %} + {% assign score_list = score_list | push: score_item %} + {% endif %} +{% endfor %} + +{% assign index_list = '' | split: '' %} + +{% if score_list.size > 0 %} + {% assign score_list = score_list | sort | reverse %} + {% for entry in score_list limit: TOTAL_SIZE %} + {% assign index = entry | split: SEPARATOR | last %} + {% assign index_list = index_list | push: index %} + {% endfor %} +{% endif %} + +{% assign relate_posts = '' | split: '' %} + +{% for index in index_list %} + {% assign i = index | to_integer %} + {% assign relate_posts = relate_posts | push: match_posts[i] %} +{% endfor %} + +{% if relate_posts.size > 0 %} + + +{% endif %} diff --git a/_includes/search-loader.html b/_includes/search-loader.html new file mode 100644 index 0000000..2582580 --- /dev/null +++ b/_includes/search-loader.html @@ -0,0 +1,47 @@ + + +{% capture result_elem %} +
+
+

{title}

+ +
+

{snippet}

+
+{% endcapture %} + +{% capture not_found %}

{{ site.data.locales[include.lang].search.no_results }}

{% endcapture %} + + diff --git a/_includes/search-results.html b/_includes/search-results.html new file mode 100644 index 0000000..00a3182 --- /dev/null +++ b/_includes/search-results.html @@ -0,0 +1,10 @@ + + +
+
+
+ {% include_cached trending-tags.html %} +
+
+
+
diff --git a/_includes/sidebar.html b/_includes/sidebar.html new file mode 100644 index 0000000..569585f --- /dev/null +++ b/_includes/sidebar.html @@ -0,0 +1,97 @@ + + + + diff --git a/_includes/toc-status.html b/_includes/toc-status.html new file mode 100644 index 0000000..4b71cae --- /dev/null +++ b/_includes/toc-status.html @@ -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 ' +

{{- site.data.locales[include.lang].panel.toc -}}

+ + +{% endif %} diff --git a/_includes/topbar.html b/_includes/topbar.html new file mode 100644 index 0000000..fd68d1f --- /dev/null +++ b/_includes/topbar.html @@ -0,0 +1,77 @@ + + +
+
+ + + + + +
+ {% if page.layout == 'home' %} + {{- site.data.locales[include.lang].title | default: site.title -}} + {% elsif page.collection == 'tabs' or page.layout == 'page' %} + {%- capture tab_key -%}{{ page.url | split: '/' }}{%- endcapture -%} + {{- site.data.locales[include.lang].tabs[tab_key] | default: page.title -}} + {% else %} + {{- site.data.locales[include.lang].layout[page.layout] | default: page.layout | capitalize -}} + {% endif %} +
+ + + + + + + + +
+
diff --git a/_includes/trending-tags.html b/_includes/trending-tags.html new file mode 100644 index 0000000..57369f0 --- /dev/null +++ b/_includes/trending-tags.html @@ -0,0 +1,46 @@ + + +{% assign MAX = 10 %} + +{% assign size_list = '' | split: '' %} +{% assign tag_list = '' | split: '' %} + +{% for tag in site.tags %} + {% assign size = tag | last | size %} + {% assign size_list = size_list | push: size %} + + {% assign tag_str = tag | first | append: '::' | append: size %} + {% assign tag_list = tag_list | push: tag_str %} +{% endfor %} + +{% assign size_list = size_list | sort | reverse %} + +{% assign tag_list = tag_list | sort_natural %} + +{% assign trending_tags = '' | split: '' %} + +{% for size in size_list limit: MAX %} + {% for tag_str in tag_list %} + {% assign tag = tag_str | split: '::' %} + {% assign tag_name = tag | first %} + {% assign tag_size = tag | last | plus: 0 %} + {% if tag_size == size %} + {% unless trending_tags contains tag_name %} + {% assign trending_tags = trending_tags | push: tag_name %} + {% break %} + {% endunless %} + {% endif %} + {% endfor %} +{% endfor %} + +{% if trending_tags.size > 0 %} +
+

{{- site.data.locales[include.lang].panel.trending_tags -}}

+
+ {% for tag_name in trending_tags %} + {% assign url = tag_name | slugify | url_encode | prepend: '/tags/' | append: '/' %} + + {% endfor %} +
+
+{% endif %} diff --git a/_includes/update-list.html b/_includes/update-list.html new file mode 100644 index 0000000..93684c3 --- /dev/null +++ b/_includes/update-list.html @@ -0,0 +1,40 @@ + + +{% assign MAX_SIZE = 5 %} + +{% assign all_list = '' | split: '' %} + +{% for post in site.posts %} + {% assign datetime = post.last_modified_at | default: post.date %} + + {% capture elem %} + {{- datetime | date: "%Y%m%d%H%M%S" -}}::{{- forloop.index0 -}} + {% endcapture %} + + {% assign all_list = all_list | push: elem %} +{% endfor %} + +{% assign all_list = all_list | sort | reverse %} + +{% assign update_list = '' | split: '' %} + +{% for entry in all_list limit: MAX_SIZE %} + {% assign update_list = update_list | push: entry %} +{% endfor %} + +{% if update_list.size > 0 %} +
+

{{- site.data.locales[include.lang].panel.lastmod -}}

+
    + {% for item in update_list %} + {% assign index = item | split: '::' | last | plus: 0 %} + {% assign post = site.posts[index] %} + {% assign url = post.url | relative_url %} +
  • + {{ post.title }} +
  • + {% endfor %} +
+
+ +{% endif %} diff --git a/_javascript/categories.js b/_javascript/categories.js new file mode 100644 index 0000000..15d8251 --- /dev/null +++ b/_javascript/categories.js @@ -0,0 +1,7 @@ +import { basic, initSidebar, initTopbar } from './modules/layouts'; +import { categoryCollapse } from './modules/plugins'; + +basic(); +initSidebar(); +initTopbar(); +categoryCollapse(); diff --git a/_javascript/commons.js b/_javascript/commons.js new file mode 100644 index 0000000..6a17fb9 --- /dev/null +++ b/_javascript/commons.js @@ -0,0 +1,5 @@ +import { basic, initSidebar, initTopbar } from './modules/layouts'; + +initSidebar(); +initTopbar(); +basic(); diff --git a/_javascript/home.js b/_javascript/home.js new file mode 100644 index 0000000..ef22cb9 --- /dev/null +++ b/_javascript/home.js @@ -0,0 +1,8 @@ +import { basic, initSidebar, initTopbar } from './modules/layouts'; +import { initLocaleDatetime, loadImg } from './modules/plugins'; + +loadImg(); +initLocaleDatetime(); +initSidebar(); +initTopbar(); +basic(); diff --git a/_javascript/misc.js b/_javascript/misc.js new file mode 100644 index 0000000..52b4043 --- /dev/null +++ b/_javascript/misc.js @@ -0,0 +1,7 @@ +import { basic, initSidebar, initTopbar } from './modules/layouts'; +import { initLocaleDatetime } from './modules/plugins'; + +initSidebar(); +initTopbar(); +initLocaleDatetime(); +basic(); diff --git a/_javascript/modules/components/back-to-top.js b/_javascript/modules/components/back-to-top.js new file mode 100644 index 0000000..40d9cd1 --- /dev/null +++ b/_javascript/modules/components/back-to-top.js @@ -0,0 +1,19 @@ +/** + * Reference: https://bootsnipp.com/snippets/featured/link-to-top-page + */ + +export function back2top() { + const btn = document.getElementById('back-to-top'); + + window.addEventListener('scroll', () => { + if (window.scrollY > 50) { + btn.classList.add('show'); + } else { + btn.classList.remove('show'); + } + }); + + btn.addEventListener('click', () => { + window.scrollTo({ top: 0 }); + }); +} diff --git a/_javascript/modules/components/category-collapse.js b/_javascript/modules/components/category-collapse.js new file mode 100644 index 0000000..0c53cb4 --- /dev/null +++ b/_javascript/modules/components/category-collapse.js @@ -0,0 +1,36 @@ +/** + * Tab 'Categories' expand/close effect. + */ + +import 'bootstrap/js/src/collapse.js'; + +const childPrefix = 'l_'; +const parentPrefix = 'h_'; +const children = document.getElementsByClassName('collapse'); + +export function categoryCollapse() { + [...children].forEach((elem) => { + const id = parentPrefix + elem.id.substring(childPrefix.length); + const parent = document.getElementById(id); + + // collapse sub-categories + elem.addEventListener('hide.bs.collapse', () => { + if (parent) { + parent.querySelector('.far.fa-folder-open').className = + 'far fa-folder fa-fw'; + parent.querySelector('.fas.fa-angle-down').classList.add('rotate'); + parent.classList.remove('hide-border-bottom'); + } + }); + + // expand sub-categories + elem.addEventListener('show.bs.collapse', () => { + if (parent) { + parent.querySelector('.far.fa-folder').className = + 'far fa-folder-open fa-fw'; + parent.querySelector('.fas.fa-angle-down').classList.remove('rotate'); + parent.classList.add('hide-border-bottom'); + } + }); + }); +} diff --git a/_javascript/modules/components/clipboard.js b/_javascript/modules/components/clipboard.js new file mode 100644 index 0000000..9566e9d --- /dev/null +++ b/_javascript/modules/components/clipboard.js @@ -0,0 +1,143 @@ +/** + * Clipboard functions + * + * Dependencies: + * clipboard.js (https://github.com/zenorocha/clipboard.js) + */ + +import Tooltip from 'bootstrap/js/src/tooltip'; + +const clipboardSelector = '.code-header>button'; + +const ICON_DEFAULT = 'far fa-clipboard'; +const ICON_SUCCESS = 'fas fa-check'; + +const ATTR_TIMEOUT = 'timeout'; +const ATTR_TITLE_SUCCEED = 'data-title-succeed'; +const ATTR_TITLE_ORIGIN = 'data-bs-original-title'; +const TIMEOUT = 2000; // in milliseconds + +function isLocked(node) { + if (node.hasAttribute(ATTR_TIMEOUT)) { + let timeout = node.getAttribute(ATTR_TIMEOUT); + if (Number(timeout) > Date.now()) { + return true; + } + } + + return false; +} + +function lock(node) { + node.setAttribute(ATTR_TIMEOUT, Date.now() + TIMEOUT); +} + +function unlock(node) { + node.removeAttribute(ATTR_TIMEOUT); +} + +function showTooltip(btn) { + const succeedTitle = btn.getAttribute(ATTR_TITLE_SUCCEED); + btn.setAttribute(ATTR_TITLE_ORIGIN, succeedTitle); + Tooltip.getInstance(btn).show(); +} + +function hideTooltip(btn) { + Tooltip.getInstance(btn).hide(); + btn.removeAttribute(ATTR_TITLE_ORIGIN); +} + +function setSuccessIcon(btn) { + const icon = btn.children[0]; + icon.setAttribute('class', ICON_SUCCESS); +} + +function resumeIcon(btn) { + const icon = btn.children[0]; + icon.setAttribute('class', ICON_DEFAULT); +} + +function setCodeClipboard() { + const clipboardList = document.querySelectorAll(clipboardSelector); + + if (clipboardList.length === 0) { + return; + } + + // Initial the clipboard.js object + const clipboard = new ClipboardJS(clipboardSelector, { + target: (trigger) => { + const codeBlock = trigger.parentNode.nextElementSibling; + return codeBlock.querySelector('code .rouge-code'); + } + }); + + [...clipboardList].map( + (elem) => + new Tooltip(elem, { + placement: 'left' + }) + ); + + clipboard.on('success', (e) => { + const trigger = e.trigger; + + e.clearSelection(); + + if (isLocked(trigger)) { + return; + } + + setSuccessIcon(trigger); + showTooltip(trigger); + lock(trigger); + + setTimeout(() => { + hideTooltip(trigger); + resumeIcon(trigger); + unlock(trigger); + }, TIMEOUT); + }); +} + +function setLinkClipboard() { + const btnCopyLink = document.getElementById('copy-link'); + + if (btnCopyLink === null) { + return; + } + + btnCopyLink.addEventListener('click', (e) => { + const target = e.target; + + if (isLocked(target)) { + return; + } + + // Copy URL to clipboard + navigator.clipboard.writeText(window.location.href).then(() => { + const defaultTitle = target.getAttribute(ATTR_TITLE_ORIGIN); + const succeedTitle = target.getAttribute(ATTR_TITLE_SUCCEED); + + // Switch tooltip title + target.setAttribute(ATTR_TITLE_ORIGIN, succeedTitle); + Tooltip.getInstance(target).show(); + + lock(target); + + setTimeout(() => { + target.setAttribute(ATTR_TITLE_ORIGIN, defaultTitle); + unlock(target); + }, TIMEOUT); + }); + }); + + btnCopyLink.addEventListener('mouseleave', (e) => { + Tooltip.getInstance(e.target).hide(); + }); +} + +export function initClipboard() { + setCodeClipboard(); + setLinkClipboard(); +} diff --git a/_javascript/modules/components/img-loading.js b/_javascript/modules/components/img-loading.js new file mode 100644 index 0000000..989d9e6 --- /dev/null +++ b/_javascript/modules/components/img-loading.js @@ -0,0 +1,67 @@ +/** + * Setting up image lazy loading and LQIP switching + */ + +const ATTR_DATA_SRC = 'data-src'; +const ATTR_DATA_LQIP = 'data-lqip'; + +const cover = { + SHIMMER: 'shimmer', + BLUR: 'blur' +}; + +function removeCover(clzss) { + this.parentElement.classList.remove(clzss); +} + +function handleImage() { + if (!this.complete) { + return; + } + + if (this.hasAttribute(ATTR_DATA_LQIP)) { + removeCover.call(this, cover.BLUR); + } else { + removeCover.call(this, cover.SHIMMER); + } +} + +/** + * Switches the LQIP with the real image URL. + */ +function switchLQIP() { + const src = this.getAttribute(ATTR_DATA_SRC); + this.setAttribute('src', encodeURI(src)); + this.removeAttribute(ATTR_DATA_SRC); +} + +export function loadImg() { + const images = document.querySelectorAll('article img'); + + if (images.length === 0) { + return; + } + + images.forEach((img) => { + img.addEventListener('load', handleImage); + }); + + // Images loaded from the browser cache do not trigger the 'load' event + document.querySelectorAll('article img[loading="lazy"]').forEach((img) => { + if (img.complete) { + removeCover.call(img, cover.SHIMMER); + } + }); + + // LQIPs set by the data URI or WebP will not trigger the 'load' event, + // so manually convert the URI to the URL of a high-resolution image. + const lqips = document.querySelectorAll( + `article img[${ATTR_DATA_LQIP}="true"]` + ); + + if (lqips.length) { + lqips.forEach((lqip) => { + switchLQIP.call(lqip); + }); + } +} diff --git a/_javascript/modules/components/img-popup.js b/_javascript/modules/components/img-popup.js new file mode 100644 index 0000000..ac12043 --- /dev/null +++ b/_javascript/modules/components/img-popup.js @@ -0,0 +1,60 @@ +/** + * Set up image popup + * + * Dependencies: https://github.com/biati-digital/glightbox + */ + +const html = document.documentElement; +const lightImages = '.popup:not(.dark)'; +const darkImages = '.popup:not(.light)'; +let selector = lightImages; + +function updateImages(current, reverse) { + if (selector === lightImages) { + selector = darkImages; + } else { + selector = lightImages; + } + + if (reverse === null) { + reverse = GLightbox({ selector: `${selector}` }); + } + + [current, reverse] = [reverse, current]; +} + +export function imgPopup() { + if (document.querySelector('.popup') === null) { + return; + } + + const hasDualImages = !( + document.querySelector('.popup.light') === null && + 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) + ) { + selector = darkImages; + } + + let current = GLightbox({ selector: `${selector}` }); + + if (hasDualImages && document.getElementById('mode-toggle')) { + let reverse = null; + + window.addEventListener('message', (event) => { + if ( + event.source === window && + event.data && + event.data.direction === ModeToggle.ID + ) { + updateImages(current, reverse); + } + }); + } +} diff --git a/_javascript/modules/components/locale-datetime.js b/_javascript/modules/components/locale-datetime.js new file mode 100644 index 0000000..eb75626 --- /dev/null +++ b/_javascript/modules/components/locale-datetime.js @@ -0,0 +1,53 @@ +/** + * Update month/day to locale datetime + * + * Requirement: + */ + +/* A tool for locale datetime */ +class LocaleHelper { + static get attrTimestamp() { + return 'data-ts'; + } + + static get attrDateFormat() { + return 'data-df'; + } + + static get locale() { + return document.documentElement.getAttribute('lang').substring(0, 2); + } + + static getTimestamp(elem) { + return Number(elem.getAttribute(this.attrTimestamp)); // unix timestamp + } + + static getDateFormat(elem) { + return elem.getAttribute(this.attrDateFormat); + } +} + +export function initLocaleDatetime() { + dayjs.locale(LocaleHelper.locale); + dayjs.extend(window.dayjs_plugin_localizedFormat); + + document + .querySelectorAll(`[${LocaleHelper.attrTimestamp}]`) + .forEach((elem) => { + const date = dayjs.unix(LocaleHelper.getTimestamp(elem)); + const text = date.format(LocaleHelper.getDateFormat(elem)); + elem.textContent = text; + elem.removeAttribute(LocaleHelper.attrTimestamp); + elem.removeAttribute(LocaleHelper.attrDateFormat); + + // setup tooltips + if ( + elem.hasAttribute('data-bs-toggle') && + elem.getAttribute('data-bs-toggle') === 'tooltip' + ) { + // see: https://day.js.org/docs/en/display/format#list-of-localized-formats + const tooltipText = date.format('llll'); + elem.setAttribute('data-bs-title', tooltipText); + } + }); +} diff --git a/_javascript/modules/components/mode-watcher.js b/_javascript/modules/components/mode-watcher.js new file mode 100644 index 0000000..9eecd09 --- /dev/null +++ b/_javascript/modules/components/mode-watcher.js @@ -0,0 +1,14 @@ +/** + * Add listener for theme mode toggle + */ +const toggle = document.getElementById('mode-toggle'); + +export function modeWatcher() { + if (!toggle) { + return; + } + + toggle.addEventListener('click', () => { + modeToggle.flipMode(); + }); +} diff --git a/_javascript/modules/components/search-display.js b/_javascript/modules/components/search-display.js new file mode 100644 index 0000000..40059ac --- /dev/null +++ b/_javascript/modules/components/search-display.js @@ -0,0 +1,110 @@ +/** + * This script make #search-result-wrapper switch to unload or shown automatically. + */ + +const btnSbTrigger = document.getElementById('sidebar-trigger'); +const btnSearchTrigger = document.getElementById('search-trigger'); +const btnCancel = document.getElementById('search-cancel'); +const content = document.querySelectorAll('#main-wrapper>.container>.row'); +const topbarTitle = document.getElementById('topbar-title'); +const search = document.getElementById('search'); +const resultWrapper = document.getElementById('search-result-wrapper'); +const results = document.getElementById('search-results'); +const input = document.getElementById('search-input'); +const hints = document.getElementById('search-hints'); + +// CSS class names +const LOADED = 'd-block'; +const UNLOADED = 'd-none'; +const FOCUS = 'input-focus'; +const FLEX = 'd-flex'; + +/* Actions in mobile screens (Sidebar hidden) */ +class MobileSearchBar { + static on() { + btnSbTrigger.classList.add(UNLOADED); + topbarTitle.classList.add(UNLOADED); + btnSearchTrigger.classList.add(UNLOADED); + search.classList.add(FLEX); + btnCancel.classList.add(LOADED); + } + + static off() { + btnCancel.classList.remove(LOADED); + search.classList.remove(FLEX); + btnSbTrigger.classList.remove(UNLOADED); + topbarTitle.classList.remove(UNLOADED); + btnSearchTrigger.classList.remove(UNLOADED); + } +} + +class ResultSwitch { + static resultVisible = false; + + static on() { + if (!this.resultVisible) { + resultWrapper.classList.remove(UNLOADED); + content.forEach((el) => { + el.classList.add(UNLOADED); + }); + this.resultVisible = true; + } + } + + static off() { + if (this.resultVisible) { + results.innerHTML = ''; + + if (hints.classList.contains(UNLOADED)) { + hints.classList.remove(UNLOADED); + } + + resultWrapper.classList.add(UNLOADED); + content.forEach((el) => { + el.classList.remove(UNLOADED); + }); + input.textContent = ''; + this.resultVisible = false; + } + } +} + +function isMobileView() { + return btnCancel.classList.contains(LOADED); +} + +export function displaySearch() { + btnSearchTrigger.addEventListener('click', () => { + MobileSearchBar.on(); + ResultSwitch.on(); + input.focus(); + }); + + btnCancel.addEventListener('click', () => { + MobileSearchBar.off(); + ResultSwitch.off(); + }); + + input.addEventListener('focus', () => { + search.classList.add(FOCUS); + }); + + input.addEventListener('focusout', () => { + search.classList.remove(FOCUS); + }); + + input.addEventListener('input', () => { + if (input.value === '') { + if (isMobileView()) { + hints.classList.remove(UNLOADED); + } else { + ResultSwitch.off(); + } + } else { + ResultSwitch.on(); + if (isMobileView()) { + hints.classList.add(UNLOADED); + } + } + }); +} diff --git a/_javascript/modules/components/sidebar.js b/_javascript/modules/components/sidebar.js new file mode 100644 index 0000000..aed759e --- /dev/null +++ b/_javascript/modules/components/sidebar.js @@ -0,0 +1,22 @@ +/** + * Expand or close the sidebar in mobile screens. + */ + +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('sidebar-display', this.#isExpanded); + $sidebar.classList.toggle('z-2', this.#isExpanded); + $mask.classList.toggle('d-none', !this.#isExpanded); + } +} + +export function sidebarExpand() { + $trigger.onclick = $mask.onclick = () => SidebarUtil.toggle(); +} diff --git a/_javascript/modules/components/toc.js b/_javascript/modules/components/toc.js new file mode 100644 index 0000000..e9086ee --- /dev/null +++ b/_javascript/modules/components/toc.js @@ -0,0 +1,33 @@ +import { TocMobile as mobile } from './toc/toc-mobile'; +import { TocDesktop as desktop } from './toc/toc-desktop'; + +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: + if (desktopMode.matches) { + desktop.init(); + } else { + mobile.init(); + } + + desktopMode.onchange = refresh; +} + +export { init as initToc }; diff --git a/_javascript/modules/components/toc/toc-desktop.js b/_javascript/modules/components/toc/toc-desktop.js new file mode 100644 index 0000000..5021a72 --- /dev/null +++ b/_javascript/modules/components/toc/toc-desktop.js @@ -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); + } + } +} diff --git a/_javascript/modules/components/toc/toc-mobile.js b/_javascript/modules/components/toc/toc-mobile.js new file mode 100644 index 0000000..20e24a7 --- /dev/null +++ b/_javascript/modules/components/toc/toc-mobile.js @@ -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(); + } +} diff --git a/_javascript/modules/components/tooltip-loader.js b/_javascript/modules/components/tooltip-loader.js new file mode 100644 index 0000000..c36c879 --- /dev/null +++ b/_javascript/modules/components/tooltip-loader.js @@ -0,0 +1,11 @@ +import Tooltip from 'bootstrap/js/src/tooltip'; + +export function loadTooptip() { + const tooltipTriggerList = document.querySelectorAll( + '[data-bs-toggle="tooltip"]' + ); + + [...tooltipTriggerList].map( + (tooltipTriggerEl) => new Tooltip(tooltipTriggerEl) + ); +} diff --git a/_javascript/modules/layouts.js b/_javascript/modules/layouts.js new file mode 100644 index 0000000..28f7962 --- /dev/null +++ b/_javascript/modules/layouts.js @@ -0,0 +1,3 @@ +export { basic } from './layouts/basic'; +export { initSidebar } from './layouts/sidebar'; +export { initTopbar } from './layouts/topbar'; diff --git a/_javascript/modules/layouts/basic.js b/_javascript/modules/layouts/basic.js new file mode 100644 index 0000000..fb36a8b --- /dev/null +++ b/_javascript/modules/layouts/basic.js @@ -0,0 +1,7 @@ +import { back2top } from '../components/back-to-top'; +import { loadTooptip } from '../components/tooltip-loader'; + +export function basic() { + back2top(); + loadTooptip(); +} diff --git a/_javascript/modules/layouts/sidebar.js b/_javascript/modules/layouts/sidebar.js new file mode 100644 index 0000000..8795693 --- /dev/null +++ b/_javascript/modules/layouts/sidebar.js @@ -0,0 +1,7 @@ +import { modeWatcher } from '../components/mode-watcher'; +import { sidebarExpand } from '../components/sidebar'; + +export function initSidebar() { + modeWatcher(); + sidebarExpand(); +} diff --git a/_javascript/modules/layouts/topbar.js b/_javascript/modules/layouts/topbar.js new file mode 100644 index 0000000..cfcd0ed --- /dev/null +++ b/_javascript/modules/layouts/topbar.js @@ -0,0 +1,5 @@ +import { displaySearch } from '../components/search-display'; + +export function initTopbar() { + displaySearch(); +} diff --git a/_javascript/modules/plugins.js b/_javascript/modules/plugins.js new file mode 100644 index 0000000..cc95c1b --- /dev/null +++ b/_javascript/modules/plugins.js @@ -0,0 +1,6 @@ +export { categoryCollapse } from './components/category-collapse'; +export { initClipboard } from './components/clipboard'; +export { loadImg } from './components/img-loading'; +export { imgPopup } from './components/img-popup'; +export { initLocaleDatetime } from './components/locale-datetime'; +export { initToc } from './components/toc'; diff --git a/_javascript/page.js b/_javascript/page.js new file mode 100644 index 0000000..76e8ce9 --- /dev/null +++ b/_javascript/page.js @@ -0,0 +1,9 @@ +import { basic, initSidebar, initTopbar } from './modules/layouts'; +import { loadImg, imgPopup, initClipboard } from './modules/plugins'; + +loadImg(); +imgPopup(); +initSidebar(); +initTopbar(); +initClipboard(); +basic(); diff --git a/_javascript/post.js b/_javascript/post.js new file mode 100644 index 0000000..1c616ec --- /dev/null +++ b/_javascript/post.js @@ -0,0 +1,18 @@ +import { basic, initTopbar, initSidebar } from './modules/layouts'; + +import { + loadImg, + imgPopup, + initLocaleDatetime, + initClipboard, + initToc +} from './modules/plugins'; + +loadImg(); +initToc(); +imgPopup(); +initSidebar(); +initLocaleDatetime(); +initClipboard(); +initTopbar(); +basic(); diff --git a/_javascript/pwa/app.js b/_javascript/pwa/app.js new file mode 100644 index 0000000..3c0ded2 --- /dev/null +++ b/_javascript/pwa/app.js @@ -0,0 +1,55 @@ +import Toast from 'bootstrap/js/src/toast'; + +if ('serviceWorker' in navigator) { + // 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) => { + // Restore the update window that was last manually closed by the user + if (registration.waiting) { + popupWindow.show(); + } + + registration.addEventListener('updatefound', () => { + registration.installing.addEventListener('statechange', () => { + if (registration.waiting) { + if (navigator.serviceWorker.controller) { + popupWindow.show(); + } + } + }); + }); + + btnRefresh.addEventListener('click', () => { + if (registration.waiting) { + registration.waiting.postMessage('SKIP_WAITING'); + } + popupWindow.hide(); + }); + }); + + let refreshing = false; + + // Detect controller change and refresh all the opened tabs + navigator.serviceWorker.addEventListener('controllerchange', () => { + if (!refreshing) { + window.location.reload(); + refreshing = true; + } + }); + } else { + navigator.serviceWorker.getRegistrations().then(function (registrations) { + for (let registration of registrations) { + registration.unregister(); + } + }); + } +} diff --git a/_javascript/pwa/sw.js b/_javascript/pwa/sw.js new file mode 100644 index 0000000..ff9125d --- /dev/null +++ b/_javascript/pwa/sw.js @@ -0,0 +1,92 @@ +importScripts('./assets/js/data/swconf.js'); + +const purge = swconf.purge; +const interceptor = swconf.interceptor; + +function verifyUrl(url) { + const requestUrl = new URL(url); + const requestPath = requestUrl.pathname; + + if (!requestUrl.protocol.startsWith('http')) { + return false; + } + + for (const prefix of interceptor.urlPrefixes) { + if (requestUrl.href.startsWith(prefix)) { + return false; + } + } + + for (const path of interceptor.paths) { + if (requestPath.startsWith(path)) { + return false; + } + } + return true; +} + +self.addEventListener('install', (event) => { + if (purge) { + return; + } + + event.waitUntil( + caches.open(swconf.cacheName).then((cache) => { + return cache.addAll(swconf.resources); + }) + ); +}); + +self.addEventListener('activate', (event) => { + event.waitUntil( + caches.keys().then((keyList) => { + return Promise.all( + keyList.map((key) => { + if (purge) { + return caches.delete(key); + } else { + if (key !== swconf.cacheName) { + return caches.delete(key); + } + } + }) + ); + }) + ); +}); + +self.addEventListener('message', (event) => { + if (event.data === 'SKIP_WAITING') { + self.skipWaiting(); + } +}); + +self.addEventListener('fetch', (event) => { + if (event.request.headers.has('range')) { + return; + } + + event.respondWith( + caches.match(event.request).then((response) => { + if (response) { + return response; + } + + return fetch(event.request).then((response) => { + const url = event.request.url; + + if (purge || event.request.method !== 'GET' || !verifyUrl(url)) { + return response; + } + + // See: + let responseToCache = response.clone(); + + caches.open(swconf.cacheName).then((cache) => { + cache.put(event.request, responseToCache); + }); + return response; + }); + }) + ); +}); diff --git a/_layouts/archives.html b/_layouts/archives.html new file mode 100644 index 0000000..4f7ad7d --- /dev/null +++ b/_layouts/archives.html @@ -0,0 +1,35 @@ +--- +layout: page +# The Archives of posts. +--- + +{% include lang.html %} + +{% assign df_strftime_m = site.data.locales[lang].df.archives.strftime | default: '/ %m' %} +{% assign df_dayjs_m = site.data.locales[lang].df.archives.dayjs | default: '/ MM' %} + +
+ {% for post in site.posts %} + {% assign cur_year = post.date | date: '%Y' %} + + {% if cur_year != last_year %} + {% unless forloop.first %}{% endunless %} + + + {{ '
    ' }} + + {% assign last_year = cur_year %} + {% endif %} + +
  • + {% assign ts = post.date | date: '%s' %} + {{ post.date | date: '%d' }} + + {{ post.date | date: df_strftime_m }} + + {{ post.title }} +
  • + + {% if forloop.last %}
{% endif %} + {% endfor %} +
diff --git a/_layouts/categories.html b/_layouts/categories.html new file mode 100644 index 0000000..0515097 --- /dev/null +++ b/_layouts/categories.html @@ -0,0 +1,138 @@ +--- +layout: page +# All the Categories of posts +--- + +{% include lang.html %} + +{% assign HEAD_PREFIX = 'h_' %} +{% assign LIST_PREFIX = 'l_' %} + +{% assign group_index = 0 %} + +{% assign sort_categories = site.categories | sort %} + +{% for category in sort_categories %} + {% assign category_name = category | first %} + {% assign posts_of_category = category | last %} + {% assign first_post = posts_of_category | first %} + + {% if category_name == first_post.categories[0] %} + {% assign sub_categories = '' | split: '' %} + + {% for post in posts_of_category %} + {% assign second_category = post.categories[1] %} + {% if second_category %} + {% unless sub_categories contains second_category %} + {% assign sub_categories = sub_categories | push: second_category %} + {% endunless %} + {% endif %} + {% endfor %} + + {% assign sub_categories = sub_categories | sort %} + {% assign sub_categories_size = sub_categories | size %} + +
+ +
+ + + + {% capture _category_url %}/categories/{{ category_name | slugify | url_encode }}/{% endcapture %} + {{ category_name }} + + + {% assign top_posts_size = site.categories[category_name] | size %} + + {% if sub_categories_size > 0 %} + {{ sub_categories_size }} + {% if sub_categories_size > 1 %} + {{ + site.data.locales[lang].categories.category_measure.plural + | default: site.data.locales[lang].categories.category_measure + }} + {% else %} + {{ + site.data.locales[lang].categories.category_measure.singular + | default: site.data.locales[lang].categories.category_measure + }} + {% endif -%} + , + {% endif %} + + {{ top_posts_size }} + + {% if top_posts_size > 1 %} + {{ + site.data.locales[lang].categories.post_measure.plural + | default: site.data.locales[lang].categories.post_measure + }} + {% else %} + {{ + site.data.locales[lang].categories.post_measure.singular + | default: site.data.locales[lang].categories.post_measure + }} + {% endif %} + + + + + {% if sub_categories_size > 0 %} + + + + {% else %} + + + + {% endif %} +
+ + + + {% if sub_categories_size > 0 %} +
+
    + {% for sub_category in sub_categories %} +
  • + + + {% capture _sub_ctg_url %}/categories/{{ sub_category | slugify | url_encode }}/{% endcapture %} + {{ sub_category }} + + {% assign posts_size = site.categories[sub_category] | size %} + + {{ posts_size }} + + {% if posts_size > 1 %} + {{ + site.data.locales[lang].categories.post_measure.plural + | default: site.data.locales[lang].categories.post_measure + }} + {% else %} + {{ + site.data.locales[lang].categories.post_measure.singular + | default: site.data.locales[lang].categories.post_measure + }} + {% endif %} + +
  • + {% endfor %} +
+
+ {% endif %} +
+ + + {% assign group_index = group_index | plus: 1 %} + {% endif %} +{% endfor %} diff --git a/_layouts/category.html b/_layouts/category.html new file mode 100644 index 0000000..b064f27 --- /dev/null +++ b/_layouts/category.html @@ -0,0 +1,24 @@ +--- +layout: page +# The Category layout +--- + +{% include lang.html %} + +
+

+ + {{ page.title }} + {{ page.posts | size }} +

+ +
    + {% for post in page.posts %} +
  • + {{ post.title }} + + {% include datetime.html date=post.date class='text-muted small text-nowrap' lang=lang %} +
  • + {% endfor %} +
+
diff --git a/_layouts/compress.html b/_layouts/compress.html new file mode 100644 index 0000000..2779e92 --- /dev/null +++ b/_layouts/compress.html @@ -0,0 +1,10 @@ +--- +# Jekyll layout that compresses HTML +# v3.2.0 +# http://jch.penibelst.de/ +# © 2014–2015 Anatol Broder +# MIT License +--- + +{% capture _LINE_FEED %} +{% endcapture %}{% if site.compress_html.ignore.envs contains jekyll.environment or site.compress_html.ignore.envs == "all" or page.compress_html == false %}{{ content }}{% else %}{% capture _content %}{{ content }}{% endcapture %}{% assign _profile = site.compress_html.profile %}{% if site.compress_html.endings == "all" %}{% assign _endings = "html head body li dt dd optgroup option colgroup caption thead tbody tfoot tr td th" | split: " " %}{% else %}{% assign _endings = site.compress_html.endings %}{% endif %}{% for _element in _endings %}{% capture _end %}{% endcapture %}{% assign _content = _content | remove: _end %}{% endfor %}{% if _profile and _endings %}{% assign _profile_endings = _content | size | plus: 1 %}{% endif %}{% for _element in site.compress_html.startings %}{% capture _start %}<{{ _element }}>{% endcapture %}{% assign _content = _content | remove: _start %}{% endfor %}{% if _profile and site.compress_html.startings %}{% assign _profile_startings = _content | size | plus: 1 %}{% endif %}{% if site.compress_html.comments == "all" %}{% assign _comments = "" | split: " " %}{% else %}{% assign _comments = site.compress_html.comments %}{% endif %}{% if _comments.size == 2 %}{% capture _comment_befores %}.{{ _content }}{% endcapture %}{% assign _comment_befores = _comment_befores | split: _comments.first %}{% for _comment_before in _comment_befores %}{% if forloop.first %}{% continue %}{% endif %}{% capture _comment_outside %}{% if _carry %}{{ _comments.first }}{% endif %}{{ _comment_before }}{% endcapture %}{% capture _comment %}{% unless _carry %}{{ _comments.first }}{% endunless %}{{ _comment_outside | split: _comments.last | first }}{% if _comment_outside contains _comments.last %}{{ _comments.last }}{% assign _carry = false %}{% else %}{% assign _carry = true %}{% endif %}{% endcapture %}{% assign _content = _content | remove_first: _comment %}{% endfor %}{% if _profile %}{% assign _profile_comments = _content | size | plus: 1 %}{% endif %}{% endif %}{% assign _pre_befores = _content | split: "" %}{% assign _pres_after = "" %}{% if _pres.size != 0 %}{% if site.compress_html.blanklines %}{% assign _lines = _pres.last | split: _LINE_FEED %}{% capture _pres_after %}{% for _line in _lines %}{% assign _trimmed = _line | split: " " | join: " " %}{% if _trimmed != empty or forloop.last %}{% unless forloop.first %}{{ _LINE_FEED }}{% endunless %}{{ _line }}{% endif %}{% endfor %}{% endcapture %}{% else %}{% assign _pres_after = _pres.last | split: " " | join: " " %}{% endif %}{% endif %}{% capture _content %}{{ _content }}{% if _pre_before contains "
" %}{% endif %}{% unless _pre_before contains "
" and _pres.size == 1 %}{{ _pres_after }}{% endunless %}{% endcapture %}{% endfor %}{% if _profile %}{% assign _profile_collapse = _content | size | plus: 1 %}{% endif %}{% if site.compress_html.clippings == "all" %}{% assign _clippings = "html head title base link meta style body article section nav aside h1 h2 h3 h4 h5 h6 hgroup header footer address p hr blockquote ol ul li dl dt dd figure figcaption main div table caption colgroup col tbody thead tfoot tr td th" | split: " " %}{% else %}{% assign _clippings = site.compress_html.clippings %}{% endif %}{% for _element in _clippings %}{% assign _edges = " ;; ;" | replace: "e", _element | split: ";" %}{% assign _content = _content | replace: _edges[0], _edges[1] | replace: _edges[2], _edges[3] | replace: _edges[4], _edges[5] %}{% endfor %}{% if _profile and _clippings %}{% assign _profile_clippings = _content | size | plus: 1 %}{% endif %}{{ _content }}{% if _profile %}
Step Bytes
raw {{ content | size }}{% if _profile_endings %}
endings {{ _profile_endings }}{% endif %}{% if _profile_startings %}
startings {{ _profile_startings }}{% endif %}{% if _profile_comments %}
comments {{ _profile_comments }}{% endif %}{% if _profile_collapse %}
collapse {{ _profile_collapse }}{% endif %}{% if _profile_clippings %}
clippings {{ _profile_clippings }}{% endif %}
{% endif %}{% endif %} diff --git a/_layouts/default.html b/_layouts/default.html new file mode 100644 index 0000000..1590ef6 --- /dev/null +++ b/_layouts/default.html @@ -0,0 +1,82 @@ +--- +layout: compress +--- + + + +{% include origin-type.html %} + +{% include lang.html %} + +{% if site.theme_mode %} + {% capture prefer_mode %}data-mode="{{ site.theme_mode }}"{% endcapture %} +{% endif %} + + + + {% include head.html %} + + + {% include sidebar.html lang=lang %} + +
+
+ {% include topbar.html lang=lang %} + +
+
+ {% if layout.refactor or layout.layout == 'default' %} + {% include refactor-content.html content=content lang=lang %} + {% else %} + {{ content }} + {% endif %} +
+ + + +
+ +
+ +
+ {% for _include in layout.tail_includes %} + {% assign _include_path = _include | append: '.html' %} + {% include {{ _include_path }} lang=lang %} + {% endfor %} + + {% include_cached footer.html lang=lang %} +
+
+ + {% include_cached search-results.html lang=lang %} +
+ + +
+ +
+ + {% if site.pwa.enabled %} + {% include_cached notification.html lang=lang %} + {% endif %} + + + {% include js-selector.html lang=lang %} + + {% include_cached search-loader.html lang=lang %} + + diff --git a/_layouts/home.html b/_layouts/home.html new file mode 100644 index 0000000..451e391 --- /dev/null +++ b/_layouts/home.html @@ -0,0 +1,122 @@ +--- +layout: default +refactor: true +--- + +{% include lang.html %} + +{% 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: '' %} + + + +{% assign visible_start = paginator.page | minus: 1 | times: paginator.per_page %} +{% assign visible_end = visible_start | plus: paginator.per_page %} + +{% 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_size = 0 %} +{% endif %} + + + +{% assign normal_size = paginator.posts | size | minus: pinned_size %} + +{% if normal_size > 0 %} + {% if pinned_size > 0 %} + {% assign normal_start = 0 %} + {% else %} + {% assign normal_start = visible_start | minus: all_pinned.size %} + {% endif %} + + {% assign normal_end = normal_start | plus: normal_size | minus: 1 %} + + {% for i in (normal_start..normal_end) %} + {% assign posts = posts | push: all_normal[i] %} + {% endfor %} +{% endif %} + +
+ {% for post in posts %} +
+ + {% assign card_body_col = '12' %} + + {% if post.image %} + {% assign src = post.image.path | default: post.image %} + {% unless src contains '//' %} + {% assign src = post.media_subpath | append: '/' | append: src | replace: '//', '/' %} + {% endunless %} + + {% assign alt = post.image.alt | xml_escape | default: 'Preview Image' %} + + {% assign lqip = null %} + + {% if post.image.lqip %} + {% capture lqip %}lqip="{{ post.image.lqip }}"{% endcapture %} + {% endif %} + +
+ {{ alt }} +
+ + {% assign card_body_col = '7' %} + {% endif %} + +
+
+

{{ post.title }}

+ +
+

{% include post-description.html %}

+
+ + + +
+ +
+
+
+ {% endfor %} +
+ + +{% if paginator.total_pages > 1 %} + {% include post-paginator.html %} +{% endif %} diff --git a/_layouts/page.html b/_layouts/page.html new file mode 100644 index 0000000..32d6582 --- /dev/null +++ b/_layouts/page.html @@ -0,0 +1,20 @@ +--- +layout: default +--- + +{% include lang.html %} + +
+ {% if page.layout == 'page' or page.collection == 'tabs' %} + {% assign tab_key = page.title | downcase %} + {% assign title = site.data.locales[lang].tabs[tab_key] | default: page.title %} +

+ {{ title }} +

+
+ {{ content }} +
+ {% else %} + {{ content }} + {% endif %} +
diff --git a/_layouts/post.html b/_layouts/post.html new file mode 100644 index 0000000..6a2deff --- /dev/null +++ b/_layouts/post.html @@ -0,0 +1,178 @@ +--- +layout: default +refactor: true +panel_includes: + - toc +tail_includes: + - related-posts + - post-nav + - comments +--- + +{% include lang.html %} + +{% include toc-status.html %} + +
+
+

{{ page.title }}

+ {% if page.description %} +

{{ page.description }}

+ {% endif %} + + +
+ + {% if enable_toc %} + + + + + +
+
{{- page.title -}}
+ +
+
+
+ {% endif %} + +
+ {{ content }} +
+ +
+ + {% if page.categories.size > 0 %} + + {% endif %} + + + {% if page.tags.size > 0 %} + + {% endif %} + +
+
+ {% if site.data.locales[lang].copyright.license.template %} + {% capture _replacement %} + + {{ site.data.locales[lang].copyright.license.name }} + + {% endcapture %} + + {{ site.data.locales[lang].copyright.license.template | replace: ':LICENSE_NAME', _replacement }} + {% endif %} +
+ + {% include post-sharing.html lang=lang %} +
+ +
+ +
diff --git a/_layouts/tag.html b/_layouts/tag.html new file mode 100644 index 0000000..d766d09 --- /dev/null +++ b/_layouts/tag.html @@ -0,0 +1,23 @@ +--- +layout: page +# The layout for Tag page +--- + +{% include lang.html %} + +
+

+ + {{ page.title }} + {{ page.posts | size }} +

+
    + {% for post in page.posts %} +
  • + {{ post.title }} + + {% include datetime.html date=post.date class='text-muted small text-nowrap' lang=lang %} +
  • + {% endfor %} +
+
diff --git a/_layouts/tags.html b/_layouts/tags.html new file mode 100644 index 0000000..7800ca0 --- /dev/null +++ b/_layouts/tags.html @@ -0,0 +1,22 @@ +--- +layout: page +# All the Tags of posts. +--- + +
+ {% assign tags = '' | split: '' %} + {% for t in site.tags %} + {% assign tags = tags | push: t[0] %} + {% endfor %} + + {% assign sorted_tags = tags | sort_natural %} + + {% for t in sorted_tags %} + + {% endfor %} +
diff --git a/_plugins/posts-lastmod-hook.rb b/_plugins/posts-lastmod-hook.rb new file mode 100644 index 0000000..1fd6ecf --- /dev/null +++ b/_plugins/posts-lastmod-hook.rb @@ -0,0 +1,14 @@ +#!/usr/bin/env ruby +# +# Check for changed posts + +Jekyll::Hooks.register :posts, :post_init do |post| + + commit_num = `git rev-list --count HEAD "#{ post.path }"` + + if commit_num.to_i > 1 + lastmod_date = `git log -1 --pretty="%ad" --date=iso "#{ post.path }"` + post.data['last_modified_at'] = lastmod_date + end + +end diff --git a/_posts/2022-10-21-gruendung-star-citizen-organisation.md b/_posts/2022-10-21-gruendung-star-citizen-organisation.md new file mode 100644 index 0000000..19695a7 --- /dev/null +++ b/_posts/2022-10-21-gruendung-star-citizen-organisation.md @@ -0,0 +1,30 @@ +--- +title: Gründung unserer Star Citizen Organisation +description: >- + neulich haben wir eine neue Organisation in Star Citizen namens "Morlana Industries" (MORINC) gegründet. + Wir möchten gerne jeden einladen, der daran Interesse hätte, uns beizutreten. +author: sitiriotv +date: 2022-10-21 18:06:00 +0200 +categories: [Star Citizen, Organisation] +tags: [star citizen, star, citizen, morinc, Morlana Industries] +pin: true +media_subpath: '/posts/2022/10' +--- + +Hallo liebe Community, + +neulich haben wir eine neue Organisation in Star Citizen namens "Morlana Industries" (*MORINC*) gegründet. +Wir möchten gerne jeden einladen, der daran Interesse hätte, uns beizutreten. + +Hier ist der Link zur Organisationsseite: +> __***[https://robertsspaceindustries.com/orgs/MORINC](https://robertsspaceindustries.com/orgs/MORINC)***__ + +Um beizutreten müsst ihr einfach eine kleine Bewerbung schreiben. Wir sind jedoch nicht all zu streng und eine kleine Vorstellung würde ausreichen. + +Bei Fragen einfach hier kommentieren. + +Ich hoffe wir sehen uns im Verse, + +> LG SitirioTV | Justin / Kosmos | Florian + +*(PS: Aktuell ist die Organisation noch WORK IN PROGRESS!)* diff --git a/_sass/addon/commons.scss b/_sass/addon/commons.scss new file mode 100644 index 0000000..5e8acea --- /dev/null +++ b/_sass/addon/commons.scss @@ -0,0 +1,1526 @@ +/* The common styles */ + +html { + font-size: 16px; + + @media (prefers-color-scheme: light) { + &:not([data-mode]), + &[data-mode='light'] { + @include light-scheme; + } + + &[data-mode='dark'] { + @include dark-scheme; + } + } + + @media (prefers-color-scheme: dark) { + &:not([data-mode]), + &[data-mode='dark'] { + @include dark-scheme; + } + + &[data-mode='light'] { + @include light-scheme; + } + } +} + +body { + background: var(--main-bg); + padding: env(safe-area-inset-top) env(safe-area-inset-right) + env(safe-area-inset-bottom) env(safe-area-inset-left); + color: var(--text-color); + -webkit-font-smoothing: antialiased; + font-family: $font-family-base; +} + +/* --- Typography --- */ + +@for $i from 1 through 5 { + h#{$i} { + @extend %heading; + + @if $i > 1 { + @extend %anchor; + } + + @if $i < 5 { + $size-factor: 0.25rem; + + @if $i > 1 { + $size-factor: 0.18rem; + + main & { + @if $i == 2 { + margin: 2.5rem 0 1.25rem; + } @else { + margin: 2rem 0 1rem; + } + } + } + + & { + font-size: 1rem + (5 - $i) * $size-factor; + } + } @else { + font-size: 1.05rem; + } + } +} + +a { + @extend %link-color; + + text-decoration: none; +} + +img { + max-width: 100%; + height: auto; + transition: all 0.35s ease-in-out; + + .blur & { + $blur: 20px; + + -webkit-filter: blur($blur); + filter: blur($blur); + } +} + +blockquote { + border-left: 0.125rem solid var(--blockquote-border-color); + padding-left: 1rem; + color: var(--blockquote-text-color); + margin-top: 0.5rem; + + > p:last-child { + margin-bottom: 0; + } + + &[class^='prompt-'] { + border-left: 0; + position: relative; + padding: 1rem 1rem 1rem 3rem; + color: var(--prompt-text-color); + + @extend %rounded; + + &::before { + text-align: center; + width: 3rem; + position: absolute; + left: 0.25rem; + margin-top: 0.4rem; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + } + } + + @include prompt('tip', '\f0eb', $fa-style: 'regular'); + @include prompt('info', '\f06a', $rotate: 180); + @include prompt('warning', '\f06a'); + @include prompt('danger', '\f071'); +} + +kbd { + font-family: Lato, sans-serif; + display: inline-block; + vertical-align: middle; + line-height: 1.3rem; + min-width: 1.75rem; + text-align: center; + margin: 0 0.3rem; + padding-top: 0.1rem; + color: var(--kbd-text-color); + background-color: var(--kbd-bg-color); + border-radius: $radius-sm; + border: solid 1px var(--kbd-wrap-color); + box-shadow: inset 0 -2px 0 var(--kbd-wrap-color); +} + +hr { + border-color: var(--main-border-color); + opacity: 1; +} + +footer { + background-color: var(--main-bg); + height: $footer-height; + border-top: 1px solid var(--main-border-color); + + @extend %text-xs; + + a { + @extend %text-highlight; + + &:hover { + @extend %link-hover; + } + } + + em { + @extend %text-highlight; + } + + p { + text-align: center; + margin-bottom: 0; + } +} + +/* fontawesome icons */ +i { + &.far, + &.fas { + @extend %no-cursor; + } +} + +/* --- Panels --- */ + +.access { + top: 2rem; + transition: top 0.2s ease-in-out; + margin-top: 3rem; + margin-bottom: 4rem; + + &:only-child { + position: -webkit-sticky; + position: sticky; + } + + > section { + padding-left: 1rem; + border-left: 1px solid var(--main-border-color); + + &:not(:last-child) { + margin-bottom: 4rem; + } + } + + .content { + font-size: 0.9rem; + } +} + +#panel-wrapper { + /* the headings */ + .panel-heading { + font-family: inherit; + line-height: inherit; + + @include label(inherit); + } + + .post-tag { + line-height: 1.05rem; + font-size: 0.85rem; + border-radius: 0.8rem; + padding: 0.3rem 0.5rem; + margin: 0 0.35rem 0.5rem 0; + + &:hover { + transition: all 0.3s ease-in; + } + } +} + +#access-lastmod { + a { + color: inherit; + + &:hover { + @extend %link-hover; + } + + @extend %no-bottom-border; + } +} + +.footnotes > ol { + padding-left: 2rem; + margin-top: 0.5rem; + + > li { + &:not(:last-child) { + margin-bottom: 0.3rem; + } + + @extend %sup-fn-target; + + > p { + margin-left: 0.25em; + + @include mt-mb(0); + } + } +} + +.footnote { + @at-root a#{&} { + @include ml-mr(1px); + @include pl-pr(2px); + + border-bottom-style: none !important; + } +} + +sup { + @extend %sup-fn-target; +} + +.reversefootnote { + @at-root a#{&} { + font-size: 0.6rem; + line-height: 1; + position: relative; + bottom: 0.25em; + margin-left: 0.25em; + border-bottom-style: none !important; + } +} + +/* --- Begin of Markdown table style --- */ + +/* it will be created by Liquid */ +.table-wrapper { + overflow-x: auto; + margin-bottom: 1.5rem; + + > table { + min-width: 100%; + overflow-x: auto; + border-spacing: 0; + + thead { + border-bottom: solid 2px rgba(210, 215, 217, 0.75); + + th { + @extend %table-cell; + } + } + + tbody { + tr { + border-bottom: 1px solid var(--tb-border-color); + + &:nth-child(2n) { + background-color: var(--tb-even-bg); + } + + &:nth-child(2n + 1) { + background-color: var(--tb-odd-bg); + } + + td { + @extend %table-cell; + } + } + } /* tbody */ + } /* table */ +} + +/* --- post --- */ + +.preview-img { + aspect-ratio: 40 / 21; + width: 100%; + height: 100%; + overflow: hidden; + + @extend %rounded; + + &:not(.no-bg) { + background: var(--img-bg); + } + + img { + height: 100%; + -o-object-fit: cover; + object-fit: cover; + + @extend %rounded; + + @at-root #post-list & { + width: 100%; + } + } +} + +.post-preview { + @extend %rounded; + + border: 0; + background: var(--card-bg); + box-shadow: var(--card-shadow); + + &::before { + @extend %rounded; + + content: ''; + width: 100%; + height: 100%; + position: absolute; + background-color: var(--card-hovor-bg); + opacity: 0; + transition: opacity 0.35s ease-in-out; + } + + &:hover { + &::before { + opacity: 0.3; + } + } +} + +main { + line-height: 1.75; + + h1 { + margin-top: 2rem; + } + + p { + > a.popup { + &:not(.normal):not(.left):not(.right) { + @include align-center; + } + } + } + + .categories, + #tags, + #archives { + a:not(:hover) { + @extend %no-bottom-border; + } + } +} + +.post-meta { + @extend %text-sm; + + a { + &:not([class]):hover { + @extend %link-hover; + } + } + + em { + @extend %normal-font-style; + } +} + +.content { + font-size: 1.08rem; + margin-top: 2rem; + overflow-wrap: break-word; + + a { + &.popup { + @extend %no-cursor; + @extend %img-caption; + @include mt-mb(0.5rem); + + cursor: zoom-in; + } + + &:not(.img-link) { + @extend %link-underline; + + &:hover { + @extend %link-hover; + } + } + } + + ol, + ul { + &:not([class]), + &.task-list { + -webkit-padding-start: 1.75rem; + padding-inline-start: 1.75rem; + + li { + margin: 0.25rem 0; + padding-left: 0.25rem; + } + + ol, + ul { + -webkit-padding-start: 1.25rem; + padding-inline-start: 1.25rem; + margin: 0.5rem 0; + } + } + } + + ul.task-list { + -webkit-padding-start: 1.25rem; + padding-inline-start: 1.25rem; + + li { + list-style-type: none; + padding-left: 0; + + /* checkbox icon */ + > i { + width: 2rem; + margin-left: -1.25rem; + color: var(--checkbox-color); + + &.checked { + color: var(--checkbox-checked-color); + } + } + + ul { + -webkit-padding-start: 1.75rem; + padding-inline-start: 1.75rem; + } + } + + input[type='checkbox'] { + margin: 0 0.5rem 0.2rem -1.3rem; + vertical-align: middle; + } + } /* ul */ + + dl > dd { + margin-left: 1rem; + } + + ::marker { + color: var(--text-muted-color); + } +} /* .content */ + +.tag:hover { + @extend %tag-hover; +} + +.post-tag { + display: inline-block; + min-width: 2rem; + text-align: center; + border-radius: 0.5rem; + border: 1px solid var(--btn-border-color); + padding: 0 0.4rem; + color: var(--text-muted-color); + line-height: 1.3rem; + + &:not(:last-child) { + margin-right: 0.2rem; + } +} + +.rounded-10 { + border-radius: 10px !important; +} + +.img-link { + color: transparent; + display: inline-flex; +} + +.shimmer { + overflow: hidden; + position: relative; + background: var(--img-bg); + + &::before { + content: ''; + position: absolute; + background: var(--shimmer-bg); + height: 100%; + width: 100%; + -webkit-animation: shimmer 1.3s infinite; + animation: shimmer 1.3s infinite; + } + + @-webkit-keyframes shimmer { + 0% { + transform: translateX(-100%); + } + + 100% { + transform: translateX(100%); + } + } + + @keyframes shimmer { + 0% { + transform: translateX(-100%); + } + + 100% { + transform: translateX(100%); + } + } +} + +.embed-video { + width: 100%; + height: 100%; + margin-bottom: 1rem; + aspect-ratio: 16 / 9; + + @extend %rounded; + + &.twitch { + aspect-ratio: 310 / 189; + } + + &.file { + display: block; + width: auto; + height: auto; + max-width: 100%; + max-height: 100%; + margin: auto; + margin-bottom: 0; + } + + @extend %img-caption; +} + +.embed-audio { + width: 100%; + display: block; + + @extend %img-caption; +} + +/* --- buttons --- */ +.btn-lang { + border: 1px solid !important; + padding: 1px 3px; + border-radius: 3px; + color: var(--link-color); + + &:focus { + box-shadow: none; + } +} + +/* --- Effects classes --- */ + +.flex-grow-1 { + flex-grow: 1 !important; +} + +.btn-box-shadow { + box-shadow: var(--card-shadow); +} + +/* overwrite bootstrap muted */ +.text-muted { + color: var(--text-muted-color) !important; +} + +/* Overwrite bootstrap tooltip */ +.tooltip-inner { + font-size: 0.7rem; + max-width: 220px; + text-align: left; +} + +/* Overwrite bootstrap outline button */ +.btn.btn-outline-primary { + &:not(.disabled):hover { + border-color: #007bff !important; + } +} + +.disabled { + color: rgb(206, 196, 196); + pointer-events: auto; + cursor: not-allowed; +} + +.hide-border-bottom { + border-bottom: none !important; +} + +.input-focus { + box-shadow: none; + border-color: var(--input-focus-border-color) !important; + background: center !important; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out; +} + +.left { + float: left; + margin: 0.75rem 1rem 1rem 0; +} + +.right { + float: right; + margin: 0.75rem 0 1rem 1rem; +} + +/* --- Overriding --- */ + +/* mermaid */ +.mermaid { + text-align: center; +} + +/* MathJax */ +mjx-container { + overflow-y: hidden; + min-width: auto !important; +} + +/* --- sidebar layout --- */ + +$sidebar-display: 'sidebar-display'; +$btn-border-width: 3px; +$btn-mb: 0.5rem; + +#sidebar { + @include pl-pr(0); + + position: fixed; + top: 0; + left: 0; + height: 100%; + overflow-y: auto; + width: $sidebar-width; + background: var(--sidebar-bg); + border-right: 1px solid var(--sidebar-border-color); + + /* Hide scrollbar for IE, Edge and Firefox */ + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ + + /* Hide scrollbar for Chrome, Safari and Opera */ + &::-webkit-scrollbar { + display: none; + } + + %sidebar-link-hover { + &:hover { + color: var(--sidebar-active-color); + } + } + + a { + @extend %sidebar-links; + } + + #avatar { + display: block; + width: 7rem; + height: 7rem; + overflow: hidden; + box-shadow: var(--avatar-border-color) 0 0 0 2px; + transform: translateZ(0); /* fixed the zoom in Safari */ + + img { + transition: transform 0.5s; + + &:hover { + transform: scale(1.2); + } + } + } + + .profile-wrapper { + @include mt-mb(2.5rem); + @extend %clickable-transition; + + padding-left: 2.5rem; + padding-right: 1.25rem; + width: 100%; + } + + .site-title { + @extend %clickable-transition; + @extend %sidebar-link-hover; + + font-family: inherit; + font-weight: 900; + font-size: 1.75rem; + line-height: 1.2; + letter-spacing: 0.25px; + margin-top: 1.25rem; + margin-bottom: 0.5rem; + width: fit-content; + color: var(--site-title-color); + } + + .site-subtitle { + font-size: 95%; + color: var(--site-subtitle-color); + margin-top: 0.25rem; + word-spacing: 1px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + } + + ul { + margin-bottom: 2rem; + + li.nav-item { + opacity: 0.9; + width: 100%; + + @include pl-pr(1.5rem); + + a.nav-link { + @include pt-pb(0.6rem); + + display: flex; + align-items: center; + border-radius: 0.75rem; + font-weight: 600; + + &:hover { + background-color: var(--sidebar-hover-bg); + } + + i { + font-size: 95%; + opacity: 0.8; + margin-right: 1.5rem; + } + + span { + font-size: 90%; + letter-spacing: 0.2px; + } + } + + &.active { + .nav-link { + color: var(--sidebar-active-color); + background-color: var(--sidebar-hover-bg); + + span { + opacity: 1; + } + } + } + + &:not(:first-child) { + margin-top: 0.25rem; + } + } + } + + .sidebar-bottom { + padding-left: 2rem; + padding-right: 1rem; + margin-bottom: 1.5rem; + + $btn-size: 1.75rem; + + %button { + width: $btn-size; + height: $btn-size; + margin-bottom: $btn-mb; // multi line gap + border-radius: 50%; + color: var(--sidebar-btn-color); + background-color: var(--sidebar-btn-bg); + text-align: center; + display: flex; + align-items: center; + justify-content: center; + + &:not(:focus-visible) { + box-shadow: var(--sidebar-border-color) 0 0 0 1px; + } + + &:hover { + background-color: var(--sidebar-hover-bg); + } + } + + a { + @extend %button; + @extend %sidebar-link-hover; + @extend %clickable-transition; + + &:not(:last-child) { + margin-right: $sb-btn-gap; + } + } + + i { + line-height: $btn-size; + } + + #mode-toggle { + @extend %button; + @extend %sidebar-links; + @extend %sidebar-link-hover; + } + + .icon-border { + @extend %no-cursor; + @include ml-mr(calc(($sb-btn-gap - $btn-border-width) / 2)); + + background-color: var(--sidebar-btn-color); + content: ''; + width: $btn-border-width; + height: $btn-border-width; + border-radius: 50%; + margin-bottom: $btn-mb; + } + } /* .sidebar-bottom */ +} /* #sidebar */ + +@media (hover: hover) { + #sidebar ul > li:last-child::after { + transition: top 0.5s ease; + } + + .nav-link { + transition: background-color 0.3s ease-in-out; + } + + .post-preview { + transition: background-color 0.35s ease-in-out; + } +} + +#search-result-wrapper { + display: none; + height: 100%; + width: 100%; + overflow: auto; + + .content { + margin-top: 2rem; + } +} + +/* --- top-bar --- */ + +#topbar-wrapper { + height: $topbar-height; + background-color: var(--topbar-bg); +} + +#topbar { + @extend %btn-color; + + #breadcrumb { + font-size: 1rem; + color: var(--text-muted-color); + padding-left: 0.5rem; + + a:hover { + @extend %link-hover; + } + + span { + &:not(:last-child) { + &::after { + content: '›'; + padding: 0 0.3rem; + } + } + } + } +} /* #topbar */ + +::-webkit-input-placeholder { + @include placeholder; +} + +::-moz-placeholder { + @include placeholder; +} + +:-ms-input-placeholder { + @include placeholder; +} + +::-ms-input-placeholder { + @include placeholder; +} + +::placeholder { + @include placeholder; +} + +:focus::-webkit-input-placeholder { + @include placeholder-focus; +} + +:focus::-moz-placeholder { + @include placeholder-focus; +} + +:focus:-ms-input-placeholder { + @include placeholder-focus; +} + +:focus::-ms-input-placeholder { + @include placeholder-focus; +} + +:focus::placeholder { + @include placeholder-focus; +} + +search { + display: flex; + width: 100%; + border-radius: 1rem; + border: 1px solid var(--search-border-color); + background: var(--main-bg); + padding: 0 0.5rem; + + i { + z-index: 2; + font-size: 0.9rem; + color: var(--search-icon-color); + } +} + +#sidebar-trigger, +#search-trigger { + display: none; +} + +/* 'Cancel' link */ +#search-cancel { + color: var(--link-color); + display: none; + white-space: nowrap; + + @extend %cursor-pointer; +} + +#search-input { + background: center; + border: 0; + border-radius: 0; + padding: 0.18rem 0.3rem; + color: var(--text-color); + height: auto; + + &:focus { + box-shadow: none; + } +} + +#search-hints { + padding: 0 1rem; + + h4 { + margin-bottom: 1.5rem; + } + + .post-tag { + display: inline-block; + line-height: 1rem; + font-size: 1rem; + background: var(--search-tag-bg); + border: none; + padding: 0.5rem; + margin: 0 1.25rem 1rem 0; + + &::before { + content: '#'; + color: var(--text-muted-color); + padding-right: 0.2rem; + } + + @extend %link-color; + } +} + +#search-results { + padding-bottom: 3rem; + + a { + font-size: 1.4rem; + line-height: 1.5rem; + + &:hover { + @extend %link-hover; + } + + @extend %link-color; + @extend %no-bottom-border; + @extend %heading; + } + + > article { + width: 100%; + + &:not(:last-child) { + margin-bottom: 1rem; + } + + /* icons */ + i { + color: #818182; + margin-right: 0.15rem; + font-size: 80%; + } + + > p { + @extend %text-ellipsis; + + white-space: break-spaces; + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + } + } +} /* #search-results */ + +#topbar-title { + display: none; + font-size: 1.1rem; + font-weight: 600; + font-family: sans-serif; + color: var(--topbar-text-color); + text-align: center; + width: 70%; + word-break: keep-all; +} + +#mask { + inset: 0 0 0 0; +} + +/* --- basic wrappers --- */ + +#main-wrapper { + position: relative; + + @include pl-pr(0); + + > .container { + min-height: 100vh; + } +} + +#topbar-wrapper.row, +#main-wrapper > .container > .row, +#search-result-wrapper > .row { + @include ml-mr(0); +} + +#tail-wrapper { + > :not(script) { + margin-top: 3rem; + } +} + +/* --- button back-to-top --- */ + +#back-to-top { + visibility: hidden; + opacity: 0; + z-index: 1; + cursor: pointer; + position: fixed; + right: 1rem; + bottom: calc($footer-height-large - $back2top-size / 2); + background: var(--button-bg); + color: var(--btn-backtotop-color); + padding: 0; + width: $back2top-size; + height: $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; + + &:hover { + transform: translate3d(0, -5px, 0); + -webkit-transform: translate3d(0, -5px, 0); + } + + i { + line-height: $back2top-size; + position: relative; + bottom: 2px; + } + + &.show { + opacity: 1; + visibility: visible; + } +} + +#notification { + @-webkit-keyframes popup { + from { + opacity: 0; + bottom: 0; + } + } + + @keyframes popup { + from { + opacity: 0; + bottom: 0; + } + } + + .toast-header { + background: none; + border-bottom: none; + color: inherit; + } + + .toast-body { + font-family: Lato, sans-serif; + line-height: 1.25rem; + + button { + font-size: 90%; + min-width: 4rem; + } + } + + &.toast { + &.show { + display: block; + min-width: 20rem; + border-radius: 0.5rem; + -webkit-backdrop-filter: blur(10px); + backdrop-filter: blur(10px); + background-color: rgba(255, 255, 255, 0.5); + color: #1b1b1eba; + position: fixed; + left: 50%; + bottom: 20%; + transform: translateX(-50%); + -webkit-animation: popup 0.8s; + animation: popup 0.8s; + } + } +} + +/* + Responsive Design: + + {sidebar, content, panel} >= 1200px screen width + {sidebar, content} >= 850px screen width + {content} <= 849px screen width + +*/ + +@media all and (max-width: 576px) { + main { + .content { + > blockquote[class^='prompt-'] { + @include ml-mr(-1rem); + + border-radius: 0; + max-width: none; + } + } + } + + #avatar { + width: 5rem; + height: 5rem; + } +} + +@media all and (max-width: 768px) { + %full-width { + max-width: 100%; + } + + #topbar { + @extend %full-width; + } + + #main-wrapper > .container { + @extend %full-width; + @include pl-pr(0); + } +} + +/* hide sidebar and panel */ +@media all and (max-width: 849px) { + @mixin slide($append: null) { + $basic: transform 0.4s ease; + + @if $append { + transition: $basic, $append; + } @else { + transition: $basic; + } + } + + footer { + @include slide; + + height: $footer-height-large; + padding: 1.5rem 0; + } + + [#{$sidebar-display}] { + #sidebar { + transform: translateX(0); + } + + #main-wrapper { + transform: translateX($sidebar-width); + } + + #back-to-top { + visibility: hidden; + } + } + + #sidebar { + @include slide; + + transform: translateX(-$sidebar-width); /* hide */ + -webkit-transform: translateX(-$sidebar-width); + } + + #main-wrapper { + @include slide; + } + + #topbar, + #main-wrapper > .container { + max-width: 100%; + } + + #search-result-wrapper { + width: 100%; + } + + #breadcrumb, + search { + display: none; + } + + #topbar-wrapper { + @include slide(top 0.2s ease); + + left: 0; + } + + main, + #panel-wrapper { + margin-top: 0; + } + + #topbar-title, + #sidebar-trigger, + #search-trigger { + display: block; + } + + #search-result-wrapper .content { + letter-spacing: 0; + } + + #tags { + justify-content: center !important; + } + + h1.dynamic-title { + display: none; + + ~ .content { + margin-top: 2.5rem; + } + } +} /* max-width: 849px */ + +/* Sidebar is visible */ +@media all and (min-width: 850px) { + /* Solved jumping scrollbar */ + html { + overflow-y: scroll; + } + + #main-wrapper { + margin-left: $sidebar-width; + } + + #sidebar { + .profile-wrapper { + margin-top: 3rem; + } + } + + #search-hints { + display: none; + } + + search { + max-width: $search-max-width; + } + + #search-result-wrapper { + max-width: $main-content-max-width; + justify-content: start !important; + } + + main { + h1 { + margin-top: 3rem; + } + } + + div.content .table-wrapper > table { + min-width: 70%; + } + + /* button 'back-to-Top' position */ + #back-to-top { + right: 5%; + bottom: calc($footer-height - $back2top-size / 2); + } + + #topbar-title { + text-align: left; + } +} + +/* Pad horizontal */ +@media all and (min-width: 992px) and (max-width: 1199px) { + #main-wrapper > .container .col-lg-11 { + flex: 0 0 96%; + max-width: 96%; + } +} + +/* Compact icons in sidebar & panel hidden */ +@media all and (min-width: 850px) and (max-width: 1199px) { + #search-results > div { + max-width: 700px; + } + + #breadcrumb { + width: 65%; + overflow: hidden; + text-overflow: ellipsis; + word-break: keep-all; + white-space: nowrap; + } +} + +/* panel hidden */ +@media all and (max-width: 1199px) { + #panel-wrapper { + display: none; + } + + #main-wrapper > .container > div.row { + justify-content: center !important; + } +} + +/* --- desktop mode, both sidebar and panel are visible --- */ + +@media all and (min-width: 1200px) { + search { + margin-right: 4rem; + } + + #search-input { + transition: all 0.3s ease-in-out; + } + + #search-results > article { + width: 45%; + + &:nth-child(odd) { + margin-right: 1.5rem; + } + + &:nth-child(even) { + margin-left: 1.5rem; + } + + &:last-child:nth-child(odd) { + position: relative; + right: 24.3%; + } + } + + .content { + font-size: 1.03rem; + } +} + +@media all and (min-width: 1400px) { + #back-to-top { + right: calc((100vw - $sidebar-width - 1140px) / 2 + 3rem); + } +} + +@media all and (min-width: 1650px) { + $icon-gap: 1rem; + + #main-wrapper { + margin-left: $sidebar-width-large; + } + + #topbar-wrapper { + left: $sidebar-width-large; + } + + search { + margin-right: calc( + $main-content-max-width / 4 - $search-max-width - 0.75rem + ); + } + + #main-wrapper > .container { + max-width: $main-content-max-width; + + @include pl-pr(1.75rem, true); + } + + main.col-12, + #tail-wrapper { + padding-right: 4.5rem !important; + } + + #back-to-top { + right: calc( + (100vw - $sidebar-width-large - $main-content-max-width) / 2 + 2rem + ); + } + + #sidebar { + width: $sidebar-width-large; + + .profile-wrapper { + margin-top: 3.5rem; + margin-bottom: 2.5rem; + padding-left: 3.5rem; + } + + ul { + li.nav-item { + @include pl-pr(2.75rem); + } + } + + .sidebar-bottom { + padding-left: 2.75rem; + margin-bottom: 1.75rem; + + a:not(:last-child) { + margin-right: $sb-btn-gap-lg; + } + + .icon-border { + @include ml-mr(calc(($sb-btn-gap-lg - $btn-border-width) / 2)); + } + } + } +} /* min-width: 1650px */ diff --git a/_sass/addon/module.scss b/_sass/addon/module.scss new file mode 100644 index 0000000..1dfb735 --- /dev/null +++ b/_sass/addon/module.scss @@ -0,0 +1,215 @@ +/* +* Mainly scss modules, only imported to `assets/css/main.scss` +*/ + +/* ---------- scss placeholder --------- */ + +%heading { + color: var(--heading-color); + font-weight: 400; + font-family: $font-family-heading; + scroll-margin-top: 3.5rem; +} + +%anchor { + .anchor { + font-size: 80%; + } + + @media (hover: hover) { + .anchor { + visibility: hidden; + opacity: 0; + transition: opacity 0.25s ease-in, visibility 0s ease-in 0.25s; + } + + &:hover { + .anchor { + visibility: visible; + opacity: 1; + transition: opacity 0.25s ease-in, visibility 0s ease-in 0s; + } + } + } +} + +%tag-hover { + background: var(--tag-hover); + transition: background 0.35s ease-in-out; +} + +%table-cell { + padding: 0.4rem 1rem; + font-size: 95%; + white-space: nowrap; +} + +%link-hover { + color: #d2603a !important; + border-bottom: 1px solid #d2603a; + text-decoration: none; +} + +%link-color { + color: var(--link-color); +} + +%link-underline { + border-bottom: 1px solid var(--link-underline-color); +} + +%clickable-transition { + transition: all 0.3s ease-in-out; +} + +%no-cursor { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +%no-bottom-border { + border-bottom: none; +} + +%cursor-pointer { + cursor: pointer; +} + +%normal-font-style { + font-style: normal; +} + +%rounded { + border-radius: $radius-lg; +} + +%img-caption { + + em { + display: block; + text-align: center; + font-style: normal; + font-size: 80%; + padding: 0; + color: #6d6c6c; + } +} + +%sidebar-links { + color: var(--sidebar-muted-color); + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +%text-clip { + display: -webkit-box; + overflow: hidden; + text-overflow: ellipsis; + -webkit-line-clamp: 2; + -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; +} + +%text-sm { + font-size: 0.85rem; +} + +%text-xs { + font-size: 0.8rem; +} + +%sup-fn-target { + &:target { + background-color: var(--footnote-target-bg); + width: -moz-fit-content; + width: -webkit-fit-content; + width: fit-content; + transition: background-color 1.75s ease-in-out; + } +} + +%btn-color { + button i { + color: #999999; + } +} + +/* ---------- scss mixin --------- */ + +@mixin mt-mb($value) { + margin-top: $value; + margin-bottom: $value; +} + +@mixin ml-mr($value) { + margin-left: $value; + margin-right: $value; +} + +@mixin pt-pb($val) { + padding-top: $val; + padding-bottom: $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 { + color: var(--text-muted-color) !important; +} + +@mixin placeholder-focus { + opacity: 0.6; +} + +@mixin label($font-size: 1rem, $font-weight: 600, $color: var(--label-color)) { + color: $color; + font-size: $font-size; + font-weight: $font-weight; +} + +@mixin align-center { + position: relative; + left: 50%; + transform: translateX(-50%); +} + +@mixin prompt($type, $fa-content, $fa-style: 'solid', $rotate: 0) { + &.prompt-#{$type} { + background-color: var(--prompt-#{$type}-bg); + + &::before { + content: $fa-content; + color: var(--prompt-#{$type}-icon-color); + font: var(--fa-font-#{$fa-style}); + + @if $rotate != 0 { + transform: rotate(#{$rotate}deg); + } + } + } +} diff --git a/_sass/addon/syntax.scss b/_sass/addon/syntax.scss new file mode 100644 index 0000000..6bd7b40 --- /dev/null +++ b/_sass/addon/syntax.scss @@ -0,0 +1,292 @@ +/* +* The syntax highlight. +*/ + +@import 'colors/syntax-light'; +@import 'colors/syntax-dark'; + +html { + @media (prefers-color-scheme: light) { + &:not([data-mode]), + &[data-mode='light'] { + @include light-syntax; + } + + &[data-mode='dark'] { + @include dark-syntax; + } + } + + @media (prefers-color-scheme: dark) { + &:not([data-mode]), + &[data-mode='dark'] { + @include dark-syntax; + } + + &[data-mode='light'] { + @include light-syntax; + } + } +} + +/* -- code snippets -- */ + +%code-snippet-bg { + background-color: var(--highlight-bg-color); +} + +%code-snippet-padding { + padding-left: 1rem; + padding-right: 1.5rem; +} + +.highlighter-rouge { + color: var(--highlighter-rouge-color); + margin-top: 0.5rem; + margin-bottom: 1.2em; /* Override BS Inline-code style */ +} + +.highlight { + @extend %rounded; + @extend %code-snippet-bg; + + overflow: auto; + padding-bottom: 0.75rem; + + @at-root figure#{&} { + @extend %code-snippet-bg; + } + + pre { + margin-bottom: 0; + font-size: $code-font-size; + line-height: 1.4rem; + word-wrap: normal; /* Fixed Safari overflow-x */ + } + + table { + td { + &:first-child { + display: inline-block; + margin-left: 1rem; + margin-right: 0.75rem; + } + + &:last-child { + padding-right: 2rem !important; + } + + pre { + overflow: visible; /* Fixed iOS safari overflow-x */ + word-break: normal; /* Fixed iOS safari linenos code break */ + } + } + } + + .lineno { + text-align: right; + color: var(--highlight-lineno-color); + -webkit-user-select: none; + -moz-user-select: none; + -o-user-select: none; + -ms-user-select: none; + user-select: none; + } +} /* .highlight */ + +code { + -webkit-hyphens: none; + -ms-hyphens: none; + hyphens: none; + color: var(--code-color); + + &.highlighter-rouge { + font-size: $code-font-size; + padding: 3px 5px; + word-break: break-word; + border-radius: $radius-sm; + background-color: var(--inline-code-bg); + } + + &.filepath { + background-color: inherit; + color: var(--filepath-text-color); + font-weight: 600; + padding: 0; + } + + a > &.highlighter-rouge { + padding-bottom: 0; /* show link's underlinke */ + color: inherit; + } + + a:hover > &.highlighter-rouge { + border-bottom: none; + } + + blockquote & { + color: inherit; + } +} + +td.rouge-code { + @extend %code-snippet-padding; + + /* + Prevent some browser extends from + changing the URL string of code block. + */ + a { + color: inherit !important; + border-bottom: none !important; + pointer-events: none; + } +} + +div[class^='language-'] { + @extend %rounded; + @extend %code-snippet-bg; + + box-shadow: var(--language-border-color) 0 0 0 1px; + + .content > & { + @include ml-mr(-1rem); + + border-radius: 0; + } + + .highlight { + border-top-left-radius: 0; + border-top-right-radius: 0; + } +} + +/* Hide line numbers for default, console, and terminal code snippets */ +div { + &.nolineno, + &.language-plaintext, + &.language-console, + &.language-terminal { + td:first-child { + padding: 0 !important; + margin-right: 0; + + .lineno { + display: none; + } + } + } +} + +.code-header { + @extend %no-cursor; + + display: flex; + justify-content: space-between; + align-items: center; + height: $code-header-height; + margin-left: 0.75rem; + margin-right: 0.25rem; + + /* the label block */ + span { + line-height: $code-header-height; + + /* label icon */ + i { + font-size: 1rem; + width: $code-icon-width; + color: var(--code-header-icon-color); + + &.small { + font-size: 70%; + } + } + + @at-root [file] #{&} > i { + position: relative; + top: 1px; /* center the file icon */ + } + + /* label text */ + &::after { + content: attr(data-label-text); + font-size: 0.85rem; + font-weight: 600; + color: var(--code-header-text-color); + } + } + + /* clipboard */ + button { + @extend %cursor-pointer; + @extend %rounded; + + border: 1px solid transparent; + height: $code-header-height; + width: $code-header-height; + padding: 0; + background-color: inherit; + + i { + color: var(--code-header-icon-color); + } + + &[timeout] { + &:hover { + border-color: var(--clipboard-checked-color); + } + + i { + color: var(--clipboard-checked-color); + } + } + + &:focus { + outline: none; + } + + &:not([timeout]):hover { + background-color: rgba(128, 128, 128, 0.37); + + i { + color: white; + } + } + } +} + +@media all and (min-width: 576px) { + div[class^='language-'] { + .content > & { + @include ml-mr(0); + + border-radius: $radius-lg; + } + + .code-header { + @include ml-mr(0); + + $dot-margin: 1rem; + + &::before { + content: ''; + display: inline-block; + margin-left: $dot-margin; + width: $code-dot-size; + height: $code-dot-size; + border-radius: 50%; + background-color: var(--code-header-muted-color); + box-shadow: ($code-dot-size + $code-dot-gap) 0 0 + var(--code-header-muted-color), + ($code-dot-size + $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); + } + } + } +} diff --git a/_sass/addon/variables.scss b/_sass/addon/variables.scss new file mode 100644 index 0000000..1d51cb1 --- /dev/null +++ b/_sass/addon/variables.scss @@ -0,0 +1,34 @@ +/* + * The SCSS variables + */ + +/* sidebar */ + +$sidebar-width: 260px !default; /* the basic width */ +$sidebar-width-large: 300px !default; /* screen width: >= 1650px */ +$sb-btn-gap: 0.8rem !default; +$sb-btn-gap-lg: 1rem !default; + +/* other framework sizes */ + +$topbar-height: 3rem !default; +$search-max-width: 200px !default; +$footer-height: 5rem !default; +$footer-height-large: 6rem !default; /* screen width: < 850px */ +$main-content-max-width: 1250px !default; +$radius-sm: 6px !default; +$radius-lg: 10px !default; +$back2top-size: 2.75rem !default; + +/* syntax highlight */ + +$code-font-size: 0.85rem !default; +$code-header-height: 2.25rem !default; +$code-dot-size: 0.75rem !default; +$code-dot-gap: 0.5rem !default; +$code-icon-width: 1.75rem !default; + +/* fonts */ + +$font-family-base: 'Source Sans Pro', 'Microsoft Yahei', sans-serif !default; +$font-family-heading: Lato, 'Microsoft Yahei', sans-serif !default; diff --git a/_sass/colors/syntax-dark.scss b/_sass/colors/syntax-dark.scss new file mode 100644 index 0000000..eb92204 --- /dev/null +++ b/_sass/colors/syntax-dark.scss @@ -0,0 +1,164 @@ +/* + * The syntax dark mode styles. + */ + +@mixin dark-syntax { + --language-border-color: #2d2d2d; + --highlight-bg-color: #151515; + --highlighter-rouge-color: #c9def1; + --highlight-lineno-color: #808080; + --inline-code-bg: rgba(255, 255, 255, 0.05); + --code-color: #b0b0b0; + --code-header-text-color: #6a6a6a; + --code-header-muted-color: #353535; + --code-header-icon-color: #565656; + --clipboard-checked-color: #2bcc2b; + --filepath-text-color: #cacaca; + + .highlight .gp { + color: #87939d; + } + + /* --- Syntax highlight theme from `rougify style base16.dark` --- */ + + .highlight table td { + padding: 5px; + } + + .highlight table pre { + margin: 0; + } + + .highlight, + .highlight .w { + color: #d0d0d0; + background-color: #151515; + } + + .highlight .err { + color: #151515; + background-color: #ac4142; + } + + .highlight .c, + .highlight .ch, + .highlight .cd, + .highlight .cm, + .highlight .cpf, + .highlight .c1, + .highlight .cs { + color: #848484; + } + + .highlight .cp { + color: #f4bf75; + } + + .highlight .nt { + color: #f4bf75; + } + + .highlight .o, + .highlight .ow { + color: #d0d0d0; + } + + .highlight .p, + .highlight .pi { + color: #d0d0d0; + } + + .highlight .gi { + color: #90a959; + } + + .highlight .gd { + color: #f08a8b; + background-color: #320000; + } + + .highlight .gh { + color: #6a9fb5; + background-color: #151515; + font-weight: bold; + } + + .highlight .k, + .highlight .kn, + .highlight .kp, + .highlight .kr, + .highlight .kv { + color: #aa759f; + } + + .highlight .kc { + color: #d28445; + } + + .highlight .kt { + color: #d28445; + } + + .highlight .kd { + color: #d28445; + } + + .highlight .s, + .highlight .sb, + .highlight .sc, + .highlight .dl, + .highlight .sd, + .highlight .s2, + .highlight .sh, + .highlight .sx, + .highlight .s1 { + color: #90a959; + } + + .highlight .sa { + color: #aa759f; + } + + .highlight .sr { + color: #75b5aa; + } + + .highlight .si { + color: #b76d45; + } + + .highlight .se { + color: #b76d45; + } + + .highlight .nn { + color: #f4bf75; + } + + .highlight .nc { + color: #f4bf75; + } + + .highlight .no { + color: #f4bf75; + } + + .highlight .na { + color: #6a9fb5; + } + + .highlight .m, + .highlight .mb, + .highlight .mf, + .highlight .mh, + .highlight .mi, + .highlight .il, + .highlight .mo, + .highlight .mx { + color: #90a959; + } + + .highlight .ss { + color: #90a959; + } +} diff --git a/_sass/colors/syntax-light.scss b/_sass/colors/syntax-light.scss new file mode 100644 index 0000000..76aa669 --- /dev/null +++ b/_sass/colors/syntax-light.scss @@ -0,0 +1,210 @@ +/* + * The syntax light mode code snippet colors. + */ + +@mixin light-syntax { + /* --- custom light colors --- */ + --language-border-color: #ececec; + --highlight-bg-color: #f6f8fa; + --highlighter-rouge-color: #3f596f; + --highlight-lineno-color: #9e9e9e; + --inline-code-bg: rgba(25, 25, 28, 0.05); + --code-color: #3a3a3a; + --code-header-text-color: #a3a3a3; + --code-header-muted-color: #e5e5e5; + --code-header-icon-color: #c9c8c8; + --clipboard-checked-color: #43c743; + + /* --- Syntax highlight theme from `rougify style github` --- */ + + .highlight table td { + padding: 5px; + } + + .highlight table pre { + margin: 0; + } + + .highlight, + .highlight .w { + color: #24292f; + background-color: #f6f8fa; + } + + .highlight .k, + .highlight .kd, + .highlight .kn, + .highlight .kp, + .highlight .kr, + .highlight .kt, + .highlight .kv { + color: #cf222e; + } + + .highlight .gr { + color: #f6f8fa; + } + + .highlight .gd { + color: #82071e; + background-color: #ffebe9; + } + + .highlight .nb { + color: #953800; + } + + .highlight .nc { + color: #953800; + } + + .highlight .no { + color: #953800; + } + + .highlight .nn { + color: #953800; + } + + .highlight .sr { + color: #116329; + } + + .highlight .na { + color: #116329; + } + + .highlight .nt { + color: #116329; + } + + .highlight .gi { + color: #116329; + background-color: #dafbe1; + } + + .highlight .kc { + color: #0550ae; + } + + .highlight .l, + .highlight .ld, + .highlight .m, + .highlight .mb, + .highlight .mf, + .highlight .mh, + .highlight .mi, + .highlight .il, + .highlight .mo, + .highlight .mx { + color: #0550ae; + } + + .highlight .sb { + color: #0550ae; + } + + .highlight .bp { + color: #0550ae; + } + + .highlight .ne { + color: #0550ae; + } + + .highlight .nl { + color: #0550ae; + } + + .highlight .py { + color: #0550ae; + } + + .highlight .nv, + .highlight .vc, + .highlight .vg, + .highlight .vi, + .highlight .vm { + color: #0550ae; + } + + .highlight .o, + .highlight .ow { + color: #0550ae; + } + + .highlight .gh { + color: #0550ae; + font-weight: bold; + } + + .highlight .gu { + color: #0550ae; + font-weight: bold; + } + + .highlight .s, + .highlight .sa, + .highlight .sc, + .highlight .dl, + .highlight .sd, + .highlight .s2, + .highlight .se, + .highlight .sh, + .highlight .sx, + .highlight .s1, + .highlight .ss { + color: #0a3069; + } + + .highlight .nd { + color: #8250df; + } + + .highlight .nf, + .highlight .fm { + color: #8250df; + } + + .highlight .err { + color: #f6f8fa; + background-color: #82071e; + } + + .highlight .c, + .highlight .ch, + .highlight .cd, + .highlight .cm, + .highlight .cp, + .highlight .cpf, + .highlight .c1, + .highlight .cs { + color: #68717a; + } + + .highlight .gl { + color: #68717a; + } + + .highlight .gt { + color: #68717a; + } + + .highlight .ni { + color: #24292f; + } + + .highlight .si { + color: #24292f; + } + + .highlight .ge { + color: #24292f; + font-style: italic; + } + + .highlight .gs { + color: #24292f; + font-weight: bold; + } +} /* light-syntax */ diff --git a/_sass/colors/typography-dark.scss b/_sass/colors/typography-dark.scss new file mode 100644 index 0000000..664c936 --- /dev/null +++ b/_sass/colors/typography-dark.scss @@ -0,0 +1,147 @@ +/* + * The main dark mode styles + */ + +@mixin dark-scheme { + /* Framework color */ + --main-bg: rgb(27, 27, 30); + --mask-bg: rgb(68, 69, 70); + --main-border-color: rgb(44, 45, 45); + + /* Common color */ + --text-color: rgb(175, 176, 177); + --text-muted-color: #868686; + --text-muted-highlight-color: #aeaeae; + --heading-color: #cccccc; + --label-color: #a7a7a7; + --blockquote-border-color: rgb(66, 66, 66); + --blockquote-text-color: #868686; + --link-color: rgb(138, 180, 248); + --link-underline-color: rgb(82, 108, 150); + --button-bg: #1e1e1e; + --btn-border-color: #2e2f31; + --btn-backtotop-color: var(--text-color); + --btn-backtotop-border-color: #212122; + --card-header-bg: #292929; + --checkbox-color: rgb(118, 120, 121); + --checkbox-checked-color: var(--link-color); + --img-bg: radial-gradient(circle, rgb(22, 22, 24) 0%, rgb(32, 32, 32) 100%); + --shimmer-bg: linear-gradient( + 90deg, + rgba(255, 255, 255, 0) 0%, + rgba(58, 55, 55, 0.4) 50%, + rgba(255, 255, 255, 0) 100% + ); + + /* Sidebar */ + --site-title-color: #717070; + --site-subtitle-color: #868686; + --sidebar-bg: #1e1e1e; + --sidebar-border-color: #292929; + --sidebar-muted-color: #868686; + --sidebar-active-color: rgb(255, 255, 255, 0.95); + --sidebar-hover-bg: #262626; + --sidebar-btn-bg: #232328; + --sidebar-btn-color: #787878; + --avatar-border-color: rgb(206, 206, 206, 0.9); + + /* Topbar */ + --topbar-bg: rgb(27, 27, 30, 0.64); + --topbar-text-color: var(--text-color); + --search-border-color: rgb(55, 55, 55); + --search-icon-color: rgb(100, 102, 105); + --input-focus-border-color: rgb(112, 114, 115); + + /* Home page */ + --post-list-text-color: rgb(175, 176, 177); + --btn-patinator-text-color: var(--text-color); + --btn-paginator-hover-color: #2e2e2e; + + /* 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 */ + --tb-border-color: var(--tb-odd-bg); + --footnote-target-bg: rgb(63, 81, 181); + --btn-share-color: #6c757d; + --btn-share-hover-color: #bfc1ca; + --card-bg: #1e1e1e; + --card-hovor-bg: #464d51; + --card-shadow: rgb(21, 21, 21, 0.72) 0 6px 18px 0, + rgb(137, 135, 135, 0.24) 0 0 0 1px; + --kbd-wrap-color: #6a6a6a; + --kbd-text-color: #d3d3d3; + --kbd-bg-color: #242424; + --prompt-text-color: rgb(216, 212, 212, 0.75); + --prompt-tip-bg: rgb(22, 60, 36, 0.64); + --prompt-tip-icon-color: rgb(15, 164, 15, 0.81); + --prompt-info-bg: rgb(7, 59, 104, 0.8); + --prompt-info-icon-color: #0075d1; + --prompt-warning-bg: rgb(90, 69, 3, 0.88); + --prompt-warning-icon-color: rgb(255, 165, 0, 0.8); + --prompt-danger-bg: rgb(86, 28, 8, 0.8); + --prompt-danger-icon-color: #cd0202; + + /* Tags */ + --tag-border: rgb(59, 79, 88); + --tag-shadow: rgb(32, 33, 33); + --dash-color: rgb(63, 65, 68); + --search-tag-bg: #292828; + + /* Categories */ + --categories-border: rgb(64, 66, 69, 0.5); + --categories-hover-bg: rgb(73, 75, 76); + --categories-icon-hover-color: white; + + /* Archive */ + --timeline-node-bg: rgb(150, 152, 156); + --timeline-color: rgb(63, 65, 68); + --timeline-year-dot-color: var(--timeline-color); + + color-scheme: dark; + + .light { + display: none; + } + + /* Categories */ + .categories.card, + .list-group-item { + background-color: var(--card-bg); + } + + .categories { + .card-header { + background-color: var(--card-header-bg); + } + + .list-group-item { + border-left: none; + border-right: none; + padding-left: 2rem; + border-color: var(--categories-border); + + &:last-child { + border-bottom-color: var(--card-bg); + } + } + } + + #archives li:nth-child(odd) { + background-image: linear-gradient( + to left, + rgb(26, 26, 30), + rgb(39, 39, 45), + rgb(39, 39, 45), + rgb(39, 39, 45), + rgb(26, 26, 30) + ); + } + + /* stylelint-disable-next-line selector-id-pattern */ + #disqus_thread { + color-scheme: none; + } +} /* dark-scheme */ diff --git a/_sass/colors/typography-light.scss b/_sass/colors/typography-light.scss new file mode 100644 index 0000000..b6fc561 --- /dev/null +++ b/_sass/colors/typography-light.scss @@ -0,0 +1,112 @@ +/* + * The syntax light mode typography colors + */ + +@mixin light-scheme { + /* Framework color */ + --main-bg: white; + --mask-bg: #c1c3c5; + --main-border-color: #f3f3f3; + + /* Common color */ + --text-color: #34343c; + --text-muted-color: #757575; + --text-muted-highlight-color: inherit; + --heading-color: #2a2a2a; + --label-color: #585858; + --blockquote-border-color: #eeeeee; + --blockquote-text-color: #757575; + --link-color: #0056b2; + --link-underline-color: #dee2e6; + --button-bg: #ffffff; + --btn-border-color: #e9ecef; + --btn-backtotop-color: #686868; + --btn-backtotop-border-color: #f1f1f1; + --checkbox-color: #c5c5c5; + --checkbox-checked-color: #07a8f7; + --img-bg: radial-gradient( + circle, + rgb(255, 255, 255) 0%, + rgb(239, 239, 239) 100% + ); + --shimmer-bg: linear-gradient( + 90deg, + rgba(250, 250, 250, 0) 0%, + rgba(232, 230, 230, 1) 50%, + rgba(250, 250, 250, 0) 100% + ); + + /* Sidebar */ + --site-title-color: rgb(113, 113, 113); + --site-subtitle-color: #717171; + --sidebar-bg: #f6f8fa; + --sidebar-border-color: #efefef; + --sidebar-muted-color: #545454; + --sidebar-active-color: #1d1d1d; + --sidebar-hover-bg: rgb(223, 233, 241, 0.64); + --sidebar-btn-bg: white; + --sidebar-btn-color: #8e8e8e; + --avatar-border-color: white; + + /* Topbar */ + --topbar-bg: rgb(255, 255, 255, 0.7); + --topbar-text-color: rgb(78, 78, 78); + --search-border-color: rgb(240, 240, 240); + --search-icon-color: #c2c6cc; + --input-focus-border-color: #b8b8b8; + + /* Home page */ + --post-list-text-color: dimgray; + --btn-patinator-text-color: #555555; + --btn-paginator-hover-color: var(--sidebar-bg); + + /* Posts */ + --toc-highlight: #0550ae; + --toc-popup-border-color: lightgray; + --btn-share-color: gray; + --btn-share-hover-color: #0d6efd; + --card-bg: white; + --card-hovor-bg: #e2e2e2; + --card-shadow: rgb(104, 104, 104, 0.05) 0 2px 6px 0, + rgba(211, 209, 209, 0.15) 0 0 0 1px; + --footnote-target-bg: lightcyan; + --tb-odd-bg: #fbfcfd; + --tb-border-color: #eaeaea; + --dash-color: silver; + --kbd-wrap-color: #bdbdbd; + --kbd-text-color: var(--text-color); + --kbd-bg-color: white; + --prompt-text-color: rgb(46, 46, 46, 0.77); + --prompt-tip-bg: rgb(123, 247, 144, 0.2); + --prompt-tip-icon-color: #03b303; + --prompt-info-bg: #e1f5fe; + --prompt-info-icon-color: #0070cb; + --prompt-warning-bg: rgb(255, 243, 205); + --prompt-warning-icon-color: #ef9c03; + --prompt-danger-bg: rgb(248, 215, 218, 0.56); + --prompt-danger-icon-color: #df3c30; + + /* Tags */ + --tag-border: #dee2e6; + --tag-shadow: var(--btn-border-color); + --tag-hover: rgb(222, 226, 230); + --search-tag-bg: #f8f9fa; + + /* Categories */ + --categories-border: rgba(0, 0, 0, 0.125); + --categories-hover-bg: var(--btn-border-color); + --categories-icon-hover-color: darkslategray; + + /* Archive */ + --timeline-color: rgba(0, 0, 0, 0.075); + --timeline-node-bg: #c2c6cc; + --timeline-year-dot-color: #ffffff; + + [class^='prompt-'] { + --link-underline-color: rgb(219, 216, 216); + } + + .dark { + display: none; + } +} /* light-scheme */ diff --git a/_sass/layout/archives.scss b/_sass/layout/archives.scss new file mode 100644 index 0000000..fd1979b --- /dev/null +++ b/_sass/layout/archives.scss @@ -0,0 +1,143 @@ +/* + Style for Archives +*/ + +#archives { + letter-spacing: 0.03rem; + + $timeline-width: 4px; + + %timeline { + content: ''; + width: $timeline-width; + position: relative; + float: left; + background-color: var(--timeline-color); + } + + .year { + height: 3.5rem; + font-size: 1.5rem; + position: relative; + left: 2px; + margin-left: -$timeline-width; + + &::before { + @extend %timeline; + + height: 72px; + left: 79px; + bottom: 16px; + } + + &:first-child::before { + @extend %timeline; + + height: 32px; + top: 24px; + } + + /* Year dot */ + &::after { + content: ''; + display: inline-block; + position: relative; + border-radius: 50%; + width: 12px; + height: 12px; + left: 21.5px; + border: 3px solid; + background-color: var(--timeline-year-dot-color); + border-color: var(--timeline-node-bg); + box-shadow: 0 0 2px 0 #c2c6cc; + z-index: 1; + } + } + + ul { + li { + font-size: 1.1rem; + line-height: 3rem; + + @extend %text-ellipsis; + + &:nth-child(odd) { + background-color: var(--main-bg, #ffffff); + background-image: linear-gradient( + to left, + #ffffff, + #fbfbfb, + #fbfbfb, + #fbfbfb, + #ffffff + ); + } + + &::before { + @extend %timeline; + + top: 0; + left: 77px; + height: 3.1rem; + } + } + + &:last-child li:last-child::before { + height: 1.5rem; + } + } /* #archives ul */ + + .date { + white-space: nowrap; + display: inline-block; + position: relative; + right: 0.5rem; + + &.month { + width: 1.4rem; + text-align: center; + } + + &.day { + font-size: 85%; + font-family: Lato, sans-serif; + } + } + + a { + /* post title in Archvies */ + margin-left: 2.5rem; + position: relative; + top: 0.1rem; + + &:hover { + border-bottom: none; + } + + &::before { + /* the dot before post title */ + content: ''; + display: inline-block; + position: relative; + border-radius: 50%; + width: 8px; + height: 8px; + float: left; + top: 1.35rem; + left: 71px; + background-color: var(--timeline-node-bg); + box-shadow: 0 0 3px 0 #c2c6cc; + z-index: 1; + } + } +} /* #archives */ + +@media all and (max-width: 576px) { + #archives { + margin-top: -1rem; + + ul { + letter-spacing: 0; + } + } +} diff --git a/_sass/layout/categories.scss b/_sass/layout/categories.scss new file mode 100644 index 0000000..f12b963 --- /dev/null +++ b/_sass/layout/categories.scss @@ -0,0 +1,83 @@ +/* + Style for Tab Categories +*/ + +%category-icon-color { + color: gray; +} + +.categories { + margin-bottom: 2rem; + border-color: var(--categories-border); + + &.card, + .list-group { + @extend %rounded; + } + + .card-header { + $radius: calc($radius-lg - 1px); + + padding: 0.75rem; + border-radius: $radius; + border-bottom: 0; + + &.hide-border-bottom { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + } + } + + i { + @extend %category-icon-color; + + font-size: 86%; /* fontawesome icons */ + } + + .list-group-item { + border-left: none; + border-right: none; + padding-left: 2rem; + + &:first-child { + border-top-left-radius: 0; + border-top-right-radius: 0; + } + + &:last-child { + border-bottom: 0; + } + } +} /* .categories */ + +.category-trigger { + width: 1.7rem; + height: 1.7rem; + border-radius: 50%; + text-align: center; + color: #6c757d !important; + + i { + position: relative; + height: 0.7rem; + width: 1rem; + transition: transform 300ms ease; + } + + &:hover { + i { + color: var(--categories-icon-hover-color); + } + } +} + +/* only works on desktop */ +@media (hover: hover) { + .category-trigger:hover { + background-color: var(--categories-hover-bg); + } +} + +.rotate { + transform: rotate(-90deg); +} diff --git a/_sass/layout/category-tag.scss b/_sass/layout/category-tag.scss new file mode 100644 index 0000000..fe7d99c --- /dev/null +++ b/_sass/layout/category-tag.scss @@ -0,0 +1,70 @@ +/* + Style for page Category and Tag +*/ + +.dash { + margin: 0 0.5rem 0.6rem 0.5rem; + border-bottom: 2px dotted var(--dash-color); +} + +#page-category, +#page-tag { + ul > li { + line-height: 1.5rem; + padding: 0.6rem 0; + + /* dot */ + &::before { + background: #999999; + width: 5px; + height: 5px; + border-radius: 50%; + display: block; + content: ''; + position: relative; + top: 0.6rem; + margin-right: 0.5rem; + } + + /* post's title */ + > a { + @extend %no-bottom-border; + + font-size: 1.1rem; + } + } +} + +/* tag icon */ +#page-tag h1 > i { + font-size: 1.2rem; +} + +#page-category h1 > i { + font-size: 1.25rem; +} + +#page-category, +#page-tag, +#access-lastmod { + a:hover { + @extend %link-hover; + + margin-bottom: -1px; /* Avoid jumping */ + } +} + +@media all and (max-width: 576px) { + #page-category, + #page-tag { + ul > li { + &::before { + margin: 0 0.5rem; + } + + > a { + @include text-ellipsis; + } + } + } +} diff --git a/_sass/layout/home.scss b/_sass/layout/home.scss new file mode 100644 index 0000000..7fff3ba --- /dev/null +++ b/_sass/layout/home.scss @@ -0,0 +1,188 @@ +/* + Style for Homepage +*/ + +#post-list { + margin-top: 2rem; + + .card-wrapper { + &:hover { + text-decoration: none; + } + + &:not(:last-child) { + margin-bottom: 1.25rem; + } + } + + .card { + border: 0; + background: none; + + %img-radius { + border-radius: $radius-lg $radius-lg 0 0; + } + + .preview-img { + @extend %img-radius; + + img { + @extend %img-radius; + } + } + + .card-body { + height: 100%; + padding: 1rem; + + .card-title { + @extend %text-clip; + + color: var(--heading-color) !important; + font-size: 1.25rem; + } + + %muted { + color: var(--text-muted-color) !important; + } + + .card-text.content { + @extend %muted; + + p { + @extend %text-clip; + + line-height: 1.5; + margin: 0; + } + } + + .post-meta { + @extend %muted; + + i { + &:not(:first-child) { + margin-left: 1.5rem; + } + } + + em { + @extend %normal-font-style; + + color: inherit; + } + + > div:first-child { + display: block; + + @extend %text-ellipsis; + } + } + } + } +} /* #post-list */ + +.pagination { + color: var(--text-color); + font-family: Lato, sans-serif; + justify-content: space-evenly; + + a:hover { + text-decoration: none; + } + + .page-item { + .page-link { + color: var(--btn-patinator-text-color); + padding: 0 0.6rem; + display: -webkit-box; + -webkit-box-pack: center; + -webkit-box-align: center; + border-radius: 0.5rem; + border: 0; + background-color: inherit; + } + + &.active { + .page-link { + background-color: var(--btn-paginator-hover-color); + } + } + + &:not(.active) { + .page-link { + &:hover { + box-shadow: inset var(--btn-border-color) 0 0 0 1px; + } + } + } + + &.disabled { + cursor: not-allowed; + + .page-link { + color: rgba(108, 117, 125, 0.57); + } + } + } /* .page-item */ +} /* .pagination */ + +/* Tablet */ +@media all and (min-width: 768px) { + %img-radius { + border-radius: 0 $radius-lg $radius-lg 0; + } + + #post-list { + .card { + .card-body { + padding: 1.75rem 1.75rem 1.25rem 1.75rem; + + .card-text { + display: inherit !important; + } + + .post-meta { + i { + &:not(:first-child) { + margin-left: 1.75rem; + } + } + } + } + } + } +} + +/* Hide SideBar and TOC */ +@media all and (max-width: 830px) { + .pagination { + .page-item { + &:not(:first-child):not(:last-child) { + display: none; + } + } + } +} + +/* Sidebar is visible */ +@media all and (min-width: 831px) { + #post-list { + margin-top: 2.5rem; + } + + .pagination { + font-size: 0.85rem; + justify-content: center; + + .page-item { + &:not(:last-child) { + margin-right: 0.7rem; + } + } + + .page-index { + display: none; + } + } /* .pagination */ +} diff --git a/_sass/layout/post.scss b/_sass/layout/post.scss new file mode 100644 index 0000000..b66e906 --- /dev/null +++ b/_sass/layout/post.scss @@ -0,0 +1,585 @@ +/** + * Post-specific styles + */ + +%btn-post-nav { + width: 50%; + position: relative; + border-color: var(--btn-border-color); +} + +@mixin dot($pl: 0.25rem, $pr: 0.25rem) { + content: '\2022'; + padding-left: $pl; + padding-right: $pr; +} + +header { + .post-desc { + @extend %heading; + + font-size: 1.125rem; + line-height: 1.6; + } + + .post-meta { + span + span::before { + @include dot; + } + + em, + time { + @extend %text-highlight; + } + + em { + a { + color: inherit; + } + } + } + + h1 + .post-meta { + margin-top: 1.5rem; + } +} + +.post-tail-wrapper { + @extend %text-sm; + + margin-top: 6rem; + border-bottom: 1px double var(--main-border-color); + + .license-wrapper { + line-height: 1.2rem; + + > a { + @extend %text-highlight; + + &:hover { + @extend %link-hover; + } + } + + span:last-child { + @extend %text-sm; + } + } /* .license-wrapper */ + + .post-meta a:not(:hover) { + @extend %link-underline; + } + + .share-wrapper { + vertical-align: middle; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + + %icon-size { + font-size: 1.125rem; + } + + .share-icons { + display: flex; + + i { + color: var(--btn-share-color); + + @extend %icon-size; + } + + > * { + @extend %icon-size; + + margin-left: 0.5rem; + + &:hover { + i { + @extend %btn-share-hover; + } + } + } + + button { + padding: 0; + border: none; + line-height: inherit; + + @extend %cursor-pointer; + } + } /* .share-icons */ + } /* .share-wrapper */ +} + +.share-mastodon { + /* See: https://github.com/justinribeiro/share-to-mastodon#properties */ + --wc-stm-font-family: $font-family-base; + --wc-stm-dialog-background-color: var(--card-bg); + --wc-stm-form-button-border: 1px solid var(--btn-border-color); + --wc-stm-form-submit-background-color: var(--sidebar-btn-bg); + --wc-stm-form-cancel-background-color: var(--sidebar-btn-bg); + --wc-stm-form-button-background-color-hover: #007bff; + --wc-stm-form-button-color-hover: white; + + font-size: 1rem; +} + +.post-tags { + line-height: 2rem; + + .post-tag { + &:hover { + @extend %link-hover; + @extend %tag-hover; + @extend %no-bottom-border; + } + } +} + +.post-navigation { + .btn { + @extend %btn-post-nav; + + &:not(:hover) { + color: var(--link-color); + } + + &:hover { + &:not(.disabled)::before { + color: whitesmoke; + } + } + + &.disabled { + @extend %btn-post-nav; + + pointer-events: auto; + cursor: not-allowed; + background: none; + color: gray; + } + + &.btn-outline-primary.disabled:focus { + box-shadow: none; + } + + &::before { + color: var(--text-muted-color); + font-size: 0.65rem; + text-transform: uppercase; + content: attr(aria-label); + } + + &:first-child { + border-radius: $radius-lg 0 0 $radius-lg; + left: 0.5px; + } + + &:last-child { + border-radius: 0 $radius-lg $radius-lg 0; + right: 0.5px; + } + } + + p { + font-size: 1.1rem; + line-height: 1.5rem; + margin-top: 0.3rem; + white-space: normal; + } +} /* .post-navigation */ + +@media (hover: hover) { + .post-navigation { + .btn, + .btn::before { + transition: all 0.35s ease-in-out; + } + } +} + +@-webkit-keyframes fade-up { + from { + opacity: 0; + position: relative; + top: 2rem; + } + + to { + opacity: 1; + position: relative; + top: 0; + } +} + +@keyframes fade-up { + from { + opacity: 0; + position: relative; + top: 2rem; + } + + to { + opacity: 1; + position: relative; + top: 0; + } +} + +/* TOC panel */ +#toc-wrapper { + border-left: 1px solid rgba(158, 158, 158, 0.17); + position: -webkit-sticky; + position: sticky; + top: 4rem; + transition: top 0.2s ease-in-out; + -webkit-animation: fade-up 0.8s; + animation: fade-up 0.8s; + + ul { + list-style: none; + font-size: 0.85rem; + line-height: 1.25; + padding-left: 0; + + li { + &:not(:last-child) { + margin: 0.4rem 0; + } + + a { + padding: 0.2rem 0 0.2rem 1.25rem; + } + } + + /* Overwrite TOC plugin style */ + + .toc-link { + display: block; + + @extend %text-ellipsis; + + &:hover { + color: var(--toc-highlight); + text-decoration: none; + } + + &::before { + display: none; + } + } + + .is-active-link { + color: var(--toc-highlight) !important; + font-weight: 600; + + &::before { + display: inline-block; + width: 1px; + left: -1px; + height: 1.25rem; + background-color: var(--toc-highlight) !important; + } + } + + ul { + padding-left: 0.75rem; + } + } +} + +/* --- TOC button, bar and popup in mobile/tablet --- */ + +#toc-bar { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1; + margin: 0 -1rem; + height: $topbar-height; + background: var(--main-bg); + border-bottom: 1px solid var(--main-border-color); + transition: all 0.2s ease-in-out; + + @extend %btn-color; + + .label { + @extend %heading; + + margin-left: 0.25rem; + padding: 0 0.75rem; + color: inherit; + } + + &.invisible { + top: -$topbar-height; + transition: none; + } +} + +#toc-solo-trigger { + color: var(--text-muted-color); + border-color: var(--btn-border-color); + border-radius: $radius-lg; + + .label { + font-size: 1rem; + font-family: $font-family-heading; + } + + &:hover { + box-shadow: none; + background: none; + } +} + +@mixin slide-in { + from { + opacity: 0.7; + transform: translateY(-$topbar-height); + } + + to { + opacity: 1; + transform: translateY(0); + } +} + +@mixin slide-out { + 0% { + transform: translateY(0); + opacity: 1; + } + + 100% { + transform: translateY(-$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: $radius-lg; + color: var(--text-color); + background: var(--card-bg); + margin-top: $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: $sidebar-width; + } + + .header { + @extend %btn-color; + + position: -webkit-sticky; + position: sticky; + top: 0; + background-color: inherit; + border-bottom: 1px solid var(--main-border-color); + + .label { + font-family: $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 * $topbar-height); + font-family: $font-family-heading; + margin-bottom: -$curtain-height; + } +} + +/* --- Related Posts --- */ + +#related-posts { + > h3 { + @include label(1.1rem, 600); + } + + time { + @extend %normal-font-style; + @extend %text-xs; + + color: var(--text-muted-color); + } + + p { + @extend %text-ellipsis; + + font-size: 0.9rem; + margin-bottom: 0.5rem; + white-space: break-spaces; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + } + + .card { + h4 { + @extend %text-clip; + } + } +} + +/* stylelint-disable-next-line selector-id-pattern */ +#disqus_thread { + min-height: 8.5rem; +} + +.utterances { + max-width: 100%; +} + +%btn-share-hover { + color: var(--btn-share-hover-color) !important; +} + +.share-label { + @include label(inherit, 400, inherit); + + &::after { + content: ':'; + } +} + +@media all and (max-width: 576px) { + .post-tail-bottom { + flex-wrap: wrap-reverse !important; + + > div:first-child { + width: 100%; + margin-top: 1rem; + } + } +} + +@media all and (max-width: 768px) { + .content > p > img { + max-width: calc(100% + 1rem); + } +} + +/* Hide SideBar and TOC */ +@media all and (max-width: 849px) { + .post-navigation { + @include pl-pr(0); + @include 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; + } +} diff --git a/_sass/layout/tags.scss b/_sass/layout/tags.scss new file mode 100644 index 0000000..4cf5d3b --- /dev/null +++ b/_sass/layout/tags.scss @@ -0,0 +1,19 @@ +/* + Styles for Tab Tags +*/ + +.tag { + border-radius: 0.7em; + padding: 6px 8px 7px; + margin-right: 0.8rem; + line-height: 3rem; + letter-spacing: 0; + border: 1px solid var(--tag-border) !important; + box-shadow: 0 0 3px 0 var(--tag-shadow); + + span { + margin-left: 0.6em; + font-size: 0.7em; + font-family: Oswald, sans-serif; + } +} diff --git a/_sass/main.bundle.scss b/_sass/main.bundle.scss new file mode 100644 index 0000000..52e893f --- /dev/null +++ b/_sass/main.bundle.scss @@ -0,0 +1,2 @@ +@import 'dist/bootstrap'; +@import 'main'; diff --git a/_sass/main.scss b/_sass/main.scss new file mode 100644 index 0000000..1c2311d --- /dev/null +++ b/_sass/main.scss @@ -0,0 +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'; diff --git a/_sass/variables-hook.scss b/_sass/variables-hook.scss new file mode 100644 index 0000000..f27e0eb --- /dev/null +++ b/_sass/variables-hook.scss @@ -0,0 +1,3 @@ +/* + Appending custom SCSS variables will override the default ones in `_sass/addon/variables.scsss` +*/ diff --git a/_tabs/about.md b/_tabs/about.md new file mode 100644 index 0000000..ddb2bc4 --- /dev/null +++ b/_tabs/about.md @@ -0,0 +1,8 @@ +--- +# the default layout is 'page' +icon: fas fa-info-circle +order: 4 +--- + +> Add Markdown syntax content to file `_tabs/about.md`{: .filepath } and it will show up on this page. +{: .prompt-tip } diff --git a/_tabs/archives.md b/_tabs/archives.md new file mode 100644 index 0000000..c3abc59 --- /dev/null +++ b/_tabs/archives.md @@ -0,0 +1,5 @@ +--- +layout: archives +icon: fas fa-archive +order: 3 +--- diff --git a/_tabs/categories.md b/_tabs/categories.md new file mode 100644 index 0000000..2d241be --- /dev/null +++ b/_tabs/categories.md @@ -0,0 +1,5 @@ +--- +layout: categories +icon: fas fa-stream +order: 1 +--- diff --git a/_tabs/tags.md b/_tabs/tags.md new file mode 100644 index 0000000..ded3adc --- /dev/null +++ b/_tabs/tags.md @@ -0,0 +1,5 @@ +--- +layout: tags +icon: fas fa-tags +order: 2 +--- diff --git a/assets/404.html b/assets/404.html new file mode 100644 index 0000000..af89d6d --- /dev/null +++ b/assets/404.html @@ -0,0 +1,14 @@ +--- +layout: page +title: "404: Page not found" +permalink: /404.html + +redirect_from: + - /norobots/ + - /assets/ + - /posts/ +--- + +{% include lang.html %} + +

{{ site.data.locales[lang].not_found.statement }}

diff --git a/assets/css/jekyll-theme-chirpy.scss b/assets/css/jekyll-theme-chirpy.scss new file mode 100644 index 0000000..d20545b --- /dev/null +++ b/assets/css/jekyll-theme-chirpy.scss @@ -0,0 +1,10 @@ +--- +--- + +@import 'main +{%- if jekyll.environment == 'production' -%} + .bundle +{%- endif -%} +'; + +/* append your custom style below */ diff --git a/assets/feed.xml b/assets/feed.xml new file mode 100644 index 0000000..d2aad4d --- /dev/null +++ b/assets/feed.xml @@ -0,0 +1,54 @@ +--- +layout: compress +permalink: /feed.xml +# Atom Feed, reference: https://validator.w3.org/feed/docs/atom.html +--- + +{% capture source %} + + {{ "/" | absolute_url }} + {{ site.title }} + {{ site.description }} + {{ site.time | date_to_xmlschema }} + + {{ site.social.name }} + {{ "/" | absolute_url }} + + + + Jekyll + © {{ 'now' | date: '%Y' }} {{ site.social.name }} + {{ site.baseurl }}/assets/img/favicons/favicon.ico + {{ site.baseurl }}/assets/img/favicons/favicon-96x96.png + +{% for post in site.posts limit: 5 %} + {% assign post_absolute_url = post.url | absolute_url %} + + {{ post.title }} + + {{ post.date | date_to_xmlschema }} + {% if post.last_modified_at %} + {{ post.last_modified_at | date_to_xmlschema }} + {% else %} + {{ post.date | date_to_xmlschema }} + {% endif %} + {{ post_absolute_url }} + + + {{ post.author | default: site.social.name }} + + + {% if post.categories %} + {% for category in post.categories %} + + {% endfor %} + {% endif %} + + {% include post-description.html max_length=400 %} + + +{% endfor %} + +{% endcapture %} +{{ source | replace: '&', '&' }} diff --git a/assets/img/favicons/android-chrome-192x192.png b/assets/img/favicons/android-chrome-192x192.png new file mode 100644 index 0000000..47b58e2 Binary files /dev/null and b/assets/img/favicons/android-chrome-192x192.png differ diff --git a/assets/img/favicons/android-chrome-512x512.png b/assets/img/favicons/android-chrome-512x512.png new file mode 100644 index 0000000..7aeb1a7 Binary files /dev/null and b/assets/img/favicons/android-chrome-512x512.png differ diff --git a/assets/img/favicons/apple-touch-icon.png b/assets/img/favicons/apple-touch-icon.png new file mode 100644 index 0000000..90e253a Binary files /dev/null and b/assets/img/favicons/apple-touch-icon.png differ diff --git a/assets/img/favicons/browserconfig.xml b/assets/img/favicons/browserconfig.xml new file mode 100644 index 0000000..a02a5c7 --- /dev/null +++ b/assets/img/favicons/browserconfig.xml @@ -0,0 +1,13 @@ +--- +layout: compress +--- + + + + + + + #da532c + + + diff --git a/assets/img/favicons/favicon-16x16.png b/assets/img/favicons/favicon-16x16.png new file mode 100644 index 0000000..272d2cc Binary files /dev/null and b/assets/img/favicons/favicon-16x16.png differ diff --git a/assets/img/favicons/favicon-32x32.png b/assets/img/favicons/favicon-32x32.png new file mode 100644 index 0000000..3884d0b Binary files /dev/null and b/assets/img/favicons/favicon-32x32.png differ diff --git a/assets/img/favicons/favicon.ico b/assets/img/favicons/favicon.ico new file mode 100644 index 0000000..30ae371 Binary files /dev/null and b/assets/img/favicons/favicon.ico differ diff --git a/assets/img/favicons/mstile-150x150.png b/assets/img/favicons/mstile-150x150.png new file mode 100644 index 0000000..c0d045e Binary files /dev/null and b/assets/img/favicons/mstile-150x150.png differ diff --git a/assets/img/favicons/site.webmanifest b/assets/img/favicons/site.webmanifest new file mode 100644 index 0000000..03c6113 --- /dev/null +++ b/assets/img/favicons/site.webmanifest @@ -0,0 +1,26 @@ +--- +layout: compress +--- + +{% assign favicon_path = "/assets/img/favicons" | relative_url %} + +{ + "name": "{{ site.title }}", + "short_name": "{{ site.title }}", + "description": "{{ site.description }}", + "icons": [ + { + "src": "{{ favicon_path }}/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "{{ favicon_path }}/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + }], + "start_url": "{{ '/index.html' | relative_url }}", + "theme_color": "#2a1e6b", + "background_color": "#ffffff", + "display": "fullscreen" +} diff --git a/assets/js/data/mathjax.js b/assets/js/data/mathjax.js new file mode 100644 index 0000000..ca3d0de --- /dev/null +++ b/assets/js/data/mathjax.js @@ -0,0 +1,25 @@ +--- +layout: compress +# WARNING: Don't use '//' to comment out code, use '{% comment %}' and '{% endcomment %}' instead. +--- + +{%- comment -%} + See: +{%- endcomment -%} + +MathJax = { + tex: { + {%- comment -%} start/end delimiter pairs for in-line math {%- endcomment -%} + inlineMath: [ + ['$', '$'], + ['\\(', '\\)'] + ], + {%- comment -%} start/end delimiter pairs for display math {%- endcomment -%} + displayMath: [ + ['$$', '$$'], + ['\\[', '\\]'] + ], + {%- comment -%} equation numbering {%- endcomment -%} + tags: 'ams' + } +}; diff --git a/assets/js/data/search.json b/assets/js/data/search.json new file mode 100644 index 0000000..2601ed0 --- /dev/null +++ b/assets/js/data/search.json @@ -0,0 +1,20 @@ +--- +layout: compress +swcache: true +--- + +[ + {% for post in site.posts %} + { + "title": {{ post.title | jsonify }}, + "url": {{ post.url | relative_url | jsonify }}, + "categories": {{ post.categories | join: ', ' | jsonify }}, + "tags": {{ post.tags | join: ', ' | jsonify }}, + "date": "{{ post.date }}", + {% include no-linenos.html content=post.content %} + {% assign _content = content | strip_html | strip_newlines %} + "snippet": {{ _content | truncate: 200 | jsonify }}, + "content": {{ _content | jsonify }} + }{% unless forloop.last %},{% endunless %} + {% endfor %} +] diff --git a/assets/js/data/swconf.js b/assets/js/data/swconf.js new file mode 100644 index 0000000..798888a --- /dev/null +++ b/assets/js/data/swconf.js @@ -0,0 +1,47 @@ +--- +layout: compress +permalink: '/:path/swconf.js' +# Note that this file will be fetched by the ServiceWorker, so it will not be cached. +--- + +const swconf = { + {% if site.pwa.cache.enabled %} + cacheName: 'chirpy-{{ "now" | date: "%s" }}', + + {%- comment -%} Resources added to the cache during PWA installation. {%- endcomment -%} + resources: [ + '{{ "/assets/css/:THEME.css" | replace: ':THEME', site.theme | relative_url }}', + '{{ "/" | relative_url }}', + {% for tab in site.tabs %} + '{{- tab.url | relative_url -}}', + {% endfor %} + + {% assign cache_list = site.static_files | where: 'swcache', true %} + {% for file in cache_list %} + '{{ file.path | relative_url }}'{%- unless forloop.last -%},{%- endunless -%} + {% endfor %} + ], + + interceptor: { + {%- comment -%} URLs containing the following paths will not be cached. {%- endcomment -%} + paths: [ + {% for path in site.pwa.cache.deny_paths %} + {% unless path == empty %} + '{{ path | relative_url }}'{%- unless forloop.last -%},{%- endunless -%} + {% endunless %} + {% endfor %} + ], + + {%- comment -%} URLs containing the following prefixes will not be cached. {%- endcomment -%} + urlPrefixes: [ + {% if site.analytics.goatcounter.id != nil and site.pageviews.provider == 'goatcounter' %} + 'https://{{ site.analytics.goatcounter.id }}.goatcounter.com/counter/' + {% endif %} + ] + }, + + purge: false + {% else %} + purge: true + {% endif %} +}; diff --git a/assets/robots.txt b/assets/robots.txt new file mode 100644 index 0000000..45c34e0 --- /dev/null +++ b/assets/robots.txt @@ -0,0 +1,10 @@ +--- +permalink: /robots.txt +# The robots rules +--- + +User-agent: * + +Disallow: /norobots/ + +Sitemap: {{ '/sitemap.xml' | absolute_url }} diff --git a/index.html b/index.html new file mode 100644 index 0000000..1357b08 --- /dev/null +++ b/index.html @@ -0,0 +1,4 @@ +--- +layout: home +# Index page +--- diff --git a/jekyll-theme-chirpy.gemspec b/jekyll-theme-chirpy.gemspec new file mode 100644 index 0000000..9de7dd0 --- /dev/null +++ b/jekyll-theme-chirpy.gemspec @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +Gem::Specification.new do |spec| + spec.name = "jekyll-theme-chirpy" + spec.version = "7.1.1" + spec.authors = ["Cotes Chung"] + spec.email = ["cotes.chung@gmail.com"] + + spec.summary = "A minimal, responsive, and feature-rich Jekyll theme for technical writing." + spec.homepage = "https://github.com/cotes2020/jekyll-theme-chirpy" + spec.license = "MIT" + + spec.files = `git ls-files -z`.split("\x0").select { |f| + f.match(%r!^((_(includes|layouts|sass|(data\/(locales|origin)))|assets)\/|README|LICENSE)!i) + } + + spec.metadata = { + "bug_tracker_uri" => "https://github.com/cotes2020/jekyll-theme-chirpy/issues", + "documentation_uri" => "https://github.com/cotes2020/jekyll-theme-chirpy/#readme", + "homepage_uri" => "https://cotes2020.github.io/chirpy-demo", + "source_code_uri" => "https://github.com/cotes2020/jekyll-theme-chirpy", + "wiki_uri" => "https://github.com/cotes2020/jekyll-theme-chirpy/wiki", + "plugin_type" => "theme" + } + + spec.required_ruby_version = "~> 3.1" + + spec.add_runtime_dependency "jekyll", "~> 4.3" + spec.add_runtime_dependency "jekyll-paginate", "~> 1.1" + spec.add_runtime_dependency "jekyll-redirect-from", "~> 0.16" + spec.add_runtime_dependency "jekyll-seo-tag", "~> 2.8" + spec.add_runtime_dependency "jekyll-archives", "~> 2.2" + spec.add_runtime_dependency "jekyll-sitemap", "~> 1.4" + spec.add_runtime_dependency "jekyll-include-cache", "~> 0.2" + +end diff --git a/package.json b/package.json new file mode 100644 index 0000000..f93e76b --- /dev/null +++ b/package.json @@ -0,0 +1,177 @@ +{ + "name": "jekyll-theme-chirpy", + "version": "7.1.1", + "description": "A minimal, responsive, and feature-rich Jekyll theme for technical writing.", + "repository": { + "type": "git", + "url": "git+https://github.com/cotes2020/jekyll-theme-chirpy.git" + }, + "author": "Cotes Chung", + "license": "MIT", + "since": 2019, + "bugs": { + "url": "https://github.com/cotes2020/jekyll-theme-chirpy/issues" + }, + "homepage": "https://github.com/cotes2020/jekyll-theme-chirpy/", + "scripts": { + "build": "concurrently npm:build:*", + "build:css": "purgecss -c purgecss.config.js", + "build:js": "rollup -c --bundleConfigAsCjs --environment BUILD:production", + "watch:js": "rollup -c --bundleConfigAsCjs -w", + "lint:scss": "stylelint _sass/**/*.scss", + "lint:fix:scss": "npm run lint:scss -- --fix", + "test": "npm run lint:scss", + "prepare": "husky" + }, + "dependencies": { + "@popperjs/core": "^2.11.8", + "bootstrap": "^5.3.3" + }, + "devDependencies": { + "@babel/core": "^7.25.2", + "@babel/plugin-transform-class-properties": "^7.25.4", + "@babel/preset-env": "^7.25.4", + "@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", + "@semantic-release/changelog": "^6.0.3", + "@semantic-release/exec": "^6.0.3", + "@semantic-release/git": "^10.0.1", + "concurrently": "^9.0.1", + "conventional-changelog-conventionalcommits": "^8.0.0", + "husky": "^9.1.6", + "purgecss": "^6.0.0", + "rollup": "^4.21.3", + "semantic-release": "^24.1.1", + "stylelint": "^16.9.0", + "stylelint-config-standard-scss": "^13.1.0" + }, + "prettier": { + "trailingComma": "none" + }, + "browserslist": [ + "last 2 versions", + "> 0.2%", + "not dead" + ], + "commitlint": { + "extends": [ + "@commitlint/config-conventional" + ], + "rules": { + "body-max-line-length": [ + 0, + "always" + ] + } + }, + "stylelint": { + "extends": "stylelint-config-standard-scss", + "rules": { + "no-descending-specificity": null, + "shorthand-property-no-redundant-values": null, + "at-rule-no-vendor-prefix": null, + "property-no-vendor-prefix": null, + "selector-no-vendor-prefix": null, + "value-no-vendor-prefix": null, + "color-function-notation": "legacy", + "alpha-value-notation": "number", + "selector-not-notation": "simple", + "color-hex-length": "long", + "declaration-block-single-line-max-declarations": 3, + "scss/operator-no-newline-after": null, + "rule-empty-line-before": [ + "always", + { + "ignore": [ + "after-comment", + "first-nested" + ] + } + ], + "value-keyword-case": [ + "lower", + { + "ignoreProperties": [ + "/^\\$/" + ] + } + ], + "media-feature-range-notation": "prefix" + } + }, + "release": { + "branches": [ + "production" + ], + "plugins": [ + [ + "@semantic-release/commit-analyzer", + { + "preset": "conventionalcommits" + } + ], + [ + "@semantic-release/release-notes-generator", + { + "preset": "conventionalcommits", + "presetConfig": { + "types": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "perf", + "section": "Improvements" + }, + { + "type": "refactor", + "section": "Changes", + "hidden": true + } + ] + } + } + ], + [ + "@semantic-release/changelog", + { + "changelogFile": "docs/CHANGELOG.md", + "changelogTitle": "# Changelog" + } + ], + [ + "@semantic-release/npm", + { + "npmPublish": false + } + ], + [ + "@semantic-release/exec", + { + "prepareCmd": "bash tools/release.sh --prepare", + "publishCmd": "bash tools/release.sh" + } + ], + [ + "@semantic-release/git", + { + "assets": [ + "docs", + "package.json", + "*.gemspec" + ], + "message": "chore(release): ${nextRelease.version}\n\n${nextRelease.notes}" + } + ], + "@semantic-release/github" + ] + } +} diff --git a/purgecss.config.js b/purgecss.config.js new file mode 100644 index 0000000..de370de --- /dev/null +++ b/purgecss.config.js @@ -0,0 +1,23 @@ +const fs = require('fs'); +const DIST_PATH = '_sass/dist'; + +fs.rm(DIST_PATH, { recursive: true, force: true }, (err) => { + if (err) { + throw err; + } + + fs.mkdirSync(DIST_PATH); +}); + +module.exports = { + content: ['_includes/**/*.html', '_layouts/**/*.html', '_javascript/**/*.js'], + css: ['node_modules/bootstrap/dist/css/bootstrap.min.css'], + keyframes: true, + variables: true, + output: `${DIST_PATH}/bootstrap.css`, + // The `safelist` should be changed appropriately for future development + safelist: { + standard: [/^collaps/, /^w-/, 'shadow', 'border', 'kbd'], + greedy: [/^col-/, /tooltip/] + } +}; diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000..19ba4da --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,74 @@ +import babel from '@rollup/plugin-babel'; +import terser from '@rollup/plugin-terser'; +import { nodeResolve } from '@rollup/plugin-node-resolve'; +import fs from 'fs'; +import pkg from './package.json'; + +const SRC_DEFAULT = '_javascript'; +const SRC_PWA = `${SRC_DEFAULT}/pwa`; +const DIST = 'assets/js/dist'; + +const banner = `/*! + * ${pkg.name} v${pkg.version} | © ${pkg.since} ${pkg.author} | ${pkg.license} Licensed | ${pkg.homepage} + */`; + +const frontmatter = `---\npermalink: /:basename\n---\n`; + +const isProd = process.env.BUILD === 'production'; + +function cleanup() { + fs.rmSync(DIST, { recursive: true, force: true }); + console.log(`> Directory "${DIST}" has been cleaned.`); +} + +function insertFrontmatter() { + return { + name: 'insert-frontmatter', + generateBundle(_, bundle) { + for (const chunkOrAsset of Object.values(bundle)) { + if (chunkOrAsset.type === 'chunk') { + chunkOrAsset.code = frontmatter + chunkOrAsset.code; + } + } + } + }; +} + +function build(filename, { src = SRC_DEFAULT, jekyll = false } = {}) { + return { + input: `${src}/${filename}.js`, + output: { + file: `${DIST}/${filename}.min.js`, + format: 'iife', + name: 'Chirpy', + banner, + sourcemap: !isProd && !jekyll + }, + watch: { + include: `${src}/**` + }, + plugins: [ + babel({ + babelHelpers: 'bundled', + presets: ['@babel/env'], + plugins: ['@babel/plugin-transform-class-properties'] + }), + nodeResolve(), + isProd && terser(), + jekyll && insertFrontmatter() + ] + }; +} + +cleanup(); + +export default [ + build('commons'), + build('home'), + build('categories'), + build('page'), + build('post'), + build('misc'), + build('app', { src: SRC_PWA, jekyll: true }), + build('sw', { src: SRC_PWA, jekyll: true }) +]; diff --git a/tools/init.sh b/tools/init.sh new file mode 100644 index 0000000..2ad72ab --- /dev/null +++ b/tools/init.sh @@ -0,0 +1,130 @@ +#!/usr/bin/env bash +# +# Init the environment for new user. + +set -eu + +# CLI Dependencies +CLI=("git" "npm") + +ACTIONS_WORKFLOW=pages-deploy.yml + +RELEASE_HASH=$(git log --grep="chore(release):" -1 --pretty="%H") + +# temporary file suffixes that make `sed -i` compatible with BSD and Linux +TEMP_SUFFIX="to-delete" + +_no_gh=false + +help() { + echo "Usage:" + echo + echo " bash /path/to/init [options]" + echo + echo "Options:" + echo " --no-gh Do not deploy to Github." + echo " -h, --help Print this help information." +} + +# BSD and GNU compatible sed +_sedi() { + regex=$1 + file=$2 + sed -i.$TEMP_SUFFIX -E "$regex" "$file" + rm -f "$file".$TEMP_SUFFIX +} + +_check_cli() { + for i in "${!CLI[@]}"; do + cli="${CLI[$i]}" + if ! command -v "$cli" &>/dev/null; then + echo "Command '$cli' not found! Hint: you should install it." + exit 1 + fi + done +} + +_check_status() { + if [[ -n $(git status . -s) ]]; then + echo "Error: Commit unstaged files first, and then run this tool again." + exit 1 + fi +} + +_check_init() { + if [[ $(git rev-parse HEAD^1) == "$RELEASE_HASH" ]]; then + echo "Already initialized." + exit 0 + fi +} + +check_env() { + _check_cli + _check_status + _check_init +} + +reset_latest() { + git reset --hard "$RELEASE_HASH" + git clean -fd + git submodule update --init --recursive +} + +init_files() { + if $_no_gh; then + rm -rf .github + else + ## Change the files of `.github/` + temp="$(mktemp -d)" + find .github/workflows -type f -name "*$ACTIONS_WORKFLOW*" -exec mv {} "$temp/$ACTIONS_WORKFLOW" \; + rm -rf .github && mkdir -p .github/workflows + mv "$temp/$ACTIONS_WORKFLOW" .github/workflows/"$ACTIONS_WORKFLOW" + rm -rf "$temp" + fi + + # Cleanup image settings in site config + _sedi "s/(^timezone:).*/\1/;s/(^.*cdn:).*/\1/;s/(^avatar:).*/\1/" _config.yml + + # remove the other files + rm -rf tools/init.sh tools/release.sh _posts/* + + # build assets + npm i && npm run build + + # track the CSS/JS output + _sedi "/.*\/dist$/d" .gitignore +} + +commit() { + git add -A + git commit -m "chore: initialize the environment" -q + echo -e "\n> Initialization successful!\n" +} + +main() { + check_env + reset_latest + init_files + commit +} + +while (($#)); do + opt="$1" + case $opt in + --no-gh) + _no_gh=true + shift + ;; + -h | --help) + help + exit 0 + ;; + *) + # unknown option + help + exit 1 + ;; + esac +done + +main diff --git a/tools/release.sh b/tools/release.sh new file mode 100644 index 0000000..522c892 --- /dev/null +++ b/tools/release.sh @@ -0,0 +1,186 @@ +#!/usr/bin/env bash +# +# Requires: Git, NPM and RubyGems + +set -eu + +opt_pre=false # option for bump gem version +opt_pkg=false # option for building gem package + +MAIN_BRANCH="master" +RELEASE_BRANCH="production" + +GEM_SPEC="jekyll-theme-chirpy.gemspec" +NODE_SPEC="package.json" +CHANGELOG="docs/CHANGELOG.md" +CONFIG="_config.yml" + +CSS_DIST="_sass/dist" +JS_DIST="assets/js/dist" + +FILES=( + "$GEM_SPEC" + "$NODE_SPEC" + "$CHANGELOG" + "$CONFIG" +) + +TOOLS=( + "git" + "npm" + "gem" +) + +help() { + echo -e "A tool to release new version Chirpy gem.\nThis tool will:" + echo " 1. Build a new gem and publish it to RubyGems.org" + echo " 2. Merge the release branch into the default branch" + echo + echo "Usage:" + echo " bash $0 [options]" + echo + echo "Options:" + echo " --prepare Preparation for release" + echo " -p, --package Build a gem package only, for local packaging in case of auto-publishing failure" + echo " -h, --help Display this help message" +} + +_check_cli() { + for i in "${!TOOLS[@]}"; do + cli="${TOOLS[$i]}" + if ! command -v "$cli" &>/dev/null; then + echo "> Command '$cli' not found!" + exit 1 + fi + done +} + +_check_git() { + $opt_pre || ( + # ensure that changes have been committed + if [[ -n $(git status . -s) ]]; then + echo "> Abort: Commit the staged files first, and then run this tool again." + exit 1 + fi + ) + + $opt_pkg || ( + if [[ "$(git branch --show-current)" != "$RELEASE_BRANCH" ]]; then + echo "> Abort: Please run the tool in the '$RELEASE_BRANCH' branch." + exit 1 + fi + ) +} + +_check_src() { + for i in "${!FILES[@]}"; do + _src="${FILES[$i]}" + if [[ ! -f $_src && ! -d $_src ]]; then + echo -e "> Error: Missing file \"$_src\"!\n" + exit 1 + fi + done +} + +init() { + _check_cli + _check_git + _check_src + echo -e "> npm install\n" + npm i +} + +## Bump new version to gem-spec file +_bump_version() { + _version="$(grep '"version":' "$NODE_SPEC" | sed 's/.*: "//;s/".*//')" + sed -i "s/[[:digit:]]\+\.[[:digit:]]\+\.[[:digit:]]\+/$_version/" "$GEM_SPEC" + echo "> Bump gem version to $_version" +} + +_improve_changelog() { + # Replace multiple empty lines with a single empty line + sed -i '/^$/N;/^\n$/D' "$CHANGELOG" + # Escape left angle brackets of HTML tag in the changelog as they break the markdown structure. e.g., '
' + sed -i -E 's/\s(<[a-z])/ \\\1/g' "$CHANGELOG" +} + +prepare() { + _bump_version + _improve_changelog +} + +## Build a Gem package +build_gem() { + # Remove unnecessary theme settings + sed -i -E "s/(^timezone:).*/\1/;s/(^cdn:).*/\1/;s/(^avatar:).*/\1/" $CONFIG + rm -f ./*.gem + + npm run build + # add CSS/JS distribution files to gem package + git add "$CSS_DIST" "$JS_DIST" -f + + echo -e "\n> gem build $GEM_SPEC\n" + gem build "$GEM_SPEC" + + echo -e "\n> Resume file changes ...\n" + git reset + git checkout . +} + +# Push the gem to RubyGems.org (using $GEM_HOST_API_KEY) +push_gem() { + gem push ./*.gem +} + +## Merge the release branch into the default branch +merge() { + git fetch origin "$MAIN_BRANCH" + git checkout -b "$MAIN_BRANCH" origin/"$MAIN_BRANCH" + + git merge --no-ff --no-edit "$RELEASE_BRANCH" || ( + git merge --abort + echo -e "\n> Conflict detected. Aborting merge.\n" + exit 0 + ) + + git push origin "$MAIN_BRANCH" +} + +main() { + init + + if $opt_pre; then + prepare + exit 0 + fi + + build_gem + $opt_pkg && exit 0 + push_gem + merge +} + +while (($#)); do + opt="$1" + case $opt in + --prepare) + opt_pre=true + shift + ;; + -p | --package) + opt_pkg=true + shift + ;; + -h | --help) + help + exit 0 + ;; + *) + # unknown option + help + exit 1 + ;; + esac +done + +main diff --git a/tools/run.sh b/tools/run.sh new file mode 100644 index 0000000..0efc452 --- /dev/null +++ b/tools/run.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# +# Run jekyll serve and then launch the site + +prod=false +command="bundle exec jekyll s -l" +host="127.0.0.1" + +help() { + echo "Usage:" + echo + echo " bash /path/to/run [options]" + echo + echo "Options:" + echo " -H, --host [HOST] Host to bind to." + echo " -p, --production Run Jekyll in 'production' mode." + echo " -h, --help Print this help information." +} + +while (($#)); do + opt="$1" + case $opt in + -H | --host) + host="$2" + shift 2 + ;; + -p | --production) + prod=true + shift + ;; + -h | --help) + help + exit 0 + ;; + *) + echo -e "> Unknown option: '$opt'\n" + help + exit 1 + ;; + esac +done + +command="$command -H $host" + +if $prod; then + command="JEKYLL_ENV=production $command" +fi + +if [ -e /proc/1/cgroup ] && grep -q docker /proc/1/cgroup; then + command="$command --force_polling" +fi + +echo -e "\n> $command\n" +eval "$command" diff --git a/tools/test.sh b/tools/test.sh new file mode 100644 index 0000000..331de1c --- /dev/null +++ b/tools/test.sh @@ -0,0 +1,89 @@ +#!/usr/bin/env bash +# +# Build and test the site content +# +# Requirement: html-proofer, jekyll +# +# Usage: See help information + +set -eu + +SITE_DIR="_site" + +_config="_config.yml" + +_baseurl="" + +help() { + echo "Build and test the site content" + echo + echo "Usage:" + echo + echo " bash $0 [options]" + echo + echo "Options:" + echo ' -c, --config "" Specify config file(s)' + echo " -h, --help Print this information." +} + +read_baseurl() { + if [[ $_config == *","* ]]; then + # multiple config + IFS="," + read -ra config_array <<<"$_config" + + # reverse loop the config files + for ((i = ${#config_array[@]} - 1; i >= 0; i--)); do + _tmp_baseurl="$(grep '^baseurl:' "${config_array[i]}" | sed "s/.*: *//;s/['\"]//g;s/#.*//")" + + if [[ -n $_tmp_baseurl ]]; then + _baseurl="$_tmp_baseurl" + break + fi + done + + else + # single config + _baseurl="$(grep '^baseurl:' "$_config" | sed "s/.*: *//;s/['\"]//g;s/#.*//")" + fi +} + +main() { + # clean up + if [[ -d $SITE_DIR ]]; then + rm -rf "$SITE_DIR" + fi + + read_baseurl + + # build + JEKYLL_ENV=production bundle exec jekyll b \ + -d "$SITE_DIR$_baseurl" -c "$_config" + + # test + bundle exec htmlproofer "$SITE_DIR" \ + --disable-external \ + --ignore-urls "/^http:\/\/127.0.0.1/,/^http:\/\/0.0.0.0/,/^http:\/\/localhost/" +} + +while (($#)); do + opt="$1" + case $opt in + -c | --config) + _config="$2" + shift + shift + ;; + -h | --help) + help + exit 0 + ;; + *) + # unknown option + help + exit 1 + ;; + esac +done + +main