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;