From a19129e0ad4ee012a840430ef971a66d8451ebf6 Mon Sep 17 00:00:00 2001
From: neveler <55753029+neveler@users.noreply.github.com>
Date: Thu, 12 Mar 2026 08:10:49 +0800
Subject: [PATCH 1/4] =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=B5=8F=E8=A7=88?=
=?UTF-8?q?=E5=99=A8=E7=BC=93=E5=AD=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
_config.yml | 2 +-
_includes/head/custom.html | 3 +-
_layouts/code.html | 5 ++
_plugins/auto-link.rb | 102 ++++++++++++++++++++++------
assets/css/main.scss | 6 --
assets/css/skins/default.scss | 6 ++
assets/js/cache.js.md | 47 +++++++++++++
assets/js/{theme.js => theme.js.md} | 24 ++++++-
8 files changed, 165 insertions(+), 30 deletions(-)
create mode 100644 _layouts/code.html
create mode 100644 assets/js/cache.js.md
rename assets/js/{theme.js => theme.js.md} (74%)
diff --git a/_config.yml b/_config.yml
index 71240344..322041c8 100644
--- a/_config.yml
+++ b/_config.yml
@@ -86,7 +86,7 @@ whitelist:
# Serving
# detach: false
# port: 4000
-# host: 127.0.0.1
+host: localhost
baseurl: ""
# show_dir_listing: false
diff --git a/_includes/head/custom.html b/_includes/head/custom.html
index 55de7508..fb80f270 100644
--- a/_includes/head/custom.html
+++ b/_includes/head/custom.html
@@ -1,2 +1,3 @@
-
+
+
diff --git a/_layouts/code.html b/_layouts/code.html
new file mode 100644
index 00000000..488e935c
--- /dev/null
+++ b/_layouts/code.html
@@ -0,0 +1,5 @@
+---
+layout: null
+---
+
+{{- content | strip_html | replace: "&", "&" | replace: "<", "<" | replace: ">", ">" | replace: """, '"' | replace: "'", "'" | replace: "/", "/" | strip -}}
diff --git a/_plugins/auto-link.rb b/_plugins/auto-link.rb
index a2d4e867..abc4deea 100644
--- a/_plugins/auto-link.rb
+++ b/_plugins/auto-link.rb
@@ -1,30 +1,94 @@
+require "digest"
require "nokogiri"
require "addressable/uri"
-Jekyll::Hooks.register [:pages, :documents], :post_convert do |doc|
- next unless doc.output_ext == ".html"
+module Jekyll
+ module CustomPlugin
+ class FindFile
+ @@find_file_cache = {}
+ def self.process(site, link)
+ return @@find_file_cache[link] if @@find_file_cache.key(link)
+ link_uri = Addressable::URI.parse(link)
+ if link_uri&.path
+ relative_path = link_uri.path[1..].strip
+ relative_path_with_leading_slash = Jekyll::PathManager.join("", relative_path)
+ site.each_site_file do |file|
+ if [file.relative_path, file.url].any? { |v| [relative_path, relative_path_with_leading_slash].include?(v) }
+ @@find_file_cache[file.url] = file
+ @@find_file_cache[file.relative_path] = file
+ return file
+ end
+ end
+ end
+ nil
+ end
- site = doc.site
- liquid_context = Liquid::Context.new({}, {}, { site: site })
+ def self.ensure_leading_slash(input)
+ return input if input.nil? || input.empty? || input.start_with?("/")
+ "/#{input}"
+ end
- process_uri = lambda do |path|
- uri = Addressable::URI.parse(path)
- if uri&.path
- uri.path = Liquid::Template.parse("{% link #{uri.path[1..]} %}").render!(liquid_context)
+ def self.hash_file(file, short = true)
+ file_hash = file.is_a?(Jekyll::StaticFile) ? Digest::SHA256.file(file.path).hexdigest : Digest::SHA256.hexdigest(file.output).slice(0, 8)
+ short ? file_hash&.slice(0, 8) : file_hash
+ end
end
- uri.to_s
- end
- fragment = Nokogiri::HTML::DocumentFragment.parse(doc.content)
- fragment.css("[src^=\"/assets/\"],[src^=\"/\"][src$=\".md\"],[src^=\"/\"][src*=\".md#\"]").each do |item|
- if item["src"]
- item["src"] = process_uri.call(item["src"])
+ module HashFilter
+ def hash_file(link)
+ site = @context.registers[:site]
+ file = FindFile::process(site, link)
+ return nil if file.nil?
+ FindFile::hash_file(file)
+ end
end
- end
- fragment.css("[href^=\"/assets/\"],[href^=\"/\"][href$=\".md\"],[href^=\"/\"][href*=\".md#\"]").each do |item|
- if item["href"]
- item["href"] = process_uri.call(item["href"])
+
+ Liquid::Template.register_filter(HashFilter)
+
+ Jekyll::Hooks.register [:pages, :documents], :post_convert do |doc|
+ next unless doc.output_ext == ".html"
+
+ site = doc.site
+ relative_url_cache = site.filter_cache[:relative_url] ||= {}
+ sanitized_baseurl = site.config["baseurl"].is_a?(String) ? FindFile::ensure_leading_slash(site.config["baseurl"].chomp("/")) : ""
+ fragment = Nokogiri::HTML::DocumentFragment.parse(doc.content)
+ %w[src href].each do |attribute|
+ fragment.css("[#{attribute}^=\"/\"]").each do |item|
+ file = FindFile::process(site, item[attribute])
+ next if file.nil?
+
+ next item[attribute] = relative_url_cache[file.url].dup if relative_url_cache.key?(file.url)
+ next item[attribute] = relative_url_cache[file.relative_path].dup if relative_url_cache.key?(file.relative_path)
+
+ relative_url_cache[file.relative_path] = relative_url_cache[file.url] = FindFile::ensure_leading_slash(file.url).prepend(sanitized_baseurl)
+ item[attribute] = relative_url_cache[file.relative_path].dup
+ end
+ end
+ doc.content = fragment.to_html
end
+
+ Jekyll::Hooks.register :site, :post_render do |site|
+ sanitized_baseurl = site.config["baseurl"].is_a?(String) ? FindFile::ensure_leading_slash(site.config["baseurl"].chomp("/")) : ""
+ file_hash_map = {}
+
+ site.each_site_file do |file|
+ next if file.url.end_with?("/") || file.url.end_with?(".html")
+ file_hash_map[file.url] = FindFile::hash_file(file)
+ end
+
+ (site.pages + site.documents).each do |doc|
+ next unless doc.output_ext == ".html"
+ fragment = Nokogiri::HTML.parse(doc.output)
+ %w[src href].each do |attribute|
+ fragment.css("[#{attribute}]").each do |item|
+ next unless item[attribute].start_with?(sanitized_baseurl)
+ file_url = item[attribute].sub(/^#{Regexp.escape(sanitized_baseurl)}/, "")
+ next unless file_hash_map.key?(file_url)
+ item[attribute] += "?hash=#{file_hash_map[file_url]}"
+ end
+ end
+ doc.output = fragment.to_html
+ end
+ end
end
- doc.content = fragment.to_html
end
diff --git a/assets/css/main.scss b/assets/css/main.scss
index 0f4452fc..a845151c 100644
--- a/assets/css/main.scss
+++ b/assets/css/main.scss
@@ -1,8 +1,2 @@
---
---
-
-@charset "utf-8";
-
-$sans-serif: -apple-system, BlinkMacSystemFont, "Roboto", "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
-@import "minimal-mistakes/skins/default";
-@import "minimal-mistakes-plus";
diff --git a/assets/css/skins/default.scss b/assets/css/skins/default.scss
index a845151c..0f4452fc 100644
--- a/assets/css/skins/default.scss
+++ b/assets/css/skins/default.scss
@@ -1,2 +1,8 @@
---
---
+
+@charset "utf-8";
+
+$sans-serif: -apple-system, BlinkMacSystemFont, "Roboto", "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
+@import "minimal-mistakes/skins/default";
+@import "minimal-mistakes-plus";
diff --git a/assets/js/cache.js.md b/assets/js/cache.js.md
new file mode 100644
index 00000000..b6a7730f
--- /dev/null
+++ b/assets/js/cache.js.md
@@ -0,0 +1,47 @@
+---
+layout: code
+permalink: /cache.js
+---
+
+```javascript
+const CACHE_VERSION = 1;
+const CACHE_NAME = "assets-cache-" + CACHE_VERSION;
+
+self.addEventListener("install", () => {
+ self.skipWaiting();
+});
+
+self.addEventListener("fetch", (event) => {
+ const request = event.request;
+ const url = new URL(request.url);
+
+ const pathname = url.pathname;
+ if (pathname.endsWith(".woff2")) {
+ const referrer = new URL(request.referrer);
+ const hash = referrer.searchParams.get("hash");
+ if (hash === null) return;
+ url.searchParams.set("hash", "referrer:" + hash);
+ }
+
+ const hash = url.searchParams.get("hash");
+ if (hash === null) return;
+
+ event.respondWith(
+ caches.open(CACHE_NAME).then((cache) => {
+ return cache.match(request).then((cached) => {
+ if (cached === undefined) {
+ return cache.delete(request, { ignoreSearch: true }).then(() => {
+ return fetch(request).then((response) => {
+ if (response && response.status === 200) {
+ cache.put(request, response.clone());
+ }
+ return response;
+ });
+ })
+ }
+ return cached;
+ });
+ })
+ );
+});
+```
diff --git a/assets/js/theme.js b/assets/js/theme.js.md
similarity index 74%
rename from assets/js/theme.js
rename to assets/js/theme.js.md
index 9a202792..f1798f8b 100644
--- a/assets/js/theme.js
+++ b/assets/js/theme.js.md
@@ -1,14 +1,31 @@
---
-layout: null
+layout: code
+permalink: /assets/js/theme.js
---
+
+{% assign skins = site.data.settings.appearance_skin_light.options | concat: site.data.settings.appearance_skin_dark.options | uniq %}
+{% capture hash -%}
+{
+{%- for skin in skins -%}
+"{{ skin }}":"{{ '/assets/css/skins/' | append: skin | append: '.css' | hash_file }}"
+{%- unless forloop.last %},{% endunless -%}
+{%- endfor -%}
+}
+{%- endcapture %}
+
+```javascript
window.addEventListener("DOMContentLoaded", function () {
+ var hash = /*{% comment %}*/{}/*{% endcomment %}*{{'/'}}{{ hash }}/**/;
var skinLink = document.getElementById("skin");
var darkModeQuery = window.matchMedia("(prefers-color-scheme: dark)");
+ function applySkin(skin) {
+ skinLink.href = "{{ '/assets/css/skins/' | relative_url }}" + skin + ".css?hash=" + hash[skin];
+ }
function applyDarkSkin() {
- skinLink.href = "{{ '/assets/css/skins/' | relative_url }}" + settings.get("appearance_skin_dark", "dark") + ".css";
+ applySkin(settings.get("appearance_skin_dark", "dark"));
}
function applyLightSkin() {
- skinLink.href = "{{ '/assets/css/skins/' | relative_url }}" + settings.get("appearance_skin_light", "default") + ".css";
+ applySkin(settings.get("appearance_skin_light", "default"));
}
function autoSchemeHandler() {
if (darkModeQuery.matches) {
@@ -68,3 +85,4 @@ window.addEventListener("DOMContentLoaded", function () {
settings.refresh("appearance_color");
});
});
+```
From 541fc0c04c7f0c6340bd0149a02dd15435d0e2c4 Mon Sep 17 00:00:00 2001
From: neveler <55753029+neveler@users.noreply.github.com>
Date: Thu, 12 Mar 2026 08:15:32 +0800
Subject: [PATCH 2/4] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E5=A4=84?=
=?UTF-8?q?=E7=AC=94=E8=AF=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
_plugins/auto-link.rb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/_plugins/auto-link.rb b/_plugins/auto-link.rb
index abc4deea..943712cc 100644
--- a/_plugins/auto-link.rb
+++ b/_plugins/auto-link.rb
@@ -29,7 +29,7 @@ def self.ensure_leading_slash(input)
end
def self.hash_file(file, short = true)
- file_hash = file.is_a?(Jekyll::StaticFile) ? Digest::SHA256.file(file.path).hexdigest : Digest::SHA256.hexdigest(file.output).slice(0, 8)
+ file_hash = file.is_a?(Jekyll::StaticFile) ? Digest::SHA256.file(file.path).hexdigest : Digest::SHA256.hexdigest(file.output)
short ? file_hash&.slice(0, 8) : file_hash
end
end
From 2a8715a44035807d6914531685651b08927eebe4 Mon Sep 17 00:00:00 2001
From: neveler <55753029+neveler@users.noreply.github.com>
Date: Sat, 14 Mar 2026 16:07:43 +0800
Subject: [PATCH 3/4] remove code layout
---
_layouts/code.html | 5 -----
assets/js/{cache.js.md => cache.js} | 4 ----
assets/js/{theme.js.md => theme.js} | 12 ++++--------
3 files changed, 4 insertions(+), 17 deletions(-)
delete mode 100644 _layouts/code.html
rename assets/js/{cache.js.md => cache.js} (93%)
rename assets/js/{theme.js.md => theme.js} (89%)
diff --git a/_layouts/code.html b/_layouts/code.html
deleted file mode 100644
index 488e935c..00000000
--- a/_layouts/code.html
+++ /dev/null
@@ -1,5 +0,0 @@
----
-layout: null
----
-
-{{- content | strip_html | replace: "&", "&" | replace: "<", "<" | replace: ">", ">" | replace: """, '"' | replace: "'", "'" | replace: "/", "/" | strip -}}
diff --git a/assets/js/cache.js.md b/assets/js/cache.js
similarity index 93%
rename from assets/js/cache.js.md
rename to assets/js/cache.js
index b6a7730f..e61fa188 100644
--- a/assets/js/cache.js.md
+++ b/assets/js/cache.js
@@ -1,9 +1,6 @@
---
-layout: code
permalink: /cache.js
---
-
-```javascript
const CACHE_VERSION = 1;
const CACHE_NAME = "assets-cache-" + CACHE_VERSION;
@@ -44,4 +41,3 @@ self.addEventListener("fetch", (event) => {
})
);
});
-```
diff --git a/assets/js/theme.js.md b/assets/js/theme.js
similarity index 89%
rename from assets/js/theme.js.md
rename to assets/js/theme.js
index f1798f8b..439d5d5e 100644
--- a/assets/js/theme.js.md
+++ b/assets/js/theme.js
@@ -1,10 +1,8 @@
---
-layout: code
-permalink: /assets/js/theme.js
+layout: null
---
-
-{% assign skins = site.data.settings.appearance_skin_light.options | concat: site.data.settings.appearance_skin_dark.options | uniq %}
-{% capture hash -%}
+{%- assign skins = site.data.settings.appearance_skin_light.options | concat: site.data.settings.appearance_skin_dark.options | uniq -%}
+{%- capture hash -%}
{
{%- for skin in skins -%}
"{{ skin }}":"{{ '/assets/css/skins/' | append: skin | append: '.css' | hash_file }}"
@@ -13,9 +11,8 @@ permalink: /assets/js/theme.js
}
{%- endcapture %}
-```javascript
window.addEventListener("DOMContentLoaded", function () {
- var hash = /*{% comment %}*/{}/*{% endcomment %}*{{'/'}}{{ hash }}/**/;
+ var hash = {{ hash }};
var skinLink = document.getElementById("skin");
var darkModeQuery = window.matchMedia("(prefers-color-scheme: dark)");
function applySkin(skin) {
@@ -85,4 +82,3 @@ window.addEventListener("DOMContentLoaded", function () {
settings.refresh("appearance_color");
});
});
-```
From b13ca893f698bd3dea5025ff389e207ef89ad142 Mon Sep 17 00:00:00 2001
From: neveler <55753029+neveler@users.noreply.github.com>
Date: Sat, 14 Mar 2026 16:10:57 +0800
Subject: [PATCH 4/4] update
---
assets/js/cache.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/assets/js/cache.js b/assets/js/cache.js
index e61fa188..32f7f238 100644
--- a/assets/js/cache.js
+++ b/assets/js/cache.js
@@ -1,4 +1,5 @@
---
+layout: null
permalink: /cache.js
---
const CACHE_VERSION = 1;