42 Commits

Author SHA1 Message Date
27380290e1 chore(release): v0.1.11
All checks were successful
Deployment / wordpress (push) Successful in 5s
Deployment / nuxt (push) Successful in 56s
2026-01-30 14:38:25 -05:00
7e44554767 chore: Update deps
Some checks failed
Deployment / wordpress (push) Successful in 6s
Deployment / nuxt (push) Has been cancelled
2026-01-30 14:38:11 -05:00
9c3dceef84 fix: ENABLE_CLOUDFLARE_IMAGE
All checks were successful
Deployment / wordpress (push) Successful in 5s
Deployment / nuxt (push) Successful in 53s
2026-01-30 14:35:21 -05:00
ba8d8e00a8 chore: Update README.md
All checks were successful
Deployment / wordpress (push) Successful in 6s
Deployment / nuxt (push) Successful in 59s
2026-01-30 14:29:26 -05:00
8f037b5950 fix: wrangler project name
All checks were successful
Deployment / wordpress (push) Successful in 6s
Deployment / nuxt (push) Successful in 57s
2026-01-30 14:13:21 -05:00
291fa6eaa4 fix: Cloudflare image provider only in production 2026-01-30 12:20:30 -05:00
4079e4ecfb minor: configure image formats in nuxt.config.ts 2026-01-30 12:10:30 -05:00
eea020b136 fix: remove unneeded wrangler main / assets 2026-01-30 11:51:03 -05:00
5161bff624 minor: typo in component name 2026-01-30 11:50:37 -05:00
3199835e83 feat: AcfSocial / parseAcfSocial 2026-01-30 11:45:39 -05:00
6dd13ead91 feat: mapSocialIcon 2026-01-30 11:18:10 -05:00
980c5271ac chore(release): v0.1.10 2026-01-30 10:42:03 -05:00
115a5d2d38 feat: AcfLink / AcfLinkButton components 2026-01-30 10:24:06 -05:00
6b17201f60 feat: AcfPhone component 2026-01-30 10:05:49 -05:00
ad3c53c5dd feat: GroupSiteOptions.phoneNumber 2026-01-30 10:03:27 -05:00
fefa98021b refactor: OptionsSite => SiteOptions for clearer naming 2026-01-30 08:58:28 -05:00
1e58b1f1e7 chore(release): v0.1.9 2026-01-30 08:50:31 -05:00
0bafc3a9dd feat: HeroSplit section 2026-01-30 08:49:03 -05:00
8bd544b5c3 feat: AcfMedia component (image + aspect + object-fit) 2026-01-30 08:48:45 -05:00
4918c638ab feat: AcfImage component based on @nuxt/image 2026-01-30 08:48:27 -05:00
bff7bce1f1 feat: Media Focus Point plugin integration 2026-01-30 08:48:00 -05:00
0fbd2bf7ac feat: Initial Media / HeroSplit acf groups 2026-01-30 08:24:33 -05:00
238aa29bd4 feat: Default site-logo.svg 2026-01-30 08:24:14 -05:00
f268bdc3c3 chore: update deps 2026-01-30 08:14:03 -05:00
11641a00d1 minor: Cleaner [...uri].vue error handling 2026-01-29 08:32:41 -05:00
6a5d60e34c chore: Update theme translations 2026-01-28 21:55:55 -05:00
80e6555c88 chore: update seo-by-rank-math 2026-01-28 21:53:48 -05:00
ff866e2078 chore: update nuxt-graphql 0.6.7 2026-01-28 21:51:45 -05:00
5e39b53a44 feat: enhance refreshAuthToken to prevent duplicate requests 2026-01-28 21:50:58 -05:00
2d93d44a93 chore(release): v0.1.8
Some checks failed
Deployment / wordpress (push) Failing after 2s
Deployment / nuxt (push) Failing after 9s
2026-01-28 21:14:24 -05:00
ec64a42c2e chore: update pnpm-lock.yaml
Some checks failed
Deployment / wordpress (push) Failing after 2s
Deployment / nuxt (push) Has been cancelled
2026-01-28 21:14:12 -05:00
5f9c29c39a feat: Configure nuxt-svgo module 2026-01-28 21:13:49 -05:00
27f4f73148 fix: update nuxt-graphql and extractNodes typing (maybe => undefined instead of null) 2026-01-28 21:13:31 -05:00
21a7036ef5 feat: all of wrangler config in nuxt.config.ts 2026-01-28 21:12:43 -05:00
b1b1aa47c9 feat: graphql cache keyPrefix from package.json version 2026-01-28 21:11:48 -05:00
54fea5f64a minor: placeholder include for rankmath logic 2026-01-28 21:10:43 -05:00
a27e6af5db fix: term_order and default WPGraphQL settings 2026-01-28 21:10:26 -05:00
2c86905c91 fix: use public wpUrl runtime config for auth refresh token mutation 2026-01-28 21:09:37 -05:00
065b729a2f minor: better wp remote executor hooks 2026-01-28 21:08:58 -05:00
c82bf47e98 feat: useResponsive with device detection on SSR 2026-01-28 21:08:27 -05:00
51f594baa5 fix: headless_home_url needs to be in mu-plugins 2026-01-28 21:07:51 -05:00
3e56ba7eb3 minor: update schema.graphql 2026-01-28 08:34:44 -05:00
55 changed files with 2061 additions and 602 deletions

View File

@@ -3,4 +3,3 @@
Headless WordPress project boilerplate using Nuxt. Headless WordPress project boilerplate using Nuxt.
[✨  Release notes](/wp-content/themes/moonshine/CHANGELOG.md) [✨  Release notes](/wp-content/themes/moonshine/CHANGELOG.md)

View File

@@ -33,12 +33,14 @@
}, },
"require": { "require": {
"axepress/wp-graphql-rank-math": "*", "axepress/wp-graphql-rank-math": "*",
"lewebsimple/acf-phone": "*",
"lewebsimple/advanced-custom-fields-pro": "*", "lewebsimple/advanced-custom-fields-pro": "*",
"lewebsimple/kaliroots": "*", "lewebsimple/kaliroots": "*",
"lewebsimple/wp-graphql-headless-login": "*", "lewebsimple/wp-graphql-headless-login": "*",
"wpackagist-plugin/acf-extended": "*", "wpackagist-plugin/acf-extended": "*",
"wpackagist-plugin/clean-image-filenames": "*", "wpackagist-plugin/clean-image-filenames": "*",
"wpackagist-plugin/disable-comments": "*", "wpackagist-plugin/disable-comments": "*",
"wpackagist-plugin/media-focus-point": "*",
"wpackagist-plugin/seo-by-rank-math": "*", "wpackagist-plugin/seo-by-rank-math": "*",
"wpackagist-plugin/wp-graphql": "*", "wpackagist-plugin/wp-graphql": "*",
"wpackagist-plugin/wpgraphql-acf": "*" "wpackagist-plugin/wpgraphql-acf": "*"

67
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "9673ea7f3e3f21866ae50f70e1d6a16b", "content-hash": "e3ce417e8c09ed84502559af141f6530",
"packages": [ "packages": [
{ {
"name": "axepress/wp-graphql-plugin-boilerplate", "name": "axepress/wp-graphql-plugin-boilerplate",
@@ -313,6 +313,43 @@
], ],
"time": "2024-06-24T20:46:46+00:00" "time": "2024-06-24T20:46:46+00:00"
}, },
{
"name": "lewebsimple/acf-phone",
"version": "v3.1.0",
"source": {
"type": "git",
"url": "https://github.com/lewebsimple/acf-phone.git",
"reference": "cf4c6440e0c2cdf7e422423bb629014204e721bf"
},
"dist": {
"type": "zip",
"url": "https://satis.ledevsimple.ca/dist/lewebsimple/acf-phone/lewebsimple-acf-phone-cf4c6440e0c2cdf7e422423bb629014204e721bf-zip-439080.zip",
"reference": "cf4c6440e0c2cdf7e422423bb629014204e721bf",
"shasum": "f9d7cbcf27985656245285e5bc035578621f1a69"
},
"require-dev": {
"lewebsimple/wp-phpcs-ruleset": "*",
"php-stubs/acf-pro-stubs": "*",
"squizlabs/php_codesniffer": "*"
},
"type": "wordpress-plugin",
"scripts": {
"post-create-project-cmd": [
"./scripts/post-create.sh"
],
"lint": [
"vendor/bin/phpcs"
],
"lintfix": [
"vendor/bin/phpcbf"
]
},
"support": {
"source": "https://github.com/lewebsimple/acf-phone/tree/v3.1.0",
"issues": "https://github.com/lewebsimple/acf-phone/issues"
},
"time": "2026-01-30T15:01:34+00:00"
},
{ {
"name": "lewebsimple/advanced-custom-fields-pro", "name": "lewebsimple/advanced-custom-fields-pro",
"version": "v6.7.0.2", "version": "v6.7.0.2",
@@ -419,16 +456,34 @@
"homepage": "https://wordpress.org/plugins/disable-comments/" "homepage": "https://wordpress.org/plugins/disable-comments/"
}, },
{ {
"name": "wpackagist-plugin/seo-by-rank-math", "name": "wpackagist-plugin/media-focus-point",
"version": "1.0.262", "version": "2.0.4",
"source": { "source": {
"type": "svn", "type": "svn",
"url": "https://plugins.svn.wordpress.org/seo-by-rank-math/", "url": "https://plugins.svn.wordpress.org/media-focus-point/",
"reference": "tags/1.0.262" "reference": "tags/2.0.4"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://downloads.wordpress.org/plugin/seo-by-rank-math.1.0.262.zip" "url": "https://downloads.wordpress.org/plugin/media-focus-point.2.0.4.zip"
},
"require": {
"composer/installers": "^1.0 || ^2.0"
},
"type": "wordpress-plugin",
"homepage": "https://wordpress.org/plugins/media-focus-point/"
},
{
"name": "wpackagist-plugin/seo-by-rank-math",
"version": "1.0.263",
"source": {
"type": "svn",
"url": "https://plugins.svn.wordpress.org/seo-by-rank-math/",
"reference": "tags/1.0.263"
},
"dist": {
"type": "zip",
"url": "https://downloads.wordpress.org/plugin/seo-by-rank-math.1.0.263.zip"
}, },
"require": { "require": {
"composer/installers": "^1.0 || ^2.0" "composer/installers": "^1.0 || ^2.0"

View File

@@ -0,0 +1,20 @@
<?php
// Customize home URL for headless WordPress
add_filter( 'home_url', 'headless_home_url', 10, 4 );
function headless_home_url( $url, $path, $orig_scheme, $blog_id ) {
// Exclude specific patterns from rewriting
$excluded_patterns = array(
'#/wp-json(/|$)#i', // WP REST API
'#\.(xsl|xml)$#i', // Sitemap and XSLT files
);
foreach ( $excluded_patterns as $pattern ) {
if ( preg_match( $pattern, $url ) ) {
return get_site_url( $blog_id, $path, $orig_scheme );
}
}
// Rewrite URL protocol to match original home scheme
$scheme = wp_parse_url( get_option( 'home' ) )['scheme'] ?? 'https';
return preg_replace( '#^https:#i', "$scheme:", $url );
}

View File

@@ -1,5 +1,67 @@
# Changelog # Changelog
## v0.1.11
[compare changes](https://gitea.websimple.com/wp-sites/wp-headless/compare/v0.1.10...v0.1.11)
### 🚀 Enhancements
- MapSocialIcon (6dd13ea)
- AcfSocial / parseAcfSocial (3199835)
### 🩹 Fixes
- Remove unneeded wrangler main / assets (eea020b)
- Cloudflare image provider only in production (291fa6e)
- Wrangler project name (8f037b5)
- ENABLE_CLOUDFLARE_IMAGE (9c3dcee)
## v0.1.10
[compare changes](https://gitea.websimple.com/templates/wp-headless/compare/v0.1.9...v0.1.10)
### 🚀 Enhancements
- GroupSiteOptions.phoneNumber (ad3c53c)
- AcfPhone component (6b17201)
- AcfLink / AcfLinkButton components (115a5d2)
### 💅 Refactors
- OptionsSite => SiteOptions for clearer naming (fefa980)
## v0.1.9
[compare changes](https://gitea.websimple.com/templates/wp-headless/compare/v0.1.8...v0.1.9)
### 🚀 Enhancements
- Enhance refreshAuthToken to prevent duplicate requests (5e39b53)
- Default site-logo.svg (238aa29)
- Initial Media / HeroSplit acf groups (0fbd2bf)
- Media Focus Point plugin integration (bff7bce)
- AcfImage component based on @nuxt/image (4918c63)
- AcfMedia component (image + aspect + object-fit) (8bd544b)
- HeroSplit section (0bafc3a)
## v0.1.8
[compare changes](https://gitea.websimple.com/templates/wp-headless/compare/v0.1.7...v0.1.8)
### 🚀 Enhancements
- UseResponsive with device detection on SSR (c82bf47)
- Graphql cache keyPrefix from package.json version (b1b1aa4)
- All of wrangler config in nuxt.config.ts (21a7036)
- Configure nuxt-svgo module (5f9c29c)
### 🩹 Fixes
- Headless_home_url needs to be in mu-plugins (51f594b)
- Use public wpUrl runtime config for auth refresh token mutation (2c86905)
- Term_order and default WPGraphQL settings (a27e6af)
- Update nuxt-graphql and extractNodes typing (maybe => undefined instead of null) (27f4f73)
## v0.1.7 ## v0.1.7
[compare changes](https://gitea.websimple.com/templates/wp-headless/compare/v0.1.6...v0.1.7) [compare changes](https://gitea.websimple.com/templates/wp-headless/compare/v0.1.6...v0.1.7)

View File

@@ -6,7 +6,14 @@ Thème WordPress en headless basé sur Nuxt.
| Nom | Description | Exemple | Requise | | Nom | Description | Exemple | Requise |
|-----|-------------|---------|---------| |-----|-------------|---------|---------|
| `NUXT_SESSION_PASSWORD` | Clé secrète pour l'authentification | `date \| md5sum` | ✅ |
| `NUXT_WP_URL` | URL du backend WordPress | https://wp.exemple.com | ✅ |
| `NUXT_SITE_URL` | URL du frontend Nuxt | https://www.example.com | |
| `NUXT_SITE_ENV` | Environnement | staging \| production | | | `NUXT_SITE_ENV` | Environnement | staging \| production | |
| `NUXT_SITE_URL` | URL du frontend Nuxt | https://www.example.com | |
| `NUXT_WP_URL` | URL du backend WordPress | https://wp.exemple.com | ✅ |
## Secrets
Configurer les secrets nécessaires au projet:
```sh
pnpm wrangler secret put NUXT_SESSION_PASSWORD
```

View File

@@ -109,6 +109,102 @@
"acfe_flexible_render_script": false, "acfe_flexible_render_script": false,
"acfe_flexible_thumbnail": false, "acfe_flexible_thumbnail": false,
"acfe_flexible_category": false "acfe_flexible_category": false
},
"layout_697caf9a3e05b": {
"key": "layout_697caf9a3e05b",
"name": "hero_split",
"label": "Héro en moitié",
"display": "block",
"sub_fields": [
{
"key": "field_697cafb13e05d",
"label": "Content",
"name": "content",
"aria-label": "",
"type": "wysiwyg",
"instructions": "",
"required": 1,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"allow_in_bindings": 0,
"tabs": "all",
"toolbar": "full",
"media_upload": 0,
"delay": 0,
"show_in_graphql": 1,
"graphql_description": "",
"graphql_field_name": "content",
"graphql_non_null": 1
},
{
"key": "field_697cafc43e05e",
"label": "Media",
"name": "media",
"aria-label": "",
"type": "clone",
"instructions": "",
"required": 1,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"graphql_field_name": "media",
"clone": [
"group_abstract_media"
],
"display": "seamless",
"layout": "block",
"prefix_label": 0,
"prefix_name": 0,
"acfe_seamless_style": 0,
"acfe_clone_modal": 0,
"acfe_clone_modal_close": 0,
"acfe_clone_modal_button": "",
"acfe_clone_modal_size": "large"
},
{
"key": "field_697cafdc3e05f",
"label": "Position de l'image",
"name": "reverse",
"aria-label": "",
"type": "true_false",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"message": "",
"default_value": 0,
"allow_in_bindings": 0,
"ui_on_text": "Gauche",
"ui_off_text": "Droite",
"ui": 1,
"show_in_graphql": 1,
"graphql_description": "",
"graphql_field_name": "reverse",
"graphql_non_null": 1
}
],
"min": "",
"max": "",
"acfe_flexible_modal_edit_size": "",
"acfe_flexible_settings": "",
"acfe_flexible_settings_size": "large",
"acfe_flexible_render_template": false,
"acfe_flexible_render_style": false,
"acfe_flexible_render_script": false,
"acfe_flexible_thumbnail": false,
"acfe_flexible_category": false
} }
}, },
"min": "", "min": "",
@@ -152,5 +248,5 @@
"graphql_types": "", "graphql_types": "",
"acfe_meta": "", "acfe_meta": "",
"acfe_note": "", "acfe_note": "",
"modified": 1768358815 "modified": 1769779666
} }

View File

@@ -0,0 +1,123 @@
{
"key": "group_abstract_media",
"title": "Abstract - Media",
"fields": [
{
"key": "field_697caec68536d",
"label": "Image",
"name": "image",
"aria-label": "",
"type": "image",
"instructions": "",
"required": 1,
"conditional_logic": 0,
"wrapper": {
"width": "33",
"class": "",
"id": ""
},
"uploader": "",
"return_format": "array",
"library": "all",
"acfe_thumbnail": 0,
"min_width": "",
"min_height": "",
"min_size": "",
"max_width": "",
"max_height": "",
"max_size": "",
"mime_types": "",
"allow_in_bindings": 0,
"preview_size": "medium",
"show_in_graphql": 1,
"graphql_description": "",
"graphql_field_name": "image"
},
{
"key": "field_697caf018536e",
"label": "Ratio d'aspect",
"name": "aspect_ratio",
"aria-label": "",
"type": "button_group",
"instructions": "",
"required": 1,
"conditional_logic": 0,
"wrapper": {
"width": "33",
"class": "",
"id": ""
},
"choices": {
"square": "Carré (1:1)",
"video": "Vidéo (16:9)",
"portrait": "Portrait (2:3)",
"auto": "Aspect d'origine"
},
"default_value": "auto",
"return_format": "value",
"allow_null": 0,
"allow_in_bindings": 0,
"layout": "horizontal",
"show_in_graphql": 1,
"graphql_description": "",
"graphql_field_name": "aspectRatio",
"graphql_non_null": 1
},
{
"key": "field_697caf378536f",
"label": "Ajustement de l'image",
"name": "object_fit",
"aria-label": "",
"type": "button_group",
"instructions": "",
"required": 1,
"conditional_logic": 0,
"wrapper": {
"width": "33",
"class": "",
"id": ""
},
"choices": {
"cover": "Recadrer si nécessaire",
"contain": "Contenir sans recadrage"
},
"default_value": "cover",
"return_format": "value",
"allow_null": 0,
"allow_in_bindings": 0,
"layout": "horizontal",
"show_in_graphql": 1,
"graphql_description": "",
"graphql_field_name": "objectFit",
"graphql_non_null": 1
}
],
"location": [
[
{
"param": "abstract"
}
]
],
"menu_order": 0,
"position": "normal",
"style": "seamless",
"label_placement": "top",
"instruction_placement": "label",
"hide_on_screen": "",
"active": true,
"description": "",
"show_in_rest": 0,
"display_title": "",
"acfe_autosync": [
"json"
],
"acfe_form": 0,
"show_in_graphql": 1,
"graphql_field_name": "GroupAbstractMedia",
"map_graphql_types_from_location_rules": 0,
"graphql_types": "",
"acfe_meta": "",
"acfe_note": "",
"modified": 1769779078
}

View File

@@ -0,0 +1,86 @@
{
"key": "group_abstract_social",
"title": "Abstract - Social",
"fields": [
{
"key": "field_6855a1d643408",
"label": "Médias sociaux",
"name": "profiles",
"aria-label": "",
"type": "repeater",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"acfe_repeater_stylised_button": 0,
"layout": "table",
"pagination": 0,
"min": 0,
"max": 0,
"collapsed": "",
"button_label": "Ajouter un élément",
"show_in_graphql": 1,
"graphql_description": "",
"graphql_field_name": "profiles",
"graphql_non_null": 1,
"rows_per_page": 20,
"sub_fields": [
{
"key": "field_6855a7e143409",
"label": "URL",
"name": "url",
"aria-label": "",
"type": "url",
"instructions": "",
"required": 1,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"allow_in_bindings": 0,
"placeholder": "",
"show_in_graphql": 1,
"graphql_description": "",
"graphql_field_name": "url",
"graphql_non_null": 1,
"parent_repeater": "field_6855a1d643408"
}
]
}
],
"location": [
[
{
"param": "abstract"
}
]
],
"menu_order": 0,
"position": "normal",
"style": "seamless",
"label_placement": "top",
"instruction_placement": "label",
"hide_on_screen": "",
"active": true,
"description": "",
"show_in_rest": 0,
"display_title": "",
"acfe_autosync": [
"json"
],
"acfe_form": 0,
"show_in_graphql": 1,
"graphql_field_name": "GroupAbstractSocial",
"map_graphql_types_from_location_rules": 0,
"graphql_types": "",
"acfe_meta": "",
"acfe_note": "",
"modified": 1769788591
}

View File

@@ -25,6 +25,104 @@
"graphql_description": "", "graphql_description": "",
"graphql_field_name": "email", "graphql_field_name": "email",
"graphql_non_null": 1 "graphql_non_null": 1
},
{
"key": "field_697cbf414fdd5",
"label": "Phone number",
"name": "phone_number",
"aria-label": "",
"type": "phone",
"instructions": "",
"required": 1,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"initial_country": "CA",
"return_format": "national",
"allow_in_bindings": 0,
"show_in_graphql": 1,
"graphql_description": "",
"graphql_field_name": "phoneNumber",
"graphql_non_null": 1
},
{
"key": "field_697cd4c5fc56a",
"label": "Médias sociaux",
"name": "social",
"aria-label": "",
"type": "clone",
"instructions": "",
"required": 1,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"graphql_field_name": "social",
"clone": [
"group_abstract_social"
],
"display": "seamless",
"layout": "block",
"prefix_label": 0,
"prefix_name": 0,
"acfe_seamless_style": 0,
"acfe_clone_modal": 0,
"acfe_clone_modal_close": 0,
"acfe_clone_modal_button": "",
"acfe_clone_modal_size": "large"
},
{
"key": "field_697cc921234cc",
"label": "Liens globaux",
"name": "links",
"aria-label": "",
"type": "group",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"layout": "row",
"acfe_seamless_style": 0,
"acfe_group_modal": 0,
"show_in_graphql": 1,
"graphql_description": "",
"graphql_field_name": "links",
"graphql_non_null": 0,
"sub_fields": [
{
"key": "field_697cc93e234cd",
"label": "Contact",
"name": "contact",
"aria-label": "",
"type": "link",
"instructions": "",
"required": 1,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"return_format": "array",
"allow_in_bindings": 0,
"show_in_graphql": 1,
"graphql_description": "",
"graphql_field_name": "contact",
"graphql_non_null": 1
}
],
"acfe_group_modal_close": 0,
"acfe_group_modal_button": "",
"acfe_group_modal_size": "large"
} }
], ],
"location": [ "location": [
@@ -51,10 +149,10 @@
], ],
"acfe_form": 0, "acfe_form": 0,
"show_in_graphql": 1, "show_in_graphql": 1,
"graphql_field_name": "GroupSite", "graphql_field_name": "GroupSiteOptions",
"map_graphql_types_from_location_rules": 0, "map_graphql_types_from_location_rules": 0,
"graphql_types": "", "graphql_types": "",
"acfe_meta": "", "acfe_meta": "",
"acfe_note": "", "acfe_note": "",
"modified": 1769087407 "modified": 1769788698
} }

View File

@@ -20,6 +20,6 @@
"post_id": "", "post_id": "",
"autoload": 0, "autoload": 0,
"show_in_graphql": 1, "show_in_graphql": 1,
"graphql_type_name": "OptionsSite", "graphql_type_name": "SiteOptions",
"modified": 1769086997 "modified": 1769693948
} }

View File

@@ -0,0 +1,4 @@
<svg version="1.1" viewBox="0 0 490.3 86.763" xmlns="http://www.w3.org/2000/svg">
<path d="m10.344 1.5312a10.208 10.208 0 0 0-9.0195 15.207l36.1 64.801a10.2 10.2 0 0 0 13.9 3.9004 10.415 10.415 0 0 0 4-14l-36.201-64.701a10.208 10.208 0 0 0-8.7793-5.207zm221.33 12.51v51.84h11.52v-2.8789a16.277 16.277 0 0 0 11.232 3.959c9.576 0 17.641-7.7004 17.641-19.152 0-11.376-8.0656-19.152-17.641-19.152a16.277 16.277 0 0 0-11.232 3.9609v-18.576h-11.52zm206 0v51.84h11.52v-51.84h-11.52zm-127.3 0.070312v11.145h11.521v-11.145h-11.521zm-21.229 14.535c-8.6942-0.004836-14.866 4.5403-15.109 11.721-0.193 5.685 3.5014 9.4123 10.15 10.863l7.291 1.6875c2.645 0.594 3.5488 1.5612 3.5078 2.7852-0.054 1.583-1.7534 2.8227-4.7754 2.7207-3.094-0.105-5.8618-1.3519-6.2598-4.4629l-11.305 1.9199c0.972 7.453 8.133 10.795 16.625 11.084 9.5 0.322 16.549-3.6186 16.82-11.602 0.181-5.325-2.8478-9.5341-9.6348-11.205l-8.4316-2.0137c-2.286-0.582-2.6824-1.6003-2.6484-2.6113 0.044-1.295 1.0992-2.773 4.1992-2.668 3.526 0.12 5.6164 2.2082 5.9004 4.4512l10.574-1.7363c-1.227-6.309-7.0597-10.613-16.055-10.918-0.28559-0.009687-0.56915-0.015469-0.84961-0.015625zm-79.43 0.009766c-10.512 0-19.152 7.7764-19.152 19.152 0 11.448 8.6401 19.152 19.08 19.152 8.28 0 14.832-3.6728 17.928-11.301l-10.225-2.0879c-1.944 3.528-5.0392 4.1758-7.6992 4.1758-3.888 0-6.8414-2.6624-7.7774-6.9824h26.5v-2.957h-0.00781c-0.216-11.808-8.4225-19.152-18.646-19.152zm140.62 0a13.682 13.682 0 0 0-10.656 4.3926v-3.3125h-11.52v36.145h11.52v-17.711c0-6.048 2.7361-9.1445 6.9121-9.1445 3.456 0 6.1191 2.5912 6.1191 6.6992v20.156h11.521v-17.711c0-6.048 2.8067-9.1445 7.0547-9.1445 3.384 0 6.0488 2.5912 6.0488 6.6992v20.156h11.447v-22.607c0-8.784-6.1202-14.617-13.824-14.617a15.58 15.58 0 0 0-12.814 6.1211c-2.448-3.96-6.7686-6.1211-11.809-6.1211zm66.391 0a16.277 16.277 0 0 0-11.232 3.9609v-2.8809h-11.52v48.385h11.52v-15.119a16.277 16.277 0 0 0 11.232 3.959c9.576 0 17.639-7.7004 17.639-19.152 0-11.376-8.0627-19.152-17.639-19.152zm54.936 0c-10.512 0-19.15 7.7764-19.15 19.152 0 11.448 8.6381 19.152 19.078 19.152 8.28 0 14.834-3.6728 17.93-11.301l-10.225-2.0879c-1.944 3.528-5.0392 4.1758-7.6992 4.1758-3.888 0-6.8414-2.6624-7.7774-6.9824h26.5v-2.957h-0.00781c-0.216-11.808-8.4244-19.152-18.648-19.152zm-336.16 1.0801 11.592 36.217h10.008l6.5527-19.584 6.4805 19.584h10.008l11.576-36.217h-11.301l-5.9004 19.512-6.8398-19.512h-8.1367l-6.8398 19.441-5.8984-19.441h-11.301zm174.88 0v36.145h11.521v-36.145h-11.521zm-100.66 8.2812a7.221 7.221 0 0 1 7.2715 5.6875h-14.9a7.918 7.918 0 0 1 7.6289-5.6875zm261.94 0a7.221 7.221 0 0 1 7.2715 5.6875h-14.9a7.918 7.918 0 0 1 7.6289-5.6875zm-219.6 0.43164c5.04 0 8.7109 3.8154 8.7109 9.3594 0 5.616-3.6709 9.3613-8.7109 9.3613a8.972 8.972 0 0 1-8.8574-9.3613 9.016 9.016 0 0 1 8.8574-9.3594zm162.29 0c5.04 0 8.7129 3.8154 8.7129 9.3594 0 5.616-3.6729 9.3613-8.7129 9.3613a8.972 8.972 0 0 1-8.8555-9.3613 9.016 9.016 0 0 1 8.8555-9.3594z" fill="#212121"/>
<path d="m123.02 1.939-.7-.5a9.735 9.735 0 0 0-13.4 3.2l-29.1 47.3a2.253 2.253 0 0 1-3.9-.1l-11.3-20.3a9.82 9.82 0 0 0-13.3-3.8l-.8.4a9.82 9.82 0 0 0-3.8 13.3l21.8 39a9.983 9.983 0 0 0 8.3 5 9.83 9.83 0 0 0 9.1-4.6l40.4-65.6a9.668 9.668 0 0 0-3.3-13.3" fill="#0ad2b7" data-name="Tracé 22516"/>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@@ -0,0 +1,9 @@
fragment AcfImage on MediaItem {
src: sourceUrl
alt: altText
mediaDetails {
width
height
}
objectPosition
}

View File

@@ -0,0 +1,17 @@
<script setup lang="ts">
import type { AcfImageFragment } from "#graphql/operations";
defineProps<{ image?: AcfImageFragment }>();
</script>
<template>
<NuxtImg
v-if="image"
:src="image.src"
:alt="image.alt"
:width="image.mediaDetails?.width"
:height="image.mediaDetails?.height"
:style="{ objectPosition: image.objectPosition || 'center' }"
placeholder
/>
</template>

View File

@@ -0,0 +1,5 @@
fragment AcfLink on AcfLink {
title
url
target
}

View File

@@ -0,0 +1,23 @@
<script setup lang="ts">
import type { AcfLinkFragment } from "#graphql/operations";
import type { LinkProps } from "@nuxt/ui";
type AcfLinkProps = Omit<LinkProps, "to" | "target" | "href"> & {
link?: AcfLinkFragment;
};
const { link, ...linkProps } = defineProps<AcfLinkProps>();
</script>
<template>
<ULink
v-if="link?.url && link?.title"
v-bind="linkProps"
:to="link.url"
:target="link.target"
:external="link.target === '_blank'"
:rel="link.target === '_blank' ? 'noopener noreferrer' : undefined"
>
<slot>{{ link.title }}</slot>
</ULink>
</template>

View File

@@ -0,0 +1,23 @@
<script setup lang="ts">
import type { AcfLinkFragment } from "#graphql/operations";
import type { ButtonProps } from "@nuxt/ui";
type AcfLinkButtonProps = & Omit<ButtonProps, "to" | "target" | "href"> & {
link?: AcfLinkFragment;
};
const { link, ...buttonProps } = defineProps<AcfLinkButtonProps>();
</script>
<template>
<UButton
v-if="link?.url && link?.title"
v-bind="buttonProps"
:to="link.url"
:target="link.target"
:external="link.target === '_blank'"
:rel="link.target === '_blank' ? 'noopener noreferrer' : undefined"
>
<slot>{{ link.title }}</slot>
</UButton>
</template>

View File

@@ -0,0 +1,9 @@
fragment AcfMedia on GroupAbstractMedia_Fields {
image {
node {
...AcfImage
}
}
aspectRatio
objectFit
}

View File

@@ -0,0 +1,36 @@
<script setup lang="ts">
import { tv, type VariantProps } from "tailwind-variants";
import type { AcfMediaFragment } from "#graphql/operations";
const tvAcfMedia = tv({
slots: {
image: "w-full",
},
variants: {
aspectRatio: {
square: { image: "aspect-[1/1]" },
video: { image: "aspect-video" },
portrait: { image: "aspect-[2/3]" },
auto: { image: "aspect-auto" },
},
objectFit: {
cover: { image: "object-cover" },
contain: { image: "object-contain" },
},
},
defaultVariants: {
aspectRatio: "auto",
objectFit: "cover",
},
});
const props = defineProps<{ media?: AcfMediaFragment }>();
const classes = tvAcfMedia({
aspectRatio: props.media?.aspectRatio,
objectFit: props.media?.objectFit,
} as VariantProps<typeof tvAcfMedia>);
</script>
<template>
<AcfImage v-if="media?.image?.node" :image="media.image.node" :class="classes.image()" />
</template>

View File

@@ -0,0 +1,5 @@
fragment AcfPhone on AcfPhone {
national
e164
extension
}

View File

@@ -0,0 +1,14 @@
<script setup lang="ts">
import type { AcfPhoneFragment } from "#graphql/operations";
defineProps<{
phone?: AcfPhoneFragment;
link?: boolean;
}>();
</script>
<template>
<Component :is="link ? 'a' : 'span'" v-if="phone" :href="link ? `tel:${phone.e164}` : undefined">
{{ phone.national }}{{ phone.extension ? ` ext. ${phone.extension}` : "" }}
</Component>
</template>

View File

@@ -0,0 +1,5 @@
fragment AcfSocial on GroupAbstractSocial_Fields {
profiles {
url
}
}

View File

@@ -0,0 +1,11 @@
<script setup lang="ts">
defineProps<{ social?: AcfSocialOutput }>();
</script>
<template>
<div v-if="social?.profiles" class="flex gap-1.5">
<a v-for="({ url, icon }, key) in social.profiles" :key="key" :href="url" target="_blank" rel="noopener noreferrer" class="flex">
<UIcon :name="icon" />
</a>
</div>
</template>

View File

@@ -1,8 +1,7 @@
fragment BuilderSections on GroupAbstractBuilder_Fields { fragment BuilderSections on GroupAbstractBuilder_Fields {
sections { sections {
__typename __typename
... on GroupAbstractBuilderSectionsTextBlockLayout { ... on GroupAbstractBuilderSectionsHeroSplitLayout { ... SectionHeroSplit }
... SectionTextBlock ... on GroupAbstractBuilderSectionsTextBlockLayout { ... SectionTextBlock }
}
} }
} }

View File

@@ -0,0 +1,6 @@
fragment SectionHeroSplit on GroupAbstractBuilderSectionsHeroSplitLayout {
content
reverse
...AcfMedia
}

View File

@@ -0,0 +1,40 @@
<script setup lang="ts">
import { tv, type VariantProps } from "tailwind-variants";
import type { SectionHeroSplitFragment } from "#graphql/operations";
const tvSectionHeroSplit = tv({
slots: {
base: "py-6",
container: "container flex flex-col gap-6 items-center",
content: "flex-1",
media: "w-full basis-1/2",
},
variants: {
reverse: {
false: {
container: "lg:flex-row",
},
true: {
container: "lg:flex-row-reverse",
},
},
},
defaultVariants: {
reverse: false,
},
});
const props = defineProps<SectionHeroSplitFragment>();
const classes = tvSectionHeroSplit({
reverse: props.reverse,
} as VariantProps<typeof tvSectionHeroSplit>);
</script>
<template>
<section :class="classes.base()">
<div :class="classes.container()">
<UiProse :content="content" :class="classes.content()" />
<AcfMedia :media="$props" :class="classes.media()" />
</div>
</section>
</template>

View File

@@ -1,13 +1,12 @@
<script setup lang="ts"> <script setup lang="ts">
const { data: siteOptions } = await useSiteOptions();
</script> </script>
<template> <template>
<UFooter id="site-footer"> <footer class="bg-accented links:link-prose">
<template #left> <div class="container py-6">
<SiteFooterCopyright /> <AcfSocial :social="parseAcfSocial(siteOptions)" />
</template> </div>
<template #right> <SiteFooterBottom />
<SiteFooterCredits /> </footer>
</template>
</UFooter>
</template> </template>

View File

@@ -0,0 +1,9 @@
<template>
<div class="bg-inverted text-inverted py-1.5">
<div class="container flex flex-col sm:flex-row items-center gap-3">
<SiteFooterCopyright class="sm:mr-auto" />
<AuthConnexionButton color="neutral" variant="link" />
<SiteFooterCredits />
</div>
</div>
</template>

View File

@@ -1,10 +1,10 @@
<script setup lang="ts"> <script setup lang="ts">
const { data } = await useAsyncGraphQLQuery("GeneralSettings", undefined, { cache: { ttl: 0 } }); const { data: generalSettings } = await useGeneralSettings();
</script> </script>
<template> <template>
<div> <div>
© {{ new Date().getFullYear() }} © {{ new Date().getFullYear() }}
<span v-if="data.generalSettings?.title">{{ data.generalSettings.title }}</span> <span v-if="generalSettings?.title">{{ generalSettings.title }}</span>
</div> </div>
</template> </template>

View File

@@ -1,9 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
const title = "Moonshine";
</script> </script>
<template> <template>
<UHeader :title="title"> <UHeader mode="slideover">
<template #left>
<NuxtLink to="/">
<SvgSiteLogo class="h-12 w-auto" />
</NuxtLink>
</template>
<template #right> <template #right>
<AuthConnexionButton /> <AuthConnexionButton />
</template> </template>

View File

@@ -0,0 +1,3 @@
export const useGeneralSettings = () => useAsyncGraphQLQuery("GeneralSettings", {}, {
transform: ({ generalSettings }) => generalSettings,
});

View File

@@ -0,0 +1,9 @@
import { breakpointsTailwind, useBreakpoints } from "@vueuse/core";
export function useResponsive() {
const { isMobileOrTablet } = useDevice();
const breakpoints = useBreakpoints(breakpointsTailwind, { ssrWidth: isMobileOrTablet ? 375 : 1024 });
const isDesktop = breakpoints.greaterOrEqual("lg");
return { breakpoints, isDesktop };
}

View File

@@ -0,0 +1,3 @@
export const useSiteOptions = () => useAsyncGraphQLQuery("SiteOptions", {}, {
transform: ({ siteOptions }) => siteOptions?.groupSiteOptions,
});

View File

@@ -1,11 +0,0 @@
fragment SiteOptions on GroupSite_Fields {
email
}
query OptionsSite {
optionsSite {
groupSite {
... SiteOptions
}
}
}

View File

@@ -0,0 +1,16 @@
fragment SiteOptions on GroupSiteOptions {
email
phoneNumber { ... AcfPhone }
...AcfSocial
links {
contact { ... AcfLink}
}
}
query SiteOptions {
siteOptions {
groupSiteOptions {
... SiteOptions
}
}
}

View File

@@ -1,10 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
// Resolve Node component from URI // Fetch node by URI and handle query errors
const { path: uri } = useRoute(); const { path: uri } = useRoute();
const { data } = await useAsyncGraphQLQuery("NodeByUri", { uri }); const { data, error } = await useAsyncGraphQLQuery("NodeByUri", { uri });
if (!data.value?.nodeByUri) { if (!data.value?.nodeByUri) {
console.error("NodeByUri query error:", error.value);
throw createError({ statusCode: 404, message: `La page demandée est introuvable: ${uri}`, fatal: true }); throw createError({ statusCode: 404, message: `La page demandée est introuvable: ${uri}`, fatal: true });
} }
// Dynamically resolve component based on node type
const componentName = `Node${data.value.nodeByUri.__typename}`; const componentName = `Node${data.value.nodeByUri.__typename}`;
if (!useNuxtApp().vueApp.component(componentName)) { if (!useNuxtApp().vueApp.component(componentName)) {
throw createError({ statusCode: 404, message: `La page demandée ne peut pas être affichée correctement: ${componentName}`, fatal: true }); throw createError({ statusCode: 404, message: `La page demandée ne peut pas être affichée correctement: ${componentName}`, fatal: true });

View File

@@ -0,0 +1,32 @@
import * as z from "zod";
import type { AcfSocialFragment } from "#graphql/operations";
const socialProfile = z.object({ url: z.url() }).transform(({ url }) => ({ url, icon: getSocialIcon(url) }));
const acfSocialSchema = z.object({
profiles: z.array(socialProfile),
});
export type AcfSocialOutput = z.infer<typeof acfSocialSchema>;
export function parseAcfSocial(data?: AcfSocialFragment) {
try {
return acfSocialSchema.parse(data);
}
catch {
return undefined;
}
}
const socialIconMap = {
"facebook.com": "i-cib-facebook-f",
"twitter.com": "i-cib-twitter",
"x.com": "i-cib-twitter",
"instagram.com": "i-cib-instagram",
"youtube.com": "i-cib-youtube",
"linkedin.com": "i-cib-linkedin",
"tiktok.com": "i-cib-tiktok",
};
function getSocialIcon(url: string): string {
const domain = new URL(url).hostname.toLowerCase().replace(/^www\./, "");
return socialIconMap[domain as keyof typeof socialIconMap] ?? "i-lucide-globe";
}

View File

@@ -5,4 +5,10 @@ require_once __DIR__ . '/includes/core/theme-setup.php';
// Vendors // Vendors
require_once __DIR__ . '/includes/vendors/acf.php'; require_once __DIR__ . '/includes/vendors/acf.php';
require_once __DIR__ . '/includes/vendors/rankmath.php';
require_once __DIR__ . '/includes/vendors/tinymce.php'; require_once __DIR__ . '/includes/vendors/tinymce.php';
require_once __DIR__ . '/includes/vendors/wpgraphql.php';
// WPGraphQL
require_once __DIR__ . '/includes/wpgraphql/media-focus-point.php';
require_once __DIR__ . '/includes/wpgraphql/term-connection.php';

View File

@@ -17,21 +17,6 @@ function moonshine_after_setup_theme() {
// Register sidebars // Register sidebars
} }
// Bypass headless home URL for specific cases
add_filter( 'home_url', 'moonshine_bypass_home_url', 10, 4 );
function moonshine_bypass_home_url( $url, $path, $orig_scheme, $blog_id ) {
$excluded_patterns = array(
'#/wp-json(/|$)#i', // WP REST API
'#\.(xsl|xml)$#i', // Sitemap and XSLT files
);
foreach ( $excluded_patterns as $pattern ) {
if ( preg_match( $pattern, $url ) ) {
return get_site_url( $blog_id, $path, $orig_scheme );
}
}
return $url;
}
// Display theme version in admin footer // Display theme version in admin footer
add_filter( 'update_footer', 'moonshine_update_footer', 100 ); add_filter( 'update_footer', 'moonshine_update_footer', 100 );
function moonshine_update_footer() { function moonshine_update_footer() {

View File

@@ -0,0 +1 @@
<?php

View File

@@ -0,0 +1,17 @@
<?php
// Default WPGraphQL settings
add_filter( 'graphql_get_setting_section_field_value', 'moonshine_wpgraphql_settings', 10, 5 );
function moonshine_wpgraphql_settings( $value, $default_value, $option_name, $section_fields, $section_name ) {
if ( $section_name === 'graphql_general_settings' ) {
switch ( $option_name ) {
case 'graphql_endpoint':
$value = 'graphql';
break;
case "public_introspection_enabled":
$value = "on";
break;
}
}
return $value;
}

View File

@@ -0,0 +1,17 @@
<?php
// Expose 'bg_pos_desktop' post meta on MediaItem type in WPGraphQL
add_action( 'graphql_register_types', 'leblanc_graphql_register_media_focus_point' );
function leblanc_graphql_register_media_focus_point() {
register_graphql_field(
'MediaItem',
'objectPosition',
array(
'type' => 'String',
'description' => 'CSS object-position value from Media Focus Point plugin',
'resolve' => static function ( $media_item ) {
return get_post_meta( $media_item->databaseId, 'bg_pos_desktop', true );
},
)
);
}

View File

@@ -0,0 +1,13 @@
<?php
// Override term connection query args
add_filter( 'graphql_term_object_connection_query_args', 'moonshine_graphql_term_object_connection_query_args', 10, 3 );
function moonshine_graphql_term_object_connection_query_args( $query_args, $source, $args ) {
// Sort by 'order' meta value instead of legacy 'term_order' field
if ( 'term_order' === $args['where']['orderby'] ?? false ) {
$query_args['meta_key'] = 'order';
$query_args['orderby'] = 'meta_value_num';
}
return $query_args;
}

View File

@@ -1,24 +1,2 @@
<?php <?php
return array( return ['project-id-version'=>'Moonshine','report-msgid-bugs-to'=>'','pot-creation-date'=>'2026-01-13 15:52+0000','po-revision-date'=>'2026-01-29 02:55+0000','last-translator'=>'','language-team'=>'Français du Canada','language'=>'fr_CA','plural-forms'=>'nplurals=2; plural=n > 1;','mime-version'=>'1.0','content-type'=>'text/plain; charset=UTF-8','content-transfer-encoding'=>'8bit','x-generator'=>'Loco https://localise.biz/','x-loco-version'=>'2.8.1; wp-6.9; php-8.3.27','x-domain'=>'moonshine','messages'=>['Heading styles'=>'Styles de titres','Headless WordPress theme based on Nuxt.'=>'Thème Wordpress headless basé sur Nuxt.','https://websimple.com/'=>'https://websimple.com/','Inline styles'=>'Styles de caractères','Link styles'=>'Styles de liens','Main menu'=>'Menu principal','Moonshine'=>'Moonshine','Paragraph styles'=>'Styles de paragraphes','Pascal Martineau '=>'Pascal Martineau ','Semi-bold'=>'Semi-gras']];
'project-id-version' => 'Moonshine',
'report-msgid-bugs-to' => '',
'pot-creation-date' => '2026-01-13 15:52+0000',
'po-revision-date' => '2026-01-13 15:53+0000',
'last-translator' => '',
'language-team' => 'Français du Canada',
'language' => 'fr_CA',
'plural-forms' => 'nplurals=2; plural=n > 1;',
'mime-version' => '1.0',
'content-type' => 'text/plain; charset=UTF-8',
'content-transfer-encoding' => '8bit',
'x-generator' => 'Loco https://localise.biz/',
'x-loco-version' => '2.8.1; wp-6.9; php-8.3.27',
'x-domain' => 'moonshine',
'messages' => array(
'Headless WordPress theme based on Nuxt.' => 'Thème Wordpress headless basé sur Nuxt.',
'https://websimple.com/' => 'https://websimple.com/',
'Main menu' => 'Menu principal',
'Moonshine' => 'Moonshine',
'Pascal Martineau ' => 'Pascal Martineau ',
),
);

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: Moonshine\n" "Project-Id-Version: Moonshine\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-01-13 15:52+0000\n" "POT-Creation-Date: 2026-01-13 15:52+0000\n"
"PO-Revision-Date: 2026-01-13 15:53+0000\n" "PO-Revision-Date: 2026-01-29 02:55+0000\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Français du Canada\n" "Language-Team: Français du Canada\n"
"Language: fr_CA\n" "Language: fr_CA\n"
@@ -15,6 +15,10 @@ msgstr ""
"X-Loco-Version: 2.8.1; wp-6.9; php-8.3.27\n" "X-Loco-Version: 2.8.1; wp-6.9; php-8.3.27\n"
"X-Domain: moonshine" "X-Domain: moonshine"
#: includes/vendors/tinymce.php:54
msgid "Heading styles"
msgstr "Styles de titres"
#. Description of the theme #. Description of the theme
msgid "Headless WordPress theme based on Nuxt." msgid "Headless WordPress theme based on Nuxt."
msgstr "Thème Wordpress headless basé sur Nuxt." msgstr "Thème Wordpress headless basé sur Nuxt."
@@ -23,6 +27,14 @@ msgstr "Thème Wordpress headless basé sur Nuxt."
msgid "https://websimple.com/" msgid "https://websimple.com/"
msgstr "https://websimple.com/" msgstr "https://websimple.com/"
#: includes/vendors/tinymce.php:34
msgid "Inline styles"
msgstr "Styles de caractères"
#: includes/vendors/tinymce.php:24
msgid "Link styles"
msgstr "Styles de liens"
#: includes/core/theme-setup.php:15 #: includes/core/theme-setup.php:15
msgid "Main menu" msgid "Main menu"
msgstr "Menu principal" msgstr "Menu principal"
@@ -31,6 +43,14 @@ msgstr "Menu principal"
msgid "Moonshine" msgid "Moonshine"
msgstr "Moonshine" msgstr "Moonshine"
#: includes/vendors/tinymce.php:44
msgid "Paragraph styles"
msgstr "Styles de paragraphes"
#. Author of the theme #. Author of the theme
msgid "Pascal Martineau " msgid "Pascal Martineau "
msgstr "Pascal Martineau " msgstr "Pascal Martineau "
#: includes/vendors/tinymce.php:37
msgid "Semi-bold"
msgstr "Semi-gras"

View File

@@ -3,7 +3,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Moonshine\n" "Project-Id-Version: Moonshine\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-01-13 15:52+0000\n" "POT-Creation-Date: 2026-01-29 02:55+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: \n" "Language-Team: \n"
@@ -16,6 +16,10 @@ msgstr ""
"X-Loco-Version: 2.8.1; wp-6.9; php-8.3.27\n" "X-Loco-Version: 2.8.1; wp-6.9; php-8.3.27\n"
"X-Domain: moonshine" "X-Domain: moonshine"
#: includes/vendors/tinymce.php:54
msgid "Heading styles"
msgstr ""
#. Description of the theme #. Description of the theme
msgid "Headless WordPress theme based on Nuxt." msgid "Headless WordPress theme based on Nuxt."
msgstr "" msgstr ""
@@ -24,6 +28,14 @@ msgstr ""
msgid "https://websimple.com/" msgid "https://websimple.com/"
msgstr "" msgstr ""
#: includes/vendors/tinymce.php:34
msgid "Inline styles"
msgstr ""
#: includes/vendors/tinymce.php:24
msgid "Link styles"
msgstr ""
#: includes/core/theme-setup.php:15 #: includes/core/theme-setup.php:15
msgid "Main menu" msgid "Main menu"
msgstr "" msgstr ""
@@ -32,6 +44,14 @@ msgstr ""
msgid "Moonshine" msgid "Moonshine"
msgstr "" msgstr ""
#: includes/vendors/tinymce.php:44
msgid "Paragraph styles"
msgstr ""
#. Author of the theme #. Author of the theme
msgid "Pascal Martineau " msgid "Pascal Martineau "
msgstr "" msgstr ""
#: includes/vendors/tinymce.php:37
msgid "Semi-bold"
msgstr ""

View File

@@ -1,3 +1,5 @@
import { version } from "./package.json";
const siteUrl = process.env.NUXT_SITE_URL; const siteUrl = process.env.NUXT_SITE_URL;
if (!siteUrl) { if (!siteUrl) {
throw new Error(`NUXT_SITE_URL is not defined. Make sure to set it in your build environment variables.`); throw new Error(`NUXT_SITE_URL is not defined. Make sure to set it in your build environment variables.`);
@@ -7,6 +9,9 @@ const wpUrl = process.env.NUXT_WP_URL;
if (!wpUrl) { if (!wpUrl) {
throw new Error(`NUXT_WP_URL is not defined. Make sure to set it in your build environment variables.`); throw new Error(`NUXT_WP_URL is not defined. Make sure to set it in your build environment variables.`);
} }
const wpDomain = new URL(wpUrl).hostname;
const enableCloudflareImage = Boolean(process.env.ENABLE_CLOUDFLARE_IMAGE);
// https://nuxt.com/docs/api/configuration/nuxt-config // https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({ export default defineNuxtConfig({
@@ -14,9 +19,12 @@ export default defineNuxtConfig({
modules: [ modules: [
"@lewebsimple/nuxt-graphql", "@lewebsimple/nuxt-graphql",
"@nuxt/eslint", "@nuxt/eslint",
"@nuxt/image",
"@nuxt/ui", "@nuxt/ui",
"@nuxtjs/device",
"@nuxtjs/seo", "@nuxtjs/seo",
"nuxt-auth-utils", "nuxt-auth-utils",
"nuxt-svgo",
], ],
components: { components: {
@@ -39,6 +47,10 @@ export default defineNuxtConfig({
colorMode: false, colorMode: false,
}, },
runtimeConfig: {
wpUrl,
},
compatibilityDate: "2026-01-01", compatibilityDate: "2026-01-01",
nitro: { nitro: {
@@ -47,10 +59,15 @@ export default defineNuxtConfig({
deployConfig: true, deployConfig: true,
nodeCompat: true, nodeCompat: true,
wrangler: { wrangler: {
name: "foobar", // Project name
name: "wp-headless",
// Cloudflare Workers settings
compatibility_date: "2026-01-27", compatibility_date: "2026-01-27",
observability: { enabled: true },
preview_urls: false,
// Environment variables
vars: { vars: {
NODE_ENV: "production", NODE_ENV: "staging",
NUXT_SITE_URL: siteUrl, NUXT_SITE_URL: siteUrl,
NUXT_WP_URL: wpUrl, NUXT_WP_URL: wpUrl,
}, },
@@ -71,6 +88,11 @@ export default defineNuxtConfig({
}, },
graphql: { graphql: {
client: {
cache: {
keyVersion: version,
},
},
server: { server: {
context: ["server/graphql/context"], context: ["server/graphql/context"],
schema: { schema: {
@@ -79,10 +101,22 @@ export default defineNuxtConfig({
}, },
}, },
image: {
provider: enableCloudflareImage ? "cloudflare" : "none",
cloudflare: { baseURL: `${siteUrl}/` },
domains: [wpDomain],
format: ["avif", "webp"],
},
robots: { robots: {
sitemap: `${wpUrl}/sitemap_index.xml`, sitemap: `${wpUrl}/sitemap_index.xml`,
}, },
sitemap: false, sitemap: false,
svgo: {
autoImportPath: "~/assets/svg/",
componentPrefix: "Svg",
defaultImport: "component",
},
}); });

View File

@@ -1,7 +1,7 @@
{ {
"name": "@lewebsimple/moonshine", "name": "@lewebsimple/moonshine",
"description": "Headless WordPress theme based on Nuxt.", "description": "Headless WordPress theme based on Nuxt.",
"version": "0.1.7", "version": "0.1.11",
"type": "module", "type": "module",
"private": true, "private": true,
"scripts": { "scripts": {
@@ -10,18 +10,22 @@
"dev": "nuxt dev", "dev": "nuxt dev",
"lint": "eslint --fix .", "lint": "eslint --fix .",
"postinstall": "nuxt prepare", "postinstall": "nuxt prepare",
"preview": "pnpm run build && wrangler dev --port 3000", "preview": "WRANGLER_ENV=dev pnpm run build && wrangler dev --port 3000",
"release": "pnpm lint && changelogen --noAuthors --release --push", "release": "pnpm lint && changelogen --noAuthors --release --push",
"typecheck": "nuxt typecheck" "typecheck": "nuxt typecheck"
}, },
"dependencies": { "dependencies": {
"@iconify-json/cib": "^1.2.3",
"@iconify-json/lucide": "^1.2.87", "@iconify-json/lucide": "^1.2.87",
"@lewebsimple/nuxt-graphql": "^0.6.1", "@lewebsimple/nuxt-graphql": "^0.6.8",
"@nuxt/image": "^2.0.0",
"@nuxt/ui": "4.3.0", "@nuxt/ui": "4.3.0",
"@nuxtjs/seo": "^3.3.0", "@nuxtjs/device": "4.0.0",
"@nuxtjs/seo": "^3.4.0",
"jwt-decode": "^4.0.0", "jwt-decode": "^4.0.0",
"nuxt": "^4.3.0", "nuxt": "^4.3.0",
"nuxt-auth-utils": "^0.5.28", "nuxt-auth-utils": "^0.5.28",
"nuxt-svgo": "^4.2.6",
"tailwindcss": "^4.1.18", "tailwindcss": "^4.1.18",
"vue": "^3.5.27", "vue": "^3.5.27",
"vue-router": "^4.6.4", "vue-router": "^4.6.4",
@@ -33,7 +37,7 @@
"eslint": "^9.39.2", "eslint": "^9.39.2",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"vue-tsc": "^3.2.4", "vue-tsc": "^3.2.4",
"wrangler": "^4.61.0" "wrangler": "^4.61.1"
}, },
"pnpm": { "pnpm": {
"overrides": { "overrides": {

File diff suppressed because it is too large Load Diff

View File

@@ -51,6 +51,31 @@ interface AcfFieldGroupFields {
fieldGroupName: String @deprecated(reason: "Use __typename instead") fieldGroupName: String @deprecated(reason: "Use __typename instead")
} }
"""ACF Link field"""
type AcfLink {
"""The target of the link (_blank, etc)"""
target: String
"""The title of the link"""
title: String
"""The url of the link"""
url: String
}
"""
Connection between the GroupAbstractBuilderSectionsHeroSplitLayout_Fields type and the MediaItem type
"""
type AcfMediaItemConnectionEdge implements Edge & MediaItemConnectionEdge & OneToOneConnection {
"""
Opaque reference to the nodes position in the connection. Value can be used with pagination args.
"""
cursor: String
"""The node of the connection, without the edges"""
node: MediaItem!
}
"""Options Page registered by ACF""" """Options Page registered by ACF"""
interface AcfOptionsPage implements Node { interface AcfOptionsPage implements Node {
"""The globally unique ID for the object""" """The globally unique ID for the object"""
@@ -66,6 +91,21 @@ interface AcfOptionsPage implements Node {
parentId: String parentId: String
} }
"""ACF Phone field"""
type AcfPhone {
"""The country code associated with the phone number"""
country: String!
"""The phone number in E.164 format"""
e164: String!
"""The phone number extension, if any"""
extension: String
"""The phone number in national format"""
national: String!
}
"""The Headless Login authentication data.""" """The Headless Login authentication data."""
type AuthenticationData { type AuthenticationData {
"""A new authentication token to use in future requests.""" """A new authentication token to use in future requests."""
@@ -3250,6 +3290,19 @@ type GeneralSettings {
"""Code local de linstallation WordPress.""" """Code local de linstallation WordPress."""
language: String language: String
"""
The media item representing the site icon configured in site settings, used as the site&#039;s favicon and app icon.
"""
siteIcon: GeneralSettingsToMediaItemConnectionEdge
"""
Site icon URL configured in site settings, used as the site&#039;s favicon and app icon.
"""
siteIconUrl(
"""Size of the site icon in pixels. Defaults to 512. Max 512."""
size: Int
): String
""" """
Le numéro du jour de la semaine à laquelle la semaine devrait commencer. Le numéro du jour de la semaine à laquelle la semaine devrait commencer.
""" """
@@ -3268,6 +3321,17 @@ type GeneralSettings {
url: String url: String
} }
"""Connection between the GeneralSettings type and the MediaItem type"""
type GeneralSettingsToMediaItemConnectionEdge implements Edge & MediaItemConnectionEdge & OneToOneConnection {
"""
Opaque reference to the nodes position in the connection. Value can be used with pagination args.
"""
cursor: String
"""The node of the connection, without the edges"""
node: MediaItem!
}
"""The Login client options for the github provider.""" """The Login client options for the github provider."""
type GithubClientOptions implements LoginClientOptions { type GithubClientOptions implements LoginClientOptions {
"""The client ID.""" """The client ID."""
@@ -3375,6 +3439,72 @@ type GroupAbstractBuilder implements AcfFieldGroup & AcfFieldGroupFields & Group
sections: [GroupAbstractBuilderSections_Layout] sections: [GroupAbstractBuilderSections_Layout]
} }
"""
The &quot;GroupAbstractBuilderSectionsHeroSplitLayout&quot; Field Group. Added to the Schema by &quot;WPGraphQL for ACF&quot;.
"""
type GroupAbstractBuilderSectionsHeroSplitLayout implements AcfFieldGroup & AcfFieldGroupFields & GroupAbstractBuilderSectionsHeroSplitLayout_Fields & GroupAbstractBuilderSections_Layout & GroupAbstractMedia_Fields {
"""
Field of the &quot;button_group&quot; Field Type added to the schema as part of the &quot;GroupAbstractBuilderSectionsHeroSplitLayout&quot; Field Group
"""
aspectRatio: String!
"""
Field of the &quot;wysiwyg&quot; Field Type added to the schema as part of the &quot;GroupAbstractBuilderSectionsHeroSplitLayout&quot; Field Group
"""
content: String!
"""The name of the field group"""
fieldGroupName: String @deprecated(reason: "Use __typename instead")
"""
Field of the &quot;image&quot; Field Type added to the schema as part of the &quot;GroupAbstractMedia&quot; Field Group
"""
image: AcfMediaItemConnectionEdge
"""
Field of the &quot;button_group&quot; Field Type added to the schema as part of the &quot;GroupAbstractBuilderSectionsHeroSplitLayout&quot; Field Group
"""
objectFit: String!
"""
Field of the &quot;true_false&quot; Field Type added to the schema as part of the &quot;GroupAbstractBuilderSectionsHeroSplitLayout&quot; Field Group
"""
reverse: Boolean!
}
"""
Interface representing fields of the ACF &quot;GroupAbstractBuilderSectionsHeroSplitLayout&quot; Field Group
"""
interface GroupAbstractBuilderSectionsHeroSplitLayout_Fields implements AcfFieldGroup & AcfFieldGroupFields & GroupAbstractBuilderSections_Layout & GroupAbstractMedia_Fields {
"""
Field of the &quot;button_group&quot; Field Type added to the schema as part of the &quot;GroupAbstractBuilderSectionsHeroSplitLayout&quot; Field Group
"""
aspectRatio: String!
"""
Field of the &quot;wysiwyg&quot; Field Type added to the schema as part of the &quot;GroupAbstractBuilderSectionsHeroSplitLayout&quot; Field Group
"""
content: String!
"""The name of the field group"""
fieldGroupName: String @deprecated(reason: "Use __typename instead")
"""
Field of the &quot;image&quot; Field Type added to the schema as part of the &quot;GroupAbstractMedia&quot; Field Group
"""
image: AcfMediaItemConnectionEdge
"""
Field of the &quot;button_group&quot; Field Type added to the schema as part of the &quot;GroupAbstractBuilderSectionsHeroSplitLayout&quot; Field Group
"""
objectFit: String!
"""
Field of the &quot;true_false&quot; Field Type added to the schema as part of the &quot;GroupAbstractBuilderSectionsHeroSplitLayout&quot; Field Group
"""
reverse: Boolean!
}
""" """
The &quot;GroupAbstractBuilderSectionsLayoutSettings&quot; Field Group. Added to the Schema by &quot;WPGraphQL for ACF&quot;. The &quot;GroupAbstractBuilderSectionsLayoutSettings&quot; Field Group. Added to the Schema by &quot;WPGraphQL for ACF&quot;.
""" """
@@ -3463,6 +3593,104 @@ interface GroupAbstractBuilder_Fields implements AcfFieldGroup & AcfFieldGroupFi
sections: [GroupAbstractBuilderSections_Layout] sections: [GroupAbstractBuilderSections_Layout]
} }
"""
The &quot;GroupAbstractMedia&quot; Field Group. Added to the Schema by &quot;WPGraphQL for ACF&quot;.
"""
type GroupAbstractMedia implements AcfFieldGroup & AcfFieldGroupFields & GroupAbstractMedia_Fields {
"""
Field of the &quot;button_group&quot; Field Type added to the schema as part of the &quot;GroupAbstractMedia&quot; Field Group
"""
aspectRatio: String!
"""The name of the field group"""
fieldGroupName: String @deprecated(reason: "Use __typename instead")
"""
Field of the &quot;image&quot; Field Type added to the schema as part of the &quot;GroupAbstractMedia&quot; Field Group
"""
image: AcfMediaItemConnectionEdge
"""
Field of the &quot;button_group&quot; Field Type added to the schema as part of the &quot;GroupAbstractMedia&quot; Field Group
"""
objectFit: String!
}
"""
Interface representing fields of the ACF &quot;GroupAbstractMedia&quot; Field Group
"""
interface GroupAbstractMedia_Fields implements AcfFieldGroup & AcfFieldGroupFields {
"""
Field of the &quot;button_group&quot; Field Type added to the schema as part of the &quot;GroupAbstractMedia&quot; Field Group
"""
aspectRatio: String!
"""The name of the field group"""
fieldGroupName: String @deprecated(reason: "Use __typename instead")
"""
Field of the &quot;image&quot; Field Type added to the schema as part of the &quot;GroupAbstractMedia&quot; Field Group
"""
image: AcfMediaItemConnectionEdge
"""
Field of the &quot;button_group&quot; Field Type added to the schema as part of the &quot;GroupAbstractMedia&quot; Field Group
"""
objectFit: String!
}
"""
The &quot;GroupAbstractSocial&quot; Field Group. Added to the Schema by &quot;WPGraphQL for ACF&quot;.
"""
type GroupAbstractSocial implements AcfFieldGroup & AcfFieldGroupFields & GroupAbstractSocial_Fields {
"""The name of the field group"""
fieldGroupName: String @deprecated(reason: "Use __typename instead")
"""
Field of the &quot;repeater&quot; Field Type added to the schema as part of the &quot;GroupAbstractSocial&quot; Field Group
"""
profiles: [GroupAbstractSocialProfiles]!
}
"""
The &quot;GroupAbstractSocialProfiles&quot; Field Group. Added to the Schema by &quot;WPGraphQL for ACF&quot;.
"""
type GroupAbstractSocialProfiles implements AcfFieldGroup & AcfFieldGroupFields & GroupAbstractSocialProfiles_Fields {
"""The name of the field group"""
fieldGroupName: String @deprecated(reason: "Use __typename instead")
"""
Field of the &quot;url&quot; Field Type added to the schema as part of the &quot;GroupAbstractSocialProfiles&quot; Field Group
"""
url: String!
}
"""
Interface representing fields of the ACF &quot;GroupAbstractSocialProfiles&quot; Field Group
"""
interface GroupAbstractSocialProfiles_Fields implements AcfFieldGroup & AcfFieldGroupFields {
"""The name of the field group"""
fieldGroupName: String @deprecated(reason: "Use __typename instead")
"""
Field of the &quot;url&quot; Field Type added to the schema as part of the &quot;GroupAbstractSocialProfiles&quot; Field Group
"""
url: String!
}
"""
Interface representing fields of the ACF &quot;GroupAbstractSocial&quot; Field Group
"""
interface GroupAbstractSocial_Fields implements AcfFieldGroup & AcfFieldGroupFields {
"""The name of the field group"""
fieldGroupName: String @deprecated(reason: "Use __typename instead")
"""
Field of the &quot;repeater&quot; Field Type added to the schema as part of the &quot;GroupAbstractSocial&quot; Field Group
"""
profiles: [GroupAbstractSocialProfiles]!
}
""" """
The &quot;GroupLayoutContained&quot; Field Group. Added to the Schema by &quot;WPGraphQL for ACF&quot;. The &quot;GroupLayoutContained&quot; Field Group. Added to the Schema by &quot;WPGraphQL for ACF&quot;.
""" """
@@ -3536,29 +3764,85 @@ interface GroupPostPage_Fields implements AcfFieldGroup & AcfFieldGroupFields &
} }
""" """
The &quot;GroupSite&quot; Field Group. Added to the Schema by &quot;WPGraphQL for ACF&quot;. The &quot;GroupSiteOptions&quot; Field Group. Added to the Schema by &quot;WPGraphQL for ACF&quot;.
""" """
type GroupSite implements AcfFieldGroup & AcfFieldGroupFields & GroupSite_Fields { type GroupSiteOptions implements AcfFieldGroup & AcfFieldGroupFields & GroupAbstractSocial_Fields & GroupSiteOptions_Fields {
""" """
Field of the &quot;email&quot; Field Type added to the schema as part of the &quot;GroupSite&quot; Field Group Field of the &quot;email&quot; Field Type added to the schema as part of the &quot;GroupSiteOptions&quot; Field Group
""" """
email: String! email: String!
"""The name of the field group""" """The name of the field group"""
fieldGroupName: String @deprecated(reason: "Use __typename instead") fieldGroupName: String @deprecated(reason: "Use __typename instead")
"""
Field of the &quot;group&quot; Field Type added to the schema as part of the &quot;GroupSiteOptions&quot; Field Group
"""
links: GroupSiteOptionsLinks
"""
Field of the &quot;phone&quot; Field Type added to the schema as part of the &quot;GroupSiteOptions&quot; Field Group
"""
phoneNumber: AcfPhone!
"""
Field of the &quot;repeater&quot; Field Type added to the schema as part of the &quot;GroupAbstractSocial&quot; Field Group
"""
profiles: [GroupAbstractSocialProfiles]!
}
"""
The &quot;GroupSiteOptionsLinks&quot; Field Group. Added to the Schema by &quot;WPGraphQL for ACF&quot;.
"""
type GroupSiteOptionsLinks implements AcfFieldGroup & AcfFieldGroupFields & GroupSiteOptionsLinks_Fields {
"""
Field of the &quot;link&quot; Field Type added to the schema as part of the &quot;GroupSiteOptionsLinks&quot; Field Group
"""
contact: AcfLink!
"""The name of the field group"""
fieldGroupName: String @deprecated(reason: "Use __typename instead")
} }
""" """
Interface representing fields of the ACF &quot;GroupSite&quot; Field Group Interface representing fields of the ACF &quot;GroupSiteOptionsLinks&quot; Field Group
""" """
interface GroupSite_Fields implements AcfFieldGroup & AcfFieldGroupFields { interface GroupSiteOptionsLinks_Fields implements AcfFieldGroup & AcfFieldGroupFields {
""" """
Field of the &quot;email&quot; Field Type added to the schema as part of the &quot;GroupSite&quot; Field Group Field of the &quot;link&quot; Field Type added to the schema as part of the &quot;GroupSiteOptionsLinks&quot; Field Group
"""
contact: AcfLink!
"""The name of the field group"""
fieldGroupName: String @deprecated(reason: "Use __typename instead")
}
"""
Interface representing fields of the ACF &quot;GroupSiteOptions&quot; Field Group
"""
interface GroupSiteOptions_Fields implements AcfFieldGroup & AcfFieldGroupFields & GroupAbstractSocial_Fields {
"""
Field of the &quot;email&quot; Field Type added to the schema as part of the &quot;GroupSiteOptions&quot; Field Group
""" """
email: String! email: String!
"""The name of the field group""" """The name of the field group"""
fieldGroupName: String @deprecated(reason: "Use __typename instead") fieldGroupName: String @deprecated(reason: "Use __typename instead")
"""
Field of the &quot;group&quot; Field Type added to the schema as part of the &quot;GroupSiteOptions&quot; Field Group
"""
links: GroupSiteOptionsLinks
"""
Field of the &quot;phone&quot; Field Type added to the schema as part of the &quot;GroupSiteOptions&quot; Field Group
"""
phoneNumber: AcfPhone!
"""
Field of the &quot;repeater&quot; Field Type added to the schema as part of the &quot;GroupAbstractSocial&quot; Field Group
"""
profiles: [GroupAbstractSocialProfiles]!
} }
""" """
@@ -4578,6 +4862,9 @@ type MediaItem implements ContentNode & DatabaseIdentifier & HierarchicalContent
""" """
modifiedGmt: String modifiedGmt: String
"""CSS object-position value from Media Focus Point plugin"""
objectPosition: String
"""The parent of the node. The parent object can be of various types""" """The parent of the node. The parent object can be of various types"""
parent: HierarchicalContentNodeToParentContentNodeConnectionEdge parent: HierarchicalContentNodeToParentContentNodeConnectionEdge
@@ -6025,23 +6312,6 @@ interface OneToOneConnection implements Edge {
node: Node! node: Node!
} }
type OptionsSite implements AcfOptionsPage & Node & WithAcfGroupSite {
"""Fields of the GroupSite ACF Field Group"""
groupSite: GroupSite
"""The globally unique ID for the object"""
id: ID!
""""""
menuTitle: String
""""""
pageTitle: String
""""""
parentId: String
}
""" """
Sort direction for ordered results. Determines whether items are returned in ascending or descending order. Sort direction for ordered results. Determines whether items are returned in ascending or descending order.
""" """
@@ -8708,7 +8978,7 @@ interface Previewable {
} }
"""The root entry point into the Graph""" """The root entry point into the Graph"""
type Query implements WithAcfOptionsPageOptionsSite { type Query implements WithAcfOptionsPageSiteOptions {
"""Entry point to get all settings for the site""" """Entry point to get all settings for the site"""
allSettings: Settings allSettings: Settings
@@ -8993,9 +9263,6 @@ type Query implements WithAcfOptionsPageOptionsSite {
uri: String! uri: String!
): UniformResourceIdentifiable ): UniformResourceIdentifiable
""""""
optionsSite: OptionsSite
"""An object of the page Type. """ """An object of the page Type. """
page( page(
""" """
@@ -9220,6 +9487,9 @@ type Query implements WithAcfOptionsPageOptionsSite {
where: RootQueryToRevisionsConnectionWhereArgs where: RootQueryToRevisionsConnectionWhereArgs
): RootQueryToRevisionsConnection ): RootQueryToRevisionsConnection
""""""
siteOptions: SiteOptions
"""A 0bject""" """A 0bject"""
tag( tag(
"""The globally unique identifier of the object.""" """The globally unique identifier of the object."""
@@ -9478,6 +9748,18 @@ type RankMathAuthorArchiveMetaSettings implements RankMathMetaSettingWithArchive
robotsMeta: [RankMathRobotsMetaValueEnum] robotsMeta: [RankMathRobotsMetaValueEnum]
} }
"""The Breadcrumb trail."""
type RankMathBreadcrumbs {
"""Whether the given breadcrumb is hidden from the schema"""
isHidden: Boolean
"""The text for the given breadcrumb"""
text: String
"""The url for the given breadcrumb"""
url: String
}
"""The RankMath SEO breadcrumbs settings.""" """The RankMath SEO breadcrumbs settings."""
type RankMathBreadcrumbsConfig { type RankMathBreadcrumbsConfig {
"""Format the label used for archive pages.""" """Format the label used for archive pages."""
@@ -9533,6 +9815,9 @@ type RankMathCategoryTermSeo implements RankMathSeo {
"""The title to use in the breadcrumbs for this post""" """The title to use in the breadcrumbs for this post"""
breadcrumbTitle: String breadcrumbTitle: String
"""The breadcrumbs trail for the given object"""
breadcrumbs: [RankMathBreadcrumbs]
"""The canonical url.""" """The canonical url."""
canonicalUrl: String canonicalUrl: String
@@ -9563,6 +9848,9 @@ interface RankMathContentNodeSeo implements RankMathSeo {
"""The title to use in the breadcrumbs for this post""" """The title to use in the breadcrumbs for this post"""
breadcrumbTitle: String breadcrumbTitle: String
"""The breadcrumbs trail for the given object"""
breadcrumbs: [RankMathBreadcrumbs]
"""The canonical url.""" """The canonical url."""
canonicalUrl: String canonicalUrl: String
@@ -9825,6 +10113,9 @@ type RankMathMediaItemObjectSeo implements RankMathContentNodeSeo & RankMathSeo
"""The title to use in the breadcrumbs for this post""" """The title to use in the breadcrumbs for this post"""
breadcrumbTitle: String breadcrumbTitle: String
"""The breadcrumbs trail for the given object"""
breadcrumbs: [RankMathBreadcrumbs]
"""The canonical url.""" """The canonical url."""
canonicalUrl: String canonicalUrl: String
@@ -9861,6 +10152,9 @@ type RankMathMediaItemTypeSeo implements RankMathSeo {
"""The title to use in the breadcrumbs for this post""" """The title to use in the breadcrumbs for this post"""
breadcrumbTitle: String breadcrumbTitle: String
"""The breadcrumbs trail for the given object"""
breadcrumbs: [RankMathBreadcrumbs]
"""The canonical url.""" """The canonical url."""
canonicalUrl: String canonicalUrl: String
@@ -10602,6 +10896,9 @@ type RankMathPageObjectSeo implements RankMathContentNodeSeo & RankMathSeo {
"""The title to use in the breadcrumbs for this post""" """The title to use in the breadcrumbs for this post"""
breadcrumbTitle: String breadcrumbTitle: String
"""The breadcrumbs trail for the given object"""
breadcrumbs: [RankMathBreadcrumbs]
"""The canonical url.""" """The canonical url."""
canonicalUrl: String canonicalUrl: String
@@ -10638,6 +10935,9 @@ type RankMathPageTypeSeo implements RankMathSeo {
"""The title to use in the breadcrumbs for this post""" """The title to use in the breadcrumbs for this post"""
breadcrumbTitle: String breadcrumbTitle: String
"""The breadcrumbs trail for the given object"""
breadcrumbs: [RankMathBreadcrumbs]
"""The canonical url.""" """The canonical url."""
canonicalUrl: String canonicalUrl: String
@@ -10668,6 +10968,9 @@ type RankMathPostFormatTermSeo implements RankMathSeo {
"""The title to use in the breadcrumbs for this post""" """The title to use in the breadcrumbs for this post"""
breadcrumbTitle: String breadcrumbTitle: String
"""The breadcrumbs trail for the given object"""
breadcrumbs: [RankMathBreadcrumbs]
"""The canonical url.""" """The canonical url."""
canonicalUrl: String canonicalUrl: String
@@ -10698,6 +11001,9 @@ type RankMathPostObjectSeo implements RankMathContentNodeSeo & RankMathSeo {
"""The title to use in the breadcrumbs for this post""" """The title to use in the breadcrumbs for this post"""
breadcrumbTitle: String breadcrumbTitle: String
"""The breadcrumbs trail for the given object"""
breadcrumbs: [RankMathBreadcrumbs]
"""The canonical url.""" """The canonical url."""
canonicalUrl: String canonicalUrl: String
@@ -10734,6 +11040,9 @@ type RankMathPostTypeSeo implements RankMathSeo {
"""The title to use in the breadcrumbs for this post""" """The title to use in the breadcrumbs for this post"""
breadcrumbTitle: String breadcrumbTitle: String
"""The breadcrumbs trail for the given object"""
breadcrumbs: [RankMathBreadcrumbs]
"""The canonical url.""" """The canonical url."""
canonicalUrl: String canonicalUrl: String
@@ -10806,6 +11115,9 @@ interface RankMathSeo {
"""The title to use in the breadcrumbs for this post""" """The title to use in the breadcrumbs for this post"""
breadcrumbTitle: String breadcrumbTitle: String
"""The breadcrumbs trail for the given object"""
breadcrumbs: [RankMathBreadcrumbs]
"""The canonical url.""" """The canonical url."""
canonicalUrl: String canonicalUrl: String
@@ -11253,6 +11565,9 @@ type RankMathTagTermSeo implements RankMathSeo {
"""The title to use in the breadcrumbs for this post""" """The title to use in the breadcrumbs for this post"""
breadcrumbTitle: String breadcrumbTitle: String
"""The breadcrumbs trail for the given object"""
breadcrumbs: [RankMathBreadcrumbs]
"""The canonical url.""" """The canonical url."""
canonicalUrl: String canonicalUrl: String
@@ -11313,6 +11628,9 @@ type RankMathUserSeo implements RankMathSeo {
"""The title to use in the breadcrumbs for this post""" """The title to use in the breadcrumbs for this post"""
breadcrumbTitle: String breadcrumbTitle: String
"""The breadcrumbs trail for the given object"""
breadcrumbs: [RankMathBreadcrumbs]
"""The canonical url.""" """The canonical url."""
canonicalUrl: String canonicalUrl: String
@@ -13408,6 +13726,23 @@ type Settings {
writingSettingsUseSmilies: Boolean writingSettingsUseSmilies: Boolean
} }
type SiteOptions implements AcfOptionsPage & Node & WithAcfGroupSiteOptions {
"""Fields of the GroupSiteOptions ACF Field Group"""
groupSiteOptions: GroupSiteOptions
"""The globally unique ID for the object"""
id: ID!
""""""
menuTitle: String
""""""
pageTitle: String
""""""
parentId: String
}
"""The Login client options for the siteToken provider.""" """The Login client options for the siteToken provider."""
type SiteTokenClientOptions implements LoginClientOptions { type SiteTokenClientOptions implements LoginClientOptions {
""" """
@@ -16359,17 +16694,17 @@ interface WithAcfGroupPostPage {
} }
""" """
Provides access to fields of the &quot;GroupSite&quot; ACF Field Group via the &quot;groupSite&quot; field Provides access to fields of the &quot;GroupSiteOptions&quot; ACF Field Group via the &quot;groupSiteOptions&quot; field
""" """
interface WithAcfGroupSite { interface WithAcfGroupSiteOptions {
"""Fields of the GroupSite ACF Field Group""" """Fields of the GroupSiteOptions ACF Field Group"""
groupSite: GroupSite groupSiteOptions: GroupSiteOptions
} }
"""Access point for the &quot;OptionsSite&quot; ACF Options Page""" """Access point for the &quot;SiteOptions&quot; ACF Options Page"""
interface WithAcfOptionsPageOptionsSite { interface WithAcfOptionsPageSiteOptions {
"""""" """"""
optionsSite: OptionsSite siteOptions: SiteOptions
} }
"""The writing setting type""" """The writing setting type"""

View File

@@ -1,11 +1,10 @@
import { defu } from "defu";
export default defineRemoteExecutorHooks({ export default defineRemoteExecutorHooks({
onRequest(request) { onRequest(request) {
if (request.context.authToken) { // Attach the Authorization header if an authToken is present in the context
request.extensions ??= {}; if (request.context?.authToken) {
request.extensions.headers = { request.extensions = defu(request.extensions, { headers: { Authorization: `Bearer ${request.context.authToken}` } });
...request.extensions.headers,
Authorization: `Bearer ${request.context.authToken}`,
};
} }
}, },
}); });

View File

@@ -40,15 +40,35 @@ function getAuthUser(user: AuthUserFragment): User {
}; };
} }
// Track in-flight refreshAuthToken calls to prevent duplicate requests
const refreshTokenPromises = new Map<string, Promise<string | undefined>>();
// Refresh auth token by calling remote GraphQL endpoint directly // Refresh auth token by calling remote GraphQL endpoint directly
export async function refreshAuthToken(refreshToken: string): Promise<string | undefined> { export async function refreshAuthToken(refreshToken: string): Promise<string | undefined> {
// TODO: const { public: { graphql: { endpoint } } } = useRuntimeConfig(); // Return existing in-flight promise if available
const endpoint = `${process.env.NUXT_WP_URL || "https://cultureat.ledevsimple.ca"}/graphql`; const inFlight = refreshTokenPromises.get(refreshToken);
if (inFlight) {
return inFlight;
}
const refreshPromise = (async () => {
const { wpUrl } = useRuntimeConfig();
const endpoint = `${wpUrl}/graphql`;
const { data } = await executeGraphQLHTTP<ResultOf<"AuthRefreshToken">>({ const { data } = await executeGraphQLHTTP<ResultOf<"AuthRefreshToken">>({
query: AuthRefreshTokenDocument, query: AuthRefreshTokenDocument,
variables: { refreshToken }, variables: { refreshToken },
}, { endpoint }); }, { endpoint });
return data?.refreshToken?.authToken || undefined; return data?.refreshToken?.authToken || undefined;
})();
refreshTokenPromises.set(refreshToken, refreshPromise);
return refreshPromise.finally(() => {
const current = refreshTokenPromises.get(refreshToken);
if (current === refreshPromise) {
refreshTokenPromises.delete(refreshToken);
}
});
} }
// Get auth token from user session (refresh if needed) // Get auth token from user session (refresh if needed)

View File

@@ -1,4 +1,4 @@
// Helper: Extracts nodes from a GraphQL connection object, returning an empty array if nodes are absent. // Helper: Extracts nodes from a GraphQL connection object, returning an empty array if nodes are absent.
export function extractNodes<T>(connection: { nodes?: readonly T[] } | null | undefined): readonly T[] { export function extractNodes<T>(connection: { nodes?: T[] } | undefined): T[] {
return connection?.nodes ?? []; return connection?.nodes ?? [];
} }

View File

@@ -1,21 +0,0 @@
/**
* For more details on how to configure Wrangler, refer to:
* https://developers.cloudflare.com/workers/wrangler/configuration/
*/
{
"$schema": "node_modules/wrangler/config-schema.json",
"main": "./.output/server/index.mjs",
/**
* Static Assets Binding
*/
"assets": {
"binding": "ASSETS",
"directory": "./.output/public/"
},
/**
* Observability & Analytics
*/
"observability": {
"enabled": true
},
}