Compare commits

...

10 Commits

Author SHA1 Message Date
kbe
8ac4c6b675 feat: Better ez-toc 2025-08-19 12:14:29 +02:00
kbe
e662c7cda4 feat: Add post-content styles and improve breadcrumb and link styles 2025-08-19 12:05:46 +02:00
kbe
78c41cc071 All pages are displayed but only published must 2025-08-19 11:41:54 +02:00
kbe
140552a35f wip page links and display 2025-08-19 11:34:40 +02:00
kbe
dd3a0240f9 Prepare footer 2025-08-19 10:31:09 +02:00
kbe
1ea47eb2da Improve SEO content 2025-08-19 10:20:34 +02:00
kbe
686ece4479 feat: Add a breadcrumb 2025-08-19 09:54:49 +02:00
kbe
90340d03aa Improve css style 2025-08-19 09:21:27 +02:00
kbe
63869c3090 Compile sass for theme 2025-08-19 09:05:50 +02:00
kbe
17fb355560 Compile sass for theme 2025-08-19 09:01:27 +02:00
66 changed files with 3367 additions and 2302 deletions

View File

@@ -20,6 +20,8 @@ h1,h2,h3,h4,h5,h6 {
color: get-color("dark");
font-family: $font-family-poppins;
font-weight: $font-weight-semi-bold;
margin-top: 1rem;
margin-bottom: 1rem;
}
.theme-font-nunito {
h1,h2,h3,h4,h5,h6 {
@@ -30,6 +32,7 @@ h1,h2,h3,h4,h5,h6 {
p {
margin: 0;
line-height: 1.74; /* 26px */
color: #000;
@include breakpoint-less(md) {
line-height: 1.6; /* 24px */
}
@@ -106,3 +109,4 @@ i {
.icon-sm {
i { font-size: 0.9em; }
}

View File

@@ -0,0 +1,155 @@
// Breadcrumb Component
.breadcrumb-wrapper {
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
border-bottom: 1px solid #dee2e6;
padding: 1rem 0;
@media (max-width: 768px) {
padding: 0.75rem 0;
}
.breadcrumb {
display: flex;
flex-wrap: wrap;
align-items: center;
margin: 0;
padding: 0;
list-style: none;
font-size: 0.875rem;
line-height: 1.4;
@media (max-width: 576px) {
font-size: 0.8125rem;
}
.breadcrumb-item {
display: flex;
align-items: center;
&:not(:last-child) {
margin-right: 0.5rem;
// &::after {
// content: "/";
// display: inline-block;
// color: #6c757d;
// margin-left: 0.75rem;
// opacity: 0.7;
// font-weight: 500;
// @media (max-width: 576px) {
// margin-left: 0.5rem;
// }
// }
}
a {
color: #495057;
text-decoration: none;
font-weight: 500;
transition: all 0.2s ease-in-out;
padding: 0.25rem 0.5rem;
border-radius: 0.25rem;
&:hover {
color: #007bff;
background-color: rgba(0, 123, 255, 0.1);
transform: translateY(-1px);
}
&:focus {
outline: 2px solid #007bff;
outline-offset: 1px;
}
i.fas {
margin-right: 0.375rem;
font-size: 0.75rem;
@media (max-width: 576px) {
margin-right: 0.25rem;
font-size: 0.6875rem;
}
}
}
&.active {
color: #6c757d;
font-weight: 600;
padding: 0.25rem 0.5rem;
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@media (max-width: 768px) {
max-width: 150px;
}
@media (max-width: 576px) {
max-width: 120px;
}
}
}
// Responsive truncation for long breadcrumbs
@media (max-width: 576px) {
.breadcrumb-item {
&:not(:last-child):not(:nth-last-child(2)) {
display: none;
& + .breadcrumb-item::before {
content: "...";
margin: 0 0.5rem;
color: #6c757d;
font-weight: bold;
}
}
}
}
}
// Dark mode support
@media (prefers-color-scheme: dark) {
background: linear-gradient(135deg, #343a40 0%, #495057 100%);
border-bottom-color: #6c757d;
.breadcrumb {
.breadcrumb-item {
&:not(:last-child):not(:only-child)::after {
color: #adb5bd;
}
a {
color: #dee2e6;
&:hover {
color: #66b2ff;
background-color: rgba(102, 178, 255, 0.1);
}
&:focus {
outline-color: #66b2ff;
}
}
&.active {
color: #adb5bd;
}
}
}
}
}
// Accessibility improvements
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}

View File

@@ -0,0 +1,144 @@
/* Styles for ez-toc-container */
#ez-toc-container {
background-color: transparent;
border: none;
border-radius: 0;
padding: 16px 0;
box-shadow: none;
max-width: 100%;
margin: 16px 0;
width: 100%;
.ez-toc-title {
font-size: 1.25rem;
font-weight: 600;
margin-bottom: 10px;
border-bottom: 2px solid #ddd;
padding-bottom: 5px;
}
.ez-toc-cssicon-toggle-label {
display: flex;
align-items: center;
cursor: pointer;
margin-bottom: 10px;
.ez-toc-icon-toggle-span {
margin-left: 8px;
}
}
.ez-toc-list {
list-style-type: none;
padding-left: 0;
/* Base styles for all list items */
.ez-toc-link {
display: block;
padding: 8px 0;
color: #333;
text-decoration: none;
border-bottom: 1px solid #f0f0f0;
&:hover {
background-color: #f9f9f9;
border-radius: 4px;
}
}
/* Hierarchy-based alignment */
.ez-toc-list-level-1 {
padding-left: 0;
.ez-toc-link {
font-size: 1em;
font-weight: 600;
}
}
.ez-toc-list-level-2 {
padding-left: 20px;
.ez-toc-link {
font-size: 0.95em;
font-weight: 500;
padding-left: 10px;
}
}
.ez-toc-list-level-3 {
padding-left: 40px;
.ez-toc-link {
font-size: 0.9em;
font-weight: 400;
padding-left: 10px;
}
}
.ez-toc-list-level-4 {
padding-left: 60px;
.ez-toc-link {
font-size: 0.85em;
font-weight: 400;
padding-left: 10px;
}
}
/* Add more levels if needed */
}
/* Responsive styles */
@media (max-width: 768px) {
padding: 12px 0;
.ez-toc-title {
font-size: 1.125rem;
}
.ez-toc-link {
padding: 6px 0;
font-size: 0.9em;
}
/* Adjust hierarchy padding for tablets */
.ez-toc-list-level-2 {
padding-left: 15px;
}
.ez-toc-list-level-3 {
padding-left: 30px;
}
.ez-toc-list-level-4 {
padding-left: 45px;
}
}
@media (max-width: 480px) {
padding: 10px 0;
.ez-toc-title {
font-size: 1rem;
}
.ez-toc-link {
padding: 4px 0;
font-size: 0.85em;
}
/* Adjust hierarchy padding for mobile */
.ez-toc-list-level-2 {
padding-left: 10px;
}
.ez-toc-list-level-3 {
padding-left: 20px;
}
.ez-toc-list-level-4 {
padding-left: 30px;
}
}
}

View File

@@ -0,0 +1,27 @@
/* Styles for tables */
figure.wp-block-table table {
border-collapse: collapse;
width: 100%;
margin-top: 1.25rem;
margin-bottom: 1.25rem;
thead {
background-color: #f8f9fa;
color: #000;
}
th, td {
border: 1px solid #dee2e6;
padding: 0.5rem;
text-align: left;
}
th {
background-color: #f1f3f5;
font-weight: bold;
}
tbody tr:nth-child(odd) {
background-color: #f9f9f9;
}
}

View File

@@ -1,15 +1,15 @@
//
// Button styles //
//
a {
color: get-color("dark", 0.9);
text-decoration: none;
@include transition(linear 0.1s);
&:hover {
color: get-color("dark");
text-decoration: none;
}
}
// a {
// color: get-color("dark", 0.9);
// text-decoration: none;
// @include transition(linear 0.1s);
// &:hover {
// color: get-color("dark");
// text-decoration: none;
// }
// }
button {
background: transparent;
box-shadow: none;

View File

@@ -0,0 +1,5 @@
.post-content {
p {
line-height: 2rem;
}
}

View File

@@ -1,6 +1,31 @@
//
// Text Link Styles //
//
a {
// color: #007bff;
// color: get-color("dark", 0.8);
color: #000;
text-decoration: none;
&:hover {
color: #0056b3;
text-decoration: none;
}
}
.post-content {
a {
// color: #007bff;
color: get-color("dark", 0.8);
text-decoration: underline;
&:hover {
color: #0056b3;
text-decoration: none;
}
}
}
*[class*='text-link-'] {
color: get-color("dark", 0.9);
&:hover, &:focus {

View File

@@ -40,6 +40,7 @@
@import "elements/testimonial";
@import "elements/text-link";
@import "elements/timeline";
@import "elements/post-content";
//
// Import Components //
@@ -49,10 +50,14 @@
@import "components/fullscreen-menu";
@import "components/gallery";
@import "components/header";
@import "components/breadcrumb";
@import "components/masonry";
@import "components/portfolio";
@import "components/table";
@import "components/preloader";
@import "components/section";
@import "components/_ez-toc";
//
// Import Utilities //
@@ -63,3 +68,34 @@
// Import Custom Styles //
//
@import "helpers/custom";
ul.wp-block-list {
margin-top: 1rem;
margin-bottom: 1rem;
}
figure.wp-block-image {
margin-top: 2rem;
margin-bottom: 2rem;
}
//
// Figcaption Styles //
//
figcaption, .wp-element-caption {
font-size: 0.9rem;
color: #aeaeae;
margin-top: 0.5em;
text-align: center;
// display: flex;
// align-items: center;
// justify-content: center;
}
figure.wp-block-image {
img {
margin: auto;
display: block;
}
}

BIN
assets/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

BIN
assets/images/og-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

BIN
assets/images/og-logo.xcf Normal file

Binary file not shown.

114
docs/SEO-IMPROVEMENTS.md Normal file
View File

@@ -0,0 +1,114 @@
# SEO Improvements Documentation
## Overview
This project has been enhanced with comprehensive SEO capabilities using a modular partial system in Hugo.
## File Structure
```
layouts/partials/seo/
├── seo-config.html # Main SEO configuration loader
├── seo-meta.html # Core SEO meta tags
├── opengraph.html # Open Graph tags for social media
├── twitter-cards.html # Twitter Card tags
├── structured-data.html # JSON-LD schema markup
└── favicons.html # Favicon variations and PWA support
```
## Features Added
### 1. Core SEO Meta Tags
- Dynamic meta description
- Keywords (with fallback)
- Author information
- Canonical URLs
- Robots meta tags
- Dublin Core metadata
- Geo tags (if configured)
### 2. Open Graph Tags
- og:title, og:description, og:image
- og:type (article/website)
- og:site_name
- og:url
- Article-specific tags for blog posts
### 3. Twitter Cards
- twitter:card (summary_large_image)
- twitter:title, twitter:description
- twitter:image
- Site and creator handles
### 4. Structured Data (JSON-LD)
- WebSite schema
- Article schema for blog posts
- BreadcrumbList schema
- Organization schema
### 5. Favicon & PWA Support
- Multiple favicon sizes
- Apple Touch Icons
- Android icons
- PWA manifest.json
- Theme colors
## Configuration
### Hugo Configuration (hugo.toml)
```toml
[params.seo]
description = "Your site description"
keywords = ["keyword1", "keyword2"]
author = "Your Name"
theme_color = "#007bff"
default_image = "/images/og-default.jpg"
logo = "/images/logo.png"
[params.seo.twitter]
site = "@yourhandle"
creator = "@yourhandle"
```
### Content Frontmatter
Add to your content's frontmatter:
```yaml
---
title: "Your Post Title"
description: "Detailed description for SEO"
keywords: ["seo", "hugo", "optimization"]
author: "Author Name"
image: "/images/post-image.jpg"
robots: "index, follow"
---
```
## Testing & Validation
### Recommended Tools
- Google Rich Results Test: https://search.google.com/test/rich-results
- Facebook Sharing Debugger: https://developers.facebook.com/tools/debug/
- Twitter Card Validator: https://cards-dev.twitter.com/validator
- Schema.org Validator: https://validator.schema.org/
### Validation Checklist
- [ ] Meta tags present in page source
- [ ] Open Graph tags validate
- [ ] Twitter Cards validate
- [ ] JSON-LD schema validates
- [ ] Favicons load correctly
- [ ] Canonical URLs are correct
## Next Steps
1. Generate favicon files for all sizes (use a favicon generator)
2. Create og-default.jpg and twitter-default.jpg images
3. Set up Google Search Console and add verification code
4. Set up Bing Webmaster Tools
5. Test with social media sharing
## Fallback Values
The system includes intelligent fallback values:
- Description: Page → Site → Title
- Keywords: Page → Site → Empty string
- Image: Page → Site → Default
- Author: Page → Site → Site Title
All SEO improvements have been successfully implemented!

View File

@@ -13,6 +13,32 @@ ignoreLogs = ["warning-goldmark-raw-html"]
[markup.goldmark.renderer]
unsafe = true
# SEO Configuration
[params.seo]
description = "Mistergeek - Tutoriels et guides en informatique"
keywords = ["développement web", "technologies", "innovation", "solutions digitales", "mistergeek"]
author = "Mistergeek"
theme_color = "#007bff"
default_image = "/assets/images/og-logo.png"
logo = "/assets/images/logo.png"
# Social Media
[params.seo.twitter]
site = "@mistergeekfrance"
creator = "@mistergeekfrance"
# Search Engine Verification
# google_verification = "your-google-verification-code"
# bing_verification = "your-bing-verification-code"
# yandex_verification = "your-yandex-verification-code"
# Geo Location (if applicable)
# [params.seo.geo]
# region = "FR-IDF"
# placename = "Paris"
# latitude = "48.8566"
# longitude = "2.3522"
# WordPress API Configuration
[params.wordpress]
apiUrl = "https://www.mistergeek.net/wp-json/wp/v2"
@@ -31,3 +57,20 @@ ignoreLogs = ["warning-goldmark-raw-html"]
[[build.cachebusters]]
source = "assets/.*\\.(css|sass|scss)$"
target = "css"
# Output formats for search index
[outputs]
home = ["HTML", "RSS", "JSON"]
[outputFormats]
[outputFormats.JSON]
mediaType = "application/json"
baseName = "search-index"
isPlainText = true
notAlternative = true
# Search configuration
[params.search]
enabled = true
minQueryLength = 2
maxResults = 10

View File

@@ -1,13 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<html lang="{{ .Site.Language.Lang | default "en" }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="{{ if .Description }}{{ .Description }}{{ else }}{{ .Site.Title }}{{ end }}">
<meta name="keywords" content="">
<!-- SEO & Social Meta Tags -->
<title>{{ if .IsHome }}{{ .Site.Title }}{{ else }}{{ .Title }} - {{ .Site.Title }}{{ end }}</title>
<!-- Favicon -->
<link href="/assets/images/favicon.png" rel="shortcut icon">
{{ partial "seo/seo-config.html" . }}
<!-- CSS -->
<link href="/assets/plugins/bootstrap/bootstrap.min.css" rel="stylesheet">
<link href="/assets/plugins/owl-carousel/owl.carousel.min.css" rel="stylesheet">
@@ -16,6 +16,7 @@
<link href="/assets/plugins/scrollcue/scrollcue.css" rel="stylesheet">
<link href="/assets/plugins/swiper/swiper-bundle.min.css" rel="stylesheet">
<link href="/assets/css/theme.css" rel="stylesheet">
<link href="/assets/css/theme-colors/theme-color-blue.css" rel="stylesheet">
<!-- Fonts/Icons -->
<link href="/assets/plugins/bootstrap-icons/bootstrap-icons.css" rel="stylesheet">
<link href="/assets/plugins/font-awesome/css/all.css" rel="stylesheet">
@@ -25,34 +26,15 @@
<!-- Header -->
{{ partial "header.html" . }}
<!-- Breadcrumb -->
{{ partial "breadcrumb.html" . }}
<main>
{{ block "main" . }}{{ end }}
</main>
<footer>
<div class="section bg-dark">
<div class="container">
<div class="row g-4 align-items-center">
<div class="col-12 col-md-6 text-center text-md-start">
<ul class="list-inline-dash">
<li><a href="/faq/">FAQ</a></li>
<li><a href="/careers/">Careers</a></li>
<li><a href="/clients/">Clients</a></li>
</ul>
<p class="mt-2">&copy; {{ now.Format "2006" }} {{ .Site.Title }}, All Rights Reserved.</p>
</div>
<div class="col-12 col-md-6 text-center text-md-end">
<ul class="list-inline">
<li><a href="#"><i class="bi bi-facebook"></i></a></li>
<li><a href="#"><i class="bi bi-twitter-x"></i></a></li>
<li><a href="#"><i class="bi bi-pinterest"></i></a></li>
<li><a href="#"><i class="bi bi-instagram"></i></a></li>
</ul>
</div>
</div><!-- end row -->
</div><!-- end container -->
</div>
</footer>
<!-- Footer -->
{{ partial "footer.html" . }}
<!-- Scroll to top button -->
<div class="scrolltotop icon-lg">
@@ -64,5 +46,6 @@
<script src="/assets/plugins/jquery.min.js"></script>
<script src="/assets/plugins/plugins.js"></script>
<script src="/assets/js/functions.js"></script>
{{ partial "scripts.html" . }}
</body>
</html>

View File

@@ -14,8 +14,6 @@
{{ range . }}
<li><a href="/categories/{{ . | urlize }}">{{ . }}</a></li>
{{ end }}
{{ else }}
<li><a href="/categories/non-classe">Non classé</a></li>
{{ end }}
<li><a href="#">{{ .Date.Format "Jan 2, 2006" }}</a></li>
</ul>

View File

@@ -0,0 +1,70 @@
<!-- Breadcrumb Navigation with SEO optimization -->
<div class="breadcrumb-wrapper">
<div class="container">
<div class="row">
<div class="col-12">
<nav aria-label="breadcrumb">
<ol class="breadcrumb" itemscope itemtype="https://schema.org/BreadcrumbList">
<!-- Home/Accueil with SEO -->
<li class="breadcrumb-item" itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
<a href="{{ "/" | relLangURL }}" itemprop="item" title="{{ i18n "home" | default "Accueil" }}">
<span itemprop="name">{{ i18n "home" | default "Accueil" }}</span>
</a>
<meta itemprop="position" content="1" />
</li>
<!-- Category with SEO and linking -->
{{ if or .Params.categories .Section }}
{{ $category := "" }}
{{ $categorySlug := "" }}
{{ $categoryUrl := "" }}
{{ if .Params.categories }}
{{ $category = index .Params.categories 0 }}
{{ $categorySlug = urlize $category }}
{{ $categoryUrl = printf "/categories/%s" $categorySlug }}
{{ else if .Section }}
{{ $category = humanize .Section }}
{{ $categorySlug = .Section }}
{{ $categoryUrl = printf "/%s" .Section }}
{{ end }}
{{ if $category }}
<li class="breadcrumb-item active" itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem" aria-current="page">
<a href="{{ $categoryUrl | relLangURL }}" itemprop="item" title="{{ $category }}">
<span itemprop="name">{{ $category }}</span>
</a>
<meta itemprop="position" content="2" />
</li>
{{ end }}
{{ end }}
</ol>
</nav>
</div>
</div>
</div>
</div>
<!-- JSON-LD Structured Data for SEO -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "{{ i18n "home" | default "Accueil" }}",
"item": "{{ "/" | absLangURL }}"
}{{ if or .Params.categories .Section }},
{
"@type": "ListItem",
"position": 2,
"name": "{{ if .Params.categories }}{{ index .Params.categories 0 }}{{ else }}{{ humanize .Section }}{{ end }}",
"item": "{{ if .Params.categories }}{{ printf "%s/categories/%s" (absLangURL "") (urlize (index .Params.categories 0)) }}{{ else }}{{ printf "%s/%s" (absLangURL "") .Section }}{{ end }}"
}{{ end }}
]
}
</script>

View File

@@ -0,0 +1,66 @@
<footer>
<div class="section-sm bg-dark">
<div class="container">
<div class="row g-4">
<div class="col-6 col-sm-6 col-lg-3">
<h3 class="uppercase letter-spacing-1">{{ .Site.Title }}</h3>
</div>
<div class="col-6 col-sm-6 col-lg-3">
<h6 class="font-small fw-medium uppercase">Pages</h6>
<ul class="list-unstyled">
<li><a href="/">Accueil</a></li>
{{ if .Site.Data.wordpress }}
{{ range $index, $element := .Site.Data.wordpress.navigation }}
<li>
<a href="/{{ $element.slug }}">{{ $element.title }}</a>
</li>
{{ end }}
{{ end }}
</ul>
</div>
<div class="col-6 col-sm-6 col-lg-3">
<h6 class="font-small fw-medium uppercase">Toutes les catégories</h6>
<ul class="list-unstyled">
{{ if .Site.Data.wordpress }}
{{ $count := 0 }}
{{ range $index, $element := .Site.Data.wordpress.categories }}
<li class="nav-item">
<a class="nav-link" href="/categories/{{ $element.slug }}">{{ $element.name }}</a>
</li>
{{ end }}
{{ end }}
</ul>
</div>
<div class="col-6 col-sm-6 col-lg-3">
<h6 class="font-small fw-medium uppercase">Contact Info</h6>
<ul class="list-unstyled">
<li>121 King St, Melbourne VIC 3000</li>
<li>contact@example.com</li>
<li>+(123) 456 789 01</li>
</ul>
</div>
</div><!-- end row -->
</div><!-- end container -->
</div>
<div class="bg-black py-4">
<div class="container">
<div class="row align-items-center g-2 g-lg-3">
<div class="col-12 col-md-6 text-center text-md-start">
<p>&copy; {{ now.Format "2006" }} {{ .Site.Title }}, All Rights Reserved.</p>
</div>
<div class="col-12 col-md-6 text-center text-md-end">
<ul class="list-inline-sm">
<li><a class="button-circle button-circle-sm button-circle-social-facebook" href="#"><i class="bi bi-facebook"></i></a></li>
<li><a class="button-circle button-circle-sm button-circle-social-twitter" href="#"><i class="bi bi-twitter-x"></i></a></li>
<li><a class="button-circle button-circle-sm button-circle-social-pinterest" href="#"><i class="bi bi-pinterest"></i></a></li>
<li><a class="button-circle button-circle-sm button-circle-social-instagram" href="#"><i class="bi bi-instagram"></i></a></li>
</ul>
</div>
</div><!-- end row -->
</div><!-- end container -->
</div>
</footer>

View File

@@ -0,0 +1,46 @@
<!-- Header -->
<div class="header center header-color-dark">
<div class="container">
<!-- Logo -->
<div class="header-logo">
<h3 class="uppercase letter-spacing-1"><a href="/">{{ .Site.Title }}</a></h3>
</div>
<!-- Menu -->
<div class="header-menu">
<ul class="nav">
<li class="nav-item">
<a class="nav-link" href="/">Accueil</a>
</li>
{{ if .Site.Data.wordpress }}
{{ $count := 0 }}
{{ range $index, $element := .Site.Data.wordpress.categories }}
{{ if or (eq $element.name "Featured") (eq $element.name "Non classé") }}
{{ continue }}
{{ else if lt $count 5 }}
<li class="nav-item">
<a class="nav-link" href="/categories/{{ $element.slug }}">{{ $element.name }}</a>
</li>
{{ $count = add $count 1 }}
{{ else }}
{{ break }}
{{ end }}
{{ end }}
{{ end }}
</ul>
</div>
<!-- Menu Extra -->
{{/* <div class="header-menu-extra">
<ul class="list-inline-sm">
<li><a href="#"><i class="bi bi-facebook"></i></a></li>
<li><a href="#"><i class="bi bi-twitter-x"></i></a></li>
<li><a href="#"><i class="bi bi-linkedin"></i></a></li>
</ul>
</div> */}}
<!-- Menu Toggle -->
<button class="header-toggle">
<span></span>
</button>
</div><!-- end container -->
</div>
<!-- end Header -->

View File

@@ -0,0 +1,3 @@
<!-- Search Scripts -->
<script src="https://cdn.jsdelivr.net/npm/lunr@2.3.9/lunr.min.js"></script>
<script src="/assets/js/search.js"></script>

View File

@@ -0,0 +1,32 @@
<!-- Favicon -->
<link rel="icon" type="image/x-icon" href="/assets/images/favicon.ico">
<link rel="shortcut icon" type="image/x-icon" href="/assets/images/favicon.ico">
<!-- Apple Touch Icons -->
<link rel="apple-touch-icon" sizes="57x57" href="/assets/images/apple-touch-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/assets/images/apple-touch-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/assets/images/apple-touch-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="/assets/images/apple-touch-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="/assets/images/apple-touch-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="/assets/images/apple-touch-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/assets/images/apple-touch-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/assets/images/apple-touch-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/assets/images/apple-touch-icon-180x180.png">
<!-- Android Icons -->
<link rel="icon" type="image/png" sizes="192x192" href="/assets/images/android-icon-192x192.png">
<link rel="icon" type="image/png" sizes="32x32" href="/assets/images/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="96x96" href="/assets/images/favicon-96x96.png">
<link rel="icon" type="image/png" sizes="16x16" href="/assets/images/favicon-16x16.png">
<!-- Microsoft Tiles -->
<meta name="msapplication-TileColor" content="{{ .Site.Params.seo.theme_color | default "#007bff" }}">
<meta name="msapplication-TileImage" content="/assets/images/ms-icon-144x144.png">
<meta name="msapplication-config" content="/assets/images/browserconfig.xml">
<!-- PWA Manifest -->
<link rel="manifest" href="/manifest.json">
<!-- Theme Color -->
<meta name="theme-color" content="{{ .Site.Params.seo.theme_color | default "#007bff" }}">
<meta name="msapplication-TileColor" content="{{ .Site.Params.seo.theme_color | default "#007bff" }}">

View File

@@ -0,0 +1,33 @@
{{- $title := .Title | default .Site.Title -}}
{{- $description := .Description | default .Summary | default .Site.Params.description | default .Site.Title -}}
{{- $image := .Params.image | default .Site.Params.seo.default_image | default "/assets/images/og-default.jpg" -}}
{{- $image = $image | absURL -}}
{{- $url := .Permalink | default .RelPermalink | absURL -}}
{{- $siteName := .Site.Title -}}
{{- $locale := .Site.Language.Lang | default "en_US" -}}
<!-- Open Graph / Facebook -->
<meta property="og:type" content="{{ if .IsPage }}article{{ else }}website{{ end }}">
<meta property="og:site_name" content="{{ $siteName }}">
<meta property="og:title" content="{{ $title }}">
<meta property="og:description" content="{{ $description }}">
<meta property="og:url" content="{{ $url }}">
<meta property="og:locale" content="{{ $locale }}">
<meta property="og:image" content="{{ $image }}">
<meta property="og:image:alt" content="{{ $title }}">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<!-- Article specific -->
{{ if .IsPage }}
<meta property="article:section" content="{{ .Section | default "general" }}">
<meta property="article:author" content="{{ .Params.author | default .Site.Params.author | default .Site.Title }}">
{{ range .Params.tags | default .Site.Params.tags }}
<meta property="article:tag" content="{{ . }}">
{{ end }}
{{ end }}
<!-- Additional locales for multilingual sites -->
{{ range .Translations }}
<meta property="og:locale:alternate" content="{{ .Language.Lang | default "en_US" }}">
{{ end }}

View File

@@ -0,0 +1,42 @@
{{- /* SEO Configuration Partial */ -}}
{{- /* This partial includes all SEO-related partials */ -}}
<!-- Core SEO Meta Tags -->
{{ partial "seo/seo-meta.html" . }}
<!-- Open Graph Tags -->
{{ partial "seo/opengraph.html" . }}
<!-- Twitter Cards -->
{{ partial "seo/twitter-cards.html" . }}
<!-- Structured Data (JSON-LD) -->
{{ partial "seo/structured-data.html" . }}
<!-- Favicons and PWA Support -->
{{ partial "seo/favicons.html" . }}
<!-- Additional SEO Tags -->
{{- if .Site.Params.seo.google_verification }}
<meta name="google-site-verification" content="{{ .Site.Params.seo.google_verification }}">
{{ end }}
{{- if .Site.Params.seo.bing_verification }}
<meta name="msvalidate.01" content="{{ .Site.Params.seo.bing_verification }}">
{{ end }}
{{- if .Site.Params.seo.yandex_verification }}
<meta name="yandex-verification" content="{{ .Site.Params.seo.yandex_verification }}">
{{ end }}
{{- if .Site.Params.seo.alexa_verification }}
<meta name="alexaVerifyID" content="{{ .Site.Params.seo.alexa_verification }}">
{{ end }}
<!-- hreflang for multilingual sites -->
{{ range .Translations }}
<link rel="alternate" hreflang="{{ .Language.Lang }}" href="{{ .Permalink }}">
{{ end }}
{{ if .IsTranslated }}
<link rel="alternate" hreflang="x-default" href="{{ .Permalink }}">
{{ end }}

View File

@@ -0,0 +1,40 @@
{{- $description := .Description | default .Summary | default .Site.Params.description | default .Site.Title -}}
{{- $keywords := delimit (.Keywords | default .Site.Params.keywords | default (slice)) ", " -}}
{{- $author := .Params.author | default .Site.Params.author | default .Site.Title -}}
{{- $robots := .Params.robots | default "index, follow" -}}
{{- $canonical := .Permalink | default .RelPermalink -}}
<!-- Primary Meta Tags -->
<meta name="description" content="{{ $description }}">
<meta name="keywords" content="{{ $keywords }}">
<meta name="author" content="{{ $author }}">
<meta name="robots" content="{{ $robots }}">
<meta name="generator" content="Hugo {{ hugo.Version }}">
<!-- Canonical URL -->
<link rel="canonical" href="{{ $canonical }}">
<!-- Theme Color -->
<meta name="theme-color" content="{{ .Site.Params.seo.theme_color | default "#007bff" }}">
<meta name="msapplication-TileColor" content="{{ .Site.Params.seo.theme_color | default "#007bff" }}">
<!-- Additional SEO -->
<meta name="rating" content="general">
<meta name="language" content="{{ .Site.Language.Lang | default "en" }}">
<meta name="revisit-after" content="7 days">
<meta name="distribution" content="global">
<meta name="web_author" content="{{ .Site.Params.author | default .Site.Title }}">
<!-- Geo Tags (if location is specified) -->
{{ with .Site.Params.seo.geo }}
<meta name="geo.region" content="{{ .region }}">
<meta name="geo.placename" content="{{ .placename }}">
<meta name="geo.position" content="{{ .latitude }};{{ .longitude }}">
<meta name="ICBM" content="{{ .latitude }}, {{ .longitude }}">
{{ end }}
<!-- Dublin Core -->
<meta name="DC.Title" content="{{ .Title | default .Site.Title }}">
<meta name="DC.Creator" content="{{ $author }}">
<meta name="DC.Description" content="{{ $description }}">
<meta name="DC.Language" content="{{ .Site.Language.Lang | default "en" }}">

View File

@@ -0,0 +1,85 @@
{{- $title := .Title | default .Site.Title -}}
{{- $description := .Description | default .Summary | default .Site.Params.description | default .Site.Title -}}
{{- $image := .Params.image | default .Site.Params.seo.default_image | default "/assets/images/logo.png" -}}
{{- $image = $image | absURL -}}
{{- $url := .Permalink | default .RelPermalink | absURL -}}
{{- $siteName := .Site.Title -}}
{{- $logo := .Site.Params.seo.logo | default "/assets/images/logo.png" | absURL -}}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebSite",
"name": "{{ $siteName }}",
"description": "{{ $description }}",
"url": "{{ .Site.BaseURL }}",
"logo": {
"@type": "ImageObject",
"url": "{{ $logo }}"
}
}
</script>
{{ if .IsPage }}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "{{ $title }}",
"description": "{{ $description }}",
"image": "{{ $image }}",
"url": "{{ $url }}",
"author": {
"@type": "Person",
"name": "{{ .Params.author | default .Site.Params.author | default .Site.Title }}"
},
"publisher": {
"@type": "Organization",
"name": "{{ $siteName }}",
"logo": {
"@type": "ImageObject",
"url": "{{ $logo }}"
}
}
}
</script>
{{ end }}
<!-- Breadcrumb Schema -->
{{ if and .IsPage .Section }}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Accueil",
"item": "{{ .Site.BaseURL }}"
}
{{ if .Section }}
,{
"@type": "ListItem",
"position": 2,
"name": "{{ .Section | humanize }}",
"item": "{{ .Site.BaseURL }}{{ .Section }}/"
}
,{
"@type": "ListItem",
"position": 3,
"name": "{{ $title }}",
"item": "{{ $url }}"
}
{{ else }}
,{
"@type": "ListItem",
"position": 2,
"name": "{{ $title }}",
"item": "{{ $url }}"
}
{{ end }}
]
}
</script>
{{ end }}

View File

@@ -0,0 +1,27 @@
{{- $title := .Title | default .Site.Title -}}
{{- $description := .Description | default .Summary | default .Site.Params.description | default .Site.Title -}}
{{- $image := .Params.image | default .Site.Params.seo.default_image | default "/assets/images/twitter-default.jpg" -}}
{{- $image = $image | absURL -}}
{{- $siteName := .Site.Title -}}
{{- $twitter := .Site.Params.seo.twitter -}}
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="{{ $title }}">
<meta name="twitter:description" content="{{ $description }}">
<meta name="twitter:image" content="{{ $image }}">
<meta name="twitter:image:alt" content="{{ $title }}">
<!-- Twitter Site -->
{{ with $twitter.site }}
<meta name="twitter:site" content="{{ . }}">
{{ end }}
<!-- Twitter Creator -->
{{ with $twitter.creator }}
<meta name="twitter:creator" content="{{ . }}">
{{ else }}
{{ with .Params.author }}
<meta name="twitter:creator" content="{{ . }}">
{{ end }}
{{ end }}

510
package-lock.json generated
View File

@@ -10,6 +10,349 @@
"dependencies": {
"he": "^1.2.0",
"node-fetch": "^3.3.2"
},
"devDependencies": {
"sass": "^1.90.0"
}
},
"node_modules/@parcel/watcher": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz",
"integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"dependencies": {
"detect-libc": "^1.0.3",
"is-glob": "^4.0.3",
"micromatch": "^4.0.5",
"node-addon-api": "^7.0.0"
},
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
"@parcel/watcher-android-arm64": "2.5.1",
"@parcel/watcher-darwin-arm64": "2.5.1",
"@parcel/watcher-darwin-x64": "2.5.1",
"@parcel/watcher-freebsd-x64": "2.5.1",
"@parcel/watcher-linux-arm-glibc": "2.5.1",
"@parcel/watcher-linux-arm-musl": "2.5.1",
"@parcel/watcher-linux-arm64-glibc": "2.5.1",
"@parcel/watcher-linux-arm64-musl": "2.5.1",
"@parcel/watcher-linux-x64-glibc": "2.5.1",
"@parcel/watcher-linux-x64-musl": "2.5.1",
"@parcel/watcher-win32-arm64": "2.5.1",
"@parcel/watcher-win32-ia32": "2.5.1",
"@parcel/watcher-win32-x64": "2.5.1"
}
},
"node_modules/@parcel/watcher-android-arm64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz",
"integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-darwin-arm64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz",
"integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-darwin-x64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz",
"integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-freebsd-x64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz",
"integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm-glibc": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz",
"integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm-musl": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz",
"integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm64-glibc": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz",
"integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm64-musl": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz",
"integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-x64-glibc": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz",
"integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-x64-musl": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz",
"integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-arm64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz",
"integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-ia32": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz",
"integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-x64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz",
"integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/braces": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"fill-range": "^7.1.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/chokidar": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"dev": true,
"license": "MIT",
"dependencies": {
"readdirp": "^4.0.1"
},
"engines": {
"node": ">= 14.16.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/data-uri-to-buffer": {
@@ -21,6 +364,20 @@
"node": ">= 12"
}
},
"node_modules/detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
"dev": true,
"license": "Apache-2.0",
"optional": true,
"bin": {
"detect-libc": "bin/detect-libc.js"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/fetch-blob": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
@@ -44,6 +401,20 @@
"node": "^12.20 || >= 14.13"
}
},
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"to-regex-range": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/formdata-polyfill": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
@@ -65,6 +436,72 @@
"he": "bin/he"
}
},
"node_modules/immutable": {
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz",
"integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==",
"dev": true,
"license": "MIT"
},
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"is-extglob": "^2.1.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/micromatch": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"braces": "^3.0.3",
"picomatch": "^2.3.1"
},
"engines": {
"node": ">=8.6"
}
},
"node_modules/node-addon-api": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
"dev": true,
"license": "MIT",
"optional": true
},
"node_modules/node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
@@ -103,6 +540,79 @@
"url": "https://opencollective.com/node-fetch"
}
},
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/readdirp": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
"integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 14.18.0"
},
"funding": {
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/sass": {
"version": "1.90.0",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.90.0.tgz",
"integrity": "sha512-9GUyuksjw70uNpb1MTYWsH9MQHOHY6kwfnkafC24+7aOMZn9+rVMBxRbLvw756mrBFbIsFg6Xw9IkR2Fnn3k+Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"chokidar": "^4.0.0",
"immutable": "^5.0.2",
"source-map-js": ">=0.6.2 <2.0.0"
},
"bin": {
"sass": "sass.js"
},
"engines": {
"node": ">=14.0.0"
},
"optionalDependencies": {
"@parcel/watcher": "^2.4.1"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"is-number": "^7.0.0"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/web-streams-polyfill": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",

View File

@@ -6,12 +6,16 @@
"fetch-data": "node scripts/fetch-wordpress.js",
"generate-content": "node scripts/generate-content.js",
"prebuild": "npm run fetch-data && npm run generate-content",
"build": "hugo --minify",
"dev": "npm run fetch-data && npm run generate-content && hugo server -D",
"build": "npm run build:css && hugo --minify",
"build:css": "sass assets/css/scss:static/assets/css",
"dev": "npm run fetch-data && npm run generate-content && npm run build:css && hugo server -D",
"clean": "rm -rf data/wordpress content/posts public"
},
"dependencies": {
"he": "^1.2.0",
"node-fetch": "^3.3.2"
},
"devDependencies": {
"sass": "^1.69.5"
}
}

View File

@@ -4,32 +4,32 @@ const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch
const WORDPRESS_API = 'https://www.mistergeek.net/wp-json/wp/v2';
const OUTPUT_DIR = path.join(__dirname, '..', 'data', 'wordpress');
const HUGO_DATA_DIR = path.join(__dirname, '..', 'data');
async function fetchPosts(page = 1, perPage = 100) {
const response = await fetch(`${WORDPRESS_API}/posts?page=${page}&per_page=${perPage}&_embed`);
const posts = await response.json();
async function fetchAll(endpoint, perPage = 100) {
let page = 1;
let items = [];
if (response.headers.get('x-wp-totalpages') > page) {
const nextPosts = await fetchPosts(page + 1, perPage);
return [...posts, ...nextPosts];
while (true) {
const url = `${WORDPRESS_API}/${endpoint}?page=${page}&per_page=${perPage}&_embed`;
const response = await fetch(url);
// If endpoint does not support paging or returns empty, break
if (!response.ok) {
// 400 often means "page out of range" for WP; stop paging
if (response.status === 400) break;
throw new Error(`Failed to fetch ${endpoint} (page ${page}): ${response.status} ${response.statusText}`);
}
return posts;
}
const batch = await response.json();
items = items.concat(batch);
async function fetchCategories() {
const response = await fetch(`${WORDPRESS_API}/categories?per_page=100`);
return response.json();
}
const totalPages = parseInt(response.headers.get('x-wp-totalpages') || '1', 10);
if (page >= totalPages) break;
page++;
}
async function fetchTags() {
const response = await fetch(`${WORDPRESS_API}/tags?per_page=100`);
return response.json();
}
async function fetchAuthors() {
const response = await fetch(`${WORDPRESS_API}/users?per_page=100`);
return response.json();
return items;
}
async function generateData() {
@@ -40,20 +40,44 @@ async function generateData() {
console.log('Fetching WordPress data...');
const [posts, categories, tags, authors] = await Promise.all([
fetchPosts(),
fetchCategories(),
fetchTags(),
fetchAuthors()
// Fetch all relevant endpoints concurrently (pages + posts + taxonomies + users)
const [
posts,
pages,
categories,
tags,
authors
] = await Promise.all([
fetchAll('posts'),
fetchAll('pages'),
fetchAll('categories'),
fetchAll('tags'),
fetchAll('users')
]);
// Filter pages to only include published pages
const publishedPages = pages.filter(page => page.status === 'publish');
// Create navigation data from published pages
const navigationData = publishedPages.map(page => ({
id: page.id,
title: page.title?.rendered || page.slug,
slug: page.slug,
link: page.link,
date: page.date,
modified: page.modified
}));
// Save data as JSON files
fs.writeFileSync(path.join(OUTPUT_DIR, 'posts.json'), JSON.stringify(posts, null, 2));
fs.writeFileSync(path.join(OUTPUT_DIR, 'pages.json'), JSON.stringify(publishedPages, null, 2));
fs.writeFileSync(path.join(OUTPUT_DIR, 'categories.json'), JSON.stringify(categories, null, 2));
fs.writeFileSync(path.join(OUTPUT_DIR, 'tags.json'), JSON.stringify(tags, null, 2));
fs.writeFileSync(path.join(OUTPUT_DIR, 'authors.json'), JSON.stringify(authors, null, 2));
fs.writeFileSync(path.join(OUTPUT_DIR, 'navigation.json'), JSON.stringify(navigationData, null, 2));
console.log(`✅ Fetched ${posts.length} posts, ${categories.length} categories, ${tags.length} tags, ${authors.length} authors`);
console.log(`✅ Fetched ${posts.length} posts, ${publishedPages.length} pages, ${categories.length} categories, ${tags.length} tags, ${authors.length} authors`);
console.log(`✅ Generated navigation data with ${navigationData.length} items`);
}
generateData().catch(console.error);

View File

@@ -4,16 +4,23 @@ const he = require('he');
const DATA_DIR = path.join(__dirname, '..', 'data', 'wordpress');
const CONTENT_DIR = path.join(__dirname, '..', 'content');
const PAGES_DIR = path.join(CONTENT_DIR, 'pages');
function generateContent() {
const posts = JSON.parse(fs.readFileSync(path.join(DATA_DIR, 'posts.json'), 'utf8'));
const pages = JSON.parse(fs.readFileSync(path.join(DATA_DIR, 'pages.json'), 'utf8'));
// Ensure content directory exists
// Ensure content directories exist
if (!fs.existsSync(CONTENT_DIR)) {
fs.mkdirSync(CONTENT_DIR, { recursive: true });
}
posts.forEach(post => {
if (!fs.existsSync(PAGES_DIR)) {
fs.mkdirSync(PAGES_DIR, { recursive: true });
}
// Process posts - only include published posts
posts.filter(post => post.status === 'publish').forEach(post => {
const slug = post.slug;
const date = new Date(post.date);
const year = date.getFullYear();
@@ -33,7 +40,7 @@ function generateContent() {
const frontmatter = {
title: he.decode(post.title.rendered),
date: post.date,
draft: post.status !== 'publish',
draft: false,
slug: slug,
wordpress_id: post.id,
excerpt: he.decode(post.excerpt.rendered.replace(/<[^>]*>/g, '')),
@@ -47,21 +54,27 @@ function generateContent() {
// Decode HTML entities in the content and clean up HTML tags
let contentHtml = he.decode(post.content.rendered);
// Convert absolute URLs in a href to relative URLs
// Convert absolute URLs in a href to relative URLs (only for wp.mistergeek.net)
contentHtml = contentHtml.replace(/<a\s+[^>]*href="([^"]+)"[^>]*>/g, (match, href) => {
// Check if the href is an absolute URL (starts with http:// or https://)
// Check if the href is an absolute URL containing wp.mistergeek.net
if (href.startsWith('http://') || href.startsWith('https://')) {
// Extract the path part of the URL
try {
const url = new URL(href);
// Return the modified a tag with relative URL
if (url.hostname === 'wp.mistergeek.net' || url.hostname === 'www.wp.mistergeek.net') {
// Only convert wp.mistergeek.net URLs to relative paths
return match.replace(href, url.pathname);
}
} catch (error) {
// If URL parsing fails, return the original href
console.warn('Failed to parse URL:', href, error);
}
}
return match;
});
contentHtml = contentHtml
.replace(/<p>\s*<\/p>/g, '') // Remove empty paragraphs
.replace(/<\/p>\s*<p>/g, '\n\n') // Replace paragraph breaks with newlines
.replace(/<\/p>\s*<p>/g, '\n\n'); // Replace paragraph breaks with newlines
const content = `---
${Object.entries(frontmatter)
@@ -73,7 +86,67 @@ ${contentHtml.trim()}`;
fs.writeFileSync(path.join(contentDir, 'index.md'), content);
});
console.log(`✅ Generated ${posts.length} content files`);
// Process pages - only include published pages
pages.filter(page => page.status === 'publish').forEach(page => {
const slug = page.slug;
const contentDir = path.join(PAGES_DIR, slug);
if (!fs.existsSync(contentDir)) {
fs.mkdirSync(contentDir, { recursive: true });
}
const frontmatter = {
title: he.decode(page.title.rendered),
slug: slug,
type: "pages",
layout: "single",
wordpress_id: page.id,
date: page.date,
modified: page.modified,
draft: false,
aliases: [`/${slug}/`]
};
// Decode HTML entities in the content and clean up HTML tags
let contentHtml = he.decode(page.content.rendered);
// Convert absolute URLs in a href to relative URLs (only for wp.mistergeek.net)
contentHtml = contentHtml.replace(/<a\s+[^>]*href="([^"]+)"[^>]*>/g, (match, href) => {
// Check if the href is an absolute URL containing wp.mistergeek.net
if (href.startsWith('http://') || href.startsWith('https://')) {
try {
const url = new URL(href);
if (url.hostname === 'wp.mistergeek.net' || url.hostname === 'www.wp.mistergeek.net') {
// Only convert wp.mistergeek.net URLs to relative paths
return match.replace(href, url.pathname);
}
} catch (error) {
// If URL parsing fails, return the original href
console.warn('Failed to parse URL:', href, error);
}
}
return match;
});
contentHtml = contentHtml
.replace(/<p>\s*<\/p>/g, '') // Remove empty paragraphs
.replace(/<\/p>\s*<p>/g, '\n\n'); // Replace paragraph breaks with newlines
const content = `---
${Object.entries(frontmatter)
.map(([key, value]) => `${key}: ${JSON.stringify(value)}`)
.join('\n')}
---
${contentHtml.trim()}`;
fs.writeFileSync(path.join(contentDir, 'index.md'), content);
});
const publishedPosts = posts.filter(post => post.status === 'publish');
const publishedPages = pages.filter(page => page.status === 'publish');
console.log(`✅ Generated ${publishedPosts.length} content files`);
console.log(`✅ Generated ${publishedPages.length} page files`);
}
generateContent();

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

39
test-table.html Normal file
View File

@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Table Test</title>
<link rel="stylesheet" href="assets/css/scss/theme.scss">
</head>
<body>
<figure class="wp-block-table">
<table class="has-fixed-layout">
<thead>
<tr>
<th>TECHNOLOGIE</th>
<th>DÉBIT MAXIMUM THÉORIQUE (Mégabit/s)</th>
<th>REMARQUE(S)</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>FTTH (Fibre jusquà labonné)</strong></td>
<td>Jusquà 10 000 Mégabit/s</td>
<td>Débit symétrique, très haute performance.</td>
</tr>
<tr>
<td><strong>FTTLa (Fibre jusquau dernier amplificateur)</strong></td>
<td>Jusquà 1000 Mégabit/s</td>
<td>Débit variable selon la distance au point de raccordement.</td>
</tr>
<tr>
<td><strong>Boucle Locale Radio</strong></td>
<td>Jusquà 100 Mégabit/s</td>
<td>Dépend de la qualité du signal et de lenvironnement.</td>
</tr>
</tbody>
</table>
</figure>
</body>
</html>

183
yarn.lock
View File

@@ -2,11 +2,119 @@
# yarn lockfile v1
"@parcel/watcher-android-arm64@2.5.1":
version "2.5.1"
resolved "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz"
integrity sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==
"@parcel/watcher-darwin-arm64@2.5.1":
version "2.5.1"
resolved "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz"
integrity sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==
"@parcel/watcher-darwin-x64@2.5.1":
version "2.5.1"
resolved "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz"
integrity sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==
"@parcel/watcher-freebsd-x64@2.5.1":
version "2.5.1"
resolved "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz"
integrity sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==
"@parcel/watcher-linux-arm-glibc@2.5.1":
version "2.5.1"
resolved "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz"
integrity sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==
"@parcel/watcher-linux-arm-musl@2.5.1":
version "2.5.1"
resolved "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz"
integrity sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==
"@parcel/watcher-linux-arm64-glibc@2.5.1":
version "2.5.1"
resolved "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz"
integrity sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==
"@parcel/watcher-linux-arm64-musl@2.5.1":
version "2.5.1"
resolved "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz"
integrity sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==
"@parcel/watcher-linux-x64-glibc@2.5.1":
version "2.5.1"
resolved "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz"
integrity sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==
"@parcel/watcher-linux-x64-musl@2.5.1":
version "2.5.1"
resolved "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz"
integrity sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==
"@parcel/watcher-win32-arm64@2.5.1":
version "2.5.1"
resolved "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz"
integrity sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==
"@parcel/watcher-win32-ia32@2.5.1":
version "2.5.1"
resolved "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz"
integrity sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==
"@parcel/watcher-win32-x64@2.5.1":
version "2.5.1"
resolved "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz"
integrity sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==
"@parcel/watcher@^2.4.1":
version "2.5.1"
resolved "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz"
integrity sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==
dependencies:
detect-libc "^1.0.3"
is-glob "^4.0.3"
micromatch "^4.0.5"
node-addon-api "^7.0.0"
optionalDependencies:
"@parcel/watcher-android-arm64" "2.5.1"
"@parcel/watcher-darwin-arm64" "2.5.1"
"@parcel/watcher-darwin-x64" "2.5.1"
"@parcel/watcher-freebsd-x64" "2.5.1"
"@parcel/watcher-linux-arm-glibc" "2.5.1"
"@parcel/watcher-linux-arm-musl" "2.5.1"
"@parcel/watcher-linux-arm64-glibc" "2.5.1"
"@parcel/watcher-linux-arm64-musl" "2.5.1"
"@parcel/watcher-linux-x64-glibc" "2.5.1"
"@parcel/watcher-linux-x64-musl" "2.5.1"
"@parcel/watcher-win32-arm64" "2.5.1"
"@parcel/watcher-win32-ia32" "2.5.1"
"@parcel/watcher-win32-x64" "2.5.1"
braces@^3.0.3:
version "3.0.3"
resolved "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz"
integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
dependencies:
fill-range "^7.1.1"
chokidar@^4.0.0:
version "4.0.3"
resolved "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz"
integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==
dependencies:
readdirp "^4.0.1"
data-uri-to-buffer@^4.0.0:
version "4.0.1"
resolved "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz"
integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==
detect-libc@^1.0.3:
version "1.0.3"
resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz"
integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==
fetch-blob@^3.1.2, fetch-blob@^3.1.4:
version "3.2.0"
resolved "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz"
@@ -15,6 +123,13 @@ fetch-blob@^3.1.2, fetch-blob@^3.1.4:
node-domexception "^1.0.0"
web-streams-polyfill "^3.0.3"
fill-range@^7.1.1:
version "7.1.1"
resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz"
integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
dependencies:
to-regex-range "^5.0.1"
formdata-polyfill@^4.0.10:
version "4.0.10"
resolved "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz"
@@ -27,6 +142,41 @@ he@^1.2.0:
resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
immutable@^5.0.2:
version "5.1.3"
resolved "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz"
integrity sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==
is-extglob@^2.1.1:
version "2.1.1"
resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz"
integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
is-glob@^4.0.3:
version "4.0.3"
resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz"
integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
dependencies:
is-extglob "^2.1.1"
is-number@^7.0.0:
version "7.0.0"
resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz"
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
micromatch@^4.0.5:
version "4.0.8"
resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz"
integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
dependencies:
braces "^3.0.3"
picomatch "^2.3.1"
node-addon-api@^7.0.0:
version "7.1.1"
resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz"
integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==
node-domexception@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz"
@@ -41,6 +191,39 @@ node-fetch@^3.3.2:
fetch-blob "^3.1.4"
formdata-polyfill "^4.0.10"
picomatch@^2.3.1:
version "2.3.1"
resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
readdirp@^4.0.1:
version "4.1.2"
resolved "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz"
integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==
sass@^1.90.0:
version "1.90.0"
resolved "https://registry.npmjs.org/sass/-/sass-1.90.0.tgz"
integrity sha512-9GUyuksjw70uNpb1MTYWsH9MQHOHY6kwfnkafC24+7aOMZn9+rVMBxRbLvw756mrBFbIsFg6Xw9IkR2Fnn3k+Q==
dependencies:
chokidar "^4.0.0"
immutable "^5.0.2"
source-map-js ">=0.6.2 <2.0.0"
optionalDependencies:
"@parcel/watcher" "^2.4.1"
"source-map-js@>=0.6.2 <2.0.0":
version "1.2.1"
resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz"
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
to-regex-range@^5.0.1:
version "5.0.1"
resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz"
integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
dependencies:
is-number "^7.0.0"
web-streams-polyfill@^3.0.3:
version "3.3.3"
resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz"