Initial microblog

This commit is contained in:
Ming Di Leom 2024-09-28 06:31:52 +00:00
commit 132f0eb30b
No known key found for this signature in database
GPG Key ID: 32D3E28E96A695E8
100 changed files with 6711 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
.DS_Store
Thumbs.db
db.json
*.log
node_modules/
package-lock.json
public/
.deploy*/

73
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,73 @@
image: node:alpine # Use latest version of Node.js on Alpine
cache:
paths:
- node_modules/
variables:
NODE_ENV: "production"
# Rename to 'pages' for gitlab pages
build:
stage: build
before_script:
- apk update && apk add git
- npm install --include=optional --force
script:
# Generate site
- npm run build
rules:
# Only trigger through push & "Run pipeline" events not in "site" branch; Skip in renovate job
- if: '$RENOVATE != "true" && $CI_COMMIT_REF_NAME != "site" && ($CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "web")'
when: always
artifacts:
paths:
- public/
expire_in: 30 days
test:
stage: test
script:
# /microblog/ should exist and non-empty
- sh check-homepage.sh
rules:
- if: '$RENOVATE != "true" && $CI_COMMIT_REF_NAME == "master" && ($CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "web")'
when: always
artifacts:
paths:
- public/
expire_in: 30 days
deploy:
stage: deploy
before_script:
- apk update && apk add openssh-client rsync
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
- echo "$SSH_KEY_1" > ~/.ssh/id_host_1
- chmod 600 ~/.ssh/id_host_1
- echo "$SSH_KEY_2" > ~/.ssh/id_host_2
- chmod 600 ~/.ssh/id_host_2
- echo "$SSH_CONFIG" > ~/.ssh/config
- chmod 600 ~/.ssh/config
script:
- rsync -azvh --delete public/microblog/ host-1:/var/www/microblog/
- rsync -azvh --delete public/microblog/ host-2:/var/www/microblog/
rules:
- if: '$RENOVATE != "true" && $CI_COMMIT_REF_NAME == "microblog" && ($CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "web")'
when: always
include:
- template: Security/Secret-Detection.gitlab-ci.yml

4
.npmrc Normal file
View File

@ -0,0 +1,4 @@
package-lock=false
optional=true
lockfile=false
force=true

1
.nvmrc Normal file
View File

@ -0,0 +1 @@
20

14
.snyk Normal file
View File

@ -0,0 +1,14 @@
# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
version: v1.13.5
# ignores vulnerabilities until expiry date; change duration by modifying expiry date
ignore:
SNYK-JS-HTMLMINIFIER-3091181:
- "*":
reason: Patch unavailable
expires: "2024-06-30T00:00:00.000Z"
SNYK-JS-HEXO-5889980:
- "*":
reason: "https://github.com/hexojs/hexo/pull/5251"
expires: "2024-06-30T00:00:00.000Z"
# patches apply the minimum changes required to fix a vulnerability
patch:

22
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,22 @@
{
"editor.tabSize": 2,
"files.eol": "\n",
"files.trimTrailingWhitespace": true,
"files.insertFinalNewline": true,
"files.trimFinalNewlines": true,
"standard.run": "onSave",
"[markdown]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"[yaml]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}

22
LICENSE.md Normal file
View File

@ -0,0 +1,22 @@
MIT License
Copyright (c) 2016-2018 GeekPlux <geekplux@gmail.com> ([Typing](https://github.com/geekplux/hexo-theme-typing) theme)
Copyright (c) 2018-2024 Ming Di Leom
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.

83
README.md Normal file
View File

@ -0,0 +1,83 @@
# [mdleom.com](https://mdleom.com/)
[![Website Status](https://img.shields.io/website?url=https%3A%2F%2Fmdleom.com)](https://stats.uptimerobot.com/1394zup2LQ)
[![Netlify Status](https://api.netlify.com/api/v1/badges/3f4b970a-c258-454c-97d6-3a8164f63262/deploy-status)](https://app.netlify.com/sites/curben/deploys)
[![Build Status](https://gitlab.com/curben/blog/badges/master/pipeline.svg)](https://gitlab.com/curben/blog/-/jobs)
[![Hexo version](https://img.shields.io/badge/hexo-hexojs/hexo-brightgreen.svg)](https://github.com/hexojs/hexo)
### Plugins
- Installed [hexo-yam](https://github.com/curbengh/hexo-yam) (author) to pre-compress static assets (html, css, js and svg).
- Installed [hexo-nofollow](https://github.com/curbengh/hexo-nofollow) for SEO purpose to prevent search engines from following external links.
- [copy-button.js](/themes/chameleon/scripts/copy-button.js) - A [filter](https://hexo.io/api/filter) plugin to add a copy button to each code block.
- [feed](/themes/chameleon/scripts/feed) - A generator plugin to generate [an RSS feed](https://en.wikipedia.org/wiki/Web_feed). Adapted from [hexo-generator-feed](https://github.com/hexojs/hexo-generator-feed) for simpler use case.
- [heading-link.js](/themes/chameleon/scripts/heading-link.js) - A filter plugin to add a link button next to each heading.
- [image.js](/scripts/image.js) - A [tag](https://hexo.io/api/tag) plugin to easily embed images in a post with responsive image support.
- [link.js](/themes/chameleon/scripts/link.js) - A [helper](https://hexo.io/api/helper) plugin to add a link. Modified from [link_to.js](https://github.com/hexojs/hexo/blob/master/lib/plugins/helper/link_to.js) to remove title attribute and 'external' option.
- [openGraph](/themes/chameleon/scripts/openGraph.js) - A helper plugin to add [Open Graph](https://ogp.me/) tags. Modified from [open_graph.js](https://github.com/hexojs/hexo/blob/master/lib/plugins/helper/open_graph.js) to use additional [Open Graph](https://www.ogp.me/) tags and [WHATWG URL API](https://nodejs.org/api/url.html#url_the_whatwg_url_api).
- [sitemap](/scripts/sitemap) - A [generator](https://hexo.io/api/generator) plugin to generate [a sitemap](https://en.wikipedia.org/wiki/Sitemaps). Modified from [hexo-generator-sitemap](https://github.com/hexojs/hexo-generator-sitemap) with this [patch](https://github.com/hexojs/hexo-generator-sitemap/pull/26) to include tags/categories and [remove](https://github.com/pyyzcwg2833/hexo-generator-sitemap/commit/a92dbbb83cc39ff60d43faa5cd688a56574a3889) [index.html](https://github.com/hexojs/hexo-generator-sitemap/pull/59) from the URL.
## Changes
The following are the major changes I made compared to the upstream.
### [Hexo](https://gitlab.com/pages/hexo) site
- Updated [.gitlab-ci.yml](.gitlab-ci.yml) to use the latest version of [Node.js](https://hub.docker.com/_/node/) in Alpine docker image.
- [All packages](package.json) are installed from their respective master branch, instead of npm published version.
- Removed unused packages, [hexo-generator-category](https://github.com/hexojs/hexo-generator-category) and [hexo-renderer-stylus](https://github.com/hexojs/hexo-renderer-stylus) from the [default packages](https://github.com/hexojs/hexo-starter/blob/571320ba41a83e065d7560e050eb3fa63ad74a57/package.json#L9-L17).
- Replaced the bundled Landscape theme with Typing theme.
- Add `lastUpdated` to the front-matter to manually set updated time of a post, instead of using `post.updated`.
- This is no longer necessary, thanks to [`updated_option`](https://github.com/hexojs/hexo/pull/4278) feature.
### [Chameleon theme](/themes/chameleon)
Chameleon is a fork of [Typing](https://github.com/geekplux/hexo-theme-typing) theme, rewrote from scratch with the following changes/features:
- [sanitize.css](https://github.com/csstools/sanitize.css/) and [autoprefixer](https://github.com/csstools/sanitize.css/) for consistent cross-browser styling.
- Utilise [relative length](https://www.w3schools.com/CSSref/css_units.asp) instead of absolute length in the css.
- [`prefers-color-scheme`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme) to apply light/dark theme according to the OS preference.
- Add a [Copy](https://clipboardjs.com/) button to each code block.
- Removed jQuery, fancyBox, web analytics, comment plugins, donation links and [typo.css](https://github.com/sofish/typo.css).
- Homepage shows recent posts and tags.
## Installation
Refer to [this post](https://mdleom.com/2018/09/21/how-to-create-a-hexo-blog/) for more detailed instruction.
## License
Copyright (c) 2018-2024 Ming Di Leom
The content of this blog is licensed under a [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/) license, and the underlying source code used to format and display that content is licensed under an [MIT license](LICENSE.md), unless indicated otherwise.
## Website Mirrors
- https://curben.pages.dev/
- https://curben.netlify.app/
- https://curben.gitlab.io/
- https://curbengh.github.io/
- https://xw226dvxac7jzcpsf4xb64r4epr6o5hgn46dxlqk7gnjptakik6xnzqd.onion/
- http://mdleom.i2p/ ([address helper](http://mdleom.i2p/?i2paddresshelper=-NjUAy6H3wkgRfB3rBwGrpS56L2P~RHRDnD8HnRV1mLSKFdbzxHTMsGLo-mdgGq360Kni2Ec0qhRzm-IUc8X4Y0Ug1eYvcEp2ubXwLe5JJg7yZJOdGxqdy5y5VbdHfIuUe2ooG3MNA4v6b4pGk7pUQ7hnTkUi0EObD~79ik4AY-vSsxIFrc8kJxtbRMCQ3NQRhAuvS1A14rSVk0wv50YwKS23y~FUIQWyG8ZpjTVYu50n~oBnJtVKSAHbCMWRcnJx6iGFsbTRh4ZsRtDh0drwfeRkvaQQqQmf6nZOc4-GLxZ0RT5QlS5gdPXL4V7eaIETbNJAIeYr2NzcpwVHs~zp93Ga-p7dlH3TsJX5gJSyqJWCc64vvmkxf7Vseh3uGaa4xqiLjTH5XsOyFQLp5D6myt-yH7ggReZbs70NKqG1Mj5iRLhIC3Q~pJ6LkPnMBJN6QeLNYNWcOPXkMzRfsavvH2l3yxdpkn41BLM2-7bBUdJNXfu4OhGAR22O0gFngjUBQAEAAcAAA==&update=true))
- http://ggucqf2jmtfxcw7us5sts3x7u2qljseocfzlhzebfpihkyvhcqfa.b32.i2p/
## Repository Mirrors
- https://github.com/curbengh
- https://pagure.io/projects/curben/%2A
- https://codeberg.org/curben
- https://git.disroot.org/curben
- https://git.nixnet.services/curben
- https://gitea.com/curben
- https://framagit.org/curben
## Uptime Status
- https://stats.uptimerobot.com/1394zup2LQ
---
Useful links:
- [Hexo Docs](https://hexo.io/docs/)
- [GitLab Pages](https://docs.gitlab.com/ee/user/project/pages/index.html)

111
_config.yml Normal file
View File

@ -0,0 +1,111 @@
# Hexo Configuration
## Docs: https://hexo.io/docs/configuration.html
## Source: https://github.com/hexojs/hexo/
# Site
title: Ming Di Leom's Blog
subtitle:
description:
author: "Ming Di Leom"
language: en-GB
timezone:
# URL
## If your site is put in a subdirectory, set url as 'http://yoursite.com/child' and root as '/child/'
url: https://mdleom.com
root: /
permalink: microblog/:title/
permalink_defaults:
pretty_urls:
trailing_index: false
trailing_html: false
# Directory
source_dir: source
public_dir: public
tag_dir: tags
tag_generator:
enable_index_page: true
archive_dir: microblog
category_dir: categories
code_dir: ""
i18n_dir: :lang
skip_render:
include:
- "_headers" # Netlify header rule
- "_redirects" # Netlify redirects rule
# Writing
new_post_name: :title.md # File name of new posts
default_layout: post
titlecase: false # Transform title into titlecase
external_link:
enable: false # Open external links in new tab
filename_case: 0
render_drafts: false
post_asset_folder: false
relative_link: false
future: true
highlight:
enable: true
line_number: false
auto_detect: false
tab_replace: " "
hljs: true
wrap: false
# Tag
tag_map:
# Date / Time format
## Hexo uses Moment.js to parse and display date
## You can customize the date format as defined in
## http://momentjs.com/docs/#/displaying/format/
date_format: YYYY-MM-DD
time_format: HH:mm:ss
updated_option: empty
# Pagination
## Set per_page to 0 to disable pagination
per_page: 10
pagination_dir: page
marked:
descriptionLists: false
modifyAnchors: 1
# Extensions
## Plugins: https://hexo.io/plugins/
## Themes: https://hexo.io/themes/
theme: chameleon
feed:
icon: "/svg/favicon.svg"
# theme config
theme_config:
# Header
menu:
Home: /
Blog: /blog/
Microblog: /microblog/
About: /about/
Feed: /atom.xml
GitLab: https://gitlab.com/curben/blog
footer:
Disclaimer: /disclaimer/
Status: https://stats.uptimerobot.com/1394zup2LQ
Onion: https://xw226dvxac7jzcpsf4xb64r4epr6o5hgn46dxlqk7gnjptakik6xnzqd.onion/
Eepsite: https://gitlab.com/curben/blog#mirrors
# Load forkawesome icons?
icons: false
# https://github.com/curbengh/hexo-yam
minify:
svg:
plugins:
cleanupIds: false
zstd:
enable: true

16
check-homepage.sh Normal file
View File

@ -0,0 +1,16 @@
#!/bin/sh
## Homepage should exists and non-empty
HOMEPAGE="public/microblog/index.html"
if [ ! -f "$HOMEPAGE" ]; then
echo "Error: homepage doesn't exist"
exit 1
else
FILE_SIZE=$(ls -s "$HOMEPAGE" | cut -d" " -f1)
if [ "$FILE_SIZE" = 0 ]; then
echo "Error: homepage is empty"
exit 1
fi
fi

4
netlify.toml Normal file
View File

@ -0,0 +1,4 @@
[context.production]
environment = { NODE_ENV = "production" }
publish = "public"
command = "npm run build"

26
package.json Normal file
View File

@ -0,0 +1,26 @@
{
"name": "blog",
"version": "1.0.0",
"private": true,
"hexo": {
"version": "7.2.0"
},
"scripts": {
"build": "hexo generate",
"snyk": "snyk auth $SNYK_TOKEN && snyk-protect && snyk test && snyk monitor",
"renovate": "renovate --platform 'gitlab' --autodiscover false --onboarding false --update-lock-files false --labels 'renovate' --require-config='ignored' \"$CI_PROJECT_PATH\""
},
"dependencies": {
"hexo": "^7.0.0",
"hexo-filter-nofollow": "hexojs/hexo-filter-nofollow",
"hexo-generator-archive": "hexojs/hexo-generator-archive",
"hexo-generator-index": "hexojs/hexo-generator-index",
"hexo-generator-tag": "hexojs/hexo-generator-tag",
"hexo-renderer-ejs": "hexojs/hexo-renderer-ejs",
"hexo-renderer-marked": "hexojs/hexo-renderer-marked",
"hexo-yam": "curbengh/hexo-yam"
},
"devDependencies": {
"hexo-server": "hexojs/hexo-server"
}
}

4
scaffolds/draft.md Normal file
View File

@ -0,0 +1,4 @@
---
title: {{ title }}
tags:
---

4
scaffolds/page.md Normal file
View File

@ -0,0 +1,4 @@
---
title: {{ title }}
date: {{ date }}
---

5
scaffolds/post.md Normal file
View File

@ -0,0 +1,5 @@
---
title: {{ title }}
date: {{ date }}
tags:
---

42
scripts/image.js Normal file
View File

@ -0,0 +1,42 @@
'use strict'
/* global hexo */
/*
* Embed an image with responsive images in a post
* https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images
* Image is resized on-the-fly using Statically (https://statically.io/)
* Usage: ![alt](/path/to/img "title")
*/
const { join } = require('path').posix
hexo.extend.filter.register('marked:renderer', (renderer) => {
renderer.image = (href, title, alt) => {
if (!alt) alt = ''
if (!title) title = alt
if (href.endsWith('.svg')) return `<img class="svg" src="${href}" alt="${alt}" title="${title}">`
// embed external image
if (href.startsWith('http')) return `<img src="${href}" alt="${alt}" title="${title}">`
const fLink = (path, width) => {
const query = new URLSearchParams('f=auto')
if (typeof width === 'number') query.set('width', width)
const url = new URL('http://example.com/' + join('img', path) + '?' + query)
return url.pathname + url.search
}
return `<a href="${join('/img', href)}">` +
`<img srcset="${fLink(href, 320)} 320w,` +
`${fLink(href, 468)} 468w,` +
`${fLink(href, 768)} 768w,` +
`${fLink(href)} 800w"` +
' sizes="(max-width: 320px) 320px,' +
'(max-width: 468px) 468px,' +
'(max-width: 768px) 768px,' +
'800px"' +
` src="${fLink(href)}" title="${title}" alt="${alt}" loading="lazy"></a>`
}
})

48
scripts/smartypants.js Normal file
View File

@ -0,0 +1,48 @@
const { escapeHTML: escape } = require('hexo-util')
// https://github.com/markedjs/marked/blob/b6773fca412c339e0cedd56b63f9fa1583cfd372/src/Lexer.js#L8-L24
// Replace dashes only
const smartypants = (str) => {
return str
// em-dashes
.replace(/---/g, '\u2014')
// en-dashes
.replace(/--/g, '\u2013')
// opening singles
.replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
// closing singles & apostrophes
.replace(/'/g, '\u2019')
// opening doubles
.replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
// closing doubles
.replace(/"/g, '\u201d')
// ellipses
.replace(/\.{3}/g, '\u2026')
// right arrow
.replace(/->/g, '\u2192')
}
hexo.extend.filter.register('marked:tokenizer', function (tokenizer) {
const { smartypants: isSmarty } = this.config.marked
tokenizer.inlineText = function (src) {
const { rules } = this
// https://github.com/markedjs/marked/blob/b6773fca412c339e0cedd56b63f9fa1583cfd372/src/Tokenizer.js#L643-L658
const cap = rules.inline.text.exec(src)
if (cap) {
let text
if (this.lexer.state.inRawBlock) {
text = cap[0]
} else {
text = escape(isSmarty ? smartypants(cap[0]) : cap[0])
}
return {
// `type` value is a corresponding renderer method
// https://marked.js.org/using_pro#inline-level-renderer-methods
type: 'text',
raw: cap[0],
text
}
}
}
})

5
source/404.md Normal file
View File

@ -0,0 +1,5 @@
---
title: Page not found
layout: page
sitemap: false
---

10
source/_headers Normal file
View File

@ -0,0 +1,10 @@
/*
Content-Security-Policy: default-src 'self'; child-src 'none'; connect-src 'none'; font-src 'none'; frame-src 'none'; img-src 'self'; manifest-src 'self'; media-src 'none'; object-src 'none'; prefetch-src 'none'; script-src 'self'; style-src 'self'; worker-src 'none'; base-uri 'none'; form-action https://duckduckgo.com https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion; frame-ancestors 'none'; block-all-mixed-content; sandbox allow-forms allow-scripts
Expires: 0
Permissions-Policy: accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), gamepad=(), geolocation=(), gyroscope=(), hid=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), serial=(), speaker=(), speaker-selection=(), usb=(), web-share=(self), xr-spatial-tracking=(), interest-cohort=()
Referrer-Policy: no-referrer
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
X-Robots-Tag: noindex

View File

@ -0,0 +1,6 @@
---
title: SSSD cache
date: 2024-07-15
---
If sssd unable to starts after upgrade/downgrade, clear its cache by deleting all files in the "/var/lib/sss/db/" folder. Encountered [this issue](https://bugzilla.redhat.com/show_bug.cgi?id=1576597) when upgrading/switching AlmaLinux 8 and CentOS Stream 9 to AlmaLinux 9.

View File

@ -0,0 +1,6 @@
---
title: Failing task
date: 2024-07-16
---
If a scheduled task has been failing for a while and no one bats an eye, just disable it.

View File

@ -0,0 +1,6 @@
---
title: TLS 1.3 in AWS services
date: 2024-07-18
---
At least 30% of `PUT` traffic in a Cloudtrail bucket is still TLS 1.2. Don't set [`minimum_tls_version=1.3`](https://docs.aws.amazon.com/cdk/api/v2/python/aws_cdk.aws_s3/Bucket.html) on that bucket yet.

View File

@ -0,0 +1,6 @@
---
title: Opt-out from ChatGPT model training
date: 2024-07-22
---
In OpenAI ChatGPT, it is now possible to disable [model training](https://help.openai.com/en/articles/7730893-data-controls-faq) while still keeping the chat history. [Previously](https://openai.com/index/new-ways-to-manage-your-data-in-chatgpt/), both had to be disabled together.

View File

@ -0,0 +1,6 @@
---
title: Huawei USB 4G + Creta
date: 2024-08-23
---
Huawei USB 4G modem E3372h-608 firmware 10.0.5.1 works out-of-the-box as a tethering connection with GL.iNet Creta AR750 firmware 4.3.17 running OpenWrt 22.03.4 r20123-38ccc47687.

View File

@ -0,0 +1,6 @@
---
title: Uninstall CentOS kernel
date: 2024-09-08
---
After converting from CentOS to AlmaLinux, uninstall CentOS kernels which would otherwise prevent dnf from installing newer Alma kernel due to higher version of CentOS kernel.

View File

@ -0,0 +1,6 @@
---
title: eSIM RSP certificate
date: 2024-09-15
---
If you are using any of these [removable eSIMs](https://euicc-manual.osmocom.org/docs/lpa/known-card/#product), when adding a new profile, check the SM-DP+ address has `81370f5125d0b1d408d4c3b232e6d25e795bebfb` (GSM Association - RSP2 Root CI1) as one of its issuer(s) in the [registry.csv](https://github.com/CursedHardware/gsma-rsp-certificates/blob/main/registry.csv) and does not contain an [ARA-M applet](https://osmocom.org/projects/sim-card-related/wiki/ESIM_profile_database) (which [complicates things](https://sysmocom.de/manuals/sysmoeuicc-manual.pdf) p. 26).

View File

@ -0,0 +1,8 @@
---
title: Splunk Connect for Syslog
date: 2024-09-24
---
In Splunk Connect for Syslog ([SC4S](https://splunk.github.io/splunk-connect-for-syslog/main/)), [`SC4S_DEST_GLOBAL_ALTERNATES`](https://splunk.github.io/splunk-connect-for-syslog/1.110.1/configuration/#configuration-of-alternate-destinations) has been deprecated since version 2. Instead, enable [`SC4S_ARCHIVE_GLOBAL`](https://splunk.github.io/splunk-connect-for-syslog/3.30.1/configuration/#archive-file-configuration) which stores a copy of events locally, not only for archiving (if that's what you intend) but also useful for troubleshooting dropped events.
If [`compliance_meta_by_source.conf`](https://splunk.github.io/splunk-connect-for-syslog/3.30.1/configuration/#override-index-or-metadata-based-on-host-ip-or-subnet-compliance-overrides) is not working, you may have to create a [custom post-filter](https://splunk.github.io/splunk-connect-for-syslog/3.30.1/create-parser/#create-a-parser_1) in "/opt/sc4s/local/config/filters/" instead.

6
source/_redirects Normal file
View File

@ -0,0 +1,6 @@
# Reverse proxy to cdn
/img/* https://curben.gitlab.io/blog/:splat 200
/files/* https://curben.gitlab.io/blog/:splat 200
# https://statically.discourse.group/t/mobile-version-not-working-on-screenshot/225
# /screenshot/* https://cdn.statically.io/screenshot/device=mobile/curben.netlify.app/:splat 200
/screenshot/* https://cdn.statically.io/screenshot/curben.netlify.app/:splat 200

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
source/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
source/icon/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

4
source/icon/logo.svg Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1" width="933.333" height="933.333" viewBox="0 0 700.000000 700.000000">
<path style="fill:#3D73B0" d="M482.3 117.3c-1.8 4-4.7 10.3-6.4 14.2-1.7 3.8-4.8 10.8-6.9 15.5-2.1 4.7-4.9 11-6.3 14-1.3 3-5 11.3-8.2 18.5-3.1 7.1-7.2 16.3-9.1 20.3-1.9 4.1-3.4 7.5-3.4 7.7 0 .2-1.3 3.3-3 6.8-1.6 3.6-3 6.7-3 7 0 .3-1.8 4.3-3.9 8.9-2.1 4.5-7.6 16.6-12.1 26.8-7.4 16.7-12.8 28.8-18 40.6-1.1 2.3-5.1 11.5-9.1 20.5-3.9 9-8.7 19.7-10.5 23.7-1.9 4.1-3.4 7.5-3.4 7.7 0 .2-1.6 3.7-3.5 7.8s-3.5 7.7-3.5 8c0 .3-1.7 4.3-3.9 8.9-2.1 4.5-5.5 12.1-7.6 16.8-2.1 4.7-5 11.2-6.5 14.5-1.5 3.3-3.7 8.2-4.9 11-1.3 2.7-3 6.7-4 8.8l-1.7 3.8-3.1-6.8c-6.4-13.8-10.2-22.3-18.7-41.3-4.8-10.7-9.6-21.3-10.6-23.5-1-2.2-8.1-18.2-15.8-35.5-7.7-17.3-19.7-44.3-26.7-60-7-15.7-15.4-34.4-18.6-41.7-3.2-7.3-6.8-15.4-8-18-4.7-10.4-5.9-13.1-9.9-22.3-5-11.3-7.2-16.2-11.5-25.5-1.7-3.9-4-9-5.1-11.5-1-2.4-3.3-7.4-5-11-3.1-6.3-3.2-6.4-4.2-3.9-.6 1.4-2.9 6.5-5.1 11.3-2.3 4.9-4.1 9-4.1 9.2 0 .2-1.4 3.4-3.1 7.1-1.7 3.8-15 33.6-29.6 66.3-26.5 59.7-28.1 63.3-32.3 72.5-1.2 2.7-10.6 23.7-20.7 46.5-10.2 22.8-19.5 43.7-20.8 46.5-1.2 2.7-11.8 26.6-23.4 53-11.7 26.4-22.2 50-23.3 52.5-1.1 2.5-9.9 22.3-19.5 44S6.2 577.8 5.5 579.5c-.7 1.6-2.2 5.1-3.4 7.7l-2 4.8 12.2.1c13.1.1 232.5.1 249.2 0l10-.1 1.4-3.2c.8-1.8 2.4-5.6 3.7-8.3 1.2-2.8 8.4-19 15.9-36 16.9-38.3 42-94.3 47.1-105l3.7-8 4 8.5c2.1 4.7 7.5 16.6 11.9 26.5 4.4 9.9 8.9 20 10 22.5 1.1 2.5 9 20.2 17.5 39.5 8.5 19.2 16.7 37.8 18.4 41.3 1.6 3.5 2.9 6.5 2.9 6.7 0 1.6 7.2 15.3 8.2 15.7 2.7.9 280.3.2 281.9-.8 1.5-.8 1.4-1.4-.7-5.9-2.1-4.6-8-18-22.9-52-3.1-7.2-6.7-15.3-8-18-1.2-2.8-5.7-12.9-10-22.5-4.2-9.6-8.6-19.5-9.7-21.9-1.1-2.5-5.6-12.6-10-22.5-4.3-10-8.8-20.1-10-22.6-1.1-2.5-3.6-8.1-5.5-12.5-2-4.4-4.5-10.3-5.8-13-1.2-2.8-4.8-10.9-8-18-3.1-7.2-6.6-15-7.7-17.5-1.2-2.5-3.5-7.9-5.3-12-1.8-4.1-4.4-10-5.8-13-1.3-3-4.9-11.1-8-18-7-16.1-9.5-21.6-11.8-26.5-1-2.2-19.6-44.3-41.4-93.5-21.8-49.2-40.2-90-40.8-90.7-.9-.8-2 .6-4.4 6z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,6 @@
# This file only applies to mdleom.com via web server's internal rewrite
User-agent: *
Disallow: /404
Disallow: /404.html
Sitemap: https://mdleom.com/sitemap.xml

3
source/robots.txt Normal file
View File

@ -0,0 +1,3 @@
# This file only applies to mirrors, see "robots-mdleom.com.txt"
User-agent: *
Disallow: /

20
source/site.webmanifest Normal file
View File

@ -0,0 +1,20 @@
{
"name": "",
"short_name": "",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#3f72af",
"background_color": "#3f72af",
"start_url": "https://mdleom.com/",
"display": "standalone"
}

4
source/svg/codeberg.svg Normal file
View File

@ -0,0 +1,4 @@
<!-- https://shields.io/badge/codeberg-curben-3f72af?logo=codeberg&style=for-the-badge -->
<!-- Shields.io - https://shields.io/ - https://github.com/badges/shields - License: https://github.com/badges/shields/blob/master/LICENSE (Icons & Code: CC0 License) -->
<!-- Codeberg icon is a trademark of Codeberg e.V. -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="184.5" height="28" role="img" aria-label="CODEBERG: CURBEN"><title>CODEBERG: CURBEN</title><g shape-rendering="crispEdges"><rect width="107" height="28" fill="#555"/><rect x="107" width="77.5" height="28" fill="#3f72af"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="100"><image x="9" y="7" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjMjE4NUQwIiByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+Q29kZWJlcmc8L3RpdGxlPjxwYXRoIGQ9Ik0xMS45NTUuNDlBMTIgMTIgMCAwIDAgMCAxMi40OWExMiAxMiAwIDAgMCAxLjgzMiA2LjM3M0wxMS44MzggNS45MjhhLjE4Ny4xNCAwIDAgMSAuMzI0IDBsMTAuMDA2IDEyLjkzNUExMiAxMiAwIDAgMCAyNCAxMi40OWExMiAxMiAwIDAgMC0xMi0xMiAxMiAxMiAwIDAgMC0uMDQ1IDB6bS4zNzUgNi40NjdsNC40MTYgMTYuNTUzYTEyIDEyIDAgMCAwIDUuMTM3LTQuMjEzeiIvPjwvc3ZnPg=="/><text transform="scale(.1)" x="620" y="175" textLength="660" fill="#fff">CODEBERG</text><text transform="scale(.1)" x="1457.5" y="175" textLength="535" fill="#fff" font-weight="bold">CURBEN</text></g></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

1
source/svg/favicon.svg Normal file
View File

@ -0,0 +1 @@
<svg version="1" xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 736.000000 736.000000" fill="#3f72af"><path d="M436.1 282.7c-40.9 92.3-74.6 168.1-75 168.5-.3.4-32.5-71.3-71.5-159.3-39-87.9-71.2-159.9-71.5-159.9C217.6 132 2 616.9.2 622.1c-.2.6 53.9.8 142.4.7l142.8-.3 37.5-84.7c20.7-46.6 37.9-84.4 38.2-84 .4.4 17.5 38.6 38 85l37.3 84.2h149.9c142.3 0 149.9-.1 149.4-1.8-.2-.9-39.7-90.1-87.7-198.2-47.9-108.1-98.3-221.6-111.8-252.2-13.6-30.7-24.9-55.8-25.2-55.8-.3 0-34 75.5-74.9 167.7z"/></svg>

After

Width:  |  Height:  |  Size: 521 B

4
source/svg/github.svg Normal file
View File

@ -0,0 +1,4 @@
<!-- https://shields.io/badge/github-curbengh-3f72af?logo=github&style=for-the-badge -->
<!-- Shields.io - https://shields.io/ - https://github.com/badges/shields - License: https://github.com/badges/shields/blob/master/LICENSE (Icons & Code: CC0 License) -->
<!-- GitHub icon is a trademark of GitHub Inc -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="188" height="28"><g shape-rendering="crispEdges"><path fill="#555" d="M0 0h89v28H0z"/><path fill="#3f72af" d="M89 0h99v28H89z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="100"><image x="9" y="7" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyBmaWxsPSJ3aGl0ZXNtb2tlIiByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+R2l0SHViIGljb248L3RpdGxlPjxwYXRoIGQ9Ik0xMiAuMjk3Yy02LjYzIDAtMTIgNS4zNzMtMTIgMTIgMCA1LjMwMyAzLjQzOCA5LjggOC4yMDUgMTEuMzg1LjYuMTEzLjgyLS4yNTguODItLjU3NyAwLS4yODUtLjAxLTEuMDQtLjAxNS0yLjA0LTMuMzM4LjcyNC00LjA0Mi0xLjYxLTQuMDQyLTEuNjFDNC40MjIgMTguMDcgMy42MzMgMTcuNyAzLjYzMyAxNy43Yy0xLjA4Ny0uNzQ0LjA4NC0uNzI5LjA4NC0uNzI5IDEuMjA1LjA4NCAxLjgzOCAxLjIzNiAxLjgzOCAxLjIzNiAxLjA3IDEuODM1IDIuODA5IDEuMzA1IDMuNDk1Ljk5OC4xMDgtLjc3Ni40MTctMS4zMDUuNzYtMS42MDUtMi42NjUtLjMtNS40NjYtMS4zMzItNS40NjYtNS45MyAwLTEuMzEuNDY1LTIuMzggMS4yMzUtMy4yMi0uMTM1LS4zMDMtLjU0LTEuNTIzLjEwNS0zLjE3NiAwIDAgMS4wMDUtLjMyMiAzLjMgMS4yMy45Ni0uMjY3IDEuOTgtLjM5OSAzLS40MDUgMS4wMi4wMDYgMi4wNC4xMzggMyAuNDA1IDIuMjgtMS41NTIgMy4yODUtMS4yMyAzLjI4NS0xLjIzLjY0NSAxLjY1My4yNCAyLjg3My4xMiAzLjE3Ni43NjUuODQgMS4yMyAxLjkxIDEuMjMgMy4yMiAwIDQuNjEtMi44MDUgNS42MjUtNS40NzUgNS45Mi40Mi4zNi44MSAxLjA5Ni44MSAyLjIyIDAgMS42MDYtLjAxNSAyLjg5Ni0uMDE1IDMuMjg2IDAgLjMxNS4yMS42OS44MjUuNTdDMjAuNTY1IDIyLjA5MiAyNCAxNy41OTIgMjQgMTIuMjk3YzAtNi42MjctNS4zNzMtMTItMTItMTIiLz48L3N2Zz4="/> <text x="530" y="175" transform="scale(.1)" textLength="480">GITHUB</text><text x="1385" y="175" font-weight="bold" transform="scale(.1)" textLength="750">CURBENGH</text></g> </svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

4
source/svg/gitlab.svg Normal file
View File

@ -0,0 +1,4 @@
<!-- https://shields.io/badge/gitlab-curben-3f72af?logo=gitlab&style=for-the-badge -->
<!-- Shields.io - https://shields.io/ - https://github.com/badges/shields - License: https://github.com/badges/shields/blob/master/LICENSE (Icons & Code: CC0 License) -->
<!-- GitLab icon is a trademark of GitLab Inc -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="166" height="28"><g shape-rendering="crispEdges"><path fill="#555" d="M0 0h87v28H0z"/><path fill="#3f72af" d="M87 0h79v28H87z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="100"><image x="9" y="7" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIzLjk1NiAxMy41ODdsLTEuMzQ0LTQuMTMzYTQ1NDkuODE0IDQ1NDkuODE0IDAgMCAwLTIuNjYzLTguMTg5LjQ1Ni40NTYgMCAwIDAtLjg3IDBsLTIuNjU4IDguMTg5SDcuNTg1TDQuOTIgMS4yNjVhLjQ1Ni40NTYgMCAwIDAtLjg3IDBBNDU0OS44MTQgNDU0OS44MTQgMCAwIDAgLjA0NCAxMy41ODdhLjkwOC45MDggMCAwIDAgLjMzNiAxLjAyTDEyIDIzLjA1NGwxMS42Mi04LjQ0N2EuOTA4LjkwOCAwIDAgMCAuMzM2LTEuMDIiIGZpbGw9IiNmYzZkMjYiLz48cGF0aCBkPSJNMTIgMjMuMDU0bDQuNDIxLTEzLjZINy41OHoiIGZpbGw9IiNlMjQzMjkiLz48cGF0aCBkPSJNNy41NzkgOS40NTRIMS4zODhMMTIgMjMuMDU0eiIgZmlsbD0iI2ZjNmQyNiIvPjxwYXRoIGQ9Ik0xLjM4OCA5LjQ1NEwuMDQ0IDEzLjU4N2EuOTA4LjkwOCAwIDAgMCAuMzM2IDEuMDJMMTIgMjMuMDU0eiIgZmlsbD0iI2ZjYTMyNiIvPjxwYXRoIGQ9Ik03LjU3OSA5LjQ1NEw0LjkyIDEuMjY1YS40NTYuNDU2IDAgMCAwLS44NyAwTDEuMzg4IDkuNDU0eiIgZmlsbD0iI2UyNDMyOSIvPjxwYXRoIGQ9Ik0xNi40MjEgOS40NTRoNi4xOTFMMTIgMjMuMDU0eiIgZmlsbD0iI2ZjNmQyNiIvPjxwYXRoIGQ9Ik0yMi42MTIgOS40NTRsMS4zNDQgNC4xMzNhLjkwOC45MDggMCAwIDEtLjMzNiAxLjAyTDEyIDIzLjA1NHoiIGZpbGw9IiNmY2EzMjYiLz48cGF0aCBkPSJNMTYuNDIxIDkuNDU0bDIuNjU4LTguMTg5YS40NTYuNDU2IDAgMCAxIC44NyAwbDIuNjYzIDguMTg5eiIgZmlsbD0iI2UyNDMyOSIvPjwvc3ZnPg=="/> <text x="520" y="175" transform="scale(.1)" textLength="460">GITLAB</text><text x="1265" y="175" font-weight="bold" transform="scale(.1)" textLength="550">CURBEN</text></g> </svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

4
source/svg/linkedin.svg Normal file
View File

@ -0,0 +1,4 @@
<!-- https://shields.io/badge/linkedin-mdleom-3f72af?logo=linkedin&style=for-the-badge -->
<!-- Shields.io - https://shields.io/ - https://github.com/badges/shields - License: https://github.com/badges/shields/blob/master/LICENSE (Icons & Code: CC0 License) -->
<!-- LinkedIn icon is a trademark of LinkedIn Corporation -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="180" height="28" role="img" aria-label="LINKEDIN: MDLEOM"><title>LINKEDIN: MDLEOM</title><g shape-rendering="crispEdges"><rect width="102" height="28" fill="#555"/><rect x="102" width="78" height="28" fill="#3f72af"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="100"><image x="9" y="7" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyBmaWxsPSJ3aGl0ZXNtb2tlIiByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+TGlua2VkSW4gaWNvbjwvdGl0bGU+PHBhdGggZD0iTTIwLjQ0NyAyMC40NTJoLTMuNTU0di01LjU2OWMwLTEuMzI4LS4wMjctMy4wMzctMS44NTItMy4wMzctMS44NTMgMC0yLjEzNiAxLjQ0NS0yLjEzNiAyLjkzOXY1LjY2N0g5LjM1MVY5aDMuNDE0djEuNTYxaC4wNDZjLjQ3Ny0uOSAxLjYzNy0xLjg1IDMuMzctMS44NSAzLjYwMSAwIDQuMjY3IDIuMzcgNC4yNjcgNS40NTV2Ni4yODZ6TTUuMzM3IDcuNDMzYy0xLjE0NCAwLTIuMDYzLS45MjYtMi4wNjMtMi4wNjUgMC0xLjEzOC45Mi0yLjA2MyAyLjA2My0yLjA2MyAxLjE0IDAgMi4wNjQuOTI1IDIuMDY0IDIuMDYzIDAgMS4xMzktLjkyNSAyLjA2NS0yLjA2NCAyLjA2NXptMS43ODIgMTMuMDE5SDMuNTU1VjloMy41NjR2MTEuNDUyek0yMi4yMjUgMEgxLjc3MUMuNzkyIDAgMCAuNzc0IDAgMS43Mjl2MjAuNTQyQzAgMjMuMjI3Ljc5MiAyNCAxLjc3MSAyNGgyMC40NTFDMjMuMiAyNCAyNCAyMy4yMjcgMjQgMjIuMjcxVjEuNzI5QzI0IC43NzQgMjMuMiAwIDIyLjIyMiAwaC4wMDN6Ii8+PC9zdmc+"/><text fill="#fff" x="595" y="175" transform="scale(.1)" textLength="610">LINKEDIN</text><text fill="#fff" x="1410" y="175" font-weight="bold" transform="scale(.1)" textLength="540">MDLEOM</text></g></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

4
source/svg/npm.svg Normal file
View File

@ -0,0 +1,4 @@
<!-- https://shields.io/badge/npm-curben-3f72af?logo=npm&style=for-the-badge -->
<!-- Shields.io - https://shields.io/ - https://github.com/badges/shields - License: https://github.com/badges/shields/blob/master/LICENSE (Icons & Code: CC0 License) -->
<!-- npm icon is a trademark of npm Inc -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="145.5" height="28"><g shape-rendering="crispEdges"><path fill="#555" d="M0 0h66.5v28H0z"/><path fill="#3f72af" d="M66.5 0h79v28H66.5z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="100"><image x="9" y="7" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MCA0MCI+PHBhdGggZD0iTTAgMGg0MHY0MEgwVjB6IiBmaWxsPSIjY2IwMDAwIi8+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTcgN2gyNnYyNmgtN1YxNGgtNnYxOUg3eiIvPjwvc3ZnPgo="/> <text x="417.5" y="175" transform="scale(.1)" textLength="255">NPM</text><text x="1060" y="175" font-weight="bold" transform="scale(.1)" textLength="550">CURBEN</text></g> </svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

2
themes/chameleon/.npmrc Normal file
View File

@ -0,0 +1,2 @@
package-lock=false
optional=false

View File

@ -0,0 +1,56 @@
# Chameleon theme
Chameleon is a fork of [Typing](https://github.com/geekplux/hexo-theme-typing) theme, rewrote from scratch with the following changes/features:
- [sanitize.css](https://github.com/csstools/sanitize.css/) and [autoprefixer](https://github.com/csstools/sanitize.css/) for consistent cross-browser styling.
- Utilise [relative length](https://www.w3schools.com/CSSref/css_units.asp) instead of absolute length in the css.
- [`prefers-color-scheme`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme) to apply light/dark theme according to the OS preference.
- Add a [Copy](https://clipboardjs.com/) button to each code block.
- Removed jQuery, fancyBox, web analytics, comment plugins, donation links and [typo.css](https://github.com/sofish/typo.css).
- Homepage shows index of posts (same as /archives).
## Options
Configure this theme from your **site**'s configuration:
``` yml
# _config.yml
theme_config:
# Header menu
menu:
Home: /
Archives: /archives/
Feed: /atom.xml
# Footer menu
footer:
GitLab: https://gitlab.com/curben/blog
# Load forkawesome icons?
icons: false
```
## Codeblock highlight
Following configurations need to be updated.
``` yml
# _config.yml
highlight:
enable: true
line_number: false
hljs: true
wrap: false
prismjs:
enable: false
```
## Build
Build "source/css/_source.css" into "source/css/chameleon.css"
```
$ npm install
$ npm run build
```

View File

@ -0,0 +1,33 @@
'use strict'
/*
* Normalize chameleon.css using sanitize.css
* Add browser prefixes using autoprefixer
*/
const { readFile, writeFile } = require('fs').promises
const { join, resolve } = require('path')
const deps = ['autoprefixer', 'postcss', 'postcss-normalize']
deps.forEach((dep) => {
try {
require.resolve(dep)
} catch (err) {
console.error(`Missing "${dep}" dependency. Please install it by running "npm install ${dep}".`)
}
})
const autoprefixer = require('autoprefixer')
const normalize = require('postcss-normalize')
const postcss = require('postcss')
const build = async () => {
const cssPath = resolve(__dirname, '../source/css/')
const cssSource = join(cssPath, '_source.css')
const cssSave = join(cssPath, 'chameleon.css')
const inCss = await readFile(cssSource)
const { css: outCss } = await postcss([normalize, autoprefixer]).process(inCss, { from: cssSource })
await writeFile(cssSave, outCss)
}
build()

View File

@ -0,0 +1 @@
en-GB.yml

View File

@ -0,0 +1,22 @@
categories: Categories
search: Search
tags: Tags
tagcloud: Tag Cloud
tweets: Tweets
prev: Prev
next: Next
comment: Comments
archive_a: Archives
archive_b: "Archives: %s"
page: Page %d
recent_posts: Recent Posts
newer: Newer
older: Older
share: Share
powered_by: Powered by
rss_feed: RSS Feed
category: Category
tag: Tag
date_published: Published Date
date_updated: Last Updated
source: Source

View File

@ -0,0 +1,9 @@
<% if (is_post()) { %>
<%- js([
{ src: '/libs/clipboard-2.0.6.min.js', defer: true },
'/js/copy-button.js'
]) %>
<% } %>
<%/* javascript of Chameleon theme */%>
<%- js('/js/chameleon.js') %>

View File

@ -0,0 +1,9 @@
<article class="archive-article archive-type-<%= post.layout %>">
<div class="archive-article-inner">
<header class="archive-article-header">
<span class="archive-article-date"><%- partial('post/date') %></span>
<%- partial('post/title', {class_name: 'archive-article-title'}) %>
<span><%- post.more %></span>
</header>
</div>
</article>

View File

@ -0,0 +1,22 @@
<% page.posts.each((post, i) => { %>
<section class="archives-wrap">
<div class="archives">
<%- partial('archive-post', { post }) %>
<% }) %>
<% if (page.posts.length) { %>
</div>
</section>
<% } %>
<% if (page.total > 1) { %>
<nav id="page-nav" class="page-nav">
<%
const prev_text = '&laquo; ' + __('prev')
const next_text = __('next') + ' &raquo;'
%>
<%- paginator({
prev_text: prev_text,
next_text: next_text,
escape: false
}) %>
</nav>
<% } %>

View File

@ -0,0 +1,53 @@
<article id="<%= post.layout %>-<%= post.slug %>" class="h-entry article article-type-<%= post.layout %>" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
<%- partial('_partial/header', {}, { cache: true }) %>
<hr class="header-hr">
<div class="article-inner">
<%- partial('post/gallery') %>
<% if (post.link || post.title) { %>
<header class="article-header">
<%- partial('post/title', {class_name: 'p-name entry-title article-title'}) %>
</header>
<% } %>
<% if (post.excerpt) { %>
<p class="p-summary"><%- post.excerpt %></p>
<% } %>
<div class="e-content article-entry" itemprop="articleBody">
<%- toc(page.content, {list_number: false}) %>
<%- post.more %>
</div>
<footer class="article-footer">
<ul class="article-meta">
<% if (post.updated) { %>
<li>
<span class="label"><%= __('date_updated') %>:</span>
<%- partial('post/updated-date') %>
</li>
<% } %>
<li>
<span class="label"><%= __('date_published') %>:</span>
<%- partial('post/date') %>
</li>
<% if (post.categories && post.categories.length) { %>
<li>
<span class="label"><%= __('category') %>:</span>
<%- partial('post/category') %>
</li>
<% } %>
<% if (post.tags && post.tags.length) { %>
<li>
<span class="label"><%= __('tag') %>:</span>
<%- partial('post/tag') %>
</li>
<% } %>
<li>
<span class="label"><%= __('source') %>:</span>
<%- link('https://gitlab.com/curben/blog/-/blob/microblog/source/' + post.source, post.source.replace('_posts/', '')) %>
</li>
<hr/>
</ul>
</footer>
</div>
<% if (!index) { %>
<%- partial('post/nav') %>
<% } %>
</article>

View File

@ -0,0 +1,16 @@
<%/* Display footer on every page */%>
<%/* Use different CSS layout for different page layout */%>
<% if (is_post()) { %>
<footer id="footer" class="post-footer footer">
<hr>
<% } else { %>
<footer id="footer" class="archive-footer footer">
<hr>
<% } %>
<div id="copyright" class="copyright">
&copy; 2018-<%= new Date().getUTCFullYear() %> <%= config.author %>. <%- link('https://gitlab.com/curben/blog', 'Powered by ') %> <%- link('https://hexo.io/', 'Hexo') %> with <%- link('https://gitlab.com/curben/blog/tree/master/themes/chameleon', 'Chameleon') %> theme. <br/>Content is available under <a href="https://creativecommons.org/licenses/by-sa/4.0/" rel="license" itemprop="license">CC-BY-SA 4.0</a>, unless indicated otherwise.<br/>
<% for (const i in theme.footer) { %>
<a class="main-nav-link" href="<%- url_for(theme.footer[i]) %>"><%= i %></a>
<% } %>
</div>
</footer>

View File

@ -0,0 +1,66 @@
<head>
<meta charset="utf-8">
<%
let title = page.title
if (is_archive()) {
title = titleCase(config.archive_dir)
if (is_month()) {
title += ': ' + page.year + '/' + page.month
} else if (is_year()) {
title += ': ' + page.year
} else if (page.current_url) {
const pageNum = Number(page.current_url.charAt(page.current_url.length - 2))
if (Number.isInteger(pageNum)) title += ': Page ' + pageNum
}
} else if (is_category()) {
title = __('category') + ': ' + page.category
} else if (is_tag()) {
title = __('tag') + ': ' + page.tag
}
%>
<title><% if (title) { %><%= title %> | <% } %><%= config.title %></title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="canonical" href="<%- full_url_for(path) %>">
<%/* Add Open Graph meta tags for easier sharing on social networking sites */%>
<%/* Modified from original source for compatibility with my blog */%>
<%- openGraph() %>
<% if (config.feed) { %>
<%- feed_tag() %>
<% } else if (theme.rss) { %>
<%- feed_tag(theme.rss) %>
<% } %>
<%/* Favicon */%>
<%/* https://evilmartians.com/chronicles/how-to-favicon-in-2021-six-files-that-fit-most-needs */%>
<link rel="icon" href="<%- url_for('/favicon.ico') %>">
<link rel="icon" href="<%- url_for('/svg/favicon.svg') %>" type="image/svg+xml" sizes="any">
<link rel="apple-touch-icon" href="<%- url_for('/apple-touch-icon-180x180.png') %>">
<link rel="manifest" href="<%- url_for('/site.webmanifest') %>">
<link rel="sitemap" type="application/xml" title="Sitemap" href="<%- url_for('/sitemap.xml') %>">
<%/* CSS of Chameleon theme */%>
<%- css('/css/chameleon.css') %>
<% if (theme.icons) { %>
<%- css('/libs/forkawesome/css/forkawesome-1.1.7.min.css') %>
<% } %>
<% if (is_post() && page.series) { %>
<%
if (page.series !== 'first' && page.next?.path?.startsWith(`${config.archive_dir}/`)) {
const pageTitle = page.next.title
%>
<%/* page.next is not a typo */%>
<link rel="prev" href="<%- url_for(page.next.path) %>" title="<%= pageTitle %>">
<% } %>
<%
if (page.series !== 'last' && page.prev?.path?.startsWith(`${config.archive_dir}/`)) {
const pageTitle = page.prev.title
%>
<link rel="next" href="<%- url_for(page.prev.path) %>" title="<%= pageTitle %>">
<% } %>
<% } %>
<meta name="yandex-verification" content="e026a03a5dd06730" />
</head>

View File

@ -0,0 +1,51 @@
<header id="header" class="header">
<%/* Nav menu for desktop */%>
<nav class="main-nav">
<% if (!theme.menu) { %><a class="main-nav-link" href="<%- config.root %>">Home</a>
<% } else { %>
<% for (const i in theme.menu) { %>
<a class="main-nav-link" href="<%- url_for(theme.menu[i]) %>"><%= i %></a>
<% }} %>
<div class="search-container">
<form id="searchForm" method="post" action="https://duckduckgo.com/" rel="noopener external nofollow noreferrer" role="search">
<input type="search" name="q" class="search-box" placeholder="Search..." title="Powered by DuckDuckGo" enterkeyhint="search">
<input type="hidden" name="sites" value="<%- config.url.replace(/^http(s)?:\/\//, '') %>">
<input type="hidden" name="kd" value="-1">
<input type="hidden" name="kg" value="p">
<input type="hidden" name="kae" value="#000">
<a class="search-button" id="search-click">
<img src="/svg/search.svg">
</a>
</form>
</div>
</nav>
<%/* Nav menu for mobile */%>
<nav class="mobile-nav">
<a class="site-title" href="<%- config.root %>"><%- config.url.replace(/^http(s)?:\/\//, '') %></a>
<ul class="mobile-nav-menu">
<label for="mobile-menu-toggle"><a class="no-underline" id="menu-button">&#9776;</a></label>
<input id="mobile-menu-toggle" type="checkbox"/>
<ul id="mobile-nav-link">
<div class="search-container">
<form id="searchFormMob" method="post" action="https://duckduckgo.com/" rel="noopener external nofollow noreferrer" role="search">
<input type="search" name="q" class="search-box" placeholder="Search..." title="Powered by DuckDuckGo" enterkeyhint="search">
<input type="hidden" name="sites" value="<%- config.url.replace(/^http(s)?:\/\//, '') %>">
<input type="hidden" name="kd" value="-1">
<input type="hidden" name="kg" value="p">
<input type="hidden" name="kae" value="#000">
<a class="search-button" id="search-click-mobile">
<img src="/svg/search.svg">
</a>
</form>
</div>
<i>Powered by DuckDuckGo</i>
<% if (!theme.menu) { %><a class="mobile-nav-link-a" href="<%- config.root %>">Home</a>
<% } else { %>
<% for (const i in theme.menu) { %>
<a class="mobile-nav-link-a" href="<%- url_for(theme.menu[i]) %>"><%= i %></a>
<% }} %>
</ul>
</ul>
</nav>
</header>

View File

@ -0,0 +1,10 @@
<% if (post.categories && post.categories.length) { %>
<div class="article-category">
<%- list_categories(post.categories, {
show_count: false,
class: 'article-category',
style: 'none',
separator: '/'
}) %>
</div>
<% } %>

View File

@ -0,0 +1,6 @@
<%/* Date format is specified to skip timezone conversion */%>
<time <% if (is_post()) { %>class="dt-published"<% } %> datetime="<%= date(post.date, 'YYYY-MM-DD[T00:00:00.000Z]') %>" itemprop="datePublished">
<%= date(post.date, 'D') %>
<a href="/<%= config.archive_dir %>/<%= date(post.date, 'YYYY') %>/<%= date(post.date, 'MM') %>/" title="See <%= date(post.date, 'MMMM') %> <%= date(post.date, 'YYYY') %>'s posts"><%= date(post.date, ' MMM') %></a>
<a href="/<%= config.archive_dir %>/<%= date(post.date, 'YYYY') %>/" title="See <%= date(post.date, 'YYYY') %>'s posts"><%= date(post.date, ' YYYY') %></a>
</time>

View File

@ -0,0 +1,11 @@
<% if (post.photos && post.photos.length) { %>
<div class="article-gallery">
<div class="article-gallery-photos">
<% post.photos.forEach((photo, i) => { %>
<a class="article-gallery-img" href="<%- url_for(photo) %>" rel="gallery_<%= post._id %>">
<img src="<%- url_for(photo) %>" itemprop="image">
</a>
<% }) %>
</div>
</div>
<% } %>

View File

@ -0,0 +1,20 @@
<% if (post.prev || post.next) { %>
<nav id="article-nav" class="article-nav">
<span class="article-nav-link-wrap newer">
<% if (post.prev) { %>
<strong><%= __('newer') %></strong>
<a href="<%- url_for(post.prev.path) %>">
<div class="article-nav-title"><%= post.prev.title %></div>
</a>
<% } %>
</span>
<span class="article-nav-link-wrap older">
<% if (post.next) { %>
<strong><%= __('older') %></strong>
<a href="<%- url_for(post.next.path) %>">
<div class="article-nav-title"><%= post.next.title %></div>
</a>
<% } %>
</span>
</nav>
<% } %>

View File

@ -0,0 +1,7 @@
<% if (post.tags && post.tags.length) { %>
<%- list_tags(post.tags, {
show_count: false,
class: 'article-tag',
transform: titleCase
}) %>
<% } %>

View File

@ -0,0 +1,18 @@
<% if (post.link) { %>
<h1 itemprop="name">
<a class="<%= class_name %>" href="<%- url_for(post.link) %>" itemprop="url"><%= post.title %></a>
</h1>
<% } else if (post.title) { %>
<% if (index) { %>
<h1 itemprop="name">
<a class="<%= class_name %>" href="<%- url_for(post.path) %>"><%= post.title %></a>
</h1>
<% } else { %>
<h1 class="<%= class_name %>" itemprop="headline name"><%= post.title %>
<%/* Share button */%>
<a id="btnshare">
<img src="/svg/share.svg">
</a>
</h1>
<% } %>
<% } %>

View File

@ -0,0 +1,3 @@
<%/* User-specified updated date */%>
<%/* default post.updated is (almost) always is the time 'hexo generate' is executed */%>
<time class="dt-updated" datetime="<%= date(post.updated, 'YYYY-MM-DD[T00:00:00.000Z]') %>" itemprop="dateModified"><%= date(post.updated, 'D MMM YYYY') %></time>

View File

@ -0,0 +1,5 @@
<div class="archive-container">
<%- partial('_partial/header', {}, { cache: true }) %>
<hr class="header-hr"/>
<%- partial('_partial/archive', {pagination: config.archive, index: true}) %>
</div>

View File

@ -0,0 +1,5 @@
<div class="archive-container">
<%- partial('_partial/header', {}, { cache: true }) %>
<hr class="header-hr">
<%- partial('_partial/archive', {pagination: config.category, index: true}) %>
</div>

View File

@ -0,0 +1,15 @@
<div class="archive-container">
<%- partial('_partial/header', {}, { cache: true }) %>
<hr class="header-hr"/>
<h2>Recent posts:</h2>
<%- list_posts({
amount: 10,
class: 'index'
}) %>
<h2>Tags:</h2>
<%- list_tags({
amount: 0,
class: 'index',
transform: titleCase
}) %>
</div>

View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="<%= config.language %>">
<%- partial('_partial/head', {}) %>
<%/* set /about page to use "home" page layout */%>
<% if (is_current('about/index.html')) { %>
<body class="about-body">
<% } else {%>
<body>
<% } %>
<div id="container" class="container">
<%- body %>
</div>
<%- partial('_partial/footer') %>
<%- partial('_partial/after-footer') %>
</body>
</html>

View File

@ -0,0 +1 @@
<%- partial('_partial/article', {post: page, index: false}) %>

View File

@ -0,0 +1 @@
<%- partial('_partial/article', {post: page, index: false}) %>

View File

@ -0,0 +1,10 @@
<div class="archive-container">
<%- partial('_partial/header', {}, { cache: true }) %>
<hr class="header-hr"/>
<h2>Tags:</h2>
<%- list_tags({
amount: 0,
class: 'index',
transform: titleCase
}) %>
</div>

View File

@ -0,0 +1,5 @@
<div class="archive-container">
<%- partial('_partial/header', {}, { cache: true }) %>
<hr class="header-hr">
<%- partial('_partial/archive', {pagination: config.tag, index: true}) %>
</div>

View File

@ -0,0 +1,17 @@
{
"name": "hexo-theme-chameleon",
"version": "0.0.1",
"private": true,
"scripts": {
"build": "node build/script.js"
},
"devDependencies": {
"autoprefixer": "^10.0.0",
"postcss": "^8.0.6",
"postcss-normalize": "^10.0.0",
"sanitize.css": "^13.0.0"
},
"engines": {
"node": ">= 18.12.0"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

View File

@ -0,0 +1,16 @@
'use strict'
/* global hexo */
/*
* Add "Copy" button to code snippet
*/
hexo.extend.filter.register('after_render:html', (data) => {
const copyBtn = '<button class="copy-button">Copy</button>'
// Regex is based on https://github.com/hexojs/hexo/pull/3697
return data.replace(/<pre>(?!<\/pre>).+?<\/pre>/gs, (str) => {
if (!str.includes(copyBtn)) return str.replace('</pre>', copyBtn + '</pre>')
return str
})
})

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>{{ config.title }}</title>
{% if icon %}<icon>{{ icon }}</icon>{% endif %}
{% if config.subtitle %}<subtitle>{{ config.subtitle }}</subtitle>{% endif %}
<link href="{{ feed_url | uriencode }}" rel="self"/>
{% if config.feed.hub %}<link href="{{ config.feed.hub | uriencode }}" rel="hub"/>{% endif %}
<link href="{{ url | uriencode }}"/>
<updated>{% if posts.first().updated %}{{ posts.first().updated | formatDate }}{% else %}{{ posts.first().date | formatDate }}{% endif %}</updated>
<id>{{ url | uriencode }}</id>
{% if config.author %}
<author>
<name>{{ config.author }}</name>
{% if config.email %}<email>{{ config.email }}</email>{% endif %}
</author>
{% endif %}
<generator uri="https://hexo.io/">Hexo</generator>
{% for post in posts.toArray() %}
<entry>
<title>{{ post.title }}</title>
<link href="{{ post.permalink }}"/>
<id>{{ post.permalink }}</id>
<published>{{ post.date | formatDate }}</published>
<updated>{% if post.updated %}{{ post.updated | formatDate }}{% else %}{{ post.date | formatDate }}{% endif %}</updated>
{% if config.feed.content and post.content %}
<content type="html"><![CDATA[{{ post.content | noControlChars | safe }}]]></content>
{% endif %}
{% if post.description %}
<summary type="html">{{ post.description }}</summary>
{% elif post.intro %}
<summary type="html">{{ post.intro }}</summary>
{% elif post.excerpt %}
<summary type="html">{{ post.excerpt }}</summary>
{% elif post.content %}
{% set short_content = post.content.substring(0, config.feed.content_limit) %}
{% if config.feed.content_limit_delim %}
{% set delim_pos = short_content.lastIndexOf(config.feed.content_limit_delim) %}
{% if delim_pos > -1 %}
<summary type="html">{{ short_content.substring(0, delim_pos) }}</summary>
{% else %}
<summary type="html">{{ short_content }}</summary>
{% endif %}
{% else %}
<summary type="html">{{ short_content }}</summary>
{% endif %}
{% endif %}
{% if post.image %}
<content src="{{ post.image | fullUrlFor }}" type="image" />
{% endif %}
{% for tag in post.tags.toArray() %}
<category term="{{ tag.name }}" scheme="{{ tag.path | fullUrlFor }}"/>
{% endfor %}
</entry>
{% endfor %}
</feed>

View File

@ -0,0 +1,58 @@
'use strict'
const nunjucks = require('nunjucks')
const env = new nunjucks.Environment()
const { join } = require('path')
const { readFileSync } = require('fs')
const moment = require('moment')
const { encodeURL, full_url_for } = require('hexo-util')
env.addFilter('uriencode', str => {
return encodeURL(str)
})
env.addFilter('noControlChars', str => {
return str.replace(/[\x00-\x1F\x7F]/g, '') // eslint-disable-line no-control-regex
})
env.addFilter('formatDate', str => {
return moment(str).format('YYYY-MM-DD[T00:00:00.000Z]')
})
const atomTmplSrc = join(__dirname, './.atom.xml')
const atomTmpl = nunjucks.compile(readFileSync(atomTmplSrc, 'utf8'), env)
module.exports = function (locals) {
const { config } = this
const { feed, url } = config
const { icon: iconCfg, limit, order_by, path } = feed
const template = atomTmpl
env.addFilter('fullUrlFor', str => {
return full_url_for.call(this, str)
})
let posts = locals.posts.sort(order_by || '-date')
posts = posts.filter(post => {
return post.draft !== true
})
if (limit) posts = posts.limit(limit)
const icon = iconCfg ? full_url_for.call(this, iconCfg) : ''
const feed_url = full_url_for.call(this, path);
const data = template.render({
config,
url,
icon,
posts,
feed_url
})
return {
path,
data
}
}

View File

@ -0,0 +1,32 @@
/* global hexo */
'use strict'
/*
* Modified from the hexo version,
* https://github.com/hexojs/hexo-generator-feed
* to remove timezone
*/
const { extname } = require('path')
const config = hexo.config.feed = Object.assign({
type: 'atom',
limit: 20,
hub: '',
content: true,
content_limit: 140,
content_limit_delim: '',
order_by: '-date'
}, hexo.config.feed)
// Set default feed path
if (!config.path) {
config.path = config.type + '.xml'
}
// Add extension name if don't have
if (!extname(config.path)) {
config.path += '.xml'
}
hexo.extend.generator.register('feed', require('./generator'))

View File

@ -0,0 +1,33 @@
'use strict'
/* global hexo */
/*
* Add Link button next to a heading
* based on https://github.com/hexojs/hexo-renderer-marked/blob/master/lib/renderer.js
*/
const { slugize, stripHTML, unescapeHTML: unescape } = require('hexo-util')
const anchorId = (str, transformOption) => {
return slugize(stripHTML(unescape(str.replace('.', ''))).trim(), { transform: transformOption })
}
hexo.extend.filter.register('marked:renderer', function (renderer) {
const { config } = this
const headingId = {}
renderer.heading = function (text, level) {
const { modifyAnchors } = config.marked
const transformOption = modifyAnchors
let id = anchorId(text, transformOption)
// Add a number after id if repeated
if (headingId[id]) {
id += `-${headingId[id]++}`
} else {
headingId[id] = 1
}
// add headerlink
return `<h${level} id="${id}">${text} <a href="#${id}" class="headerlink" title="${stripHTML(text)}">§</a></h${level}>`
}
})

View File

@ -0,0 +1,26 @@
'use strict'
/* global hexo */
/*
* Modified from the hexo version,
* https://github.com/hexojs/hexo/blob/master/lib/plugins/helper/link_to.js
* to remove title attribute and 'external' option
*/
const { htmlTag } = require('hexo-util')
hexo.extend.helper.register('link', (path, text) => {
const urlFor = hexo.extend.helper.get('url_for').bind(hexo)
if (!text) text = path.replace(/^https?:\/\/|\/$/g, '')
const attrs = Object.assign({
href: urlFor(path)
})
if (attrs.class && Array.isArray(attrs.class)) {
attrs.class = attrs.class.join(' ')
}
return htmlTag('a', attrs, text)
})

View File

@ -0,0 +1,94 @@
'use strict'
/* global hexo */
/*
* Modified from the hexo version,
* https://github.com/hexojs/hexo/blob/master/lib/plugins/helper/open_graph.js
* to include https://github.com/hexojs/hexo/pull/3674
* and use WHATWG URL API
* https://nodejs.org/api/url.html#url_the_whatwg_url_api
*/
const moment = require('moment')
const { escapeHTML, htmlTag, prettyUrls, stripHTML } = require('hexo-util')
const fullUrlFor = require('hexo-util').full_url_for
function meta (name, content) {
return `${htmlTag('meta', {
name,
content
})}\n`
}
function og (name, content) {
return `${htmlTag('meta', {
property: name,
content
})}\n`
}
function openGraphHelper () {
const { config, page } = this
let description = page.excerpt || ''
const author = config.author
const keywords = page.tags || ''
const title = page.title || config.title || ''
const type = (this.is_post() ? 'article' : 'website')
const url = prettyUrls(this.url, config.pretty_urls)
const screenshot = '/screenshot/' + prettyUrls(this.path, config.pretty_urls)
const siteName = config.title || ''
const published = page.date || ''
const updated = page.updated || ''
const language = config.language.replace('-', '_')
let result = ''
if (description) {
description = escapeHTML(stripHTML(description)
.trim()
.replace(/\n/g, ' ')
.substring(0, 200))
result += meta('description', description)
}
result += og('article:author', author)
if (keywords) {
keywords.forEach(tag => {
result += og('article:tag', tag.name)
})
}
result += og('og:type', type)
result += og('og:title', title)
result += og('og:url', url)
if (siteName) {
result += og('og:site_name', siteName)
}
if (description) {
result += og('og:description', description)
}
result += og('og:locale', language)
result += og('og:image', fullUrlFor.call(this, screenshot))
if (published) {
if ((moment.isMoment(published) || moment.isDate(published)) && !isNaN(published.valueOf())) {
// Skip timezone conversion
result += og('article:published_time', moment(published).format('YYYY-MM-DD[T00:00:00.000Z]'))
}
}
if (updated) {
if ((moment.isMoment(updated) || moment.isDate(updated)) && !isNaN(updated.valueOf())) {
result += og('article:modified_time', moment(updated).format('YYYY-MM-DD[T00:00:00.000Z]'))
}
}
return result.trim()
}
hexo.extend.helper.register('openGraph', openGraphHelper)

View File

@ -0,0 +1,7 @@
Copyright (c) 2014 Tommy Chen, Alex Chauvin
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.

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
{% for post in posts %}
<url>
<loc>{{ post.permalink }}</loc>
{% if post.updated %}
<lastmod>{{ post.updated | formatDate }}</lastmod>
{% elif post.date %}
<lastmod>{{ post.date | formatDate }}</lastmod>
{% endif %}
</url>
{% endfor %}
{# home page #}
<url>
<loc>{{ config.root | fullUrlFor }}</loc>
<lastmod>{% if posts[0].updated %}{{ posts[0].updated | formatDate }}{% else %}{{ posts[0].date | formatDate }}{% endif %}</lastmod>
</url>
{# archive page #}
<url>
<loc>{{ config.archive_dir | fullUrlFor + '/' }}</loc>
<lastmod>{% if posts[0].updated %}{{ posts[0].updated | formatDate }}{% else %}{{ posts[0].date | formatDate }}{% endif %}</lastmod>
</url>
{% for tag in tags %}
<url>
<loc>{{ tag.permalink }}</loc>
<lastmod>{% if tag.posts.first().updated %}{{ tag.posts.first().updated | formatDate }}{% else %}{{ tag.posts.first().date | formatDate }}{% endif %}</lastmod>
</url>
{% endfor %}
</urlset>

View File

@ -0,0 +1,47 @@
'use strict'
const micromatch = require('micromatch')
const template = require('./template')
const isMatch = (path, patterns) => {
if (patterns && patterns.length) {
if (micromatch.isMatch(path, patterns, { matchBase: true })) return true
}
return false
}
module.exports = function (locals) {
const { config } = this
const { sitemap, skip_render } = config
const { path } = sitemap
const skipRenderList = ['*.js', '*.css']
if (Array.isArray(skip_render)) {
skipRenderList.push(...skip_render)
} else if (skip_render != null) {
skipRenderList.push(skip_render)
}
const posts = [].concat(locals.posts.toArray(), locals.pages.toArray())
.filter((post) => {
return post.sitemap !== false && !isMatch(post.source, skipRenderList)
})
.sort((a, b) => {
return b.date - a.date
})
// configuration dictionary
const xmlConfig = {
config,
posts,
tags: locals.tags.toArray()
}
const data = template(config).render(xmlConfig)
return {
path,
data
}
}

View File

@ -0,0 +1,19 @@
'use strict'
/* global hexo */
/*
* Add sitemap.xml
* Based on https://github.com/hexojs/hexo-generator-sitemap/pull/26
*/
const pathFn = require('path')
const config = hexo.config.sitemap = Object.assign({
path: 'sitemap.xml'
}, hexo.config.sitemap)
if (!pathFn.extname(config.path)) {
config.path += '.xml'
}
hexo.extend.generator.register('sitemap', require('./generator'))

View File

@ -0,0 +1,30 @@
'use strict'
const { join } = require('path')
const { readFileSync } = require('fs')
const moment = require('moment')
const { full_url_for } = require('hexo-util')
let sitemapTmpl = ''
module.exports = function (config) {
if (sitemapTmpl) return sitemapTmpl
const nunjucks = require('nunjucks')
const env = new nunjucks.Environment(null, {
autoescape: false,
watch: false
})
env.addFilter('formatDate', str => {
return moment(str).format('YYYY-MM-DD[T00:00:00.000Z]').substring(0, 10)
})
env.addFilter('fullUrlFor', str => {
return full_url_for.call({ config }, str)
})
const sitemapSrc = config.sitemap.template || join(__dirname, '.sitemap.xml')
sitemapTmpl = nunjucks.compile(readFileSync(sitemapSrc, 'utf8'), env)
return sitemapTmpl
}

View File

@ -0,0 +1,13 @@
'use strict'
/* global hexo */
/*
* Simpler version of titlecase() https://github.com/rvagg/titlecase
* Does not ignore words with a dot
*/
hexo.extend.helper.register('titleCase', (str) => {
return str.replace(/[\w]+[^\s]*/g, (match) => {
return match[0].toUpperCase() + match.substring(1).toLowerCase()
})
})

View File

@ -0,0 +1,920 @@
@charset "utf-8";
@import "sanitize";
@import "sanitize/forms";
:root {
--main-bg-color: black;
--main-font-color: #e6e6e6;
--alt-bg-color: #333;
--link-underline: #3f72af;
--code-add-bg-color: #144212;
--code-del-bg-color: #600;
}
html {
cursor: unset;
height: 100%;
line-height: 1.15;
}
body {
color: var(--main-font-color);
background: var(--main-bg-color);
font-family: sans-serif;
font-size: 1.2em;
/* https://stackoverflow.com/a/6654996 */
min-height: 100%;
line-height: 1.5;
}
/*
* Restrict sizing to the page width in all browsers (opinionated).
* sanitize/page
*/
iframe,
img,
input,
select,
textarea {
height: auto;
max-width: 100%;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: serif;
font-weight: bold;
line-height: 1.35;
margin-top: 1em;
margin-bottom: 0.5em;
}
h1 {
font-size: 1.5em;
margin: 0.5em 0;
}
/* Disable line break for site title */
a.site-title {
font-family: serif;
font-size: 1.5em;
font-weight: bold;
line-height: 1.35;
margin: 0.5em 0;
white-space: nowrap;
}
h2 {
font-size: 1.3em;
}
h3 {
font-size: 1.2em;
}
h4 {
font-size: 1.1em;
}
a {
background: none;
color: var(--link-underline);
cursor: pointer;
text-decoration: none;
}
a:hover,
a:focus {
text-decoration: underline;
}
a:visited {
color: purple;
}
a:active {
color: #faa700;
}
button,
input,
select,
textarea {
font-size: 100%;
line-height: 1.15;
}
hr {
background-image: -webkit-linear-gradient(0deg,transparent,var(--main-font-color),transparent);
border: none;
box-sizing: content-box;
height: 1px;
margin-bottom: 1em;
margin-left: 25%;
overflow: visible;
width: 50%;
}
.archive-container hr,
.article hr {
width: 100%;
margin-left: 0;
}
.article .article-footer,
.article .article-footer hr {
margin-top: 1em;
}
.header-hr {
background-image: -webkit-linear-gradient(90deg,var(--main-font-color),var(--main-font-color),transparent);
}
figcaption,
small,
pre div.caption {
font-size: 0.9em;
color: #888;
}
/* code caption */
figcaption,
pre div.caption {
margin-bottom: 0.5em;
}
pre figcaption a,
pre div.caption a {
float: right;
}
dl,
form,
hr,
ol,
p,
pre,
table,
ul,
blockquote {
margin-bottom: 1em;
}
ul.index-list {
font-size: 1.1em;
}
ol {
list-style: decimal;
}
ol.toc {
list-style: disc;
}
ol.toc-child {
list-style: circle;
margin-bottom: unset;
}
li ol,
li ul,
ol,
ul {
margin-bottom: 0.8em;
}
li ul,
ul {
list-style: disc;
}
table caption,
table td,
table th,
td,
th {
border: 1px solid #ddd;
padding: 0.5em 1em;
}
table th,
th,
table tr:nth-child(even),
tr:nth-child(even) {
background: var(--alt-bg-color);
}
em,
caption,
legend {
font-weight: inherit;
}
code,
kbd,
pre,
samp {
font-size: 0.9em;
}
kbd {
border: 1px solid var(--alt-bg-color);
border-radius: 3px;
/* https://github.com/alvaromontoro/almond.css */
padding: 0.125rem 0.25rem;
}
/* set bg colour of single backtick wrap */
p code {
background: var(--alt-bg-color);
}
/* set codeblock width to minimum */
pre {
border-left: 0.1em solid var(--link-underline);
padding-left: 1em;
max-width: max-content;
}
mark {
background: var(--alt-bg-color);
color: unset;
}
/* Hide post's excerpt in article */
.p-summary {
display: none;
}
.main-nav-link {
margin-right: 0.5em;
padding-bottom: 0.5em;
display: inline-block;
border: 0;
}
b,
strong {
font-weight: 700;
}
blockquote {
background: var(--alt-bg-color);
border-left: 0.1em solid #666;
padding-left: 1em;
margin: 1em 3em 1em 0em;
max-width: max-content;
}
.page-nav {
margin-top: 2em;
text-align: center;
}
.footer {
box-sizing: border-box;
}
.container {
width: 100%;
}
.copyright,
.footer-content,
.footer-links {
text-align: center;
font-size: 0.95em;
max-width: 80ch;
padding: 0 2ch;
margin: auto;
}
.copyright li,
.footer-content li,
.footer-links li {
display: inline-block;
}
.footer-content {
margin: 0 auto;
text-align: left;
}
.footer-links {
font-size: 0.9em;
padding: 0;
}
.footer-links a {
text-decoration: none;
}
.footer-links li {
margin-right: 1em;
}
/* Wrap count in brackets */
span.index-list-count:before {
content: ' (';
}
span.index-list-count:after {
content: ')';
}
/* align badge in /about/ */
.about-body .article .article-entry img.svg {
/* display same line */
display: inline;
/* align to the left */
margin: auto 0 .5em;
}
/* https://jrl.ninja/etc/1/ */
.archive-container {
position: relative;
max-width: 80ch;
padding: 2ch;
margin: auto;
}
.archive-container article {
margin-bottom: 1em;
line-height: 1em;
}
.archive-container a,
.archive-container a:hover {
border: 0;
}
.archive-container .header,
.article .header {
font-size: 1.1em;
}
.archive-container .archive-article-header > a,
.archive-container .archive-article-header > h1 {
vertical-align: middle;
}
.archive-container .archive-article-header h1 {
font-size: 1.2em;
margin: 0;
}
.archive-container .archive-article-date {
font-size: 0.8em;
}
.archive-container .extend,
.archive-container .page-number {
margin-right: 1em;
}
.article {
max-width: 80ch;
padding: 2ch;
margin: auto;
position: relative;
}
.article .article-entry {
word-break: break-word;
}
.article .article-entry img {
display: block;
margin: auto;
margin-bottom: 0.5em;
max-height: 30em;
}
.article .article-entry blockquote footer {
text-align: center;
font-size: 80%;
}
.article .article-entry blockquote footer cite {
font-style: italic;
}
.article .article-entry blockquote footer cite:before {
content: "—";
padding: 0 0.5em;
}
.article .article-entry blockquote footer strong {
font-weight: 400;
}
.article .article-entry table caption,
.article .article-entry table td,
.article .article-entry table th,
.article .article-entry td,
.article .article-entry th {
word-break: normal;
}
.article .article-entry .pullquote {
width: 45%;
border: 0;
}
.article .article-entry .pullquote.left {
float: left;
text-align: left;
}
.article .article-entry .pullquote.right {
float: right;
text-align: right;
}
.article .article-entry .video-container {
position: relative;
padding-top: 56.25%;
margin-bottom: 1em;
height: 0;
overflow: hidden;
}
.article .article-entry .video-container iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
margin-top: 0;
}
.article .article-nav {
max-width: 100%;
}
.article .article-nav-link-wrap {
display: inline-block;
padding: 0 1em;
width: 49%;
box-sizing: border-box;
vertical-align: top;
border: 0;
}
.article .article-nav-link-wrap:hover {
border-bottom: 0;
}
.article .article-nav-link-wrap.newer {
text-align: right;
}
.article .article-nav-link-wrap.older {
text-align: left;
}
.article .article-meta {
font-size: 0.9em;
list-style: none;
margin: 0;
padding: 0;
}
.article .article-meta .label {
margin-right: 0.5em;
}
.article .article-meta .article-category,
.article .article-meta .article-tag-list {
display: inline-block;
margin: 0;
padding: 0;
}
.article .article-meta .article-tag-list-item {
display: inline-block;
margin-right: 1em;
}
.article .article-meta .article-tag-list-item:before {
content: "#";
}
.article .article-gallery img {
width: 100%;
}
.article-type-page .article-footer {
display: none;
}
table caption,
table td,
td {
border: 1px solid #ddd;
}
/*
* Visual Studio 2015 dark style
* Author: Nicolas LLOBERA <nllobera@gmail.com>
* https://github.com/highlightjs/highlight.js/blob/master/src/styles/vs2015.css
*/
.hljs {
display: block;
overflow-x: auto;
/* padding: 0.5em;
background: #1E1E1E;
color: #DCDCDC; */
}
.hljs-keyword,
.hljs-literal,
.hljs-symbol,
.hljs-name {
color: #569CD6;
}
.hljs-link {
color: #569CD6;
text-decoration: underline;
}
.hljs-built_in,
.hljs-type {
color: #4EC9B0;
}
.hljs-number,
.hljs-class {
color: #689E44;
}
.hljs-string,
.hljs-meta-string {
color: #D69D85;
}
.hljs-regexp,
.hljs-template-tag {
color: #9A5334;
}
.hljs-subst,
.hljs-function,
.hljs-title,
.hljs-params,
.hljs-formula {
color: #808080;
}
.hljs-comment,
.hljs-quote {
color: #57A64A;
font-style: italic;
}
.hljs-doctag {
color: #608B4E;
}
.hljs-meta,
.hljs-meta-keyword,
.hljs-tag {
color: #9B9B9B;
}
.hljs-variable,
.hljs-template-variable {
color: #BD63C5;
}
.hljs-attr,
.hljs-attribute,
.hljs-builtin-name {
color: #9CDCFE;
}
.hljs-section {
color: gold;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}
/*.hljs-code {
font-family:'Monospace';
}*/
.hljs-bullet,
.hljs-selector-tag,
.hljs-selector-id,
.hljs-selector-class,
.hljs-selector-attr,
.hljs-selector-pseudo {
color: #D7BA7D;
}
.hljs-addition {
background-color: var(--code-add-bg-color);
display: inline-block;
}
.hljs-deletion {
background-color: var(--code-del-bg-color);
display: inline-block;
}
/* hide mobile nav menu and its checkbox */
/* when it's not mobile */
.mobile-nav,
#mobile-menu-toggle,
#mobile-nav-link {
display: none;
}
/* set line-height in /archives page */
.mobile-nav-menu {
line-height: 1.8;
}
/* overlap other elements */
#mobile-nav-link {
background: var(--main-bg-color);
color: var(--main-font-color);
border: 2px solid #999;
border-radius: 5px;
right: 0;
margin-left: 5px;
padding: 0.5em;
position: absolute;
}
.mobile-nav-link-a {
text-align: center;
}
#menu-button {
font-size: 2em;
}
/* search bar */
.search-container {
border-radius: 5px;
width: 10em;
background: var(--alt-bg-color);
outline: 0;
float: right;
height: 1.8em;
position: relative;
margin: auto;
}
/* inline-block to position them in same line */
.search-box,
.search-button{
display:inline-block;
}
/* hide the border and fill the height to parent's */
.search-box {
border: 0;
border-radius: 5px;
height:1.8em;
width: 85%;
padding-left: 0.2em;
box-sizing: border-box;
}
/*
* Override sanitize/forms
* to prevent conflict with
* .search-box padding-left
*/
input {
padding: 0;
}
/* highlight the text box when focused */
.search-box:focus {
border: 1px solid var(--link-underline);
}
/* align the button to center */
.search-button {
width: 10%;
position: absolute;
top: 45%;
transform: translate(30%, -50%);
}
/* fill the svg with parent's font color */
svg {
fill: currentColor;
}
/* don't underline specific <a> tag */
.no-underline {
border-bottom: none;
}
/* fill the search, permalink and share icons with link's color */
svg#search,
svg#link,
svg#share {
fill: var(--link-underline);
}
/* use font color when hover */
svg#search:hover,
svg#link:hover,
svg#share:hover {
fill: currentColor;
}
/* hide share button by default
unhide (via JS) if Web Share API is supported */
.article-title a#btnshare {
display: none;
height: 1em;
width: 1em;
}
.article-title a#btnshare img {
display: block;
margin: auto;
margin-bottom: 0.5em;
max-height: 30em;
}
.article .article-entry a.headerlink,
.article-title a#btnshare {
margin-left: 0.5em;
}
@media (max-width: 768px) {
.article-title {
font-size: 1.8rem;
}
/* hide desktop nav */
.main-nav {
display: none;
}
/* display mobile nav */
.mobile-nav {
display: flex;
align-items: center;
justify-content: space-between;
/* reduce height of mobile header */
height: 1.5em;
}
/* display links when menu button is clicked */
/* use grid to display each link in new line */
#mobile-menu-toggle:checked + #mobile-nav-link {
display: grid;
}
}
@media (max-width: 468px) {
.archive-container .archive-footer,
.archive-container .post-footer {
padding: 1em;
}
.archive-container .archive-article-header > a,
.archive-container .archive-article-header > h1 {
width: 100%;
}
}
@media (max-width: 320px) {
.article {
font-size: 1em;
}
.archive-container .archive-footer,
.archive-container .post-footer {
padding: 1em;
}
.footer,
.footer-content {
font-size: 0.9em;
}
}
.copy-button {
border: 1px solid #999;
border-radius: 5px;
cursor: pointer;
font-size: 0.8em;
line-height: 1em;
margin-top: 1em;
padding: 5px;
user-select: none;
}
.copy-button:hover {
background: var(--main-font-color);
color: var(--main-bg-color);
}
@media (prefers-color-scheme: dark) {
:root {
--main-bg-color: black;
--main-font-color: #e6e6e6;
--alt-bg-color: #333;
--code-add-bg-color: #144212;
--code-del-bg-color: #600;
}
}
@media (prefers-color-scheme: light) {
:root {
--main-bg-color: #f1f1f1;
--main-font-color: #333;
--alt-bg-color: #d8d8d8;
--code-add-bg-color: #e6ffec;
--code-del-bg-color: #ffebe9;
}
/*
* Visual Studio-like style based on original C# coloring
* Author: Jason Diamond <jason@diamond.name>
* https://github.com/highlightjs/highlight.js/blob/master/src/styles/vs.css
*/
.hljs {
display: block;
overflow-x: auto;
/* padding: 0.5em;
background: white;
color: black; */
}
.hljs-comment,
.hljs-quote,
.hljs-variable {
color: #008000;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-built_in,
.hljs-name,
.hljs-tag {
color: #00f;
}
.hljs-string,
.hljs-title,
.hljs-section,
.hljs-attribute,
.hljs-literal,
.hljs-template-tag,
.hljs-template-variable,
.hljs-type {
color: #a31515;
}
.hljs-selector-attr,
.hljs-selector-pseudo,
.hljs-meta {
color: #2b91af;
}
.hljs-doctag {
color: #808080;
}
.hljs-attr {
color: #f00;
}
.hljs-symbol,
.hljs-bullet,
.hljs-link {
color: #00b0e8;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,70 @@
// Search button function
document.getElementById('search-click').addEventListener('click', () => {
searchForm.submit()
}, false)
document.getElementById('search-click-mobile').addEventListener('click', () => {
searchFormMob.submit()
}, false)
// Hide mobile menu when click outside of the menu
document.addEventListener('click', (evt) => {
const mainNavDisplay = window.getComputedStyle(document.getElementsByClassName('main-nav')[0]).getPropertyValue('display')
const mobileNav = document.getElementById('mobile-nav-link')
const mobileToggle = document.getElementById('mobile-menu-toggle')
const isClickedOutside = !mobileNav.contains(evt.target)
// Exit if not in mobile view or menu button is clicked or menu is currently hidden
// Menu button click triggers `menu-button` and `mobile-menu-toggle`
if (mainNavDisplay !== 'none' ||
evt.target.id === 'menu-button' || evt.target.id === 'mobile-menu-toggle' ||
mobileToggle.checked === false) return
if (isClickedOutside) {
mobileToggle.checked = false
}
}, false)
// Web Share API
// https://developer.mozilla.org/en-US/docs/Web/API/Navigator/share
// Only available on supporting browsers and HTTPS
if (navigator.share && document.location.protocol === 'https:') {
const shareBtn = document.getElementById('btnshare')
// Unhide share-button if supported
shareBtn.style.display = 'inline-block'
shareBtn.addEventListener('click', async () => {
const query = (selector) => {
return document.querySelector(selector)
}
const title = query('meta[property="og:title"]') ? query('meta[property="og:title"]').content : ''
const text = query('meta[property="og:description"]') ? query('meta[property="og:description"]').content : ''
const url = query('link[rel="canonical"]') ? query('link[rel="canonical"]').href : document.location.href
await navigator.share({ title, text, url })
}, false)
}
// Use duckduckgo's onion address when the site is accessed via .onion
if (document.location.hostname.endsWith('.onion')) {
const searchForms = document.querySelectorAll('form#searchForm, form#searchFormMob')
searchForms.forEach((form) => {
form.setAttribute('action', 'https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion/')
})
}
// Remove navigation link of current page
const navLink = document.querySelectorAll('a.main-nav-link, a.mobile-nav-link-a')
navLink.forEach((ele) => {
const eleHref = new URL(ele.href)
const docPath = document.location.pathname
if (eleHref.pathname !== '/' && eleHref.pathname === docPath) {
const span = document.createElement('span')
span.className = ele.className
span.textContent = ele.textContent
ele.outerHTML = span.outerHTML
}
})

View File

@ -0,0 +1,29 @@
/*
* Copy button
* Following functions only execute after
* the 'document' is ready or
* <script src> is executed.
*/
document.addEventListener('DOMContentLoaded', () => {
// Copy button
const copyCode = new ClipboardJS('.copy-button', {
target: (trigger) => {
return trigger.previousElementSibling
}
})
copyCode.on('success', (event) => {
event.clearSelection()
event.trigger.textContent = 'Copied'
window.setTimeout(() => {
event.trigger.textContent = 'Copy'
}, 2000)
})
copyCode.on('error', (event) => {
event.trigger.textContent = 'Press "Ctrl + C" to copy'
window.setTimeout(() => {
event.trigger.textContent = 'Copy'
}, 2000)
})
}, false)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 470 KiB

View File

@ -0,0 +1,9 @@
<!--
Fork Awesome 1.1.7
License - https://forkaweso.me/Fork-Awesome/license
Copyright 2018 Dave Gandy & Fork Awesome
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.
-->
<svg id="link" xmlns="http://www.w3.org/2000/svg" viewBox="15 15 1635 1635"><path d="M1456 1216c0-26-10-50-28-68l-208-208c-18-18-43-28-68-28-29 0-52 11-72 32 33 33 72 61 72 112 0 53-43 96-96 96-51 0-79-39-112-72-21 20-33 43-33 73 0 25 10 50 28 68l206 207c18 18 43 27 68 27s50-9 68-26l147-146c18-18 28-42 28-67zM753 511c0-25-10-50-28-68L519 236c-18-18-43-28-68-28s-50 10-68 27L236 381c-18 18-28 42-28 67 0 26 10 50 28 68l208 208c18 18 43 27 68 27 29 0 52-10 72-31-33-33-72-61-72-112 0-53 43-96 96-96 51 0 79 39 112 72 21-20 33-43 33-73zm895 705c0 76-31 150-85 203l-147 146c-54 54-127 83-203 83-77 0-150-30-204-85l-206-207c-54-54-83-127-83-203 0-79 32-154 88-209l-88-88c-55 56-129 88-208 88-76 0-150-30-204-84L100 652c-55-55-84-127-84-204 0-76 31-150 85-203L248 99c54-54 127-83 203-83 77 0 150 30 204 85l206 207c54 54 83 127 83 203 0 79-32 154-88 209l88 88c55-56 129-88 208-88 76 0 150 30 204 84l208 208c55 55 84 127 84 204z" fill="#3f72af"/></svg>

View File

@ -0,0 +1,2 @@
<!-- Font Awesome Free 5.5.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) -->
<svg id="search" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z" fill="#3f72af"/></svg>

After

Width:  |  Height:  |  Size: 633 B

View File

@ -0,0 +1,11 @@
<!--
Fork Awesome 1.1.7
License - https://forkaweso.me/Fork-Awesome/license
Copyright 2018 Dave Gandy & Fork Awesome
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.
-->
<svg id="share" viewBox="0 50 1635 1635" xmlns="http://www.w3.org/2000/svg">
<path d="M1216 1024c177 0 320 143 320 320s-143 320-320 320-320-143-320-320c0-11 1-23 2-34l-360-180c-57 53-134 86-218 86-177 0-320-143-320-320s143-320 320-320c84 0 161 33 218 86l360-180c-1-11-2-23-2-34 0-177 143-320 320-320s320 143 320 320-143 320-320 320c-84 0-161-33-218-86L638 862c1 11 2 23 2 34s-1 23-2 34l360 180c57-53 134-86 218-86z" fill="#3f72af"/>
</svg>