diff --git a/biome.json b/biome.json
index b2bd4463..77b602b2 100644
--- a/biome.json
+++ b/biome.json
@@ -16,13 +16,12 @@
"!**/vendor",
"!**/*.html",
"!**/layouts/**/*.html",
- "!**/layouts/**/*.htm",
"!**/*.webmanifest",
"!**/*.backlinks.json",
"!**/hugo_stats.json",
"!**/*.code-workspace",
"!**/modules/blox/data/icons/devicon.json",
- "!**/templates",
+ "!**/templates/**/*.html",
"!**/test",
"!**/modules/**/package.json",
"!**/assets/jsconfig.json",
@@ -64,7 +63,13 @@
}
}
},
- "assist": { "actions": { "source": { "organizeImports": "on" } } },
+ "assist": {
+ "actions": {
+ "source": {
+ "organizeImports": "on"
+ }
+ }
+ },
"css": {
"parser": {
"tailwindDirectives": true
diff --git a/modules/blox/assets/css/blox/biography.css b/modules/blox/assets/css/blox/biography.css
index 3ac8b703..064b3aaa 100644
--- a/modules/blox/assets/css/blox/biography.css
+++ b/modules/blox/assets/css/blox/biography.css
@@ -90,22 +90,20 @@
line-height: 1.2;
/* Subtle gradient text for premium feel */
- background: linear-gradient(135deg,
- var(--color-gray-500) 0%,
- var(--color-gray-400) 100%);
+ background: linear-gradient(135deg, var(--color-gray-500) 0%, var(--color-gray-400) 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
/* Smooth reveal animation */
opacity: 0.85;
- transition: opacity 0.3s ease, transform 0.3s ease;
+ transition:
+ opacity 0.3s ease,
+ transform 0.3s ease;
}
.dark .hb-ruby-name rt {
- background: linear-gradient(135deg,
- var(--color-gray-400) 0%,
- var(--color-gray-300) 100%);
+ background: linear-gradient(135deg, var(--color-gray-400) 0%, var(--color-gray-300) 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
@@ -280,4 +278,4 @@
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%);
-}
\ No newline at end of file
+}
diff --git a/modules/blox/blox/cta-card/client.jsx b/modules/blox/blox/cta-card/client.jsx
index 76d2f9fc..a686f379 100644
--- a/modules/blox/blox/cta-card/client.jsx
+++ b/modules/blox/blox/cta-card/client.jsx
@@ -1,25 +1,25 @@
-import { render } from "preact";
-import { CtaCardBlock } from "./component.jsx";
+import {render} from "preact";
+import {CtaCardBlock} from "./component.jsx";
function renderCtaCardBlock() {
- const blocks = document.querySelectorAll('.cta-card-block-container[data-preact-render="true"]');
+ const blocks = document.querySelectorAll('.cta-card-block-container[data-preact-render="true"]');
- blocks.forEach((block) => {
- try {
- if (block.dataset.rendered === "true") return;
+ blocks.forEach((block) => {
+ try {
+ if (block.dataset.rendered === "true") return;
- const props = JSON.parse(block.dataset.props);
- render(, block);
- block.dataset.rendered = "true";
- } catch (e) {
- console.error("Error rendering CTA Card Block:", e, block);
- }
- });
+ const props = JSON.parse(block.dataset.props);
+ render(, block);
+ block.dataset.rendered = "true";
+ } catch (e) {
+ console.error("Error rendering CTA Card Block:", e, block);
+ }
+ });
}
// Initial render
if (document.readyState === "loading") {
- document.addEventListener("DOMContentLoaded", renderCtaCardBlock);
+ document.addEventListener("DOMContentLoaded", renderCtaCardBlock);
} else {
- renderCtaCardBlock();
+ renderCtaCardBlock();
}
diff --git a/modules/blox/blox/cta-card/component.jsx b/modules/blox/blox/cta-card/component.jsx
index 50b0669c..25ee8863 100644
--- a/modules/blox/blox/cta-card/component.jsx
+++ b/modules/blox/blox/cta-card/component.jsx
@@ -4,125 +4,125 @@
* Supports glassmorphism, custom backgrounds, and automatic text color adjustment.
*/
-import { Icon } from "../../shared/components/Icon.jsx";
+import {Icon} from "../../shared/components/Icon.jsx";
// Simple markdown renderer
function renderText(text) {
- if (!text) return "";
- return String(text)
- .replace(/\*\*(.*?)\*\*/g, "$1")
- .replace(/\*(.*?)\*/g, "$1")
- .replace(/`(.*?)`/g, "$1")
- .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1');
+ if (!text) return "";
+ return String(text)
+ .replace(/\*\*(.*?)\*\*/g, "$1")
+ .replace(/\*(.*?)\*/g, "$1")
+ .replace(/`(.*?)`/g, "$1")
+ .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1');
}
-export const CtaCardBlock = ({ content, design, id, button_icon_svg }) => {
- const card = design?.card || {};
- const cardClass = card.css_class || "";
- const cardStyle = card.css_style || "";
+export const CtaCardBlock = ({content, design, _id, button_icon_svg}) => {
+ const card = design?.card || {};
+ const cardClass = card.css_class || "";
+ const cardStyle = card.css_style || "";
- // Overlay Opacity Logic
- let overlayOpacity = 0.15;
- if (card.overlay_opacity !== undefined) {
- const val = parseFloat(card.overlay_opacity);
- if (!isNaN(val)) overlayOpacity = Math.max(0, Math.min(1, val));
+ // Overlay Opacity Logic
+ let overlayOpacity = 0.15;
+ if (card.overlay_opacity !== undefined) {
+ const val = parseFloat(card.overlay_opacity);
+ if (!Number.isNaN(val)) overlayOpacity = Math.max(0, Math.min(1, val));
+ }
+
+ // Text Color Logic
+ let textColorMode = "auto";
+ if (["light", "dark", "auto"].includes(card.text_color?.toLowerCase())) {
+ textColorMode = card.text_color.toLowerCase();
+ }
+
+ let textClasses = "text-gray-900 dark:text-white";
+ let buttonClasses = "text-gray-900 dark:text-gray-100";
+
+ // Auto-detect based on background gradient
+ const bg = design?.background || {};
+ const gradientStart = bg.gradient?.start || "";
+ const isDarkBg = ["primary-7", "primary-8", "primary-9", "secondary-7", "secondary-8", "secondary-9"].some((p) => gradientStart.startsWith(p));
+
+ if (textColorMode === "light") {
+ textClasses = "text-white";
+ buttonClasses = "text-gray-900 dark:text-white";
+ } else if (textColorMode === "dark") {
+ textClasses = "text-gray-900 dark:text-white";
+ buttonClasses = "text-gray-900 dark:text-white";
+ } else if (isDarkBg) {
+ textClasses = "text-white";
+ buttonClasses = "text-gray-900";
+ }
+
+ // Button Logic
+ const button = content?.button || {};
+ const buttonText = button.text?.trim();
+ const buttonUrl = button.url?.trim();
+ const showButton = !!(buttonText && buttonUrl);
+
+ let isExternal = false;
+ let isNewTab = button.new_tab === true;
+ if (buttonUrl) {
+ try {
+ const url = new URL(buttonUrl, window.location.origin);
+ isExternal = url.origin !== window.location.origin;
+ } catch {
+ isExternal = /^https?:\/\//.test(buttonUrl);
}
+ if (buttonUrl.endsWith(".pdf")) isNewTab = true;
+ if (isExternal) isNewTab = true;
+ }
- // Text Color Logic
- let textColorMode = "auto";
- if (["light", "dark", "auto"].includes(card.text_color?.toLowerCase())) {
- textColorMode = card.text_color.toLowerCase();
- }
+ const defaultBgClass = "bg-gradient-to-br from-primary-500/90 via-primary-600/95 to-primary-700/90";
+ const finalCardClass = cardClass || defaultBgClass;
- let textClasses = "text-gray-900 dark:text-white";
- let buttonClasses = "text-gray-900 dark:text-gray-100";
+ const style = `--glassmorphism-opacity: ${overlayOpacity}; ${cardStyle}`;
- // Auto-detect based on background gradient
- const bg = design?.background || {};
- const gradientStart = bg.gradient?.start || "";
- const isDarkBg = ["primary-7", "primary-8", "primary-9", "secondary-7", "secondary-8", "secondary-9"].some(p => gradientStart.startsWith(p));
+ return (
+
+ {/* Title */}
+ {content.title && (
+
+ )}
- if (textColorMode === "light") {
- textClasses = "text-white";
- buttonClasses = "text-gray-900 dark:text-white";
- } else if (textColorMode === "dark") {
- textClasses = "text-gray-900 dark:text-white";
- buttonClasses = "text-gray-900 dark:text-white";
- } else if (isDarkBg) {
- textClasses = "text-white";
- buttonClasses = "text-gray-900";
- }
-
- // Button Logic
- const button = content?.button || {};
- const buttonText = button.text?.trim();
- const buttonUrl = button.url?.trim();
- const showButton = !!(buttonText && buttonUrl);
-
- let isExternal = false;
- let isNewTab = button.new_tab === true;
- if (buttonUrl) {
- try {
- const url = new URL(buttonUrl, window.location.origin);
- isExternal = url.origin !== window.location.origin;
- } catch {
- isExternal = /^https?:\/\//.test(buttonUrl);
- }
- if (buttonUrl.endsWith(".pdf")) isNewTab = true;
- if (isExternal) isNewTab = true;
- }
-
- const defaultBgClass = "bg-gradient-to-br from-primary-500/90 via-primary-600/95 to-primary-700/90";
- const finalCardClass = cardClass || defaultBgClass;
-
- const style = `--glassmorphism-opacity: ${overlayOpacity}; ${cardStyle}`;
-
- return (
+ {/* Text */}
+ {content.text && (
- {/* Title */}
- {content.title && (
-
+ class={`${textClasses}/80 mt-6 text-lg sm:text-xl lg:text-2xl max-w-3xl leading-relaxed font-light`}
+ dangerouslySetInnerHTML={{__html: renderText(content.text)}}
+ />
+ )}
+
+ {/* Button */}
+ {showButton && (
+
- );
+ )}
+
+ );
};
diff --git a/modules/blox/blox/cta-image-paragraph/client.jsx b/modules/blox/blox/cta-image-paragraph/client.jsx
index 298411f9..1e93646c 100644
--- a/modules/blox/blox/cta-image-paragraph/client.jsx
+++ b/modules/blox/blox/cta-image-paragraph/client.jsx
@@ -2,24 +2,24 @@
* CTA Image Paragraph Block - Client-side Hydration
*/
-import { render } from "preact";
-import { CtaImageParagraphBlock } from "./component.jsx";
+import {render} from "preact";
+import {CtaImageParagraphBlock} from "./component.jsx";
function renderCtaImageParagraphBlocks() {
- const blocks = document.querySelectorAll('[data-block-type="cta-image-paragraph"]');
+ const blocks = document.querySelectorAll('[data-block-type="cta-image-paragraph"]');
- blocks.forEach((block) => {
- const propsData = block.dataset.props;
- if (propsData) {
- try {
- const props = JSON.parse(propsData);
- render(
, block);
- console.debug(`✓ CTA Image Paragraph block "${block.id}" rendered with Preact`);
- } catch (error) {
- console.error(`Failed to render CTA Image Paragraph block "${block.id}":`, error);
- }
- }
- });
+ blocks.forEach((block) => {
+ const propsData = block.dataset.props;
+ if (propsData) {
+ try {
+ const props = JSON.parse(propsData);
+ render(
, block);
+ console.debug(`✓ CTA Image Paragraph block "${block.id}" rendered with Preact`);
+ } catch (error) {
+ console.error(`Failed to render CTA Image Paragraph block "${block.id}":`, error);
+ }
+ }
+ });
}
renderCtaImageParagraphBlocks();
diff --git a/modules/blox/blox/cta-image-paragraph/component.jsx b/modules/blox/blox/cta-image-paragraph/component.jsx
index 3d893452..54f0114f 100644
--- a/modules/blox/blox/cta-image-paragraph/component.jsx
+++ b/modules/blox/blox/cta-image-paragraph/component.jsx
@@ -4,137 +4,131 @@
* Images are pre-processed by Hugo's responsive image pipeline (srcset)
*/
-import { Icon } from "../../shared/components/Icon.jsx";
+import {Icon} from "../../shared/components/Icon.jsx";
// Simple markdown renderer
function renderText(text) {
- if (!text) return "";
- return String(text)
- .replace(/\*\*(.*?)\*\*/g, "
$1")
- .replace(/\*(.*?)\*/g, "
$1")
- .replace(/`(.*?)`/g, "
$1")
- .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '
$1');
+ if (!text) return "";
+ return String(text)
+ .replace(/\*\*(.*?)\*\*/g, "
$1")
+ .replace(/\*(.*?)\*/g, "
$1")
+ .replace(/`(.*?)`/g, "
$1")
+ .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '
$1');
}
// Arrow icon for CTA buttons
const ArrowIcon = () => (
-
+
);
-function CtaItem({ item, idx, imageData, featureIconSvg }) {
- const isReversed = idx % 2 === 1;
- const hasImage = !!imageData;
- const featureIcon = item.feature_icon || "check";
+function CtaItem({item, idx, imageData, featureIconSvg}) {
+ const isReversed = idx % 2 === 1;
+ const hasImage = !!imageData;
+ const _featureIcon = item.feature_icon || "check";
- // Resolve button link
- let buttonUrl = item.button?.url || "";
- let isExternal = false;
- if (buttonUrl) {
- try {
- const url = new URL(buttonUrl, window.location.origin);
- isExternal = url.origin !== window.location.origin;
- } catch {
- isExternal = /^https?:\/\//.test(buttonUrl);
- }
+ // Resolve button link
+ const buttonUrl = item.button?.url || "";
+ let isExternal = false;
+ if (buttonUrl) {
+ try {
+ const url = new URL(buttonUrl, window.location.origin);
+ isExternal = url.origin !== window.location.origin;
+ } catch {
+ isExternal = /^https?:\/\//.test(buttonUrl);
}
+ }
- const features = Array.isArray(item.features)
- ? item.features
- : typeof item.features === "string"
- ? [item.features]
- : [];
+ const features = Array.isArray(item.features) ? item.features : typeof item.features === "string" ? [item.features] : [];
- return (
-
- {/* Image */}
- {hasImage && (
- imageData.srcset ? (
-

- ) : (
-

- )
- )}
+ return (
+
+ {/* Image */}
+ {hasImage &&
+ (imageData.srcset ? (
+

+ ) : (
+

+ ))}
- {/* Text Content */}
-
- {item.title && (
-
- )}
- {item.text && (
-
- )}
+ {/* Text Content */}
+
+ {item.title && (
+
+ )}
+ {item.text && (
+
+ )}
- {/* Feature List */}
- {features.length > 0 && (
-
- {features.map((feature, fIdx) => (
- -
- {featureIconSvg ? (
-
- ) : null}
-
-
- ))}
-
- )}
+ {/* Feature List */}
+ {features.length > 0 && (
+
+ {features.map((feature, fIdx) => (
+ -
+ {featureIconSvg ? : null}
+
+
+ ))}
+
+ )}
- {/* CTA Button */}
- {item.button?.text && item.button?.url && (
-
- {item.button.text}
-
-
- )}
-
-
- );
+ {/* CTA Button */}
+ {item.button?.text && item.button?.url && (
+
+ {item.button.text}
+
+
+ )}
+
+
+ );
}
-export const CtaImageParagraphBlock = ({ content, design, id, item_images, icon_svgs }) => {
- const items = Array.isArray(content?.items) ? content.items : [];
- const imageMap = item_images || {};
- const iconMap = icon_svgs || {};
+export const CtaImageParagraphBlock = ({content, _design, _id, item_images, icon_svgs}) => {
+ const items = Array.isArray(content?.items) ? content.items : [];
+ const imageMap = item_images || {};
+ const iconMap = icon_svgs || {};
- return (
-
- {items.map((item, idx) => (
-
- ))}
-
- );
+ return (
+
+ {items.map((item, idx) => (
+
+ ))}
+
+ );
};
diff --git a/modules/blox/blox/features/client.jsx b/modules/blox/blox/features/client.jsx
index bab1d116..f43bca00 100644
--- a/modules/blox/blox/features/client.jsx
+++ b/modules/blox/blox/features/client.jsx
@@ -2,24 +2,24 @@
* Features Block - Client-side Hydration
*/
-import { render } from "preact";
-import { FeaturesBlock } from "./component.jsx";
+import {render} from "preact";
+import {FeaturesBlock} from "./component.jsx";
function renderFeaturesBlocks() {
- const blocks = document.querySelectorAll('[data-block-type="features"]');
+ const blocks = document.querySelectorAll('[data-block-type="features"]');
- blocks.forEach((block) => {
- const propsData = block.dataset.props;
- if (propsData) {
- try {
- const props = JSON.parse(propsData);
- render(
, block);
- console.debug(`✓ Features block "${block.id}" rendered with Preact`);
- } catch (error) {
- console.error(`Failed to render Features block "${block.id}":`, error);
- }
- }
- });
+ blocks.forEach((block) => {
+ const propsData = block.dataset.props;
+ if (propsData) {
+ try {
+ const props = JSON.parse(propsData);
+ render(
, block);
+ console.debug(`✓ Features block "${block.id}" rendered with Preact`);
+ } catch (error) {
+ console.error(`Failed to render Features block "${block.id}":`, error);
+ }
+ }
+ });
}
renderFeaturesBlocks();
diff --git a/modules/blox/blox/features/component.jsx b/modules/blox/blox/features/component.jsx
index 2cc5bca1..b9d80cdc 100644
--- a/modules/blox/blox/features/component.jsx
+++ b/modules/blox/blox/features/component.jsx
@@ -3,79 +3,60 @@
* Responsive grid of feature items with icon, name, and description
*/
-import { Icon } from "../../shared/components/Icon.jsx";
+import {Icon} from "../../shared/components/Icon.jsx";
// Simple markdown renderer
function renderText(text) {
- if (!text) return "";
- return String(text)
- .replace(/\*\*(.*?)\*\*/g, "
$1")
- .replace(/\*(.*?)\*/g, "
$1")
- .replace(/`(.*?)`/g, "
$1");
+ if (!text) return "";
+ return String(text)
+ .replace(/\*\*(.*?)\*\*/g, "
$1")
+ .replace(/\*(.*?)\*/g, "
$1")
+ .replace(/`(.*?)`/g, "
$1");
}
-function FeatureItem({ item, iconSvg }) {
- return (
-
- {iconSvg && (
-
-
-
- )}
- {item.name && (
-
- )}
- {item.description && (
-
- )}
+function FeatureItem({item, iconSvg}) {
+ return (
+
+ {iconSvg && (
+
+
- );
+ )}
+ {item.name &&
}
+ {item.description &&
}
+
+ );
}
-export const FeaturesBlock = ({ content, design, id, icon_svgs }) => {
- const title = content?.title;
- const text = content?.text;
- const items = Array.isArray(content?.items) ? content.items : [];
- const iconMap = icon_svgs || {};
+export const FeaturesBlock = ({content, _design, _id, icon_svgs}) => {
+ const title = content?.title;
+ const text = content?.text;
+ const items = Array.isArray(content?.items) ? content.items : [];
+ const iconMap = icon_svgs || {};
- return (
-
-
- {/* Section Header */}
- {(title || text) && (
-
- {title && (
-
- )}
- {text && (
-
- )}
-
- )}
+ return (
+
+
+ {/* Section Header */}
+ {(title || text) && (
+
+ {title && (
+
+ )}
+ {text &&
}
+
+ )}
- {/* Feature Grid */}
-
- {items.map((item, idx) => (
-
- ))}
-
-
-
- );
+ {/* Feature Grid */}
+
+ {items.map((item, idx) => (
+
+ ))}
+
+
+
+ );
};
diff --git a/modules/blox/blox/hero/component.jsx b/modules/blox/blox/hero/component.jsx
index 4b166138..b8d0bc4a 100644
--- a/modules/blox/blox/hero/component.jsx
+++ b/modules/blox/blox/hero/component.jsx
@@ -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 (
@@ -73,7 +73,7 @@ export const HeroBlock = ({ content, design, id, icon_svg }) => {
{content.title && (
)}
@@ -81,7 +81,7 @@ export const HeroBlock = ({ content, design, id, icon_svg }) => {
{content.text && (
)}
diff --git a/modules/blox/blox/shared/js/components/Icon.jsx b/modules/blox/blox/shared/js/components/Icon.jsx
index 78379a9a..338f1e7d 100644
--- a/modules/blox/blox/shared/js/components/Icon.jsx
+++ b/modules/blox/blox/shared/js/components/Icon.jsx
@@ -1,5 +1,5 @@
// biome-ignore lint/correctness/noUnusedImports: Classic Preact JSX runtime may require 'h' for JSX transform
-import { h } from "preact";
+import {h} from "preact";
/**
* Icon component
@@ -13,7 +13,7 @@ import { h } from "preact";
* the SVG fills that container with height:100%;width:auto instead.
* - If the caller provides explicit w-/h- Tailwind classes, those take precedence.
*/
-export const Icon = ({ svg, attributes }) => {
+export const Icon = ({svg, attributes}) => {
if (!svg) return null;
// Clean the SVG string: decode HTML entities and TRIM whitespace
@@ -35,9 +35,7 @@ export const Icon = ({ svg, attributes }) => {
// use 100% to fill the container; otherwise default to 1em for self-sizing.
const callerStyle = attributes?.style || "";
const hasExplicitHeight = /height\s*:/i.test(String(callerStyle));
- const svgStyle = hasExplicitHeight
- ? "height:100%;width:auto"
- : "height:1em;width:auto";
+ const svgStyle = hasExplicitHeight ? "height:100%;width:auto" : "height:1em;width:auto";
decoded = decoded.replace(/^(
` }} />;
+ return
${decoded}`}} />;
};
diff --git a/modules/blox/blox/stats/client.jsx b/modules/blox/blox/stats/client.jsx
index bae456fc..bb2e6518 100644
--- a/modules/blox/blox/stats/client.jsx
+++ b/modules/blox/blox/stats/client.jsx
@@ -3,28 +3,28 @@
* Uses the shared StatsBlock component for consistency
*/
-import { render } from "preact";
-import { StatsBlock } from "./component.jsx";
+import {render} from "preact";
+import {StatsBlock} from "./component.jsx";
function renderStatsBlocks() {
- const statsBlocks = document.querySelectorAll('[data-block-type="stats"]');
+ const statsBlocks = document.querySelectorAll('[data-block-type="stats"]');
- statsBlocks.forEach((block) => {
- const propsData = block.dataset.props;
- if (propsData) {
- try {
- const props = JSON.parse(propsData);
- render(, block);
- console.debug(`✓ Stats block "${block.id}" rendered with Preact`);
- } catch (error) {
- console.error(`Failed to render Stats block "${block.id}":`, error);
- }
- }
- });
-
- if (statsBlocks.length > 0) {
- console.debug(`✓ ${statsBlocks.length} Stats blocks initialized with Preact`);
+ statsBlocks.forEach((block) => {
+ const propsData = block.dataset.props;
+ if (propsData) {
+ try {
+ const props = JSON.parse(propsData);
+ render(, block);
+ console.debug(`✓ Stats block "${block.id}" rendered with Preact`);
+ } catch (error) {
+ console.error(`Failed to render Stats block "${block.id}":`, error);
+ }
}
+ });
+
+ if (statsBlocks.length > 0) {
+ console.debug(`✓ ${statsBlocks.length} Stats blocks initialized with Preact`);
+ }
}
// Initialize immediately — script is already deferred
diff --git a/modules/blox/blox/stats/component.jsx b/modules/blox/blox/stats/component.jsx
index e0ab07e4..e7fea6af 100644
--- a/modules/blox/blox/stats/component.jsx
+++ b/modules/blox/blox/stats/component.jsx
@@ -6,280 +6,264 @@
* Features: counter animation, per-item icons, stagger reveal
*/
-import { useEffect, useRef } from "preact/hooks";
-import { Icon } from "../../shared/components/Icon.jsx";
+import {useEffect, useRef} from "preact/hooks";
+import {Icon} from "../../shared/components/Icon.jsx";
// Simple markdown renderer (shared with Hero)
function renderText(text) {
- if (!text) return "";
- return text
- .replace(/\*\*(.*?)\*\*/g, "$1")
- .replace(/\*(.*?)\*/g, "$1")
- .replace(/`(.*?)`/g, "$1");
+ if (!text) return "";
+ return text
+ .replace(/\*\*(.*?)\*\*/g, "$1")
+ .replace(/\*(.*?)\*/g, "$1")
+ .replace(/`(.*?)`/g, "$1");
}
// Extract numeric target from a statistic string like "10,000+" → 10000
function parseTarget(statistic) {
- if (!statistic) return 0;
- const raw = String(statistic).replace(/[^0-9.]/g, "");
- const num = Number(raw);
- return isNaN(num) ? 0 : num;
+ if (!statistic) return 0;
+ const raw = String(statistic).replace(/[^0-9.]/g, "");
+ const num = Number(raw);
+ return Number.isNaN(num) ? 0 : num;
}
// Extract prefix, number, suffix from "~10,000+" → { prefix: "~", number: "10,000", suffix: "+" }
function extractParts(text) {
- if (!text) return { prefix: "", numberPart: "", suffix: "" };
- const parts = String(text).match(/([^0-9]*)([0-9][0-9,.]*)(.*)/);
- return {
- prefix: parts ? parts[1] : "",
- numberPart: parts ? parts[2] : "",
- suffix: parts ? parts[3] : "",
- };
+ if (!text) return {prefix: "", numberPart: "", suffix: ""};
+ const parts = String(text).match(/([^0-9]*)([0-9][0-9,.]*)(.*)/);
+ return {
+ prefix: parts ? parts[1] : "",
+ numberPart: parts ? parts[2] : "",
+ suffix: parts ? parts[3] : "",
+ };
}
// Counter animation hook
function useCounterAnimation(containerRef) {
- useEffect(() => {
- const container = containerRef.current;
- if (!container) return;
+ useEffect(() => {
+ const container = containerRef.current;
+ if (!container) return;
- const prefersReducedMotion = window.matchMedia(
- "(prefers-reduced-motion: reduce)"
- ).matches;
+ const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
- function animateCounters(el) {
- el.classList.add("animate");
- const counters = el.querySelectorAll(".counter");
+ function animateCounters(el) {
+ el.classList.add("animate");
+ const counters = el.querySelectorAll(".counter");
- counters.forEach((counter) => {
- const target = parseTarget(counter.getAttribute("data-target"));
- if (target <= 0) return;
+ counters.forEach((counter) => {
+ const target = parseTarget(counter.getAttribute("data-target"));
+ if (target <= 0) return;
- const { prefix, numberPart, suffix } = extractParts(
- counter.textContent
- );
- const decimals = (numberPart.split(".")[1] || "").length;
- const formatter = new Intl.NumberFormat(undefined, {
- minimumFractionDigits: decimals,
- maximumFractionDigits: decimals,
- });
-
- if (prefersReducedMotion) {
- counter.textContent = `${prefix}${formatter.format(target)}${suffix}`;
- return;
- }
-
- const duration = 2000;
- const start = Date.now();
-
- function update() {
- const now = Date.now();
- const progress = Math.min((now - start) / duration, 1);
- const easeOut = 1 - Math.pow(1 - progress, 3);
- const current = target * easeOut;
- counter.textContent = `${prefix}${formatter.format(current)}${suffix}`;
- if (progress < 1) requestAnimationFrame(update);
- }
-
- requestAnimationFrame(update);
- });
- }
-
- const observer = new IntersectionObserver(
- (entries) => {
- entries.forEach((entry) => {
- if (entry.isIntersecting) {
- animateCounters(entry.target);
- observer.unobserve(entry.target);
- }
- });
- },
- { threshold: 0.2, rootMargin: "0px 0px -10% 0px" }
- );
-
- // Observe each stat item
- const items = container.querySelectorAll(".stats-item");
- items.forEach((item) => {
- const rect = item.getBoundingClientRect();
- const vh = window.innerHeight;
- if (rect.top < vh * 0.9 && rect.bottom > 0) {
- animateCounters(item);
- } else {
- observer.observe(item);
- }
+ const {prefix, numberPart, suffix} = extractParts(counter.textContent);
+ const decimals = (numberPart.split(".")[1] || "").length;
+ const formatter = new Intl.NumberFormat(undefined, {
+ minimumFractionDigits: decimals,
+ maximumFractionDigits: decimals,
});
- return () => observer.disconnect();
- }, []);
+ if (prefersReducedMotion) {
+ counter.textContent = `${prefix}${formatter.format(target)}${suffix}`;
+ return;
+ }
+
+ const duration = 2000;
+ const start = Date.now();
+
+ function update() {
+ const now = Date.now();
+ const progress = Math.min((now - start) / duration, 1);
+ const easeOut = 1 - (1 - progress) ** 3;
+ const current = target * easeOut;
+ counter.textContent = `${prefix}${formatter.format(current)}${suffix}`;
+ if (progress < 1) requestAnimationFrame(update);
+ }
+
+ requestAnimationFrame(update);
+ });
+ }
+
+ const observer = new IntersectionObserver(
+ (entries) => {
+ entries.forEach((entry) => {
+ if (entry.isIntersecting) {
+ animateCounters(entry.target);
+ observer.unobserve(entry.target);
+ }
+ });
+ },
+ {threshold: 0.2, rootMargin: "0px 0px -10% 0px"},
+ );
+
+ // Observe each stat item
+ const items = container.querySelectorAll(".stats-item");
+ items.forEach((item) => {
+ const rect = item.getBoundingClientRect();
+ const vh = window.innerHeight;
+ if (rect.top < vh * 0.9 && rect.bottom > 0) {
+ animateCounters(item);
+ } else {
+ observer.observe(item);
+ }
+ });
+
+ return () => observer.disconnect();
+ }, []);
}
// Grid columns class based on item count
function gridColsClass(count) {
- if (count <= 1) return "lg:grid-cols-1";
- if (count === 2) return "lg:grid-cols-2";
- if (count >= 4) return "lg:grid-cols-4";
- return "lg:grid-cols-3";
+ if (count <= 1) return "lg:grid-cols-1";
+ if (count === 2) return "lg:grid-cols-2";
+ if (count >= 4) return "lg:grid-cols-4";
+ return "lg:grid-cols-3";
}
// ─── Stat Item (Cards) ───────────────────────────────────────────────
-function StatCard({ item, iconSvg }) {
- return (
-
- {/* Hover gradient */}
-
+function StatCard({item, iconSvg}) {
+ return (
+
+ {/* Hover gradient */}
+
-
- {/* Icon */}
- {iconSvg && (
-
-
-
- )}
+
+ {/* Icon */}
+ {iconSvg && (
+
+
+
+ )}
- {/* Statistic */}
-
-
-
-
- {/* Description */}
-
-
- {/* Sub-metric */}
- {item.sub_metric && (
-
- {item.sub_metric}
-
- )}
-
+ {/* Statistic */}
+
+
- );
+
+ {/* Description */}
+
+
+ {/* Sub-metric */}
+ {item.sub_metric &&
{item.sub_metric}
}
+
+
+ );
}
// ─── Stat Item (Compact) ─────────────────────────────────────────────
-function StatCompact({ item, iconSvg }) {
- return (
-
- {iconSvg && (
-
-
-
- )}
-
-
-
-
+function StatCompact({item, iconSvg}) {
+ return (
+
+ {iconSvg && (
+
+
- );
+ )}
+
+
+
+
+
+ );
}
// ─── Stat Item (Minimal) ─────────────────────────────────────────────
-function StatMinimal({ item, iconSvg }) {
- return (
-
- {iconSvg && (
-
-
-
- )}
-
-
-
-
+function StatMinimal({item, iconSvg}) {
+ return (
+
+ {iconSvg && (
+
+
- );
+ )}
+
+
+
+
+
+ );
}
// ─── Main Stats Block ────────────────────────────────────────────────
-export const StatsBlock = ({ content, design, id, icon_svgs }) => {
- const containerRef = useRef(null);
- useCounterAnimation(containerRef);
+export const StatsBlock = ({content, design, _id, icon_svgs}) => {
+ const containerRef = useRef(null);
+ useCounterAnimation(containerRef);
- const title = content?.title;
- const text = content?.text;
- const items = Array.isArray(content?.items) ? content.items : [];
- const layout = (design?.layout || "cards").toLowerCase();
+ const title = content?.title;
+ const text = content?.text;
+ const items = Array.isArray(content?.items) ? content.items : [];
+ const layout = (design?.layout || "cards").toLowerCase();
- const count = Math.max(1, Math.min(items.length, 4));
- const lgCols = gridColsClass(count);
- const baseCols = count === 1 ? "grid-cols-1" : "grid-cols-2";
+ const count = Math.max(1, Math.min(items.length, 4));
+ const lgCols = gridColsClass(count);
+ const baseCols = count === 1 ? "grid-cols-1" : "grid-cols-2";
- // icon_svgs is a dict keyed by icon name, provided by preact-wrapper
- const iconMap = icon_svgs || {};
+ // icon_svgs is a dict keyed by icon name, provided by preact-wrapper
+ const iconMap = icon_svgs || {};
- return (
-
-
- {/* Section Header */}
- {(title || text) && (
-
- {title && (
-
- )}
- {text && (
-
- )}
-
- )}
+ return (
+
+
+ {/* Section Header */}
+ {(title || text) && (
+
+ {title && (
+
+ )}
+ {text &&
}
+
+ )}
- {/* Cards Layout */}
- {layout === "cards" && (
-
- {items.map((item, idx) => (
-
- ))}
-
- )}
+ {/* Cards Layout */}
+ {layout === "cards" && (
+
+ {items.map((item, idx) => (
+
+ ))}
+
+ )}
- {/* Compact Layout */}
- {layout === "compact" && (
-
-
- {items.map((item, idx) => (
-
- ))}
-
-
- )}
-
- {/* Minimal Layout */}
- {layout === "minimal" && (
-
- {items.map((item, idx) => (
-
- ))}
-
- )}
+ {/* Compact Layout */}
+ {layout === "compact" && (
+
+
+ {items.map((item, idx) => (
+
+ ))}
+
+ )}
- {/* Animation styles (scoped via container ref) */}
-
-
- );
+
+ );
};
diff --git a/modules/blox/blox/testimonials/client.jsx b/modules/blox/blox/testimonials/client.jsx
index ee4181fc..07acb2a5 100644
--- a/modules/blox/blox/testimonials/client.jsx
+++ b/modules/blox/blox/testimonials/client.jsx
@@ -2,24 +2,24 @@
* Testimonials Block - Client-side Hydration
*/
-import { render } from "preact";
-import { TestimonialsBlock } from "./component.jsx";
+import {render} from "preact";
+import {TestimonialsBlock} from "./component.jsx";
function renderTestimonialsBlocks() {
- const blocks = document.querySelectorAll('[data-block-type="testimonials"]');
+ const blocks = document.querySelectorAll('[data-block-type="testimonials"]');
- blocks.forEach((block) => {
- const propsData = block.dataset.props;
- if (propsData) {
- try {
- const props = JSON.parse(propsData);
- render(
, block);
- console.debug(`✓ Testimonials block "${block.id}" rendered with Preact`);
- } catch (error) {
- console.error(`Failed to render Testimonials block "${block.id}":`, error);
- }
- }
- });
+ blocks.forEach((block) => {
+ const propsData = block.dataset.props;
+ if (propsData) {
+ try {
+ const props = JSON.parse(propsData);
+ render(
, block);
+ console.debug(`✓ Testimonials block "${block.id}" rendered with Preact`);
+ } catch (error) {
+ console.error(`Failed to render Testimonials block "${block.id}":`, error);
+ }
+ }
+ });
}
renderTestimonialsBlocks();
diff --git a/modules/blox/blox/testimonials/component.jsx b/modules/blox/blox/testimonials/component.jsx
index 3858cd68..7ccb3fcb 100644
--- a/modules/blox/blox/testimonials/component.jsx
+++ b/modules/blox/blox/testimonials/component.jsx
@@ -6,103 +6,76 @@
// Quote mark SVG (inline for zero dependencies)
const QuoteSvg = () => (
-
+
);
// Simple markdown renderer
function renderText(text) {
- if (!text) return "";
- return String(text)
- .replace(/\*\*(.*?)\*\*/g, "
$1")
- .replace(/\*(.*?)\*/g, "
$1")
- .replace(/`(.*?)`/g, "
$1");
+ if (!text) return "";
+ return String(text)
+ .replace(/\*\*(.*?)\*\*/g, "
$1")
+ .replace(/\*(.*?)\*/g, "
$1")
+ .replace(/`(.*?)`/g, "
$1");
}
-function TestimonialItem({ item, avatarUrl, isLast }) {
- return (
-
-
-
-
-
-
- {/* Avatar */}
- {avatarUrl ? (
-

- ) : (
-
- )}
-
- {item.name && (
-
- {item.name}
-
- )}
- {item.role && (
-
- {item.role}
-
- )}
-
-
+function TestimonialItem({item, avatarUrl, isLast}) {
+ return (
+
+
+
+
+
+
+ {/* Avatar */}
+ {avatarUrl ? (
+

+ ) : (
+
+ )}
+
+ {item.name &&
{item.name}
}
+ {item.role &&
{item.role}
}
- );
+
+
+ );
}
-export const TestimonialsBlock = ({ content, design, id, item_images }) => {
- const title = content?.title;
- const text = content?.text;
- const items = Array.isArray(content?.items) ? content.items : [];
- const imageMap = item_images || {};
+export const TestimonialsBlock = ({content, _design, _id, item_images}) => {
+ const title = content?.title;
+ const text = content?.text;
+ const items = Array.isArray(content?.items) ? content.items : [];
+ const imageMap = item_images || {};
- return (
-
-
- {/* Section Header */}
- {title && (
-
- )}
-
- {/* Testimonial Items */}
- {items.map((item, idx) => (
-
- ))}
+ return (
+
+
+ {/* Section Header */}
+ {title && (
+
+ )}
+
+ {/* Testimonial Items */}
+ {items.map((item, idx) => (
+
+ ))}
+
+
+ );
};
diff --git a/package.json b/package.json
index cfecca9a..4903c62e 100644
--- a/package.json
+++ b/package.json
@@ -34,12 +34,9 @@
"dev": "scripts/view-starter-dev.sh",
"dev:stop": "lsof -ti :8081 | xargs kill -9",
"test": "scripts/view-test.sh",
- "lint": "biome check --reporter=summary .",
- "lint:code": "biome check --reporter=summary . --linter-enabled=true --formatter-enabled=false",
- "lint:code:fix": "biome check --write . --linter-enabled=true --formatter-enabled=false",
- "lint:style": "biome check --reporter=summary modules/blox/assets/css/ modules/slides/assets/css/ --linter-enabled=true --formatter-enabled=false",
- "lint:style:fix": "biome check --write modules/blox/assets/css/ modules/slides/assets/css/ --linter-enabled=true --formatter-enabled=false",
- "format": "biome check --write .",
+ "lint": "biome check --reporter=summary . --linter-enabled=true --formatter-enabled=false",
+ "lint:fix": "biome check --write . --linter-enabled=true --formatter-enabled=false",
+ "format": "biome format --write .",
"update:starters": "poetry run python ./scripts/update.py",
"vendor:libs": "vite build --config vite.config.js",
"vendor:libs:watch": "vite build --config vite.config.js --watch",
@@ -47,4 +44,4 @@
"test:releaser": "poetry run python scripts/test_release_modules.py"
},
"browserslist": "> 1%"
-}
\ No newline at end of file
+}
diff --git a/templates/academic-cv/.devcontainer/devcontainer.json b/templates/academic-cv/.devcontainer/devcontainer.json
index 0028089d..c294a800 100644
--- a/templates/academic-cv/.devcontainer/devcontainer.json
+++ b/templates/academic-cv/.devcontainer/devcontainer.json
@@ -5,21 +5,15 @@
"postCreateCommand": "pnpm --version && hugo version",
"customizations": {
"vscode": {
- "extensions": [
- "ownable.ownable"
- ]
+ "extensions": ["ownable.ownable"]
}
},
- "mounts": [
- "source=pnpm-store,target=/home/vscode/.local/share/pnpm,type=volume"
- ],
+ "mounts": ["source=pnpm-store,target=/home/vscode/.local/share/pnpm,type=volume"],
"remoteUser": "vscode",
- "forwardPorts": [
- 1313
- ],
+ "forwardPorts": [1313],
"portsAttributes": {
"1313": {
"label": "Hugo Server"
}
}
-}
\ No newline at end of file
+}
diff --git a/templates/academic-cv/.vscode/extensions.json b/templates/academic-cv/.vscode/extensions.json
index f4e3ff14..0d558ec2 100644
--- a/templates/academic-cv/.vscode/extensions.json
+++ b/templates/academic-cv/.vscode/extensions.json
@@ -1,5 +1,3 @@
{
- "recommendations": [
- "ownable.ownable"
- ]
-}
\ No newline at end of file
+ "recommendations": ["ownable.ownable"]
+}
diff --git a/templates/data-science-blog/.devcontainer/devcontainer.json b/templates/data-science-blog/.devcontainer/devcontainer.json
index 0028089d..c294a800 100644
--- a/templates/data-science-blog/.devcontainer/devcontainer.json
+++ b/templates/data-science-blog/.devcontainer/devcontainer.json
@@ -5,21 +5,15 @@
"postCreateCommand": "pnpm --version && hugo version",
"customizations": {
"vscode": {
- "extensions": [
- "ownable.ownable"
- ]
+ "extensions": ["ownable.ownable"]
}
},
- "mounts": [
- "source=pnpm-store,target=/home/vscode/.local/share/pnpm,type=volume"
- ],
+ "mounts": ["source=pnpm-store,target=/home/vscode/.local/share/pnpm,type=volume"],
"remoteUser": "vscode",
- "forwardPorts": [
- 1313
- ],
+ "forwardPorts": [1313],
"portsAttributes": {
"1313": {
"label": "Hugo Server"
}
}
-}
\ No newline at end of file
+}
diff --git a/templates/data-science-blog/.vscode/extensions.json b/templates/data-science-blog/.vscode/extensions.json
index f4e3ff14..0d558ec2 100644
--- a/templates/data-science-blog/.vscode/extensions.json
+++ b/templates/data-science-blog/.vscode/extensions.json
@@ -1,5 +1,3 @@
{
- "recommendations": [
- "ownable.ownable"
- ]
-}
\ No newline at end of file
+ "recommendations": ["ownable.ownable"]
+}
diff --git a/templates/dev-portfolio/.devcontainer/devcontainer.json b/templates/dev-portfolio/.devcontainer/devcontainer.json
index 0028089d..c294a800 100644
--- a/templates/dev-portfolio/.devcontainer/devcontainer.json
+++ b/templates/dev-portfolio/.devcontainer/devcontainer.json
@@ -5,21 +5,15 @@
"postCreateCommand": "pnpm --version && hugo version",
"customizations": {
"vscode": {
- "extensions": [
- "ownable.ownable"
- ]
+ "extensions": ["ownable.ownable"]
}
},
- "mounts": [
- "source=pnpm-store,target=/home/vscode/.local/share/pnpm,type=volume"
- ],
+ "mounts": ["source=pnpm-store,target=/home/vscode/.local/share/pnpm,type=volume"],
"remoteUser": "vscode",
- "forwardPorts": [
- 1313
- ],
+ "forwardPorts": [1313],
"portsAttributes": {
"1313": {
"label": "Hugo Server"
}
}
-}
\ No newline at end of file
+}
diff --git a/templates/documentation/.devcontainer/devcontainer.json b/templates/documentation/.devcontainer/devcontainer.json
index 0028089d..c294a800 100644
--- a/templates/documentation/.devcontainer/devcontainer.json
+++ b/templates/documentation/.devcontainer/devcontainer.json
@@ -5,21 +5,15 @@
"postCreateCommand": "pnpm --version && hugo version",
"customizations": {
"vscode": {
- "extensions": [
- "ownable.ownable"
- ]
+ "extensions": ["ownable.ownable"]
}
},
- "mounts": [
- "source=pnpm-store,target=/home/vscode/.local/share/pnpm,type=volume"
- ],
+ "mounts": ["source=pnpm-store,target=/home/vscode/.local/share/pnpm,type=volume"],
"remoteUser": "vscode",
- "forwardPorts": [
- 1313
- ],
+ "forwardPorts": [1313],
"portsAttributes": {
"1313": {
"label": "Hugo Server"
}
}
-}
\ No newline at end of file
+}
diff --git a/templates/documentation/.vscode/extensions.json b/templates/documentation/.vscode/extensions.json
index f4e3ff14..0d558ec2 100644
--- a/templates/documentation/.vscode/extensions.json
+++ b/templates/documentation/.vscode/extensions.json
@@ -1,5 +1,3 @@
{
- "recommendations": [
- "ownable.ownable"
- ]
-}
\ No newline at end of file
+ "recommendations": ["ownable.ownable"]
+}
diff --git a/templates/link-in-bio/.devcontainer/devcontainer.json b/templates/link-in-bio/.devcontainer/devcontainer.json
index 0028089d..c294a800 100644
--- a/templates/link-in-bio/.devcontainer/devcontainer.json
+++ b/templates/link-in-bio/.devcontainer/devcontainer.json
@@ -5,21 +5,15 @@
"postCreateCommand": "pnpm --version && hugo version",
"customizations": {
"vscode": {
- "extensions": [
- "ownable.ownable"
- ]
+ "extensions": ["ownable.ownable"]
}
},
- "mounts": [
- "source=pnpm-store,target=/home/vscode/.local/share/pnpm,type=volume"
- ],
+ "mounts": ["source=pnpm-store,target=/home/vscode/.local/share/pnpm,type=volume"],
"remoteUser": "vscode",
- "forwardPorts": [
- 1313
- ],
+ "forwardPorts": [1313],
"portsAttributes": {
"1313": {
"label": "Hugo Server"
}
}
-}
\ No newline at end of file
+}
diff --git a/templates/link-in-bio/.vscode/extensions.json b/templates/link-in-bio/.vscode/extensions.json
index f4e3ff14..0d558ec2 100644
--- a/templates/link-in-bio/.vscode/extensions.json
+++ b/templates/link-in-bio/.vscode/extensions.json
@@ -1,5 +1,3 @@
{
- "recommendations": [
- "ownable.ownable"
- ]
-}
\ No newline at end of file
+ "recommendations": ["ownable.ownable"]
+}
diff --git a/templates/markdown-slides/.devcontainer/devcontainer.json b/templates/markdown-slides/.devcontainer/devcontainer.json
index 0028089d..c294a800 100644
--- a/templates/markdown-slides/.devcontainer/devcontainer.json
+++ b/templates/markdown-slides/.devcontainer/devcontainer.json
@@ -5,21 +5,15 @@
"postCreateCommand": "pnpm --version && hugo version",
"customizations": {
"vscode": {
- "extensions": [
- "ownable.ownable"
- ]
+ "extensions": ["ownable.ownable"]
}
},
- "mounts": [
- "source=pnpm-store,target=/home/vscode/.local/share/pnpm,type=volume"
- ],
+ "mounts": ["source=pnpm-store,target=/home/vscode/.local/share/pnpm,type=volume"],
"remoteUser": "vscode",
- "forwardPorts": [
- 1313
- ],
+ "forwardPorts": [1313],
"portsAttributes": {
"1313": {
"label": "Hugo Server"
}
}
-}
\ No newline at end of file
+}
diff --git a/templates/resume/.devcontainer/devcontainer.json b/templates/resume/.devcontainer/devcontainer.json
index 0028089d..c294a800 100644
--- a/templates/resume/.devcontainer/devcontainer.json
+++ b/templates/resume/.devcontainer/devcontainer.json
@@ -5,21 +5,15 @@
"postCreateCommand": "pnpm --version && hugo version",
"customizations": {
"vscode": {
- "extensions": [
- "ownable.ownable"
- ]
+ "extensions": ["ownable.ownable"]
}
},
- "mounts": [
- "source=pnpm-store,target=/home/vscode/.local/share/pnpm,type=volume"
- ],
+ "mounts": ["source=pnpm-store,target=/home/vscode/.local/share/pnpm,type=volume"],
"remoteUser": "vscode",
- "forwardPorts": [
- 1313
- ],
+ "forwardPorts": [1313],
"portsAttributes": {
"1313": {
"label": "Hugo Server"
}
}
-}
\ No newline at end of file
+}
diff --git a/templates/resume/.vscode/extensions.json b/templates/resume/.vscode/extensions.json
index f4e3ff14..0d558ec2 100644
--- a/templates/resume/.vscode/extensions.json
+++ b/templates/resume/.vscode/extensions.json
@@ -1,5 +1,3 @@
{
- "recommendations": [
- "ownable.ownable"
- ]
-}
\ No newline at end of file
+ "recommendations": ["ownable.ownable"]
+}
diff --git a/templates/startup-landing-page/.devcontainer/devcontainer.json b/templates/startup-landing-page/.devcontainer/devcontainer.json
index 0028089d..c294a800 100644
--- a/templates/startup-landing-page/.devcontainer/devcontainer.json
+++ b/templates/startup-landing-page/.devcontainer/devcontainer.json
@@ -5,21 +5,15 @@
"postCreateCommand": "pnpm --version && hugo version",
"customizations": {
"vscode": {
- "extensions": [
- "ownable.ownable"
- ]
+ "extensions": ["ownable.ownable"]
}
},
- "mounts": [
- "source=pnpm-store,target=/home/vscode/.local/share/pnpm,type=volume"
- ],
+ "mounts": ["source=pnpm-store,target=/home/vscode/.local/share/pnpm,type=volume"],
"remoteUser": "vscode",
- "forwardPorts": [
- 1313
- ],
+ "forwardPorts": [1313],
"portsAttributes": {
"1313": {
"label": "Hugo Server"
}
}
-}
\ No newline at end of file
+}
diff --git a/templates/startup-landing-page/.vscode/extensions.json b/templates/startup-landing-page/.vscode/extensions.json
index f4e3ff14..0d558ec2 100644
--- a/templates/startup-landing-page/.vscode/extensions.json
+++ b/templates/startup-landing-page/.vscode/extensions.json
@@ -1,5 +1,3 @@
{
- "recommendations": [
- "ownable.ownable"
- ]
-}
\ No newline at end of file
+ "recommendations": ["ownable.ownable"]
+}