From a3c21b401ee76f06e236a8ca7d848e56eb68dc6f Mon Sep 17 00:00:00 2001 From: George Cushen Date: Thu, 19 Feb 2026 14:15:26 +0000 Subject: [PATCH] fix: lint --- biome.json | 11 +- modules/blox/assets/css/blox/biography.css | 14 +- modules/blox/blox/cta-card/client.jsx | 30 +- modules/blox/blox/cta-card/component.jsx | 212 ++++----- .../blox/blox/cta-image-paragraph/client.jsx | 30 +- .../blox/cta-image-paragraph/component.jsx | 222 +++++---- modules/blox/blox/features/client.jsx | 30 +- modules/blox/blox/features/component.jsx | 111 ++--- modules/blox/blox/hero/component.jsx | 14 +- .../blox/blox/shared/js/components/Icon.jsx | 12 +- modules/blox/blox/stats/client.jsx | 36 +- modules/blox/blox/stats/component.jsx | 436 +++++++++--------- modules/blox/blox/testimonials/client.jsx | 30 +- modules/blox/blox/testimonials/component.jsx | 149 +++--- package.json | 11 +- .../.devcontainer/devcontainer.json | 14 +- templates/academic-cv/.vscode/extensions.json | 6 +- .../.devcontainer/devcontainer.json | 14 +- .../data-science-blog/.vscode/extensions.json | 6 +- .../.devcontainer/devcontainer.json | 14 +- .../.devcontainer/devcontainer.json | 14 +- .../documentation/.vscode/extensions.json | 6 +- .../.devcontainer/devcontainer.json | 14 +- templates/link-in-bio/.vscode/extensions.json | 6 +- .../.devcontainer/devcontainer.json | 14 +- .../resume/.devcontainer/devcontainer.json | 14 +- templates/resume/.vscode/extensions.json | 6 +- .../.devcontainer/devcontainer.json | 14 +- .../.vscode/extensions.json | 6 +- 29 files changed, 683 insertions(+), 813 deletions(-) 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 ? ( - {item.title - ) : ( - {item.title - ) - )} + return ( +
+ {/* Image */} + {hasImage && + (imageData.srcset ? ( + {item.title + ) : ( + {item.title + ))} - {/* 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(/^( { }; // eslint-disable-next-line lint/security/noDangerouslySetInnerHtml - return ; + return ; } const finalAttributes = { @@ -62,5 +60,5 @@ export const Icon = ({ svg, attributes }) => { .join(" "); // eslint-disable-next-line lint/security/noDangerouslySetInnerHtml - return ${decoded}` }} />; + 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.name} -
- )} - {item.role && ( -
- {item.role} -
- )} -
-
+function TestimonialItem({item, avatarUrl, isLast}) { + return ( +
+ +
+

+

+
+ {/* Avatar */} + {avatarUrl ? ( + {item.name + ) : ( +
+ )} +
+ {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 && ( -
-
-

- {text && ( -

- )} -

-
- )} - - {/* Testimonial Items */} - {items.map((item, idx) => ( - - ))} + return ( +
+
+ {/* Section Header */} + {title && ( +
+
+

+ {text &&

}

-
- ); +
+ )} + + {/* 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"] +}