Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion _config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ whitelist:
# Serving
# detach: false
# port: 4000
# host: 127.0.0.1
host: localhost
baseurl: ""
# show_dir_listing: false

Expand Down
3 changes: 2 additions & 1 deletion _includes/head/custom.html
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
<link rel="shortcut icon" href="{{ '/favicon.ico' | relative_url }}">
<link rel="stylesheet" id="skin">
<link rel="stylesheet" id="skin" href="{{ '/assets/css/skins/default.css' | relative_url }}">
<script>navigator.serviceWorker && navigator.serviceWorker.register("{{ '/cache.js' | relative_url }}")</script>
102 changes: 83 additions & 19 deletions _plugins/auto-link.rb
Original file line number Diff line number Diff line change
@@ -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)
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
6 changes: 0 additions & 6 deletions assets/css/main.scss
Original file line number Diff line number Diff line change
@@ -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";
6 changes: 6 additions & 0 deletions assets/css/skins/default.scss
Original file line number Diff line number Diff line change
@@ -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";
44 changes: 44 additions & 0 deletions assets/js/cache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
layout: null
permalink: /cache.js
---
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;
});
})
);
});
18 changes: 16 additions & 2 deletions assets/js/theme.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,28 @@
---
layout: null
---
{%- 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 %}

window.addEventListener("DOMContentLoaded", function () {
var hash = {{ 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) {
Expand Down