refactor: implement multi-tier logging and drop panicOnWarning

- Centralize logger: warnf/errorf for normal pages; HTML comments for adapters
- Add dev console logs and export structured logs to /hbx-debug.json
- Remove --panicOnWarning from dev scripts; builds no longer fail on warnings
- Fix prior lint/format issues across scripts and templates
This commit is contained in:
George Cushen committed 2025-09-22 22:48:26 +01:00
1 parent 0fc34ba8c8
commit 220330d872
47 files changed
+217 -501

No files matched your search

+6 -1
View File
@@ -22,7 +22,9 @@
"starters/**",
"test/**",
"modules/**/package.json",
"**/assets/jsconfig.json"
"**/assets/jsconfig.json",
"docs-local",
"backups/**"
]
},
"formatter": {
@@ -52,6 +54,9 @@
"complexity": { "all": false },
"suspicious": {
"noEmptyBlock": "off"
},
"security": {
"noDangerouslySetInnerHtml": "off"
}
}
},
@@ -12,13 +12,7 @@
import * as params from "@params";
// Enable core slide features.
var enabledPlugins = [
RevealMarkdown,
RevealSearch,
RevealNotes,
RevealMath.KaTeX,
RevealZoom,
];
var enabledPlugins = [RevealMarkdown, RevealSearch, RevealNotes, RevealMath.KaTeX, RevealZoom];
const isObject = function (o) {
return o === Object(o) && !isArray(o) && typeof o !== "function";
@@ -97,9 +91,7 @@ if (params.slides.diagram) {
// Lazily render Mermaid diagrams within Reveal.JS slides
// See: https://github.com/hakimel/reveal.js/issues/2863#issuecomment-1107444425
let renderMermaidDiagrams = function renderMermaidDiagrams(event) {
let mermaidDivs = event.currentSlide.querySelectorAll(
".mermaid:not(.done)",
);
let mermaidDivs = event.currentSlide.querySelectorAll(".mermaid:not(.done)");
let indices = Reveal.getIndices();
let pageno = `${indices.h}-${indices.v}`;
@@ -109,11 +101,7 @@ if (params.slides.diagram) {
mermaidDiv.classList.add("done");
};
let graphDefinition = mermaidDiv.textContent;
mermaid.mermaidAPI.render(
`mermaid${pageno}-${i}`,
graphDefinition,
insertSvg,
);
mermaid.mermaidAPI.render(`mermaid${pageno}-${i}`, graphDefinition, insertSvg);
});
Reveal.layout();
};
@@ -152,14 +152,12 @@
font-size: 20px;
background-color: #fff;
color: #000; /* override parent alpha */
box-shadow: 0 10px 20px rgb(0 0 0 / 4%), 0 2px 6px rgb(0 0 0 / 4%), 0 0 1px
rgb(0 0 0 / 4%);
box-shadow: 0 10px 20px rgb(0 0 0 / 4%), 0 2px 6px rgb(0 0 0 / 4%), 0 0 1px rgb(0 0 0 / 4%);
}
.dark .avatar-emoji {
background-color: #000;
box-shadow: 0 10px 20px rgb(0 0 0 / 4%), 0 2px 6px rgb(0 0 0 / 4%), 0 0 1px
rgb(255 255 255 / 96%);
box-shadow: 0 10px 20px rgb(0 0 0 / 4%), 0 2px 6px rgb(0 0 0 / 4%), 0 0 1px rgb(255 255 255 / 96%);
}
/* Biography Block */
@@ -184,22 +182,14 @@
/* Light Mode Gradient */
.hbx-bg-gradient::before {
background-image: radial-gradient(
at 20% 25%,
hsla(212, 80%, 95%, 1) 0px,
transparent 50%
), radial-gradient(at 75% 30%, hsla(160, 80%, 95%, 1) 0px, transparent 50%),
radial-gradient(at 25% 80%, hsla(280, 80%, 95%, 1) 0px, transparent 50%),
background-image: radial-gradient(at 20% 25%, hsla(212, 80%, 95%, 1) 0px, transparent 50%),
radial-gradient(at 75% 30%, hsla(160, 80%, 95%, 1) 0px, transparent 50%), radial-gradient(at 25% 80%, hsla(280, 80%, 95%, 1) 0px, transparent 50%),
radial-gradient(at 75% 85%, hsla(30, 80%, 95%, 1) 0px, transparent 50%);
}
/* Dark Mode Gradient */
.dark .hbx-bg-gradient::before {
background-image: radial-gradient(
at 20% 25%,
hsla(212, 35%, 15%, 1) 0px,
transparent 50%
), radial-gradient(at 75% 30%, hsla(160, 35%, 15%, 1) 0px, transparent 50%),
radial-gradient(at 25% 80%, hsla(280, 35%, 15%, 1) 0px, transparent 50%),
background-image: radial-gradient(at 20% 25%, hsla(212, 35%, 15%, 1) 0px, transparent 50%),
radial-gradient(at 75% 30%, hsla(160, 35%, 15%, 1) 0px, transparent 50%), radial-gradient(at 25% 80%, hsla(280, 35%, 15%, 1) 0px, transparent 50%),
radial-gradient(at 75% 85%, hsla(30, 35%, 15%, 1) 0px, transparent 50%);
}
@@ -3,10 +3,7 @@
}
.hb-cards {
grid-template-columns: repeat(
auto-fill,
minmax(max(250px, calc((100% - 1rem * 2) / var(--hb-cols))), 1fr)
);
grid-template-columns: repeat(auto-fill, minmax(max(250px, calc((100% - 1rem * 2) / var(--hb-cols))), 1fr));
}
.hb-card {
@@ -10,12 +10,7 @@
.glassmorphism-primary {
@apply relative overflow-hidden;
background: linear-gradient(
135deg,
rgb(var(--color-primary-500) / 0.9),
rgb(var(--color-primary-600) / 0.95),
rgb(var(--color-primary-700) / 0.9)
);
background: linear-gradient(135deg, rgb(var(--color-primary-500) / 0.9), rgb(var(--color-primary-600) / 0.95), rgb(var(--color-primary-700) / 0.9));
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
}
@@ -23,12 +18,7 @@
.glassmorphism-primary::before {
content: "";
@apply absolute inset-0 pointer-events-none;
background: linear-gradient(
45deg,
rgb(0 0 0 / 0.1),
transparent,
rgb(255 255 255 / 0.1)
);
background: linear-gradient(45deg, rgb(0 0 0 / 0.1), transparent, rgb(255 255 255 / 0.1));
mix-blend-mode: overlay;
}
@@ -47,23 +37,13 @@
.glassmorphism-secondary::before {
content: "";
@apply absolute inset-0 pointer-events-none;
background: linear-gradient(
45deg,
rgb(0 0 0 / 0.1),
transparent,
rgb(255 255 255 / 0.1)
);
background: linear-gradient(45deg, rgb(0 0 0 / 0.1), transparent, rgb(255 255 255 / 0.1));
mix-blend-mode: overlay;
}
.glassmorphism-dark {
@apply relative overflow-hidden;
background: linear-gradient(
135deg,
rgb(0 0 0 / 0.7),
rgb(31 41 55 / 0.8),
rgb(0 0 0 / 0.9)
);
background: linear-gradient(135deg, rgb(0 0 0 / 0.7), rgb(31 41 55 / 0.8), rgb(0 0 0 / 0.9));
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
}
@@ -71,23 +51,13 @@
.glassmorphism-dark::before {
content: "";
@apply absolute inset-0 pointer-events-none;
background: linear-gradient(
45deg,
rgb(255 255 255 / 0.05),
transparent,
rgb(255 255 255 / 0.1)
);
background: linear-gradient(45deg, rgb(255 255 255 / 0.05), transparent, rgb(255 255 255 / 0.1));
mix-blend-mode: overlay;
}
.glassmorphism-light {
@apply relative overflow-hidden;
background: linear-gradient(
135deg,
rgb(255 255 255 / 0.9),
rgb(248 250 252 / 0.95),
rgb(241 245 249 / 0.9)
);
background: linear-gradient(135deg, rgb(255 255 255 / 0.9), rgb(248 250 252 / 0.95), rgb(241 245 249 / 0.9));
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
}
@@ -95,12 +65,7 @@
.glassmorphism-light::before {
content: "";
@apply absolute inset-0 pointer-events-none;
background: linear-gradient(
45deg,
rgb(0 0 0 / 0.02),
transparent,
rgb(255 255 255 / 0.05)
);
background: linear-gradient(45deg, rgb(0 0 0 / 0.02), transparent, rgb(255 255 255 / 0.05));
mix-blend-mode: overlay;
}
@@ -136,13 +101,11 @@
}
.glass-shadow {
box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25), 0 0 0 1px
rgb(255 255 255 / 0.1), inset 0 1px 0 rgb(255 255 255 / 0.1);
box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25), 0 0 0 1px rgb(255 255 255 / 0.1), inset 0 1px 0 rgb(255 255 255 / 0.1);
}
.dark .glass-shadow {
box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.5), 0 0 0 1px
rgb(255 255 255 / 0.05), inset 0 1px 0 rgb(255 255 255 / 0.05);
box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.5), 0 0 0 1px rgb(255 255 255 / 0.05), inset 0 1px 0 rgb(255 255 255 / 0.05);
}
/* ==========================================================================
@@ -169,8 +132,7 @@
/* Modern border and shadow for light mode */
border: 1px solid rgb(255 255 255 / calc(var(--glassmorphism-opacity) + 0.1));
box-shadow: 0 32px 64px -12px rgb(0 0 0 / 0.25), inset 0 1px 0
rgb(255 255 255 / calc(var(--glassmorphism-opacity) + 0.05));
box-shadow: 0 32px 64px -12px rgb(0 0 0 / 0.25), inset 0 1px 0 rgb(255 255 255 / calc(var(--glassmorphism-opacity) + 0.05));
}
.cta-glassmorphism:hover {
@@ -183,8 +145,7 @@
);
border: 1px solid rgb(255 255 255 / calc(var(--glassmorphism-opacity) + 0.2));
box-shadow: 0 40px 80px -16px rgb(0 0 0 / 0.3), inset 0 1px 0
rgb(255 255 255 / calc(var(--glassmorphism-opacity) + 0.1));
box-shadow: 0 40px 80px -16px rgb(0 0 0 / 0.3), inset 0 1px 0 rgb(255 255 255 / calc(var(--glassmorphism-opacity) + 0.1));
}
/* Dark mode adjustments for CTA glassmorphism */
@@ -197,8 +158,7 @@
);
border: 1px solid rgb(255 255 255 / var(--glassmorphism-opacity));
box-shadow: 0 32px 64px -12px rgb(0 0 0 / 0.4), inset 0 1px 0
rgb(255 255 255 / calc(var(--glassmorphism-opacity) * 0.5));
box-shadow: 0 32px 64px -12px rgb(0 0 0 / 0.4), inset 0 1px 0 rgb(255 255 255 / calc(var(--glassmorphism-opacity) * 0.5));
}
.dark .cta-glassmorphism:hover {
@@ -231,23 +191,20 @@
/* Light mode button background */
background: rgb(255 255 255 / 0.9);
border: 1px solid rgb(0 0 0 / 0.1);
box-shadow: 0 10px 25px -5px rgb(0 0 0 / 0.1), 0 4px 6px -2px rgb(0 0 0 / 0.1),
inset 0 1px 0 rgb(255 255 255 / 0.3);
box-shadow: 0 10px 25px -5px rgb(0 0 0 / 0.1), 0 4px 6px -2px rgb(0 0 0 / 0.1), inset 0 1px 0 rgb(255 255 255 / 0.3);
}
.dark .cta-glassmorphism .group > div:first-child {
/* Dark mode button background */
background: rgb(0 0 0 / 0.4);
border: 1px solid rgb(255 255 255 / 0.2);
box-shadow: 0 10px 25px -5px rgb(0 0 0 / 0.3), 0 4px 6px -2px rgb(0 0 0 / 0.2),
inset 0 1px 0 rgb(255 255 255 / 0.1);
box-shadow: 0 10px 25px -5px rgb(0 0 0 / 0.3), 0 4px 6px -2px rgb(0 0 0 / 0.2), inset 0 1px 0 rgb(255 255 255 / 0.1);
}
.cta-glassmorphism .group:hover > div:first-child {
background: rgb(255 255 255);
border: 1px solid rgb(0 0 0 / 0.15);
box-shadow: 0 20px 40px -8px rgb(0 0 0 / 0.15), 0 8px 12px -4px
rgb(0 0 0 / 0.1), inset 0 1px 0 rgb(255 255 255 / 0.4);
box-shadow: 0 20px 40px -8px rgb(0 0 0 / 0.15), 0 8px 12px -4px rgb(0 0 0 / 0.1), inset 0 1px 0 rgb(255 255 255 / 0.4);
}
.dark .cta-glassmorphism .group:hover > div:first-child {
@@ -320,9 +320,7 @@
/* Note: Providing these palettes in @theme ensures theme-pack mappings like var(--color-rose-600) always resolve */
/* Font family */
--font-family-sans:
inter var, ui-sans-serif, system-ui, sans-serif, apple color emoji, segoe ui emoji,
segoe ui symbol, noto color emoji;
--font-family-sans: inter var, ui-sans-serif, system-ui, sans-serif, apple color emoji, segoe ui emoji, segoe ui symbol, noto color emoji;
/* Font sizes */
--font-size-xs: 0.75rem;
+6 -11
View File
@@ -3,9 +3,9 @@
* Copies BibTeX citation to clipboard when cite button is clicked
*/
import { hugoEnvironment, i18n } from "@params";
import { ClipboardCache, copyToClipboardSync } from "./hb-clipboard.js";
import { showNotification } from "./hb-notifier.js";
import {hugoEnvironment, i18n} from "@params";
import {ClipboardCache, copyToClipboardSync} from "./hb-clipboard.js";
import {showNotification} from "./hb-notifier.js";
// Debug mode based on environment
const isDebugMode = hugoEnvironment === "development";
@@ -36,9 +36,7 @@ function initializeCitation() {
* Prefetch all citations on page load
*/
function prefetchAllCitations() {
const citeButtons = document.querySelectorAll(
".js-cite-clipboard[data-filename]",
);
const citeButtons = document.querySelectorAll(".js-cite-clipboard[data-filename]");
citeButtons.forEach((button) => {
const filename = button.getAttribute("data-filename");
if (filename && !citationCache.has(filename)) {
@@ -151,10 +149,7 @@ async function fetchAndCopyWithFallback(filename, button) {
}
// If it's a NotAllowedError, suggest hovering first
if (error.name === "NotAllowedError") {
showNotification(
"Please hover over the button first, then click",
"info",
);
showNotification("Please hover over the button first, then click", "info");
} else {
showNotification("Failed to copy citation", "error");
}
@@ -191,4 +186,4 @@ function updateButtonText(button) {
}
// Export functions for potential reuse
export { handleCiteClick, prefetchAllCitations };
export {handleCiteClick, prefetchAllCitations};
@@ -3,7 +3,7 @@
* Provides cross-browser clipboard functionality
*/
import { hugoEnvironment } from "@params";
import {hugoEnvironment} from "@params";
// Debug mode based on environment
const isDebugMode = hugoEnvironment === "development";
@@ -1,4 +1,4 @@
import { hugoEnvironment, i18n } from "@params";
import {hugoEnvironment, i18n} from "@params";
// Constants
const NOTIFICATION_DURATION = 2000; // milliseconds
@@ -93,10 +93,7 @@ function createCopyButton() {
const copyBtn = document.createElement("button");
copyBtn.classList.add("copy-button");
copyBtn.innerHTML = i18n["copy"];
copyBtn.setAttribute(
"aria-label",
i18n["copyLabel"] || "Copy code to clipboard",
);
copyBtn.setAttribute("aria-label", i18n["copyLabel"] || "Copy code to clipboard");
copyBtn.setAttribute("type", "button"); // Explicit button type
return copyBtn;
}
@@ -144,18 +141,12 @@ function initializeCodeCopyButtons() {
const copyBtn = createCopyButton();
// Use debounced version of copy function
const debouncedCopy = debounce(
() => copyCodeToClipboard(copyBtn, wrapper),
DEBOUNCE_DELAY,
);
const debouncedCopy = debounce(() => copyCodeToClipboard(copyBtn, wrapper), DEBOUNCE_DELAY);
copyBtn.addEventListener("click", debouncedCopy);
wrapper.appendChild(copyBtn);
} catch (err) {
console.error(
`Failed to initialize copy button for code block ${index}:`,
err,
);
console.error(`Failed to initialize copy button for code block ${index}:`, err);
}
});
} catch (err) {
+1 -1
View File
@@ -1,4 +1,4 @@
import { applyHugoStyleFixes, initTheme } from "./hb-init.js";
import {applyHugoStyleFixes, initTheme} from "./hb-init.js";
// Initialize Hugo Blox Builder global object
const root = document.documentElement;
+2 -5
View File
@@ -1,13 +1,10 @@
window.addEventListener("DOMContentLoaded", () => {
// Toggle language chooser sub-menu
const languageChoosers = document.querySelectorAll(
"[data-hb-language-chooser]",
);
const languageChoosers = document.querySelectorAll("[data-hb-language-chooser]");
languageChoosers.forEach((languageChooser) => {
languageChooser.addEventListener("click", (e) => {
e.preventDefault();
languageChooser.dataset.state =
languageChooser.dataset.state === "open" ? "closed" : "open";
languageChooser.dataset.state = languageChooser.dataset.state === "open" ? "closed" : "open";
const languageOptions = languageChooser.nextElementSibling;
languageOptions.classList.toggle("hidden");
const languageChooserRect = languageChooser.getBoundingClientRect();
+3 -12
View File
@@ -17,18 +17,14 @@ export function initTheme() {
if (defaultTheme === "dark") setDark();
else if (defaultTheme === "light") setLight();
else if (defaultTheme === "system") {
window.matchMedia("(prefers-color-scheme: dark)").matches
? setDark()
: setLight();
window.matchMedia("(prefers-color-scheme: dark)").matches ? setDark() : setLight();
}
}
}
export function applyHugoStyleFixes() {
document.addEventListener("DOMContentLoaded", function () {
const checkboxes = document.querySelectorAll(
"li input[type='checkbox'][disabled]",
);
const checkboxes = document.querySelectorAll("li input[type='checkbox'][disabled]");
checkboxes.forEach((e) => {
const parent = e.parentElement?.parentElement;
if (parent) parent.classList.add("task-list");
@@ -36,12 +32,7 @@ export function applyHugoStyleFixes() {
const liNodes = document.querySelectorAll(".task-list li");
liNodes.forEach((nodes) => {
const textNodes = Array.from(nodes.childNodes).filter(
(node) =>
node.nodeType === 3 &&
node.textContent &&
node.textContent.trim().length > 1,
);
const textNodes = Array.from(nodes.childNodes).filter((node) => node.nodeType === 3 && node.textContent && node.textContent.trim().length > 1);
if (textNodes.length > 0) {
const span = document.createElement("label");
textNodes[0].after(span);
@@ -1,7 +1,5 @@
function getCssVar(name) {
const value = getComputedStyle(document.documentElement)
.getPropertyValue(name)
.trim();
const value = getComputedStyle(document.documentElement).getPropertyValue(name).trim();
return value ? "rgb(" + value + ")" : null;
}
@@ -16,9 +14,7 @@ window.mermaid.initialize({
secondaryBorderColor: getCssVar("--color-secondary-400") || "#22d3ee",
tertiaryBorderColor: getCssVar("--color-neutral-400") || "#a3a3a3",
lineColor: getCssVar("--color-neutral-600") || "#525252",
fontFamily: getComputedStyle(document.documentElement).getPropertyValue(
"font-family",
),
fontFamily: getComputedStyle(document.documentElement).getPropertyValue("font-family"),
fontSize: "16px",
},
});
+2 -5
View File
@@ -3,16 +3,13 @@
const applyScrollPadding = () => {
const header = document.querySelector(".page-header");
let position = header.getBoundingClientRect();
document.documentElement.style.scrollPaddingTop =
position.height.toString() + "px";
document.documentElement.style.scrollPaddingTop = position.height.toString() + "px";
const r = document.querySelector(":root");
r.style.setProperty("--navbar-height", position.height.toString() + "px");
};
window.addEventListener("DOMContentLoaded", () => {
const dropdownMenus = document.querySelectorAll(
".nav-dropdown > .nav-link[role='button']",
);
const dropdownMenus = document.querySelectorAll(".nav-dropdown > .nav-link[role='button']");
dropdownMenus.forEach((toggler) => {
const toggle = (el) => {
@@ -3,7 +3,7 @@
* Provides reusable notification functionality
*/
import { hugoEnvironment } from "@params";
import {hugoEnvironment} from "@params";
// Debug mode based on environment
const isDebugMode = hugoEnvironment === "development";
@@ -21,10 +21,7 @@ export function showNotification(message, type = "success", duration = 3000) {
// Remove any existing notifications with same message
const existingNotification = container.querySelector(".hb-notification");
if (
existingNotification &&
existingNotification.textContent.includes(message)
) {
if (existingNotification && existingNotification.textContent.includes(message)) {
existingNotification.remove();
}
@@ -86,10 +83,10 @@ function createNotificationElement(message, type) {
// Define colors for different types
const colors = {
success: { bg: "#10b981", bgClass: "bg-green-500" },
error: { bg: "#ef4444", bgClass: "bg-red-500" },
info: { bg: "#3b82f6", bgClass: "bg-blue-500" },
warning: { bg: "#f59e0b", bgClass: "bg-amber-500" },
success: {bg: "#10b981", bgClass: "bg-green-500"},
error: {bg: "#ef4444", bgClass: "bg-red-500"},
info: {bg: "#3b82f6", bgClass: "bg-blue-500"},
warning: {bg: "#f59e0b", bgClass: "bg-amber-500"},
};
const color = colors[type] || colors.info;
+3 -12
View File
@@ -11,10 +11,7 @@ window.addEventListener("DOMContentLoaded", () => {
const searchConfig = JSON.parse(configScript.textContent);
if (!searchConfig || typeof searchConfig.baseUrl === "undefined") {
console.error(
"Pagefind search config is missing or invalid.",
searchConfig,
);
console.error("Pagefind search config is missing or invalid.", searchConfig);
return;
}
@@ -35,9 +32,7 @@ window.addEventListener("DOMContentLoaded", () => {
// Add click handlers to triggers
let triggers = document.querySelectorAll("[data-search-toggle]");
triggers.forEach((trigger) =>
trigger.addEventListener("click", handleSearchToggle),
);
triggers.forEach((trigger) => trigger.addEventListener("click", handleSearchToggle));
});
function handleSearchToggle() {
@@ -68,11 +63,7 @@ function handleSearchToggle() {
// Add keyboard shortcut to close search modal with Escape key
document.addEventListener("keydown", (event) => {
if (
event.key === "Escape" &&
searchWrapper &&
!searchWrapper.classList.contains("hidden")
) {
if (event.key === "Escape" && searchWrapper && !searchWrapper.classList.contains("hidden")) {
searchWrapper.classList.add("hidden");
}
});
+2 -10
View File
@@ -22,13 +22,7 @@ document.addEventListener("DOMContentLoaded", function () {
}
// Initialize the overlay
const overlayClasses = [
"fixed",
"z-10",
"inset-0",
"bg-white",
"dark:bg-black/80",
];
const overlayClasses = ["fixed", "z-10", "inset-0", "bg-white", "dark:bg-black/80"];
overlay.classList.add("bg-transparent");
overlay.classList.remove("hidden", ...overlayClasses);
@@ -37,9 +31,7 @@ document.addEventListener("DOMContentLoaded", function () {
// See `hb-nav.js` - it handles the default behavior (irrespective of if sidebar is shown).
// When menu is open, show the navigation sidebar
sidebarContainer.classList.toggle(
"max-lg:[transform:translate3d(0,-100%,0)]",
);
sidebarContainer.classList.toggle("max-lg:[transform:translate3d(0,-100%,0)]");
sidebarContainer.classList.toggle("max-lg:[transform:translate3d(0,0,0)]");
// When menu is open, prevent body from scrolling
+12 -19
View File
@@ -11,9 +11,7 @@ function addThemeToggleListener() {
if (
localStorage.getItem("wc-color-theme") === "dark" ||
(!("wc-color-theme" in localStorage) &&
((window.matchMedia("(prefers-color-scheme: dark)").matches &&
defaultTheme === "system") ||
defaultTheme === "dark"))
((window.matchMedia("(prefers-color-scheme: dark)").matches && defaultTheme === "system") || defaultTheme === "dark"))
) {
themeToggleButtons.forEach((el) => {
el.dataset.theme = "dark";
@@ -45,15 +43,12 @@ function addThemeToggleListener() {
localStorage.setItem("wc-color-theme", "dark");
}
}
el.dataset.theme = document.documentElement.classList.contains("dark")
? "dark"
: "light";
el.dataset.theme = document.documentElement.classList.contains("dark") ? "dark" : "light";
// Dispatch `hbThemeChange` event to support themeable user plugins.
const themeChangeEvent = new CustomEvent("hbThemeChange", {
detail: {
isDarkTheme: () =>
document.documentElement.classList.contains("dark"),
isDarkTheme: () => document.documentElement.classList.contains("dark"),
},
});
document.dispatchEvent(themeChangeEvent);
@@ -61,15 +56,13 @@ function addThemeToggleListener() {
});
// Listen for dark mode toggling in OS
window
.matchMedia("(prefers-color-scheme: dark)")
.addEventListener("change", (event) => {
if (defaultTheme === "system" && !("wc-color-theme" in localStorage)) {
event.matches ? window.hbb.setDarkTheme() : window.hbb.setLightTheme();
themeToggleButtons.forEach((el) => {
const isDark = document.documentElement.classList.contains("dark");
el.dataset.theme = isDark ? "dark" : "light";
});
}
});
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", (event) => {
if (defaultTheme === "system" && !("wc-color-theme" in localStorage)) {
event.matches ? window.hbb.setDarkTheme() : window.hbb.setLightTheme();
themeToggleButtons.forEach((el) => {
const isDark = document.documentElement.classList.contains("dark");
el.dataset.theme = isDark ? "dark" : "light";
});
}
});
}
@@ -1,10 +1,10 @@
document.addEventListener("DOMContentLoaded", function () {
window.renderMathInElement(document.body, {
delimiters: [
{ left: "$$", right: "$$", display: true },
{ left: "$", right: "$", display: false },
{ left: "\\(", right: "\\)", display: false },
{ left: "\\[", right: "\\]", display: true },
{left: "$$", right: "$$", display: true},
{left: "$", right: "$", display: false},
{left: "\\(", right: "\\)", display: false},
{left: "\\[", right: "\\]", display: true},
],
throwOnError: false,
});
@@ -4,25 +4,10 @@
"version": "1.0.0",
"license": "MIT",
"category": "content",
"tags": [
"collection",
"blog",
"publications",
"filter",
"sort",
"content-display",
"archive"
],
"tags": ["collection", "blog", "publications", "filter", "sort", "content-display", "archive"],
"description": "Display filtered and sorted collections of pages with customizable views and pagination",
"author": "Hugo Blox",
"homepage": "https://hugoblox.com/blocks/",
"repository": "https://github.com/HugoBlox/hugo-blox-builder",
"keywords": [
"hugo",
"static-site",
"content",
"blog",
"publications",
"filtering"
]
"keywords": ["hugo", "static-site", "content", "blog", "publications", "filtering"]
}
@@ -4,25 +4,10 @@
"version": "1.0.0",
"license": "MIT",
"category": "marketing",
"tags": [
"cta",
"buttons",
"links",
"social",
"marketing",
"conversion",
"list"
],
"tags": ["cta", "buttons", "links", "social", "marketing", "conversion", "list"],
"description": "Create compelling vertical lists of call-to-action buttons with icons and hover effects",
"author": "Hugo Blox",
"homepage": "https://hugoblox.com/blocks/",
"repository": "https://github.com/HugoBlox/hugo-blox-builder",
"keywords": [
"hugo",
"cta",
"buttons",
"conversion",
"marketing",
"social-links"
]
"keywords": ["hugo", "cta", "buttons", "conversion", "marketing", "social-links"]
}
@@ -4,15 +4,7 @@
"version": "1.0.0",
"license": "MIT",
"category": "marketing",
"tags": [
"cta",
"card",
"conversion",
"marketing",
"hero",
"call-to-action",
"banner"
],
"tags": ["cta", "card", "conversion", "marketing", "hero", "call-to-action", "banner"],
"description": "Create high-impact call-to-action cards with bold typography and compelling buttons",
"author": "Hugo Blox",
"homepage": "https://hugoblox.com/blocks/",
@@ -120,18 +120,7 @@
},
"position": {
"type": "string",
"enum": [
"center",
"top",
"bottom",
"left",
"right",
"repeat",
"top left",
"top right",
"bottom left",
"bottom right"
],
"enum": ["center", "top", "bottom", "left", "right", "repeat", "top left", "top right", "bottom left", "bottom right"],
"description": "Background position (use 'repeat' for tiling textures)",
"default": "center"
},
@@ -4,25 +4,10 @@
"version": "1.0.0",
"license": "MIT",
"category": "marketing",
"tags": [
"cta",
"image",
"features",
"alternating",
"marketing",
"storytelling",
"visual"
],
"tags": ["cta", "image", "features", "alternating", "marketing", "storytelling", "visual"],
"description": "Engaging alternating image and text sections with feature lists and call-to-action buttons",
"author": "Hugo Blox",
"homepage": "https://hugoblox.com/blocks/",
"repository": "https://github.com/HugoBlox/hugo-blox-builder",
"keywords": [
"hugo",
"marketing",
"images",
"features",
"storytelling",
"conversion"
]
"keywords": ["hugo", "marketing", "images", "features", "storytelling", "conversion"]
}
@@ -4,15 +4,7 @@
"version": "1.0.0",
"license": "MIT",
"category": "content",
"tags": [
"features",
"grid",
"icons",
"benefits",
"product",
"services",
"highlights"
],
"tags": ["features", "grid", "icons", "benefits", "product", "services", "highlights"],
"description": "Showcase features, benefits, or services in an organized grid with icons and descriptions",
"author": "Hugo Blox",
"homepage": "https://hugoblox.com/blocks/",
+3 -5
View File
@@ -3,15 +3,13 @@
* Uses the shared component for consistency
*/
import { render } from "preact";
import { HeroBlock } from "./component.jsx";
import {render} from "preact";
import {HeroBlock} from "./component.jsx";
// Render function - immediately renders Hero components
function renderHeroBlocks() {
// Find all hero block containers (using both old and new selectors for compatibility)
const heroBlocks = document.querySelectorAll(
'[data-block-type="hero"], [data-hero-render="immediate"]',
);
const heroBlocks = document.querySelectorAll('[data-block-type="hero"], [data-hero-render="immediate"]');
heroBlocks.forEach((block) => {
const propsData = block.dataset.props;
+9 -12
View File
@@ -3,7 +3,7 @@
* Used for both SSR and client-side hydration
*/
import { Icon } from "../../shared/components/Icon.jsx";
import {Icon} from "../../shared/components/Icon.jsx";
// Simple markdown renderer
export function renderText(text) {
@@ -16,7 +16,7 @@ export function renderText(text) {
// Process URLs
export function processUrl(url) {
if (!url) return { href: "#" };
if (!url) return {href: "#"};
if (url.startsWith("http://") || url.startsWith("https://")) {
return {
@@ -27,14 +27,14 @@ export function processUrl(url) {
}
if (url.startsWith("#")) {
return { href: url };
return {href: url};
}
return { href: url };
return {href: url};
}
// Hero Block Component - Single implementation
export const HeroBlock = ({ content, design, id, icon_svg }) => {
export const HeroBlock = ({content, design, id, icon_svg}) => {
const paddingClasses = design?.no_padding ? "" : "py-32 sm:py-48 lg:py-56";
return (
@@ -44,6 +44,7 @@ export const HeroBlock = ({ content, design, id, icon_svg }) => {
{content.announcement?.text && (
<div class="hidden sm:mb-8 sm:flex sm:justify-center">
<div class="relative rounded-full px-3 py-1 text-sm leading-6 text-gray-600 dark:text-gray-300 ring-1 ring-gray-900/10 dark:ring-gray-300 hover:ring-gray-900/20 dark:hover:ring-gray-400">
{/* eslint-disable-next-line lint/security/noDangerouslySetInnerHtml */}
<span
dangerouslySetInnerHTML={{
__html: renderText(content.announcement.text),
@@ -59,8 +60,7 @@ export const HeroBlock = ({ content, design, id, icon_svg }) => {
class="pl-2 font-semibold text-primary-600 dark:text-primary-300"
>
<span class="absolute inset-0" aria-hidden="true"></span>
{content.announcement.link.text}{" "}
<span aria-hidden="true">→</span>
{content.announcement.link.text} <span aria-hidden="true">→</span>
</a>
)}
</div>
@@ -73,16 +73,13 @@ export const HeroBlock = ({ content, design, id, icon_svg }) => {
{content.title && (
<h1
class="text-4xl font-bold tracking-tight text-gray-900 dark:text-gray-100 sm:text-6xl"
dangerouslySetInnerHTML={{ __html: renderText(content.title) }}
dangerouslySetInnerHTML={{__html: renderText(content.title)}}
/>
)}
{/* Subtitle/Text */}
{content.text && (
<p
class="mt-6 text-lg leading-8 text-gray-600 dark:text-gray-300"
dangerouslySetInnerHTML={{ __html: renderText(content.text) }}
/>
<p class="mt-6 text-lg leading-8 text-gray-600 dark:text-gray-300" dangerouslySetInnerHTML={{__html: renderText(content.text)}} />
)}
{/* Action Buttons */}
@@ -4,15 +4,7 @@
"version": "1.0.0",
"license": "MIT",
"category": "marketing",
"tags": [
"hero",
"landing",
"cta",
"announcement",
"headline",
"conversion",
"primary"
],
"tags": ["hero", "landing", "cta", "announcement", "headline", "conversion", "primary"],
"description": "Create compelling hero sections with headlines, announcements, and dual call-to-action buttons",
"author": "Hugo Blox",
"homepage": "https://hugoblox.com/blocks/",
+1 -1
View File
@@ -98,4 +98,4 @@
"additionalProperties": false
}
]
}
}
@@ -4,15 +4,7 @@
"version": "1.0.0",
"license": "MIT",
"category": "content",
"tags": [
"markdown",
"content",
"text",
"prose",
"writing",
"simple",
"flexible"
],
"tags": ["markdown", "content", "text", "prose", "writing", "simple", "flexible"],
"description": "Display rich markdown content with beautiful typography and optional section titles",
"author": "Hugo Blox",
"homepage": "https://hugoblox.com/blocks/",
@@ -4,25 +4,10 @@
"version": "1.0.0",
"license": "MIT",
"category": "resume",
"tags": [
"resume",
"awards",
"certifications",
"achievements",
"credentials",
"professional",
"cv"
],
"tags": ["resume", "awards", "certifications", "achievements", "credentials", "professional", "cv"],
"description": "Showcase professional awards and certifications with elegant card layouts and certificate links",
"author": "Hugo Blox",
"homepage": "https://hugoblox.com/blocks/",
"repository": "https://github.com/HugoBlox/hugo-blox-builder",
"keywords": [
"hugo",
"resume",
"awards",
"certifications",
"cv",
"professional"
]
"keywords": ["hugo", "resume", "awards", "certifications", "cv", "professional"]
}
@@ -4,27 +4,10 @@
"version": "1.0.0",
"license": "MIT",
"category": "resume",
"tags": [
"resume",
"biography",
"profile",
"horizontal",
"education",
"interests",
"cv",
"layout"
],
"tags": ["resume", "biography", "profile", "horizontal", "education", "interests", "cv", "layout"],
"description": "Horizontal biography layout with integrated education and interests sections",
"author": "Hugo Blox",
"homepage": "https://hugoblox.com/blocks/",
"repository": "https://github.com/HugoBlox/hugo-blox-builder",
"keywords": [
"hugo",
"resume",
"biography",
"horizontal",
"education",
"interests",
"cv"
]
"keywords": ["hugo", "resume", "biography", "horizontal", "education", "interests", "cv"]
}
@@ -4,27 +4,10 @@
"version": "1.0.0",
"license": "MIT",
"category": "resume",
"tags": [
"resume",
"biography",
"profile",
"avatar",
"social",
"about",
"personal",
"cv"
],
"tags": ["resume", "biography", "profile", "avatar", "social", "about", "personal", "cv"],
"description": "Professional biography section with customizable avatar, social links, and personal information",
"author": "Hugo Blox",
"homepage": "https://hugoblox.com/blocks/",
"repository": "https://github.com/HugoBlox/hugo-blox-builder",
"keywords": [
"hugo",
"resume",
"biography",
"profile",
"avatar",
"social",
"cv"
]
"keywords": ["hugo", "resume", "biography", "profile", "avatar", "social", "cv"]
}
@@ -4,27 +4,10 @@
"version": "1.0.0",
"license": "MIT",
"category": "resume",
"tags": [
"resume",
"experience",
"timeline",
"work",
"education",
"career",
"professional",
"cv"
],
"tags": ["resume", "experience", "timeline", "work", "education", "career", "professional", "cv"],
"description": "Professional timeline displaying work experience and education history in chronological order",
"author": "Hugo Blox",
"homepage": "https://hugoblox.com/blocks/",
"repository": "https://github.com/HugoBlox/hugo-blox-builder",
"keywords": [
"hugo",
"resume",
"experience",
"timeline",
"work",
"education",
"career"
]
"keywords": ["hugo", "resume", "experience", "timeline", "work", "education", "career"]
}
@@ -4,27 +4,10 @@
"version": "1.0.0",
"license": "MIT",
"category": "resume",
"tags": [
"resume",
"languages",
"multilingual",
"skills",
"fluency",
"international",
"cv",
"circular-progress"
],
"tags": ["resume", "languages", "multilingual", "skills", "fluency", "international", "cv", "circular-progress"],
"description": "Showcase language proficiency with beautiful circular progress indicators and percentage displays",
"author": "Hugo Blox",
"homepage": "https://hugoblox.com/blocks/",
"repository": "https://github.com/HugoBlox/hugo-blox-builder",
"keywords": [
"hugo",
"resume",
"languages",
"multilingual",
"fluency",
"skills",
"cv"
]
"keywords": ["hugo", "resume", "languages", "multilingual", "fluency", "skills", "cv"]
}
@@ -4,27 +4,10 @@
"version": "1.0.0",
"license": "MIT",
"category": "resume",
"tags": [
"resume",
"skills",
"technical",
"progress-bars",
"expertise",
"professional",
"competencies",
"cv"
],
"tags": ["resume", "skills", "technical", "progress-bars", "expertise", "professional", "competencies", "cv"],
"description": "Display technical and professional skills with optional progress bars and organized categories",
"author": "Hugo Blox",
"homepage": "https://hugoblox.com/blocks/",
"repository": "https://github.com/HugoBlox/hugo-blox-builder",
"keywords": [
"hugo",
"resume",
"skills",
"technical",
"progress",
"expertise",
"cv"
]
"keywords": ["hugo", "resume", "skills", "technical", "progress", "expertise", "cv"]
}
@@ -1,11 +1,11 @@
import { h } from "preact";
import {h} from "preact";
/**
* Icon component
* Renders an SVG icon from a raw SVG string passed from Hugo.
* Decodes JSON-escaped sequences (\u003c) and HTML entities (&lt;, &quot;, &#34;).
*/
export const Icon = ({ svg, attributes }) => {
export const Icon = ({svg, attributes}) => {
if (!svg) return null;
let decoded = String(svg)
@@ -22,17 +22,13 @@ export const Icon = ({ svg, attributes }) => {
if (hasWrapper) {
if (/<svg[^>]*class=/i.test(decoded)) {
decoded = decoded.replace(
/<svg([^>]*?)class=\"([^\"]*)\"([^>]*)>/i,
'<svg$1class="$2 inline-block w-4 h-4"$3>'
);
decoded = decoded.replace(/<svg([^>]*?)class=\"([^\"]*)\"([^>]*)>/i, '<svg$1class="$2 inline-block w-4 h-4"$3>');
} else {
decoded = decoded.replace(/<svg\b/i, '<svg class="inline-block w-4 h-4"');
}
return (
<span class="inline-block" dangerouslySetInnerHTML={{ __html: decoded }} />
);
// eslint-disable-next-line lint/security/noDangerouslySetInnerHtml
return <span class="inline-block" dangerouslySetInnerHTML={{__html: decoded}} />;
}
const finalAttributes = {
@@ -46,10 +42,6 @@ export const Icon = ({ svg, attributes }) => {
.map(([k, v]) => `${k}="${String(v)}"`)
.join(" ");
return (
<span
class="inline-block"
dangerouslySetInnerHTML={{ __html: `<svg ${attrs}>${decoded}</svg>` }}
/>
);
// eslint-disable-next-line lint/security/noDangerouslySetInnerHtml
return <span class="inline-block" dangerouslySetInnerHTML={{__html: `<svg ${attrs}>${decoded}</svg>`}} />;
};
+2 -17
View File
@@ -4,25 +4,10 @@
"version": "1.0.0",
"license": "MIT",
"category": "content",
"tags": [
"statistics",
"numbers",
"metrics",
"achievements",
"data",
"showcase",
"grid"
],
"tags": ["statistics", "numbers", "metrics", "achievements", "data", "showcase", "grid"],
"description": "Showcase impressive statistics and achievements in a clean, impactful grid layout",
"author": "Hugo Blox",
"homepage": "https://hugoblox.com/blocks/",
"repository": "https://github.com/HugoBlox/hugo-blox-builder",
"keywords": [
"hugo",
"statistics",
"metrics",
"achievements",
"data",
"numbers"
]
"keywords": ["hugo", "statistics", "metrics", "achievements", "data", "numbers"]
}
@@ -4,25 +4,10 @@
"version": "1.0.0",
"license": "MIT",
"category": "marketing",
"tags": [
"testimonials",
"reviews",
"social-proof",
"quotes",
"customers",
"trust",
"credibility"
],
"tags": ["testimonials", "reviews", "social-proof", "quotes", "customers", "trust", "credibility"],
"description": "Build trust with authentic customer testimonials featuring quotes, names, and profile photos",
"author": "Hugo Blox",
"homepage": "https://hugoblox.com/blocks/",
"repository": "https://github.com/HugoBlox/hugo-blox-builder",
"keywords": [
"hugo",
"testimonials",
"social-proof",
"reviews",
"customers",
"trust"
]
"keywords": ["hugo", "testimonials", "social-proof", "reviews", "customers", "trust"]
}
@@ -6,12 +6,14 @@
{{ $partial_dir := printf "hooks/%s/" .hook }}
{{ $context := .context }}
{{ $hook_dir_path := path.Join "layouts/_partials" $partial_dir }}
{{ if fileExists $hook_dir_path }}
{{ range os.ReadDir $hook_dir_path }}
{{ if not .IsDir }}
{{ $partial_path := path.Join $partial_dir .Name }}
{{ partial $partial_path $context }}
{{ $loaded = true }}
{{ with try (os.ReadDir $hook_dir_path) }}
{{ with .Value }}
{{ range . }}
{{ if not .IsDir }}
{{ $partial_path := path.Join $partial_dir .Name }}
{{ partial $partial_path $context }}
{{ $loaded = true }}
{{ end }}
{{ end }}
{{ end }}
{{ end }}
@@ -22,6 +22,19 @@
{{- end -}}
{{- end -}}
{{/* Store log entry for future debug widget */}}
{{- with $page -}}
{{- $log_entry := dict "level" $level "message" $msg "page" ($.RelPermalink | default "/") "timestamp" now.Unix -}}
{{- $current_logs := .Store.Get "hbx_logs" | default slice -}}
{{- .Store.Set "hbx_logs" ($current_logs | append $log_entry) -}}
{{- end -}}
{{/* Multi-tier logging output */}}
{{- if $isDev -}}
{{/* Console logging for immediate dev feedback */}}
<script>console.log('[HBX:{{ $level }}]', {{ $msg | jsonify }});</script>
{{- end -}}
{{- if eq $level "error" -}}
{{- if not $isAdapter -}}
{{- errorf "%s" $msg -}}
@@ -0,0 +1,31 @@
{{/* Hugo Blox: Export Debug Logs for Future Debug Widget */}}
{{/* This hook collects all HBX logs and exports them to a JSON file for tooling */}}
{{- if hugo.IsServer -}}
{{/* Collect all logs from all pages */}}
{{- $all_logs := slice -}}
{{- range .Site.Pages -}}
{{- $page_logs := .Store.Get "hbx_logs" | default slice -}}
{{- $all_logs = $all_logs | append $page_logs -}}
{{- end -}}
{{- if $all_logs -}}
{{/* Create debug export as a resource for tooling */}}
{{- $debug_data := dict "logs" $all_logs "build_time" now.Unix "hugo_version" hugo.Version -}}
{{- $debug_json := $debug_data | jsonify -}}
{{- $debug_resource := resources.FromString "hbx-debug.json" $debug_json -}}
{{- $debug_resource.Publish -}}
{{/* Also emit to console for immediate visibility */}}
<script>
if (window.console && console.groupCollapsed) {
console.groupCollapsed('[HBX] Build completed with {{ len $all_logs }} log entries');
console.log('Debug data available at:', '/hbx-debug.json');
{{ range $all_logs }}
console.log('[HBX:{{ .level }}]', {{ .message | jsonify }}, 'page:', {{ .page | jsonify }});
{{ end }}
console.groupEnd();
}
</script>
{{- end -}}
{{- end -}}
@@ -0,0 +1,31 @@
{{/* Hugo Blox: Export Debug Logs for Future Debug Widget */}}
{{/* This hook collects all HBX logs and exports them to a JSON file for tooling */}}
{{- if hugo.IsServer -}}
{{/* Collect all logs from all pages */}}
{{- $all_logs := slice -}}
{{- range .Site.Pages -}}
{{- $page_logs := .Store.Get "hbx_logs" | default slice -}}
{{- $all_logs = $all_logs | append $page_logs -}}
{{- end -}}
{{- if $all_logs -}}
{{/* Create debug export as a resource for tooling */}}
{{- $debug_data := dict "logs" $all_logs "build_time" now.Unix "hugo_version" hugo.Version -}}
{{- $debug_json := $debug_data | jsonify -}}
{{- $debug_resource := resources.FromString "hbx-debug.json" $debug_json -}}
{{- $debug_resource.Publish -}}
{{/* Also emit to console for immediate visibility */}}
<script>
if (window.console && console.groupCollapsed) {
console.groupCollapsed('[HBX] Build completed with {{ len $all_logs }} log entries');
console.log('Debug data available at:', '/hbx-debug.json');
{{ range $all_logs }}
console.log('[HBX:{{ .level }}]', {{ .message | jsonify }}, 'page:', {{ .page | jsonify }});
{{ end }}
console.groupEnd();
}
</script>
{{- end -}}
{{- end -}}
@@ -33,5 +33,8 @@
{{/* EXTENSIBILITY HOOK: BODY-END */}}
{{ partial "functions/get_hook" (dict "hook" "body-end" "context" .) }}
{{/* Hugo Blox Debug Export (dev only) */}}
{{ partial "hooks/body-end/hbx-debug-export" . }}
</body>
</html>
-1
View File
@@ -107,7 +107,6 @@ HUGO_ARGS=(
if [ "$DEBUG_MODE" = true ]; then
HUGO_ARGS+=(
--logLevel debug
--panicOnWarning
--templateMetrics
--templateMetricsHints
--ignoreCache
-1
View File
@@ -85,7 +85,6 @@ HUGO_ARGS=(
if [ "$DEBUG_MODE" = true ]; then
HUGO_ARGS+=(
--logLevel debug
--panicOnWarning
--templateMetrics
--templateMetricsHints
--ignoreCache
+4 -10
View File
@@ -7,10 +7,10 @@
* Usage: pnpm run vendor:libs
*/
import { resolve } from "path";
import {resolve} from "path";
import del from "rollup-plugin-delete";
import { defineConfig } from "vite";
import { viteStaticCopy } from "vite-plugin-static-copy";
import {defineConfig} from "vite";
import {viteStaticCopy} from "vite-plugin-static-copy";
const MODULE_DIR = "modules/blox-tailwind";
const OUTPUT_DIR = resolve(MODULE_DIR, "assets/dist/lib");
@@ -26,13 +26,7 @@ export default defineConfig({
},
rollupOptions: {
// Don't bundle these libraries, just copy them
external: [
"mermaid",
"plotly.js",
"katex",
"markmap-autoloader",
"alpinejs",
],
external: ["mermaid", "plotly.js", "katex", "markmap-autoloader", "alpinejs"],
},
},