From a2165760545e4a9c7674ba9a0c83a828257f0554 Mon Sep 17 00:00:00 2001 From: dzonerzy Date: Wed, 21 Jan 2026 20:17:04 +0100 Subject: [PATCH] Fix flash of white theme on page navigation Add inline script in that applies theme/accent preferences immediately before body renders, preventing FOUC when navigating between pages in dark mode. --- src/Makefile | 18 +++++++++++++----- src/libdzonerzy.so.c | 14 ++++++++++++++ tools/embed/embed.c | 39 +++++++++++++++++++++++++++------------ 3 files changed, 54 insertions(+), 17 deletions(-) diff --git a/src/Makefile b/src/Makefile index a9abe5b..2cbb4b3 100644 --- a/src/Makefile +++ b/src/Makefile @@ -8,10 +8,10 @@ OUTPUT = libdzonerzy.so SRC_INC_DIR = src/include CFLAGS = -Wno-error=unused-parameter -Wno-error=unused-const-variable -std=c99 -O0 -shared -fPIC -I$(SRC_INC_DIR) -Wno-int-conversion -Wno-div-by-zero -LDFLAGS = +LDFLAGS = EMBED_TOOL = tools/bin/embed -EMBED_ARGS = -f=h +EMBED_ARGS = -f=h CONVERT_TOOL = $(shell which convert) CONVERT_ARGS = -define webp @@ -26,7 +26,7 @@ SITE_DIST = dist/site AWK_UTIL = awk '/^\s*\[[0-9]+\]\s+([^\.].+)/ {print $$2}' AWK_UTIL_NOCRLF = awk '/^\s*\[[0-9]+\]\s+([^\.].+)/ {printf $$2" "}' -PANDOC_ARGS = --standalone --table-of-contents --section-divs --email-obfuscation=references --css="/main.css" --include-after-body=$(RAW_DIST)/footer.html --include-after-body=$(RAW_DIST)/scripts.html +PANDOC_ARGS = --standalone --table-of-contents --section-divs --email-obfuscation=references --css="/main.css" --include-in-header=$(RAW_DIST)/head.html --include-after-body=$(RAW_DIST)/footer.html --include-after-body=$(RAW_DIST)/scripts.html PYTHON = $(shell which python3) @@ -49,7 +49,15 @@ libdzonerzy: libdzonerzy-dump: $(READELF) -S -W $(DIST)/$(OUTPUT) | $(AWK_UTIL) | xargs dirname {} | sort | uniq | xargs -I{} mkdir -p $(RAW_DIST)/{} - @$(foreach fn, $(shell $(READELF) -S -W $(DIST)/$(OUTPUT) | $(AWK_UTIL_NOCRLF)), $(OBJCOPY) $(DIST)/$(OUTPUT) --dump-section $(fn)=/dev/stdout | cat | head -c -1 > $(RAW_DIST)/$(fn);) + @$(foreach fn, $(shell $(READELF) -S -W $(DIST)/$(OUTPUT) | $(AWK_UTIL_NOCRLF)), \ + if echo "$(fn)" | grep -qE '^(articles|pages|advisory)/'; then \ + size=$$($(OBJCOPY) $(DIST)/$(OUTPUT) --dump-section $(fn)=/dev/stdout | head -c 4 | od -An -tu1 | awk '{print $$1 + $$2*256 + $$3*65536 + $$4*16777216}'); \ + $(OBJCOPY) $(DIST)/$(OUTPUT) --dump-section $(fn)=/dev/stdout | tail -c +5 | head -c $$size > $(RAW_DIST)/$(fn); \ + elif echo "$(fn)" | grep -qE '^embed/'; then \ + $(OBJCOPY) $(DIST)/$(OUTPUT) --dump-section $(fn)=/dev/stdout > $(RAW_DIST)/$(fn); \ + else \ + $(OBJCOPY) $(DIST)/$(OUTPUT) --dump-section $(fn)=/dev/stdout | tr -d '\000' > $(RAW_DIST)/$(fn); \ + fi;) libdzonerzy-gen: libdzonerzy-dump @mkdir -p $(SITE_DIST)/assets/ @@ -71,4 +79,4 @@ libdzonerzy-runlocal: libdzonerzy libdzonerzy-gen clean-libdzonerzy: @echo "/* DO NOT EDIT THIS FILE - it is machine generated */" > $(SRC_DIR)/res.h rm -rf $(DIST) - rm -f $(SRC_INC_DIR)/*.h \ No newline at end of file + rm -f $(SRC_INC_DIR)/*.h diff --git a/src/libdzonerzy.so.c b/src/libdzonerzy.so.c index 68c142d..daf9752 100644 --- a/src/libdzonerzy.so.c +++ b/src/libdzonerzy.so.c @@ -679,6 +679,18 @@ Copyright: " .accent-picker { right: 2.75rem; }\\n" \ "}\\n" +#define HEAD_HTML \ + HTML_TAG_OPEN("script", "") \ + HTML_RAWTEXT("(function(){") \ + HTML_RAWTEXT("var t=localStorage.getItem('theme-preference');") \ + HTML_RAWTEXT("var a=localStorage.getItem('accent-preference');") \ + HTML_RAWTEXT("if(!t)t=window.matchMedia('(prefers-color-scheme:dark)').matches?'dark':'light';") \ + HTML_RAWTEXT("if(t==='dark')document.documentElement.classList.add('dark');") \ + HTML_RAWTEXT("if(a&&a!=='blue')document.documentElement.classList.add('accent-'+a);") \ + HTML_RAWTEXT("})();") \ + HTML_TAG_CLOSE("script") \ + HTML_NEWLINE() + #define FOOTER_HTML \ MD_RAWTAG("footer", "" \ "" \ @@ -926,6 +938,8 @@ SECTION("footer.html") static const char footerhtml[] = FOOTER_HTML; SECTION("scripts.html") static const char scriptshtml[] = SCRIPTS_HTML; +SECTION("head.html") +static const char headhtml[] = HEAD_HTML; SECTION("main.js") static const char mainjs[] = MAIN_JS; SECTION("sitemap.xml") diff --git a/tools/embed/embed.c b/tools/embed/embed.c index 4e84e2d..aef16d3 100644 --- a/tools/embed/embed.c +++ b/tools/embed/embed.c @@ -253,13 +253,14 @@ unsigned char *read_file_data(FILE *f, size_t *size) *size = ftell(f); fseek(f, 0, SEEK_SET); - unsigned char *data = malloc(*size); + unsigned char *data = malloc(*size + 1); if (data == NULL) { return NULL; } fread(data, 1, *size, f); + data[*size] = '\0'; // null-terminate for string operations return data; } @@ -468,15 +469,25 @@ int embed(embed_t *args) data = data_updated; } + // Prepend 4-byte size (little-endian) for non-embed types + int has_size_prefix = (args->type != TYPE_EMBED); + if (args->autowrap) { + fprintf(output, " "); + + // Write 4-byte size prefix (little-endian) + if (has_size_prefix) + { + fprintf(output, "0x%02X, 0x%02X, 0x%02X, 0x%02X, ", + (unsigned char)(size & 0xFF), + (unsigned char)((size >> 8) & 0xFF), + (unsigned char)((size >> 16) & 0xFF), + (unsigned char)((size >> 24) & 0xFF)); + } + while (i < size) { - if (i == 0) - { - fprintf(output, " "); - } - fprintf(output, "0x%02X", data[i]); if (i % 16 == 15) @@ -497,13 +508,20 @@ int embed(embed_t *args) fprintf(output, "\n"); } - if (args->type == TYPE_EMBED) - fprintf(output, " 0x00,\n"); - fprintf(output, "};\n"); } else { + // Write 4-byte size prefix (little-endian) + if (has_size_prefix) + { + fprintf(output, "0x%02X, 0x%02X, 0x%02X, 0x%02X, ", + (unsigned char)(size & 0xFF), + (unsigned char)((size >> 8) & 0xFF), + (unsigned char)((size >> 16) & 0xFF), + (unsigned char)((size >> 24) & 0xFF)); + } + while (i < size) { // no wrap @@ -519,9 +537,6 @@ int embed(embed_t *args) i++; } - if (args->type == TYPE_EMBED) - fprintf(output, ", 0x00"); - fprintf(output, "};\n\n"); }