Compare commits

..

No commits in common. "6782e4d40b1dab2b255e0112ec4c4f235bbb09e0" and "ea865574b7da4452230b29853c645859ba336db6" have entirely different histories.

31 changed files with 870 additions and 1982 deletions

View File

@ -1 +1 @@
[["Map",1,2],"meta::meta",["Map",3,4,5,6],"astro-version","5.18.1","astro-config-digest","{\"root\":{},\"srcDir\":{},\"publicDir\":{},\"outDir\":{},\"cacheDir\":{},\"compressHTML\":true,\"base\":\"/\",\"trailingSlash\":\"ignore\",\"output\":\"server\",\"scopedStyleStrategy\":\"attribute\",\"build\":{\"format\":\"directory\",\"client\":{},\"server\":{},\"assets\":\"_astro\",\"serverEntry\":\"entry.mjs\",\"redirects\":false,\"inlineStylesheets\":\"auto\",\"concurrency\":1},\"server\":{\"open\":false,\"host\":false,\"port\":4321,\"streaming\":true,\"allowedHosts\":[]},\"redirects\":{},\"image\":{\"endpoint\":{\"route\":\"/_image\",\"entrypoint\":\"astro/assets/endpoint/dev\"},\"service\":{\"entrypoint\":\"astro/assets/services/sharp\",\"config\":{}},\"domains\":[],\"remotePatterns\":[],\"responsiveStyles\":false},\"devToolbar\":{\"enabled\":true},\"markdown\":{\"syntaxHighlight\":{\"type\":\"shiki\",\"excludeLangs\":[\"math\"]},\"shikiConfig\":{\"langs\":[],\"langAlias\":{},\"theme\":\"github-dark\",\"themes\":{},\"wrap\":false,\"transformers\":[]},\"remarkPlugins\":[],\"rehypePlugins\":[],\"remarkRehype\":{},\"gfm\":true,\"smartypants\":true},\"security\":{\"checkOrigin\":true,\"allowedDomains\":[],\"actionBodySizeLimit\":1048576},\"env\":{\"schema\":{},\"validateSecrets\":false},\"experimental\":{\"clientPrerender\":false,\"contentIntellisense\":false,\"headingIdCompat\":false,\"preserveScriptOrder\":false,\"liveContentCollections\":false,\"csp\":false,\"staticImportMetaEnv\":false,\"chromeDevtoolsWorkspace\":false,\"failOnPrerenderConflict\":false,\"svgo\":false},\"legacy\":{\"collections\":false},\"session\":{\"driver\":\"fs-lite\",\"options\":{\"base\":\"/Users/arthurbarre/dev/freelance/rebours/node_modules/.astro/sessions\"}}}"] [["Map",1,2],"meta::meta",["Map",3,4,5,6],"astro-version","5.18.1","astro-config-digest","{\"root\":{},\"srcDir\":{},\"publicDir\":{},\"outDir\":{},\"cacheDir\":{},\"compressHTML\":true,\"base\":\"/\",\"trailingSlash\":\"ignore\",\"output\":\"static\",\"scopedStyleStrategy\":\"attribute\",\"build\":{\"format\":\"directory\",\"client\":{},\"server\":{},\"assets\":\"_astro\",\"serverEntry\":\"entry.mjs\",\"redirects\":true,\"inlineStylesheets\":\"auto\",\"concurrency\":1},\"server\":{\"open\":false,\"host\":false,\"port\":4321,\"streaming\":true,\"allowedHosts\":[]},\"redirects\":{},\"image\":{\"endpoint\":{\"route\":\"/_image\"},\"service\":{\"entrypoint\":\"astro/assets/services/sharp\",\"config\":{}},\"domains\":[],\"remotePatterns\":[],\"responsiveStyles\":false},\"devToolbar\":{\"enabled\":true},\"markdown\":{\"syntaxHighlight\":{\"type\":\"shiki\",\"excludeLangs\":[\"math\"]},\"shikiConfig\":{\"langs\":[],\"langAlias\":{},\"theme\":\"github-dark\",\"themes\":{},\"wrap\":false,\"transformers\":[]},\"remarkPlugins\":[],\"rehypePlugins\":[],\"remarkRehype\":{},\"gfm\":true,\"smartypants\":true},\"security\":{\"checkOrigin\":true,\"allowedDomains\":[],\"actionBodySizeLimit\":1048576},\"env\":{\"schema\":{},\"validateSecrets\":false},\"experimental\":{\"clientPrerender\":false,\"contentIntellisense\":false,\"headingIdCompat\":false,\"preserveScriptOrder\":false,\"liveContentCollections\":false,\"csp\":false,\"staticImportMetaEnv\":false,\"chromeDevtoolsWorkspace\":false,\"failOnPrerenderConflict\":false,\"svgo\":false},\"legacy\":{\"collections\":false}}"]

1
.astro/types.d.ts vendored
View File

@ -1,2 +1 @@
/// <reference types="astro/client" /> /// <reference types="astro/client" />
/// <reference path="content.d.ts" />

View File

@ -3,8 +3,8 @@
"configurations": [ "configurations": [
{ {
"name": "rebours-dev", "name": "rebours-dev",
"runtimeExecutable": "/Users/arthurbarre/.nvm/versions/node/v25.6.1/bin/node", "runtimeExecutable": "pnpm",
"runtimeArgs": ["/Users/arthurbarre/.nvm/versions/node/v25.6.1/bin/pnpm", "dev"], "runtimeArgs": ["dev"],
"port": 4321 "port": 4321
} }
] ]

View File

@ -1,18 +0,0 @@
node_modules
dist
.git
.gitignore
.env
.env.*
*.md
.vscode
.idea
.DS_Store
.astro
sanity
k8s
.gitea
migrate-images.mjs
seed-sanity.mjs
seed-sanity-homepage.mjs
clean-duplicates.mjs

View File

@ -1,99 +0,0 @@
name: Build & Deploy to K3s
on:
push:
branches: [main]
env:
REGISTRY: git.arthurbarre.fr
SSR_IMAGE: git.arthurbarre.fr/ordinarthur/rebours-ssr
API_IMAGE: git.arthurbarre.fr/ordinarthur/rebours-api
REGISTRY_USER: ordinarthur
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Login to Gitea Container Registry
run: |
echo "${{ secrets.REGISTRY_PASSWORD }}" | \
docker login ${{ env.REGISTRY }} -u ${{ env.REGISTRY_USER }} --password-stdin
- name: Build SSR image
run: |
docker build \
-f Dockerfile.ssr \
-t ${{ env.SSR_IMAGE }}:${{ github.sha }} \
-t ${{ env.SSR_IMAGE }}:latest \
.
- name: Build API image
run: |
docker build \
-f Dockerfile.api \
-t ${{ env.API_IMAGE }}:${{ github.sha }} \
-t ${{ env.API_IMAGE }}:latest \
.
- name: Push SSR image
run: |
docker push ${{ env.SSR_IMAGE }}:${{ github.sha }}
docker push ${{ env.SSR_IMAGE }}:latest
- name: Push API image
run: |
docker push ${{ env.API_IMAGE }}:${{ github.sha }}
docker push ${{ env.API_IMAGE }}:latest
- name: Install kubectl
run: |
curl -LO "https://dl.k8s.io/release/$(curl -Ls https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
mv kubectl /usr/local/bin/kubectl
- name: Configure kubeconfig
run: |
mkdir -p ~/.kube
echo "${{ secrets.KUBECONFIG }}" | base64 -d > ~/.kube/config
- name: Apply namespace and shared resources
run: |
kubectl apply -f k8s/namespace.yml
kubectl apply -f k8s/configmap.yml
kubectl apply -f k8s/service.yml
- name: Create image pull secret
run: |
kubectl -n rebours create secret docker-registry gitea-registry-secret \
--docker-server=${{ env.REGISTRY }} \
--docker-username=${{ env.REGISTRY_USER }} \
--docker-password="${{ secrets.REGISTRY_PASSWORD }}" \
--dry-run=client -o yaml | kubectl apply -f -
- name: Create app secrets
run: |
kubectl -n rebours create secret generic rebours-secrets \
--from-literal=STRIPE_SECRET_KEY="${{ secrets.STRIPE_SECRET_KEY }}" \
--from-literal=STRIPE_WEBHOOK_SECRET="${{ secrets.STRIPE_WEBHOOK_SECRET }}" \
--from-literal=SANITY_API_TOKEN="${{ secrets.SANITY_API_TOKEN }}" \
--dry-run=client -o yaml | kubectl apply -f -
- name: Deploy workloads
run: |
kubectl apply -f k8s/deployment.yml
kubectl -n rebours set image deployment/rebours-ssr \
rebours-ssr=${{ env.SSR_IMAGE }}:${{ github.sha }}
kubectl -n rebours set image deployment/rebours-api \
rebours-api=${{ env.API_IMAGE }}:${{ github.sha }}
kubectl -n rebours rollout status deployment/rebours-api --timeout=120s
kubectl -n rebours rollout status deployment/rebours-ssr --timeout=180s
kubectl -n rebours rollout status deployment/rebours-proxy --timeout=60s
- name: Cleanup old images
run: |
docker image prune -f

View File

@ -37,7 +37,7 @@ rebours/
| Couche | Techno | | Couche | Techno |
|--------|--------| |--------|--------|
| Front (SSR) | Astro + HTML/CSS/JS vanilla + GSAP | | Front (SSG) | Astro + HTML/CSS/JS vanilla + GSAP |
| CMS | Sanity (headless, hébergé) | | CMS | Sanity (headless, hébergé) |
| API | Fastify (Node.js) | | API | Fastify (Node.js) |
| Paiement | Stripe Checkout (price_data inline) | | Paiement | Stripe Checkout (price_data inline) |
@ -65,21 +65,20 @@ STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_... STRIPE_WEBHOOK_SECRET=whsec_...
DOMAIN=http://localhost:4321 DOMAIN=http://localhost:4321
FASTIFY_PORT=3000 # Port Fastify API (prod) PORT=8888
ASTRO_PORT=4321 # Port Astro SSR (prod)
``` ```
### Lancer le projet ### Lancer le projet
```bash ```bash
pnpm install npm install
pnpm dev npm run dev
``` ```
Cela lance en parallèle (via `concurrently`) : Cela lance en parallèle (via `concurrently`) :
- `astro dev` sur http://localhost:4321 - `astro dev` sur http://localhost:4321
- `node --watch server.mjs` (mode dev) - `node --watch server.mjs` (mode dev, PORT=8888)
Le proxy Vite dans `astro.config.mjs` redirige `/api/*` vers le serveur Fastify. Le proxy Vite dans `astro.config.mjs` redirige `/api/*` vers `http://127.0.0.1:8888`.
### Sanity Studio ### Sanity Studio
```bash ```bash
@ -91,8 +90,8 @@ Accessible sur http://localhost:3333
### Build ### Build
```bash ```bash
pnpm build npm run build
# Génère ./dist/ (serveur Astro SSR + assets client) # Génère ./dist/ (fichiers statiques Astro)
``` ```
--- ---
@ -117,7 +116,8 @@ Champs principaux :
1. Ouvrir Sanity Studio 1. Ouvrir Sanity Studio
2. Créer un nouveau document "Produit" 2. Créer un nouveau document "Produit"
3. Remplir les champs, uploader l'image 3. Remplir les champs, uploader l'image
4. Publier → visible immédiatement sur le site (SSR, pas de rebuild nécessaire) 4. Publier
5. Rebuild le site : `npm run build` + déployer
### Images ### Images
Les images sont servies via le CDN Sanity avec transformations automatiques. Les images sont servies via le CDN Sanity avec transformations automatiques.
@ -154,19 +154,12 @@ Quand un client clique "Commander" :
### Serveur : ordinarthur@10.10.0.13 ### Serveur : ordinarthur@10.10.0.13
### Architecture prod (SSR) ### Architecture prod
``` ```
Internet -> Nginx (port 80) -> / -> proxy -> Astro SSR :4321 Internet -> Nginx (port 80) -> /var/www/html/rebours/dist/ (statiques)
-> /_astro/* -> fichiers statiques (dist/client/) -> /api/* -> proxy -> Fastify :3000
-> /api/* -> proxy -> Fastify :3000
``` ```
### Services systemd
| Service | Port | Rôle |
|---------|------|------|
| `rebours-ssr` | 4321 | Astro SSR (pages dynamiques) |
| `rebours` | 3000 | Fastify API (Stripe, checkout) |
### Variables d'environnement en prod ### Variables d'environnement en prod
```env ```env
SANITY_PROJECT_ID=... SANITY_PROJECT_ID=...
@ -174,15 +167,14 @@ SANITY_DATASET=production
STRIPE_SECRET_KEY=sk_live_... STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_... STRIPE_WEBHOOK_SECRET=whsec_...
DOMAIN=https://rebours.studio DOMAIN=https://rebours.studio
FASTIFY_PORT=3000 PORT=3000
ASTRO_PORT=4321
``` ```
### Déploiement ### Déploiement
```bash ```bash
pnpm build npm run build
scp -r dist/* ordinarthur@10.10.0.13:/tmp/rebours-dist/ scp -r dist/* ordinarthur@10.10.0.13:/tmp/rebours-dist/
ssh ordinarthur@10.10.0.13 "sudo rm -rf /var/www/html/rebours/dist && sudo mkdir -p /var/www/html/rebours/dist && sudo cp -r /tmp/rebours-dist/* /var/www/html/rebours/dist/ && sudo chown -R ordinarthur:ordinarthur /var/www/html/rebours/dist && sudo systemctl restart rebours-ssr" ssh ordinarthur@10.10.0.13 "sudo cp -r /tmp/rebours-dist/* /var/www/html/rebours/dist/"
``` ```
Si server.mjs a changé : Si server.mjs a changé :
@ -197,8 +189,8 @@ ssh ordinarthur@10.10.0.13 "sudo cp /tmp/server.mjs /var/www/html/rebours/server
| URL | Comportement | | URL | Comportement |
|-----|-------------| |-----|-------------|
| `/` | Page principale Astro (SSR, données Sanity live) | | `/` | Page principale Astro (SSG) |
| `/collection/{slug}` | Page produit (SSR), auto-open panel via `window.__OPEN_PANEL__` | | `/collection/{slug}` | Page produit (SSG), auto-open panel via `window.__OPEN_PANEL__` |
| `/success?session_id=...` | Page de confirmation Stripe | | `/success?session_id=...` | Page de confirmation Stripe |
| `/robots.txt` | Généré au build | | `/robots.txt` | Généré au build |
| `/sitemap.xml` | Généré au build depuis Sanity | | `/sitemap.xml` | Généré au build depuis Sanity |

View File

@ -1,15 +0,0 @@
# Fastify API — no build step, runs server.mjs directly
FROM node:22-alpine
WORKDIR /app
RUN corepack enable && corepack prepare pnpm@latest --activate
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile --prod
COPY server.mjs ./
ENV NODE_ENV=production
EXPOSE 3000
CMD ["node", "server.mjs"]

View File

@ -1,25 +0,0 @@
# --- Stage 1: Build Astro SSR ---
FROM node:22-alpine AS build
WORKDIR /app
RUN corepack enable && corepack prepare pnpm@latest --activate
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
COPY . .
RUN pnpm build
# --- Stage 2: Runtime ---
FROM node:22-alpine AS runtime
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/package.json ./
ENV HOST=0.0.0.0
ENV PORT=4321
EXPOSE 4321
CMD ["node", "dist/server/entry.mjs"]

View File

@ -1,10 +1,8 @@
// @ts-check // @ts-check
import { defineConfig } from 'astro/config'; import { defineConfig } from 'astro/config';
import node from '@astrojs/node';
export default defineConfig({ export default defineConfig({
output: 'server', output: 'static',
adapter: node({ mode: 'standalone' }),
outDir: './dist', outDir: './dist',
server: { port: 4321 }, server: { port: 4321 },
vite: { vite: {

View File

@ -1,44 +0,0 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: rebours-config
namespace: rebours
data:
NODE_ENV: "production"
SANITY_PROJECT_ID: "y821x5qu"
SANITY_DATASET: "production"
DOMAIN: "https://rebours.studio"
FASTIFY_PORT: "3000"
ASTRO_PORT: "4321"
proxy.conf: |
server {
listen 80;
server_name _;
client_max_body_size 10M;
# API → Fastify
location /api/ {
proxy_pass http://rebours-api:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
# Static assets from Astro client build (cached)
location /_astro/ {
proxy_pass http://rebours-ssr:4321;
proxy_set_header Host $host;
add_header Cache-Control "public, max-age=31536000, immutable";
}
# SSR → Astro
location / {
proxy_pass http://rebours-ssr:4321;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
}

View File

@ -1,144 +0,0 @@
# --- Astro SSR ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: rebours-ssr
namespace: rebours
labels:
app: rebours-ssr
spec:
replicas: 1
selector:
matchLabels:
app: rebours-ssr
template:
metadata:
labels:
app: rebours-ssr
spec:
imagePullSecrets:
- name: gitea-registry-secret
containers:
- name: rebours-ssr
image: git.arthurbarre.fr/ordinarthur/rebours-ssr:latest
ports:
- containerPort: 4321
envFrom:
- configMapRef:
name: rebours-config
- secretRef:
name: rebours-secrets
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
readinessProbe:
httpGet:
path: /
port: 4321
initialDelaySeconds: 10
periodSeconds: 10
livenessProbe:
httpGet:
path: /
port: 4321
initialDelaySeconds: 15
periodSeconds: 30
---
# --- Fastify API ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: rebours-api
namespace: rebours
labels:
app: rebours-api
spec:
replicas: 1
selector:
matchLabels:
app: rebours-api
template:
metadata:
labels:
app: rebours-api
spec:
imagePullSecrets:
- name: gitea-registry-secret
containers:
- name: rebours-api
image: git.arthurbarre.fr/ordinarthur/rebours-api:latest
ports:
- containerPort: 3000
envFrom:
- configMapRef:
name: rebours-config
- secretRef:
name: rebours-secrets
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "300m"
readinessProbe:
httpGet:
path: /api/health
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /api/health
port: 3000
initialDelaySeconds: 10
periodSeconds: 30
---
# --- Nginx Proxy ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: rebours-proxy
namespace: rebours
labels:
app: rebours-proxy
spec:
replicas: 1
selector:
matchLabels:
app: rebours-proxy
template:
metadata:
labels:
app: rebours-proxy
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
volumeMounts:
- name: proxy-config
mountPath: /etc/nginx/conf.d/default.conf
subPath: proxy.conf
resources:
requests:
memory: "32Mi"
cpu: "25m"
limits:
memory: "64Mi"
cpu: "100m"
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 10
volumes:
- name: proxy-config
configMap:
name: rebours-config

View File

@ -1,4 +0,0 @@
apiVersion: v1
kind: Namespace
metadata:
name: rebours

View File

@ -1,43 +0,0 @@
# --- SSR (ClusterIP — internal) ---
apiVersion: v1
kind: Service
metadata:
name: rebours-ssr
namespace: rebours
spec:
selector:
app: rebours-ssr
ports:
- name: http
port: 4321
targetPort: 4321
---
# --- API (ClusterIP — internal) ---
apiVersion: v1
kind: Service
metadata:
name: rebours-api
namespace: rebours
spec:
selector:
app: rebours-api
ports:
- name: http
port: 3000
targetPort: 3000
---
# --- Proxy (NodePort — external access) ---
apiVersion: v1
kind: Service
metadata:
name: rebours-proxy
namespace: rebours
spec:
type: NodePort
selector:
app: rebours-proxy
ports:
- name: http
port: 80
targetPort: 80
nodePort: 30083

View File

@ -1,30 +0,0 @@
/**
* Migration: product.image (single) product.images (array)
* Run: node migrate-images.mjs
*/
import 'dotenv/config'
import { createClient } from '@sanity/client'
const sanity = createClient({
projectId: process.env.SANITY_PROJECT_ID,
dataset: process.env.SANITY_DATASET || 'production',
apiVersion: '2024-01-01',
useCdn: false,
token: process.env.SANITY_API_TOKEN,
})
const products = await sanity.fetch('*[_type == "product" && defined(image)]{ _id, image }')
console.log(`Found ${products.length} products to migrate`)
for (const p of products) {
console.log(`Migrating ${p._id}...`)
await sanity
.patch(p._id)
.set({ images: [p.image] })
.unset(['image'])
.commit()
console.log(` ✓ done`)
}
console.log('Migration complete!')

View File

@ -2,7 +2,10 @@ server {
listen 80; listen 80;
server_name rebours.studio; server_name rebours.studio;
# ── API proxy Fastify ────────────────<EFBFBD><EFBFBD><EFBFBD>───────────────────────────────── root /var/www/html/rebours/dist;
index index.html;
# ── API proxy Fastify ──────────────────────────────────────────────────
location /api/ { location /api/ {
proxy_pass http://127.0.0.1:3000; proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host; proxy_set_header Host $host;
@ -11,23 +14,28 @@ server {
proxy_set_header X-Forwarded-Proto https; proxy_set_header X-Forwarded-Proto https;
} }
# ── Static assets from Astro client build ──────────────────<EFBFBD><EFBFBD><EFBFBD>───────────── # ── Cache : Astro hashed immutable ─────────────────────────────────────
location /_astro/ { location /_astro/ {
root /var/www/html/rebours/dist/client;
add_header Cache-Control "public, max-age=31536000, immutable"; add_header Cache-Control "public, max-age=31536000, immutable";
} }
location ~* \.(css|js|jpg|jpeg|png|gif|webp|svg|woff2|woff|ttf|ico|mp3)$ { # ── Cache : CSS/JS sans hash revalidation ─────────────────────────────
root /var/www/html/rebours/dist/client; location ~* \.(css|js)$ {
add_header Cache-Control "no-cache";
}
# ── Cache : assets 7 jours ────────────────────────────────────────────
location ~* \.(jpg|jpeg|png|gif|webp|svg|woff2|woff|ttf|ico|mp3)$ {
add_header Cache-Control "public, max-age=604800"; add_header Cache-Control "public, max-age=604800";
} }
# ── SSR Astro Node server ─────────────────────<EFBFBD><EFBFBD><EFBFBD>──────────────────────── # ── HTML : jamais caché ──────────────────────────────────────────────────
location ~* \.html$ {
add_header Cache-Control "no-store";
}
# ── SPA fallback ─────────────────────────────────────────────────────────
location / { location / {
proxy_pass http://127.0.0.1:4321; try_files $uri $uri/ $uri.html /index.html;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
} }
} }

View File

@ -7,11 +7,9 @@
"build": "astro build", "build": "astro build",
"preview": "astro preview", "preview": "astro preview",
"server": "NODE_ENV=production node server.mjs", "server": "NODE_ENV=production node server.mjs",
"start": "NODE_ENV=production node dist/server/entry.mjs",
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"@astrojs/node": "^9.5.5",
"@fastify/cors": "^10.0.2", "@fastify/cors": "^10.0.2",
"@sanity/client": "^7", "@sanity/client": "^7",
"@sanity/image-url": "^1", "@sanity/image-url": "^1",

133
pnpm-lock.yaml generated
View File

@ -8,9 +8,6 @@ importers:
.: .:
dependencies: dependencies:
'@astrojs/node':
specifier: ^9.5.5
version: 9.5.5(astro@5.18.1(@types/node@25.4.0)(jiti@2.6.1)(rollup@4.59.0)(typescript@5.9.3))
'@fastify/cors': '@fastify/cors':
specifier: ^10.0.2 specifier: ^10.0.2
version: 10.1.0 version: 10.1.0
@ -50,11 +47,6 @@ packages:
'@astrojs/markdown-remark@6.3.11': '@astrojs/markdown-remark@6.3.11':
resolution: {integrity: sha512-hcaxX/5aC6lQgHeGh1i+aauvSwIT6cfyFjKWvExYSxUhZZBBdvCliOtu06gbQyhbe0pGJNoNmqNlQZ5zYUuIyQ==} resolution: {integrity: sha512-hcaxX/5aC6lQgHeGh1i+aauvSwIT6cfyFjKWvExYSxUhZZBBdvCliOtu06gbQyhbe0pGJNoNmqNlQZ5zYUuIyQ==}
'@astrojs/node@9.5.5':
resolution: {integrity: sha512-rtU2BGU5u3SfGURpANfMxVzCIoR86MkaN05ncza9rbtuMKJ/XnRJt/BbyVknDbOJ71hoci0SIsJwKcJR8vvi/A==}
peerDependencies:
astro: ^5.17.3
'@astrojs/prism@3.3.0': '@astrojs/prism@3.3.0':
resolution: {integrity: sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ==} resolution: {integrity: sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ==}
engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0}
@ -997,10 +989,6 @@ packages:
defu@6.1.4: defu@6.1.4:
resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
depd@2.0.0:
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
engines: {node: '>= 0.8'}
dequal@2.0.3: dequal@2.0.3:
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -1050,19 +1038,12 @@ packages:
resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==} resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==}
engines: {node: '>=4'} engines: {node: '>=4'}
ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
emoji-regex@10.6.0: emoji-regex@10.6.0:
resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==}
emoji-regex@8.0.0: emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
encodeurl@2.0.0:
resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
engines: {node: '>= 0.8'}
entities@4.5.0: entities@4.5.0:
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
engines: {node: '>=0.12'} engines: {node: '>=0.12'}
@ -1088,9 +1069,6 @@ packages:
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
engines: {node: '>=6'} engines: {node: '>=6'}
escape-html@1.0.3:
resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
escape-string-regexp@5.0.0: escape-string-regexp@5.0.0:
resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -1101,10 +1079,6 @@ packages:
estree-walker@3.0.3: estree-walker@3.0.3:
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
etag@1.8.1:
resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
engines: {node: '>= 0.6'}
event-source-polyfill@1.0.31: event-source-polyfill@1.0.31:
resolution: {integrity: sha512-4IJSItgS/41IxN5UVAVuAyczwZF7ZIEsM1XAoUzIHA6A+xzusEZUutdXz2Nr+MQPLxfTiCvqE79/C8HT8fKFvA==} resolution: {integrity: sha512-4IJSItgS/41IxN5UVAVuAyczwZF7ZIEsM1XAoUzIHA6A+xzusEZUutdXz2Nr+MQPLxfTiCvqE79/C8HT8fKFvA==}
@ -1175,10 +1149,6 @@ packages:
resolution: {integrity: sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==} resolution: {integrity: sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==}
engines: {node: '>=20'} engines: {node: '>=20'}
fresh@2.0.0:
resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==}
engines: {node: '>= 0.8'}
fsevents@2.3.3: fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@ -1248,10 +1218,6 @@ packages:
http-cache-semantics@4.2.0: http-cache-semantics@4.2.0:
resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==}
http-errors@2.0.1:
resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==}
engines: {node: '>= 0.8'}
import-meta-resolve@4.2.0: import-meta-resolve@4.2.0:
resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==}
@ -1457,14 +1423,6 @@ packages:
micromark@4.0.2: micromark@4.0.2:
resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==}
mime-db@1.54.0:
resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==}
engines: {node: '>= 0.6'}
mime-types@3.0.2:
resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==}
engines: {node: '>=18'}
mimic-response@3.1.0: mimic-response@3.1.0:
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -1517,10 +1475,6 @@ packages:
resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==}
engines: {node: '>=14.0.0'} engines: {node: '>=14.0.0'}
on-finished@2.4.1:
resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
engines: {node: '>= 0.8'}
oniguruma-parser@0.12.1: oniguruma-parser@0.12.1:
resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==} resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==}
@ -1599,10 +1553,6 @@ packages:
radix3@1.1.2: radix3@1.1.2:
resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==}
range-parser@1.2.1:
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
engines: {node: '>= 0.6'}
readable-stream@3.6.2: readable-stream@3.6.2:
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
engines: {node: '>= 6'} engines: {node: '>= 6'}
@ -1713,19 +1663,9 @@ packages:
engines: {node: '>=10'} engines: {node: '>=10'}
hasBin: true hasBin: true
send@1.2.1:
resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==}
engines: {node: '>= 18'}
server-destroy@1.0.1:
resolution: {integrity: sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==}
set-cookie-parser@2.7.2: set-cookie-parser@2.7.2:
resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==}
setprototypeof@1.2.0:
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
sharp@0.34.5: sharp@0.34.5:
resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
@ -1758,10 +1698,6 @@ packages:
resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
engines: {node: '>= 10.x'} engines: {node: '>= 10.x'}
statuses@2.0.2:
resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==}
engines: {node: '>= 0.8'}
string-width@4.2.3: string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -1828,10 +1764,6 @@ packages:
resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==}
engines: {node: '>=12'} engines: {node: '>=12'}
toidentifier@1.0.1:
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
engines: {node: '>=0.6'}
tree-kill@1.2.2: tree-kill@1.2.2:
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
hasBin: true hasBin: true
@ -2129,15 +2061,6 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@astrojs/node@9.5.5(astro@5.18.1(@types/node@25.4.0)(jiti@2.6.1)(rollup@4.59.0)(typescript@5.9.3))':
dependencies:
'@astrojs/internal-helpers': 0.7.6
astro: 5.18.1(@types/node@25.4.0)(jiti@2.6.1)(rollup@4.59.0)(typescript@5.9.3)
send: 1.2.1
server-destroy: 1.0.1
transitivePeerDependencies:
- supports-color
'@astrojs/prism@3.3.0': '@astrojs/prism@3.3.0':
dependencies: dependencies:
prismjs: 1.30.0 prismjs: 1.30.0
@ -2903,8 +2826,6 @@ snapshots:
defu@6.1.4: {} defu@6.1.4: {}
depd@2.0.0: {}
dequal@2.0.3: {} dequal@2.0.3: {}
destr@2.0.5: {} destr@2.0.5: {}
@ -2948,14 +2869,10 @@ snapshots:
dset@3.1.4: {} dset@3.1.4: {}
ee-first@1.1.1: {}
emoji-regex@10.6.0: {} emoji-regex@10.6.0: {}
emoji-regex@8.0.0: {} emoji-regex@8.0.0: {}
encodeurl@2.0.0: {}
entities@4.5.0: {} entities@4.5.0: {}
entities@6.0.1: {} entities@6.0.1: {}
@ -3022,8 +2939,6 @@ snapshots:
escalade@3.2.0: {} escalade@3.2.0: {}
escape-html@1.0.3: {}
escape-string-regexp@5.0.0: {} escape-string-regexp@5.0.0: {}
estree-walker@2.0.2: {} estree-walker@2.0.2: {}
@ -3032,8 +2947,6 @@ snapshots:
dependencies: dependencies:
'@types/estree': 1.0.8 '@types/estree': 1.0.8
etag@1.8.1: {}
event-source-polyfill@1.0.31: {} event-source-polyfill@1.0.31: {}
eventemitter3@5.0.4: {} eventemitter3@5.0.4: {}
@ -3107,8 +3020,6 @@ snapshots:
dependencies: dependencies:
tiny-inflate: 1.0.3 tiny-inflate: 1.0.3
fresh@2.0.0: {}
fsevents@2.3.3: fsevents@2.3.3:
optional: true optional: true
@ -3238,14 +3149,6 @@ snapshots:
http-cache-semantics@4.2.0: {} http-cache-semantics@4.2.0: {}
http-errors@2.0.1:
dependencies:
depd: 2.0.0
inherits: 2.0.4
setprototypeof: 1.2.0
statuses: 2.0.2
toidentifier: 1.0.1
import-meta-resolve@4.2.0: {} import-meta-resolve@4.2.0: {}
inherits@2.0.4: {} inherits@2.0.4: {}
@ -3622,12 +3525,6 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
mime-db@1.54.0: {}
mime-types@3.0.2:
dependencies:
mime-db: 1.54.0
mimic-response@3.1.0: {} mimic-response@3.1.0: {}
mnemonist@0.40.0: mnemonist@0.40.0:
@ -3668,10 +3565,6 @@ snapshots:
on-exit-leak-free@2.1.2: {} on-exit-leak-free@2.1.2: {}
on-finished@2.4.1:
dependencies:
ee-first: 1.1.1
oniguruma-parser@0.12.1: {} oniguruma-parser@0.12.1: {}
oniguruma-to-es@4.3.4: oniguruma-to-es@4.3.4:
@ -3757,8 +3650,6 @@ snapshots:
radix3@1.1.2: {} radix3@1.1.2: {}
range-parser@1.2.1: {}
readable-stream@3.6.2: readable-stream@3.6.2:
dependencies: dependencies:
inherits: 2.0.4 inherits: 2.0.4
@ -3929,28 +3820,8 @@ snapshots:
semver@7.7.4: {} semver@7.7.4: {}
send@1.2.1:
dependencies:
debug: 4.4.3
encodeurl: 2.0.0
escape-html: 1.0.3
etag: 1.8.1
fresh: 2.0.0
http-errors: 2.0.1
mime-types: 3.0.2
ms: 2.1.3
on-finished: 2.4.1
range-parser: 1.2.1
statuses: 2.0.2
transitivePeerDependencies:
- supports-color
server-destroy@1.0.1: {}
set-cookie-parser@2.7.2: {} set-cookie-parser@2.7.2: {}
setprototypeof@1.2.0: {}
sharp@0.34.5: sharp@0.34.5:
dependencies: dependencies:
'@img/colour': 1.1.0 '@img/colour': 1.1.0
@ -4010,8 +3881,6 @@ snapshots:
split2@4.2.0: {} split2@4.2.0: {}
statuses@2.0.2: {}
string-width@4.2.3: string-width@4.2.3:
dependencies: dependencies:
emoji-regex: 8.0.0 emoji-regex: 8.0.0
@ -4082,8 +3951,6 @@ snapshots:
toad-cache@3.7.0: {} toad-cache@3.7.0: {}
toidentifier@1.0.1: {}
tree-kill@1.2.2: {} tree-kill@1.2.2: {}
trim-lines@3.0.1: {} trim-lines@3.0.1: {}

File diff suppressed because it is too large Load Diff

View File

@ -9,10 +9,10 @@
"deploy": "sanity deploy" "deploy": "sanity deploy"
}, },
"dependencies": { "dependencies": {
"@sanity/vision": "^3.99.0", "@sanity/vision": "^3",
"react": "^19.2.4", "react": "^18",
"react-dom": "^19.2.4", "react-dom": "^18",
"sanity": "^3.99.0", "sanity": "^3",
"styled-components": "^6" "styled-components": "^6"
}, },
"devDependencies": { "devDependencies": {

449
sanity/pnpm-lock.yaml generated
View File

@ -9,20 +9,20 @@ importers:
.: .:
dependencies: dependencies:
'@sanity/vision': '@sanity/vision':
specifier: ^3.99.0 specifier: ^3
version: 3.99.0(@babel/runtime@7.29.2)(@codemirror/lint@6.9.5)(@codemirror/theme-one-dark@6.1.3)(@emotion/is-prop-valid@1.4.0)(codemirror@6.0.2)(react-dom@19.2.4(react@19.2.4))(react-is@18.3.1)(react@19.2.4)(styled-components@6.3.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) version: 3.99.0(@babel/runtime@7.29.2)(@codemirror/lint@6.9.5)(@codemirror/theme-one-dark@6.1.3)(@emotion/is-prop-valid@1.4.0)(codemirror@6.0.2)(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1)(styled-components@6.3.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
react: react:
specifier: ^19.2.4 specifier: ^18
version: 19.2.4 version: 18.3.1
react-dom: react-dom:
specifier: ^19.2.4 specifier: ^18
version: 19.2.4(react@19.2.4) version: 18.3.1(react@18.3.1)
sanity: sanity:
specifier: ^3.99.0 specifier: ^3
version: 3.99.0(@emotion/is-prop-valid@1.4.0)(@types/node@25.5.0)(@types/react@19.2.14)(immer@10.2.0)(jiti@2.6.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(styled-components@6.3.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)(yaml@2.8.3) version: 3.99.0(@emotion/is-prop-valid@1.4.0)(@types/node@25.5.0)(@types/react@19.2.14)(immer@10.2.0)(jiti@2.6.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.3.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.9.3)(yaml@2.8.3)
styled-components: styled-components:
specifier: ^6 specifier: ^6
version: 6.3.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4) version: 6.3.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
devDependencies: devDependencies:
'@sanity/eslint-config-studio': '@sanity/eslint-config-studio':
specifier: ^4 specifier: ^4
@ -705,9 +705,6 @@ packages:
'@codemirror/view@6.40.0': '@codemirror/view@6.40.0':
resolution: {integrity: sha512-WA0zdU7xfF10+5I3HhUUq3kqOx3KjqmtQ9lqZjfK7jtYk4G72YW9rezcSywpaUMCWOMlq+6E0pO1IWg1TNIhtg==} resolution: {integrity: sha512-WA0zdU7xfF10+5I3HhUUq3kqOx3KjqmtQ9lqZjfK7jtYk4G72YW9rezcSywpaUMCWOMlq+6E0pO1IWg1TNIhtg==}
'@codemirror/view@6.41.0':
resolution: {integrity: sha512-6H/qadXsVuDY219Yljhohglve8xf4B8xJkVOEWfA5uiYKiTFppjqsvsfR5iPA0RbvRBoOyTZpbLIxe9+0UR8xA==}
'@csstools/color-helpers@5.1.0': '@csstools/color-helpers@5.1.0':
resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==}
engines: {node: '>=18'} engines: {node: '>=18'}
@ -4262,10 +4259,10 @@ packages:
peerDependencies: peerDependencies:
react: ^17.0.0 || ^18.0.0 || ^19.0.0 || ^0.0.0-experimental react: ^17.0.0 || ^18.0.0 || ^19.0.0 || ^0.0.0-experimental
react-dom@19.2.4: react-dom@18.3.1:
resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==} resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==}
peerDependencies: peerDependencies:
react: ^19.2.4 react: ^18.3.1
react-fast-compare@3.2.2: react-fast-compare@3.2.2:
resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==}
@ -4313,8 +4310,8 @@ packages:
react: ^18.3 || >=19.0.0-0 react: ^18.3 || >=19.0.0-0
rxjs: ^7 rxjs: ^7
react@19.2.4: react@18.3.1:
resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
read-pkg-up@7.0.1: read-pkg-up@7.0.1:
@ -4516,8 +4513,8 @@ packages:
resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
engines: {node: '>=v12.22.7'} engines: {node: '>=v12.22.7'}
scheduler@0.27.0: scheduler@0.23.2:
resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==}
scroll-into-view-if-needed@3.1.0: scroll-into-view-if-needed@3.1.0:
resolution: {integrity: sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==} resolution: {integrity: sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==}
@ -6289,7 +6286,7 @@ snapshots:
dependencies: dependencies:
'@codemirror/language': 6.12.3 '@codemirror/language': 6.12.3
'@codemirror/state': 6.6.0 '@codemirror/state': 6.6.0
'@codemirror/view': 6.41.0 '@codemirror/view': 6.40.0
'@lezer/highlight': 1.2.3 '@lezer/highlight': 1.2.3
'@codemirror/view@6.40.0': '@codemirror/view@6.40.0':
@ -6299,13 +6296,6 @@ snapshots:
style-mod: 4.1.3 style-mod: 4.1.3
w3c-keyname: 2.2.8 w3c-keyname: 2.2.8
'@codemirror/view@6.41.0':
dependencies:
'@codemirror/state': 6.6.0
crelt: 1.0.6
style-mod: 4.1.3
w3c-keyname: 2.2.8
'@csstools/color-helpers@5.1.0': {} '@csstools/color-helpers@5.1.0': {}
'@csstools/color-helpers@6.0.2': {} '@csstools/color-helpers@6.0.2': {}
@ -6354,36 +6344,36 @@ snapshots:
'@date-fns/utc@2.1.1': {} '@date-fns/utc@2.1.1': {}
'@dnd-kit/accessibility@3.1.1(react@19.2.4)': '@dnd-kit/accessibility@3.1.1(react@18.3.1)':
dependencies: dependencies:
react: 19.2.4 react: 18.3.1
tslib: 2.8.1 tslib: 2.8.1
'@dnd-kit/core@6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': '@dnd-kit/core@6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies: dependencies:
'@dnd-kit/accessibility': 3.1.1(react@19.2.4) '@dnd-kit/accessibility': 3.1.1(react@18.3.1)
'@dnd-kit/utilities': 3.2.2(react@19.2.4) '@dnd-kit/utilities': 3.2.2(react@18.3.1)
react: 19.2.4 react: 18.3.1
react-dom: 19.2.4(react@19.2.4) react-dom: 18.3.1(react@18.3.1)
tslib: 2.8.1 tslib: 2.8.1
'@dnd-kit/modifiers@6.0.1(@dnd-kit/core@6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)': '@dnd-kit/modifiers@6.0.1(@dnd-kit/core@6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)':
dependencies: dependencies:
'@dnd-kit/core': 6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@dnd-kit/core': 6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@dnd-kit/utilities': 3.2.2(react@19.2.4) '@dnd-kit/utilities': 3.2.2(react@18.3.1)
react: 19.2.4 react: 18.3.1
tslib: 2.8.1 tslib: 2.8.1
'@dnd-kit/sortable@7.0.2(@dnd-kit/core@6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)': '@dnd-kit/sortable@7.0.2(@dnd-kit/core@6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)':
dependencies: dependencies:
'@dnd-kit/core': 6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@dnd-kit/core': 6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@dnd-kit/utilities': 3.2.2(react@19.2.4) '@dnd-kit/utilities': 3.2.2(react@18.3.1)
react: 19.2.4 react: 18.3.1
tslib: 2.8.1 tslib: 2.8.1
'@dnd-kit/utilities@3.2.2(react@19.2.4)': '@dnd-kit/utilities@3.2.2(react@18.3.1)':
dependencies: dependencies:
react: 19.2.4 react: 18.3.1
tslib: 2.8.1 tslib: 2.8.1
'@emotion/is-prop-valid@1.4.0': '@emotion/is-prop-valid@1.4.0':
@ -6506,11 +6496,11 @@ snapshots:
'@floating-ui/core': 1.7.5 '@floating-ui/core': 1.7.5
'@floating-ui/utils': 0.2.11 '@floating-ui/utils': 0.2.11
'@floating-ui/react-dom@2.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': '@floating-ui/react-dom@2.1.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies: dependencies:
'@floating-ui/dom': 1.7.6 '@floating-ui/dom': 1.7.6
react: 19.2.4 react: 18.3.1
react-dom: 19.2.4(react@19.2.4) react-dom: 18.3.1(react@18.3.1)
'@floating-ui/utils@0.2.11': {} '@floating-ui/utils@0.2.11': {}
@ -6707,22 +6697,22 @@ snapshots:
dependencies: dependencies:
mux-embed: 5.17.10 mux-embed: 5.17.10
'@mux/mux-player-react@3.11.7(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': '@mux/mux-player-react@3.11.7(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies: dependencies:
'@mux/mux-player': 3.11.7(react@19.2.4) '@mux/mux-player': 3.11.7(react@18.3.1)
'@mux/playback-core': 0.33.3 '@mux/playback-core': 0.33.3
prop-types: 15.8.1 prop-types: 15.8.1
react: 19.2.4 react: 18.3.1
react-dom: 19.2.4(react@19.2.4) react-dom: 18.3.1(react@18.3.1)
optionalDependencies: optionalDependencies:
'@types/react': 19.2.14 '@types/react': 19.2.14
'@mux/mux-player@3.11.7(react@19.2.4)': '@mux/mux-player@3.11.7(react@18.3.1)':
dependencies: dependencies:
'@mux/mux-video': 0.30.5 '@mux/mux-video': 0.30.5
'@mux/playback-core': 0.33.3 '@mux/playback-core': 0.33.3
media-chrome: 4.18.3(react@19.2.4) media-chrome: 4.18.3(react@18.3.1)
player.style: 0.3.1(react@19.2.4) player.style: 0.3.1(react@18.3.1)
transitivePeerDependencies: transitivePeerDependencies:
- react - react
@ -6842,7 +6832,7 @@ snapshots:
get-random-values-esm: 1.0.2 get-random-values-esm: 1.0.2
lodash: 4.17.23 lodash: 4.17.23
'@portabletext/editor@1.58.1(@sanity/schema@3.99.0(@types/react@19.2.14)(debug@4.4.3))(@sanity/types@3.99.0(@types/react@19.2.14)(debug@4.4.3))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rxjs@7.8.2)': '@portabletext/editor@1.58.1(@sanity/schema@3.99.0(@types/react@19.2.14)(debug@4.4.3))(@sanity/types@3.99.0(@types/react@19.2.14)(debug@4.4.3))(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.2)':
dependencies: dependencies:
'@portabletext/block-tools': 1.1.38(@sanity/types@3.99.0(@types/react@19.2.14)(debug@4.4.3))(@types/react@19.2.14) '@portabletext/block-tools': 1.1.38(@sanity/types@3.99.0(@types/react@19.2.14)(debug@4.4.3))(@types/react@19.2.14)
'@portabletext/keyboard-shortcuts': 1.1.0 '@portabletext/keyboard-shortcuts': 1.1.0
@ -6850,19 +6840,19 @@ snapshots:
'@portabletext/to-html': 2.0.17 '@portabletext/to-html': 2.0.17
'@sanity/schema': 3.99.0(@types/react@19.2.14)(debug@4.4.3) '@sanity/schema': 3.99.0(@types/react@19.2.14)(debug@4.4.3)
'@sanity/types': 3.99.0(@types/react@19.2.14)(debug@4.4.3) '@sanity/types': 3.99.0(@types/react@19.2.14)(debug@4.4.3)
'@xstate/react': 6.1.0(@types/react@19.2.14)(react@19.2.4)(xstate@5.30.0) '@xstate/react': 6.1.0(@types/react@19.2.14)(react@18.3.1)(xstate@5.30.0)
debug: 4.4.3(supports-color@8.1.1) debug: 4.4.3(supports-color@8.1.1)
get-random-values-esm: 1.0.2 get-random-values-esm: 1.0.2
immer: 10.2.0 immer: 10.2.0
lodash: 4.17.23 lodash: 4.17.23
lodash.startcase: 4.4.0 lodash.startcase: 4.4.0
react: 19.2.4 react: 18.3.1
react-compiler-runtime: 19.1.0-rc.2(react@19.2.4) react-compiler-runtime: 19.1.0-rc.2(react@18.3.1)
rxjs: 7.8.2 rxjs: 7.8.2
slate: 0.117.2 slate: 0.117.2
slate-dom: 0.116.0(slate@0.117.2) slate-dom: 0.116.0(slate@0.117.2)
slate-react: 0.117.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(slate-dom@0.116.0(slate@0.117.2))(slate@0.117.2) slate-react: 0.117.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.116.0(slate@0.117.2))(slate@0.117.2)
use-effect-event: 1.0.2(react@19.2.4) use-effect-event: 1.0.2(react@18.3.1)
xstate: 5.30.0 xstate: 5.30.0
transitivePeerDependencies: transitivePeerDependencies:
- '@types/react' - '@types/react'
@ -6876,11 +6866,11 @@ snapshots:
'@sanity/diff-match-patch': 3.2.0 '@sanity/diff-match-patch': 3.2.0
lodash: 4.17.23 lodash: 4.17.23
'@portabletext/react@3.2.4(react@19.2.4)': '@portabletext/react@3.2.4(react@18.3.1)':
dependencies: dependencies:
'@portabletext/toolkit': 2.0.18 '@portabletext/toolkit': 2.0.18
'@portabletext/types': 2.0.15 '@portabletext/types': 2.0.15
react: 19.2.4 react: 18.3.1
'@portabletext/to-html@2.0.17': '@portabletext/to-html@2.0.17':
dependencies: dependencies:
@ -6893,16 +6883,16 @@ snapshots:
'@portabletext/types@2.0.15': {} '@portabletext/types@2.0.15': {}
'@rexxars/react-json-inspector@9.0.1(react@19.2.4)': '@rexxars/react-json-inspector@9.0.1(react@18.3.1)':
dependencies: dependencies:
debounce: 1.2.1 debounce: 1.2.1
md5-o-matic: 0.1.1 md5-o-matic: 0.1.1
react: 19.2.4 react: 18.3.1
'@rexxars/react-split-pane@1.0.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': '@rexxars/react-split-pane@1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies: dependencies:
react: 19.2.4 react: 18.3.1
react-dom: 19.2.4(react@19.2.4) react-dom: 18.3.1(react@18.3.1)
'@rolldown/pluginutils@1.0.0-beta.27': {} '@rolldown/pluginutils@1.0.0-beta.27': {}
@ -6990,13 +6980,13 @@ snapshots:
nanoid: 3.3.11 nanoid: 3.3.11
rxjs: 7.8.2 rxjs: 7.8.2
'@sanity/cli@3.99.0(@types/node@25.5.0)(@types/react@19.2.14)(react@19.2.4)(typescript@5.9.3)(yaml@2.8.3)': '@sanity/cli@3.99.0(@types/node@25.5.0)(@types/react@19.2.14)(react@18.3.1)(typescript@5.9.3)(yaml@2.8.3)':
dependencies: dependencies:
'@babel/traverse': 7.29.0 '@babel/traverse': 7.29.0
'@sanity/client': 7.20.0(debug@4.4.3) '@sanity/client': 7.20.0(debug@4.4.3)
'@sanity/codegen': 3.99.0 '@sanity/codegen': 3.99.0
'@sanity/runtime-cli': 9.2.0(@types/node@25.5.0)(debug@4.4.3)(typescript@5.9.3)(yaml@2.8.3) '@sanity/runtime-cli': 9.2.0(@types/node@25.5.0)(debug@4.4.3)(typescript@5.9.3)(yaml@2.8.3)
'@sanity/telemetry': 0.8.1(react@19.2.4) '@sanity/telemetry': 0.8.1(react@18.3.1)
'@sanity/template-validator': 2.4.6 '@sanity/template-validator': 2.4.6
'@sanity/util': 3.99.0(@types/react@19.2.14)(debug@4.4.3) '@sanity/util': 3.99.0(@types/react@19.2.14)(debug@4.4.3)
chalk: 4.1.2 chalk: 4.1.2
@ -7139,9 +7129,9 @@ snapshots:
'@sanity/generate-help-url@3.0.1': {} '@sanity/generate-help-url@3.0.1': {}
'@sanity/icons@3.7.4(react@19.2.4)': '@sanity/icons@3.7.4(react@18.3.1)':
dependencies: dependencies:
react: 19.2.4 react: 18.3.1
'@sanity/id-utils@1.0.0': '@sanity/id-utils@1.0.0':
dependencies: dependencies:
@ -7178,24 +7168,24 @@ snapshots:
- '@types/react' - '@types/react'
- supports-color - supports-color
'@sanity/insert-menu@1.1.13(@emotion/is-prop-valid@1.4.0)(@sanity/types@3.99.0(@types/react@19.2.14)(debug@4.4.3))(react-dom@19.2.4(react@19.2.4))(react-is@18.3.1)(react@19.2.4)(styled-components@6.3.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4))': '@sanity/insert-menu@1.1.13(@emotion/is-prop-valid@1.4.0)(@sanity/types@3.99.0(@types/react@19.2.14)(debug@4.4.3))(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1)(styled-components@6.3.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))':
dependencies: dependencies:
'@sanity/icons': 3.7.4(react@19.2.4) '@sanity/icons': 3.7.4(react@18.3.1)
'@sanity/types': 3.99.0(@types/react@19.2.14)(debug@4.4.3) '@sanity/types': 3.99.0(@types/react@19.2.14)(debug@4.4.3)
'@sanity/ui': 2.16.22(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.4(react@19.2.4))(react-is@18.3.1)(react@19.2.4)(styled-components@6.3.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) '@sanity/ui': 2.16.22(@emotion/is-prop-valid@1.4.0)(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1)(styled-components@6.3.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
lodash: 4.17.23 lodash: 4.17.23
react: 19.2.4 react: 18.3.1
react-compiler-runtime: 19.1.0-rc.2(react@19.2.4) react-compiler-runtime: 19.1.0-rc.2(react@18.3.1)
react-dom: 19.2.4(react@19.2.4) react-dom: 18.3.1(react@18.3.1)
react-is: 18.3.1 react-is: 18.3.1
transitivePeerDependencies: transitivePeerDependencies:
- '@emotion/is-prop-valid' - '@emotion/is-prop-valid'
- styled-components - styled-components
'@sanity/logos@2.2.2(react@19.2.4)': '@sanity/logos@2.2.2(react@18.3.1)':
dependencies: dependencies:
'@sanity/color': 3.0.6 '@sanity/color': 3.0.6
react: 19.2.4 react: 18.3.1
'@sanity/media-library-types@1.2.0': {} '@sanity/media-library-types@1.2.0': {}
@ -7250,13 +7240,13 @@ snapshots:
- '@sanity/client' - '@sanity/client'
- '@sanity/types' - '@sanity/types'
'@sanity/preview-url-secret@2.1.16(@sanity/client@7.20.0(debug@4.4.3))(@sanity/icons@3.7.4(react@19.2.4))(sanity@3.99.0(@emotion/is-prop-valid@1.4.0)(@types/node@25.5.0)(@types/react@19.2.14)(immer@10.2.0)(jiti@2.6.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(styled-components@6.3.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)(yaml@2.8.3))': '@sanity/preview-url-secret@2.1.16(@sanity/client@7.20.0(debug@4.4.3))(@sanity/icons@3.7.4(react@18.3.1))(sanity@3.99.0(@emotion/is-prop-valid@1.4.0)(@types/node@25.5.0)(@types/react@19.2.14)(immer@10.2.0)(jiti@2.6.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.3.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.9.3)(yaml@2.8.3))':
dependencies: dependencies:
'@sanity/client': 7.20.0(debug@4.4.3) '@sanity/client': 7.20.0(debug@4.4.3)
'@sanity/uuid': 3.0.2 '@sanity/uuid': 3.0.2
optionalDependencies: optionalDependencies:
'@sanity/icons': 3.7.4(react@19.2.4) '@sanity/icons': 3.7.4(react@18.3.1)
sanity: 3.99.0(@emotion/is-prop-valid@1.4.0)(@types/node@25.5.0)(@types/react@19.2.14)(immer@10.2.0)(jiti@2.6.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(styled-components@6.3.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)(yaml@2.8.3) sanity: 3.99.0(@emotion/is-prop-valid@1.4.0)(@types/node@25.5.0)(@types/react@19.2.14)(immer@10.2.0)(jiti@2.6.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.3.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.9.3)(yaml@2.8.3)
'@sanity/runtime-cli@9.2.0(@types/node@25.5.0)(debug@4.4.3)(typescript@5.9.3)(yaml@2.8.3)': '@sanity/runtime-cli@9.2.0(@types/node@25.5.0)(debug@4.4.3)(typescript@5.9.3)(yaml@2.8.3)':
dependencies: dependencies:
@ -7317,7 +7307,7 @@ snapshots:
- debug - debug
- supports-color - supports-color
'@sanity/sdk@0.0.0-alpha.25(@types/react@19.2.14)(debug@4.4.3)(immer@10.2.0)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4))': '@sanity/sdk@0.0.0-alpha.25(@types/react@19.2.14)(debug@4.4.3)(immer@10.2.0)(react@18.3.1)(use-sync-external-store@1.6.0(react@18.3.1))':
dependencies: dependencies:
'@sanity/client': 6.29.1(debug@4.4.3) '@sanity/client': 6.29.1(debug@4.4.3)
'@sanity/comlink': 3.1.1 '@sanity/comlink': 3.1.1
@ -7328,7 +7318,7 @@ snapshots:
lodash-es: 4.17.23 lodash-es: 4.17.23
reselect: 5.1.1 reselect: 5.1.1
rxjs: 7.8.2 rxjs: 7.8.2
zustand: 5.0.12(@types/react@19.2.14)(immer@10.2.0)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)) zustand: 5.0.12(@types/react@19.2.14)(immer@10.2.0)(react@18.3.1)(use-sync-external-store@1.6.0(react@18.3.1))
transitivePeerDependencies: transitivePeerDependencies:
- '@types/react' - '@types/react'
- debug - debug
@ -7336,10 +7326,10 @@ snapshots:
- react - react
- use-sync-external-store - use-sync-external-store
'@sanity/telemetry@0.8.1(react@19.2.4)': '@sanity/telemetry@0.8.1(react@18.3.1)':
dependencies: dependencies:
lodash: 4.17.23 lodash: 4.17.23
react: 19.2.4 react: 18.3.1
rxjs: 7.8.2 rxjs: 7.8.2
typeid-js: 0.3.0 typeid-js: 0.3.0
@ -7364,21 +7354,21 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- debug - debug
'@sanity/ui@2.16.22(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.4(react@19.2.4))(react-is@18.3.1)(react@19.2.4)(styled-components@6.3.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4))': '@sanity/ui@2.16.22(@emotion/is-prop-valid@1.4.0)(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1)(styled-components@6.3.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))':
dependencies: dependencies:
'@floating-ui/react-dom': 2.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@floating-ui/react-dom': 2.1.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@juggle/resize-observer': 3.4.0 '@juggle/resize-observer': 3.4.0
'@sanity/color': 3.0.6 '@sanity/color': 3.0.6
'@sanity/icons': 3.7.4(react@19.2.4) '@sanity/icons': 3.7.4(react@18.3.1)
csstype: 3.2.3 csstype: 3.2.3
motion: 12.38.0(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) motion: 12.38.0(@emotion/is-prop-valid@1.4.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: 19.2.4 react: 18.3.1
react-compiler-runtime: 1.0.0(react@19.2.4) react-compiler-runtime: 1.0.0(react@18.3.1)
react-dom: 19.2.4(react@19.2.4) react-dom: 18.3.1(react@18.3.1)
react-is: 18.3.1 react-is: 18.3.1
react-refractor: 2.2.0(react@19.2.4) react-refractor: 2.2.0(react@18.3.1)
styled-components: 6.3.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4) styled-components: 6.3.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
use-effect-event: 2.0.3(react@19.2.4) use-effect-event: 2.0.3(react@18.3.1)
transitivePeerDependencies: transitivePeerDependencies:
- '@emotion/is-prop-valid' - '@emotion/is-prop-valid'
@ -7411,7 +7401,7 @@ snapshots:
'@types/uuid': 8.3.4 '@types/uuid': 8.3.4
uuid: 8.3.2 uuid: 8.3.2
'@sanity/vision@3.99.0(@babel/runtime@7.29.2)(@codemirror/lint@6.9.5)(@codemirror/theme-one-dark@6.1.3)(@emotion/is-prop-valid@1.4.0)(codemirror@6.0.2)(react-dom@19.2.4(react@19.2.4))(react-is@18.3.1)(react@19.2.4)(styled-components@6.3.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4))': '@sanity/vision@3.99.0(@babel/runtime@7.29.2)(@codemirror/lint@6.9.5)(@codemirror/theme-one-dark@6.1.3)(@emotion/is-prop-valid@1.4.0)(codemirror@6.0.2)(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1)(styled-components@6.3.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))':
dependencies: dependencies:
'@codemirror/autocomplete': 6.20.1 '@codemirror/autocomplete': 6.20.1
'@codemirror/commands': 6.10.3 '@codemirror/commands': 6.10.3
@ -7422,25 +7412,25 @@ snapshots:
'@codemirror/view': 6.40.0 '@codemirror/view': 6.40.0
'@juggle/resize-observer': 3.4.0 '@juggle/resize-observer': 3.4.0
'@lezer/highlight': 1.2.3 '@lezer/highlight': 1.2.3
'@rexxars/react-json-inspector': 9.0.1(react@19.2.4) '@rexxars/react-json-inspector': 9.0.1(react@18.3.1)
'@rexxars/react-split-pane': 1.0.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@rexxars/react-split-pane': 1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@sanity/color': 3.0.6 '@sanity/color': 3.0.6
'@sanity/icons': 3.7.4(react@19.2.4) '@sanity/icons': 3.7.4(react@18.3.1)
'@sanity/ui': 2.16.22(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.4(react@19.2.4))(react-is@18.3.1)(react@19.2.4)(styled-components@6.3.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) '@sanity/ui': 2.16.22(@emotion/is-prop-valid@1.4.0)(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1)(styled-components@6.3.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
'@sanity/uuid': 3.0.2 '@sanity/uuid': 3.0.2
'@uiw/react-codemirror': 4.25.9(@babel/runtime@7.29.2)(@codemirror/autocomplete@6.20.1)(@codemirror/language@6.12.3)(@codemirror/lint@6.9.5)(@codemirror/search@6.6.0)(@codemirror/state@6.6.0)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.40.0)(codemirror@6.0.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@uiw/react-codemirror': 4.25.9(@babel/runtime@7.29.2)(@codemirror/autocomplete@6.20.1)(@codemirror/language@6.12.3)(@codemirror/lint@6.9.5)(@codemirror/search@6.6.0)(@codemirror/state@6.6.0)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.40.0)(codemirror@6.0.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
is-hotkey-esm: 1.0.0 is-hotkey-esm: 1.0.0
json-2-csv: 5.5.10 json-2-csv: 5.5.10
json5: 2.2.3 json5: 2.2.3
lodash: 4.17.23 lodash: 4.17.23
quick-lru: 5.1.1 quick-lru: 5.1.1
react: 19.2.4 react: 18.3.1
react-compiler-runtime: 19.1.0-rc.2(react@19.2.4) react-compiler-runtime: 19.1.0-rc.2(react@18.3.1)
react-fast-compare: 3.2.2 react-fast-compare: 3.2.2
react-rx: 4.2.2(react@19.2.4)(rxjs@7.8.2) react-rx: 4.2.2(react@18.3.1)(rxjs@7.8.2)
rxjs: 7.8.2 rxjs: 7.8.2
styled-components: 6.3.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4) styled-components: 6.3.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
use-effect-event: 2.0.3(react@19.2.4) use-effect-event: 2.0.3(react@18.3.1)
transitivePeerDependencies: transitivePeerDependencies:
- '@babel/runtime' - '@babel/runtime'
- '@codemirror/lint' - '@codemirror/lint'
@ -7484,24 +7474,24 @@ snapshots:
'@sentry/core@8.55.1': {} '@sentry/core@8.55.1': {}
'@sentry/react@8.55.1(react@19.2.4)': '@sentry/react@8.55.1(react@18.3.1)':
dependencies: dependencies:
'@sentry/browser': 8.55.1 '@sentry/browser': 8.55.1
'@sentry/core': 8.55.1 '@sentry/core': 8.55.1
hoist-non-react-statics: 3.3.2 hoist-non-react-statics: 3.3.2
react: 19.2.4 react: 18.3.1
'@tanstack/react-table@8.21.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': '@tanstack/react-table@8.21.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies: dependencies:
'@tanstack/table-core': 8.21.3 '@tanstack/table-core': 8.21.3
react: 19.2.4 react: 18.3.1
react-dom: 19.2.4(react@19.2.4) react-dom: 18.3.1(react@18.3.1)
'@tanstack/react-virtual@3.13.23(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': '@tanstack/react-virtual@3.13.23(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies: dependencies:
'@tanstack/virtual-core': 3.13.23 '@tanstack/virtual-core': 3.13.23
react: 19.2.4 react: 18.3.1
react-dom: 19.2.4(react@19.2.4) react-dom: 18.3.1(react@18.3.1)
'@tanstack/table-core@8.21.3': {} '@tanstack/table-core@8.21.3': {}
@ -7676,7 +7666,7 @@ snapshots:
'@codemirror/state': 6.6.0 '@codemirror/state': 6.6.0
'@codemirror/view': 6.40.0 '@codemirror/view': 6.40.0
'@uiw/react-codemirror@4.25.9(@babel/runtime@7.29.2)(@codemirror/autocomplete@6.20.1)(@codemirror/language@6.12.3)(@codemirror/lint@6.9.5)(@codemirror/search@6.6.0)(@codemirror/state@6.6.0)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.40.0)(codemirror@6.0.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': '@uiw/react-codemirror@4.25.9(@babel/runtime@7.29.2)(@codemirror/autocomplete@6.20.1)(@codemirror/language@6.12.3)(@codemirror/lint@6.9.5)(@codemirror/search@6.6.0)(@codemirror/state@6.6.0)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.40.0)(codemirror@6.0.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies: dependencies:
'@babel/runtime': 7.29.2 '@babel/runtime': 7.29.2
'@codemirror/commands': 6.10.3 '@codemirror/commands': 6.10.3
@ -7685,8 +7675,8 @@ snapshots:
'@codemirror/view': 6.40.0 '@codemirror/view': 6.40.0
'@uiw/codemirror-extensions-basic-setup': 4.25.9(@codemirror/autocomplete@6.20.1)(@codemirror/commands@6.10.3)(@codemirror/language@6.12.3)(@codemirror/lint@6.9.5)(@codemirror/search@6.6.0)(@codemirror/state@6.6.0)(@codemirror/view@6.40.0) '@uiw/codemirror-extensions-basic-setup': 4.25.9(@codemirror/autocomplete@6.20.1)(@codemirror/commands@6.10.3)(@codemirror/language@6.12.3)(@codemirror/lint@6.9.5)(@codemirror/search@6.6.0)(@codemirror/state@6.6.0)(@codemirror/view@6.40.0)
codemirror: 6.0.2 codemirror: 6.0.2
react: 19.2.4 react: 18.3.1
react-dom: 19.2.4(react@19.2.4) react-dom: 18.3.1(react@18.3.1)
transitivePeerDependencies: transitivePeerDependencies:
- '@codemirror/autocomplete' - '@codemirror/autocomplete'
- '@codemirror/language' - '@codemirror/language'
@ -7709,11 +7699,11 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@xstate/react@6.1.0(@types/react@19.2.14)(react@19.2.4)(xstate@5.30.0)': '@xstate/react@6.1.0(@types/react@19.2.14)(react@18.3.1)(xstate@5.30.0)':
dependencies: dependencies:
react: 19.2.4 react: 18.3.1
use-isomorphic-layout-effect: 1.2.1(@types/react@19.2.14)(react@19.2.4) use-isomorphic-layout-effect: 1.2.1(@types/react@19.2.14)(react@18.3.1)
use-sync-external-store: 1.6.0(react@19.2.4) use-sync-external-store: 1.6.0(react@18.3.1)
optionalDependencies: optionalDependencies:
xstate: 5.30.0 xstate: 5.30.0
transitivePeerDependencies: transitivePeerDependencies:
@ -8073,9 +8063,9 @@ snapshots:
dependencies: dependencies:
custom-media-element: 1.4.6 custom-media-element: 1.4.6
ce-la-react@0.3.2(react@19.2.4): ce-la-react@0.3.2(react@18.3.1):
dependencies: dependencies:
react: 19.2.4 react: 18.3.1
chalk@2.4.2: chalk@2.4.2:
dependencies: dependencies:
@ -8154,7 +8144,7 @@ snapshots:
'@codemirror/lint': 6.9.5 '@codemirror/lint': 6.9.5
'@codemirror/search': 6.6.0 '@codemirror/search': 6.6.0
'@codemirror/state': 6.6.0 '@codemirror/state': 6.6.0
'@codemirror/view': 6.41.0 '@codemirror/view': 6.40.0
color-convert@1.9.3: color-convert@1.9.3:
dependencies: dependencies:
@ -8937,15 +8927,15 @@ snapshots:
hasown: 2.0.2 hasown: 2.0.2
mime-types: 2.1.35 mime-types: 2.1.35
framer-motion@12.38.0(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): framer-motion@12.38.0(@emotion/is-prop-valid@1.4.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies: dependencies:
motion-dom: 12.38.0 motion-dom: 12.38.0
motion-utils: 12.36.0 motion-utils: 12.36.0
tslib: 2.8.1 tslib: 2.8.1
optionalDependencies: optionalDependencies:
'@emotion/is-prop-valid': 1.4.0 '@emotion/is-prop-valid': 1.4.0
react: 19.2.4 react: 18.3.1
react-dom: 19.2.4(react@19.2.4) react-dom: 18.3.1(react@18.3.1)
from2@2.3.0: from2@2.3.0:
dependencies: dependencies:
@ -9726,22 +9716,22 @@ snapshots:
mdn-data@2.27.1: {} mdn-data@2.27.1: {}
media-chrome@4.11.1(react@19.2.4): media-chrome@4.11.1(react@18.3.1):
dependencies: dependencies:
'@vercel/edge': 1.2.2 '@vercel/edge': 1.2.2
ce-la-react: 0.3.2(react@19.2.4) ce-la-react: 0.3.2(react@18.3.1)
transitivePeerDependencies: transitivePeerDependencies:
- react - react
media-chrome@4.16.1(react@19.2.4): media-chrome@4.16.1(react@18.3.1):
dependencies: dependencies:
ce-la-react: 0.3.2(react@19.2.4) ce-la-react: 0.3.2(react@18.3.1)
transitivePeerDependencies: transitivePeerDependencies:
- react - react
media-chrome@4.18.3(react@19.2.4): media-chrome@4.18.3(react@18.3.1):
dependencies: dependencies:
ce-la-react: 0.3.2(react@19.2.4) ce-la-react: 0.3.2(react@18.3.1)
transitivePeerDependencies: transitivePeerDependencies:
- react - react
@ -9852,14 +9842,14 @@ snapshots:
motion-utils@12.36.0: {} motion-utils@12.36.0: {}
motion@12.38.0(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): motion@12.38.0(@emotion/is-prop-valid@1.4.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies: dependencies:
framer-motion: 12.38.0(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) framer-motion: 12.38.0(@emotion/is-prop-valid@1.4.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
tslib: 2.8.1 tslib: 2.8.1
optionalDependencies: optionalDependencies:
'@emotion/is-prop-valid': 1.4.0 '@emotion/is-prop-valid': 1.4.0
react: 19.2.4 react: 18.3.1
react-dom: 19.2.4(react@19.2.4) react-dom: 18.3.1(react@18.3.1)
ms@2.0.0: {} ms@2.0.0: {}
@ -10162,15 +10152,15 @@ snapshots:
dependencies: dependencies:
find-up: 5.0.0 find-up: 5.0.0
player.style@0.1.10(react@19.2.4): player.style@0.1.10(react@18.3.1):
dependencies: dependencies:
media-chrome: 4.11.1(react@19.2.4) media-chrome: 4.11.1(react@18.3.1)
transitivePeerDependencies: transitivePeerDependencies:
- react - react
player.style@0.3.1(react@19.2.4): player.style@0.3.1(react@18.3.1):
dependencies: dependencies:
media-chrome: 4.16.1(react@19.2.4) media-chrome: 4.16.1(react@18.3.1)
transitivePeerDependencies: transitivePeerDependencies:
- react - react
@ -10260,69 +10250,72 @@ snapshots:
dependencies: dependencies:
performance-now: 2.1.0 performance-now: 2.1.0
react-clientside-effect@1.2.8(react@19.2.4): react-clientside-effect@1.2.8(react@18.3.1):
dependencies: dependencies:
'@babel/runtime': 7.29.2 '@babel/runtime': 7.29.2
react: 19.2.4 react: 18.3.1
react-compiler-runtime@1.0.0(react@19.2.4): react-compiler-runtime@1.0.0(react@18.3.1):
dependencies: dependencies:
react: 19.2.4 react: 18.3.1
react-compiler-runtime@19.1.0-rc.2(react@19.2.4): react-compiler-runtime@19.1.0-rc.2(react@18.3.1):
dependencies: dependencies:
react: 19.2.4 react: 18.3.1
react-dom@19.2.4(react@19.2.4): react-dom@18.3.1(react@18.3.1):
dependencies: dependencies:
react: 19.2.4 loose-envify: 1.4.0
scheduler: 0.27.0 react: 18.3.1
scheduler: 0.23.2
react-fast-compare@3.2.2: {} react-fast-compare@3.2.2: {}
react-focus-lock@2.13.7(@types/react@19.2.14)(react@19.2.4): react-focus-lock@2.13.7(@types/react@19.2.14)(react@18.3.1):
dependencies: dependencies:
'@babel/runtime': 7.29.2 '@babel/runtime': 7.29.2
focus-lock: 1.3.6 focus-lock: 1.3.6
prop-types: 15.8.1 prop-types: 15.8.1
react: 19.2.4 react: 18.3.1
react-clientside-effect: 1.2.8(react@19.2.4) react-clientside-effect: 1.2.8(react@18.3.1)
use-callback-ref: 1.3.3(@types/react@19.2.14)(react@19.2.4) use-callback-ref: 1.3.3(@types/react@19.2.14)(react@18.3.1)
use-sidecar: 1.1.3(@types/react@19.2.14)(react@19.2.4) use-sidecar: 1.1.3(@types/react@19.2.14)(react@18.3.1)
optionalDependencies: optionalDependencies:
'@types/react': 19.2.14 '@types/react': 19.2.14
react-i18next@14.0.2(i18next@23.16.8)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): react-i18next@14.0.2(i18next@23.16.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies: dependencies:
'@babel/runtime': 7.29.2 '@babel/runtime': 7.29.2
html-parse-stringify: 3.0.1 html-parse-stringify: 3.0.1
i18next: 23.16.8 i18next: 23.16.8
react: 19.2.4 react: 18.3.1
optionalDependencies: optionalDependencies:
react-dom: 19.2.4(react@19.2.4) react-dom: 18.3.1(react@18.3.1)
react-is@16.13.1: {} react-is@16.13.1: {}
react-is@18.3.1: {} react-is@18.3.1: {}
react-refractor@2.2.0(react@19.2.4): react-refractor@2.2.0(react@18.3.1):
dependencies: dependencies:
react: 19.2.4 react: 18.3.1
refractor: 3.6.0 refractor: 3.6.0
unist-util-filter: 2.0.3 unist-util-filter: 2.0.3
unist-util-visit-parents: 3.1.1 unist-util-visit-parents: 3.1.1
react-refresh@0.17.0: {} react-refresh@0.17.0: {}
react-rx@4.2.2(react@19.2.4)(rxjs@7.8.2): react-rx@4.2.2(react@18.3.1)(rxjs@7.8.2):
dependencies: dependencies:
observable-callback: 1.0.3(rxjs@7.8.2) observable-callback: 1.0.3(rxjs@7.8.2)
react: 19.2.4 react: 18.3.1
react-compiler-runtime: 1.0.0(react@19.2.4) react-compiler-runtime: 1.0.0(react@18.3.1)
rxjs: 7.8.2 rxjs: 7.8.2
use-effect-event: 2.0.3(react@19.2.4) use-effect-event: 2.0.3(react@18.3.1)
react@19.2.4: {} react@18.3.1:
dependencies:
loose-envify: 1.4.0
read-pkg-up@7.0.1: read-pkg-up@7.0.1:
dependencies: dependencies:
@ -10568,22 +10561,22 @@ snapshots:
safer-buffer@2.1.2: {} safer-buffer@2.1.2: {}
sanity@3.99.0(@emotion/is-prop-valid@1.4.0)(@types/node@25.5.0)(@types/react@19.2.14)(immer@10.2.0)(jiti@2.6.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(styled-components@6.3.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)(yaml@2.8.3): sanity@3.99.0(@emotion/is-prop-valid@1.4.0)(@types/node@25.5.0)(@types/react@19.2.14)(immer@10.2.0)(jiti@2.6.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.3.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.9.3)(yaml@2.8.3):
dependencies: dependencies:
'@dnd-kit/core': 6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@dnd-kit/core': 6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@dnd-kit/modifiers': 6.0.1(@dnd-kit/core@6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4) '@dnd-kit/modifiers': 6.0.1(@dnd-kit/core@6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
'@dnd-kit/sortable': 7.0.2(@dnd-kit/core@6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4) '@dnd-kit/sortable': 7.0.2(@dnd-kit/core@6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
'@dnd-kit/utilities': 3.2.2(react@19.2.4) '@dnd-kit/utilities': 3.2.2(react@18.3.1)
'@juggle/resize-observer': 3.4.0 '@juggle/resize-observer': 3.4.0
'@mux/mux-player-react': 3.11.7(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@mux/mux-player-react': 3.11.7(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@portabletext/block-tools': 1.1.38(@sanity/types@3.99.0(@types/react@19.2.14)(debug@4.4.3))(@types/react@19.2.14) '@portabletext/block-tools': 1.1.38(@sanity/types@3.99.0(@types/react@19.2.14)(debug@4.4.3))(@types/react@19.2.14)
'@portabletext/editor': 1.58.1(@sanity/schema@3.99.0(@types/react@19.2.14)(debug@4.4.3))(@sanity/types@3.99.0(@types/react@19.2.14)(debug@4.4.3))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rxjs@7.8.2) '@portabletext/editor': 1.58.1(@sanity/schema@3.99.0(@types/react@19.2.14)(debug@4.4.3))(@sanity/types@3.99.0(@types/react@19.2.14)(debug@4.4.3))(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.2)
'@portabletext/react': 3.2.4(react@19.2.4) '@portabletext/react': 3.2.4(react@18.3.1)
'@portabletext/toolkit': 2.0.18 '@portabletext/toolkit': 2.0.18
'@rexxars/react-json-inspector': 9.0.1(react@19.2.4) '@rexxars/react-json-inspector': 9.0.1(react@18.3.1)
'@sanity/asset-utils': 2.3.0 '@sanity/asset-utils': 2.3.0
'@sanity/bifur-client': 0.4.1 '@sanity/bifur-client': 0.4.1
'@sanity/cli': 3.99.0(@types/node@25.5.0)(@types/react@19.2.14)(react@19.2.4)(typescript@5.9.3)(yaml@2.8.3) '@sanity/cli': 3.99.0(@types/node@25.5.0)(@types/react@19.2.14)(react@18.3.1)(typescript@5.9.3)(yaml@2.8.3)
'@sanity/client': 7.20.0(debug@4.4.3) '@sanity/client': 7.20.0(debug@4.4.3)
'@sanity/color': 3.0.6 '@sanity/color': 3.0.6
'@sanity/comlink': 3.1.1 '@sanity/comlink': 3.1.1
@ -10592,28 +10585,28 @@ snapshots:
'@sanity/diff-patch': 5.0.0 '@sanity/diff-patch': 5.0.0
'@sanity/eventsource': 5.0.2 '@sanity/eventsource': 5.0.2
'@sanity/export': 3.45.3(@types/react@19.2.14) '@sanity/export': 3.45.3(@types/react@19.2.14)
'@sanity/icons': 3.7.4(react@19.2.4) '@sanity/icons': 3.7.4(react@18.3.1)
'@sanity/id-utils': 1.0.0 '@sanity/id-utils': 1.0.0
'@sanity/image-url': 1.2.0 '@sanity/image-url': 1.2.0
'@sanity/import': 3.38.3(@types/react@19.2.14) '@sanity/import': 3.38.3(@types/react@19.2.14)
'@sanity/insert-menu': 1.1.13(@emotion/is-prop-valid@1.4.0)(@sanity/types@3.99.0(@types/react@19.2.14)(debug@4.4.3))(react-dom@19.2.4(react@19.2.4))(react-is@18.3.1)(react@19.2.4)(styled-components@6.3.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) '@sanity/insert-menu': 1.1.13(@emotion/is-prop-valid@1.4.0)(@sanity/types@3.99.0(@types/react@19.2.14)(debug@4.4.3))(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1)(styled-components@6.3.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
'@sanity/logos': 2.2.2(react@19.2.4) '@sanity/logos': 2.2.2(react@18.3.1)
'@sanity/media-library-types': 1.2.0 '@sanity/media-library-types': 1.2.0
'@sanity/message-protocol': 0.13.3 '@sanity/message-protocol': 0.13.3
'@sanity/migrate': 3.99.0(@types/react@19.2.14) '@sanity/migrate': 3.99.0(@types/react@19.2.14)
'@sanity/mutator': 3.99.0(@types/react@19.2.14) '@sanity/mutator': 3.99.0(@types/react@19.2.14)
'@sanity/presentation-comlink': 1.0.33(@sanity/client@7.20.0(debug@4.4.3))(@sanity/types@3.99.0(@types/react@19.2.14)(debug@4.4.3)) '@sanity/presentation-comlink': 1.0.33(@sanity/client@7.20.0(debug@4.4.3))(@sanity/types@3.99.0(@types/react@19.2.14)(debug@4.4.3))
'@sanity/preview-url-secret': 2.1.16(@sanity/client@7.20.0(debug@4.4.3))(@sanity/icons@3.7.4(react@19.2.4))(sanity@3.99.0(@emotion/is-prop-valid@1.4.0)(@types/node@25.5.0)(@types/react@19.2.14)(immer@10.2.0)(jiti@2.6.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(styled-components@6.3.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)(yaml@2.8.3)) '@sanity/preview-url-secret': 2.1.16(@sanity/client@7.20.0(debug@4.4.3))(@sanity/icons@3.7.4(react@18.3.1))(sanity@3.99.0(@emotion/is-prop-valid@1.4.0)(@types/node@25.5.0)(@types/react@19.2.14)(immer@10.2.0)(jiti@2.6.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.3.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.9.3)(yaml@2.8.3))
'@sanity/schema': 3.99.0(@types/react@19.2.14)(debug@4.4.3) '@sanity/schema': 3.99.0(@types/react@19.2.14)(debug@4.4.3)
'@sanity/sdk': 0.0.0-alpha.25(@types/react@19.2.14)(debug@4.4.3)(immer@10.2.0)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)) '@sanity/sdk': 0.0.0-alpha.25(@types/react@19.2.14)(debug@4.4.3)(immer@10.2.0)(react@18.3.1)(use-sync-external-store@1.6.0(react@18.3.1))
'@sanity/telemetry': 0.8.1(react@19.2.4) '@sanity/telemetry': 0.8.1(react@18.3.1)
'@sanity/types': 3.99.0(@types/react@19.2.14)(debug@4.4.3) '@sanity/types': 3.99.0(@types/react@19.2.14)(debug@4.4.3)
'@sanity/ui': 2.16.22(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.4(react@19.2.4))(react-is@18.3.1)(react@19.2.4)(styled-components@6.3.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) '@sanity/ui': 2.16.22(@emotion/is-prop-valid@1.4.0)(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1)(styled-components@6.3.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
'@sanity/util': 3.99.0(@types/react@19.2.14)(debug@4.4.3) '@sanity/util': 3.99.0(@types/react@19.2.14)(debug@4.4.3)
'@sanity/uuid': 3.0.2 '@sanity/uuid': 3.0.2
'@sentry/react': 8.55.1(react@19.2.4) '@sentry/react': 8.55.1(react@18.3.1)
'@tanstack/react-table': 8.21.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@tanstack/react-table': 8.21.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@tanstack/react-virtual': 3.13.23(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@tanstack/react-virtual': 3.13.23(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@types/react-is': 19.2.0 '@types/react-is': 19.2.0
'@types/shallow-equals': 1.0.3 '@types/shallow-equals': 1.0.3
'@types/speakingurl': 13.0.6 '@types/speakingurl': 13.0.6
@ -10621,7 +10614,7 @@ snapshots:
'@types/use-sync-external-store': 1.5.0 '@types/use-sync-external-store': 1.5.0
'@types/which': 3.0.4 '@types/which': 3.0.4
'@vitejs/plugin-react': 4.7.0(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(yaml@2.8.3)) '@vitejs/plugin-react': 4.7.0(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(yaml@2.8.3))
'@xstate/react': 6.1.0(@types/react@19.2.14)(react@19.2.4)(xstate@5.30.0) '@xstate/react': 6.1.0(@types/react@19.2.14)(react@18.3.1)(xstate@5.30.0)
archiver: 7.0.1 archiver: 7.0.1
arrify: 2.0.1 arrify: 2.0.1
async-mutex: 0.4.1 async-mutex: 0.4.1
@ -10640,7 +10633,7 @@ snapshots:
exif-component: 1.0.1 exif-component: 1.0.1
fast-deep-equal: 3.1.3 fast-deep-equal: 3.1.3
form-data: 4.0.5 form-data: 4.0.5
framer-motion: 12.38.0(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) framer-motion: 12.38.0(@emotion/is-prop-valid@1.4.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
get-it: 8.7.0(debug@4.4.3) get-it: 8.7.0(debug@4.4.3)
get-random-values-esm: 1.0.2 get-random-values-esm: 1.0.2
groq-js: 1.29.0 groq-js: 1.29.0
@ -10670,22 +10663,22 @@ snapshots:
path-to-regexp: 6.3.0 path-to-regexp: 6.3.0
peek-stream: 1.1.3 peek-stream: 1.1.3
pirates: 4.0.7 pirates: 4.0.7
player.style: 0.1.10(react@19.2.4) player.style: 0.1.10(react@18.3.1)
pluralize-esm: 9.0.5 pluralize-esm: 9.0.5
polished: 4.3.1 polished: 4.3.1
preferred-pm: 4.1.1 preferred-pm: 4.1.1
pretty-ms: 7.0.1 pretty-ms: 7.0.1
quick-lru: 5.1.1 quick-lru: 5.1.1
raf: 3.4.1 raf: 3.4.1
react: 19.2.4 react: 18.3.1
react-compiler-runtime: 19.1.0-rc.2(react@19.2.4) react-compiler-runtime: 19.1.0-rc.2(react@18.3.1)
react-dom: 19.2.4(react@19.2.4) react-dom: 18.3.1(react@18.3.1)
react-fast-compare: 3.2.2 react-fast-compare: 3.2.2
react-focus-lock: 2.13.7(@types/react@19.2.14)(react@19.2.4) react-focus-lock: 2.13.7(@types/react@19.2.14)(react@18.3.1)
react-i18next: 14.0.2(i18next@23.16.8)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) react-i18next: 14.0.2(i18next@23.16.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react-is: 18.3.1 react-is: 18.3.1
react-refractor: 2.2.0(react@19.2.4) react-refractor: 2.2.0(react@18.3.1)
react-rx: 4.2.2(react@19.2.4)(rxjs@7.8.2) react-rx: 4.2.2(react@18.3.1)(rxjs@7.8.2)
read-pkg-up: 7.0.1 read-pkg-up: 7.0.1
refractor: 3.6.0 refractor: 3.6.0
resolve-from: 5.0.0 resolve-from: 5.0.0
@ -10699,15 +10692,15 @@ snapshots:
semver: 7.7.4 semver: 7.7.4
shallow-equals: 1.0.0 shallow-equals: 1.0.0
speakingurl: 14.0.1 speakingurl: 14.0.1
styled-components: 6.3.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4) styled-components: 6.3.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
tar-fs: 2.1.4 tar-fs: 2.1.4
tar-stream: 3.1.8 tar-stream: 3.1.8
tinyglobby: 0.2.15 tinyglobby: 0.2.15
urlpattern-polyfill: 10.1.0 urlpattern-polyfill: 10.1.0
use-device-pixel-ratio: 1.1.2(react@19.2.4) use-device-pixel-ratio: 1.1.2(react@18.3.1)
use-effect-event: 2.0.3(react@19.2.4) use-effect-event: 2.0.3(react@18.3.1)
use-hot-module-reload: 2.0.0(react@19.2.4) use-hot-module-reload: 2.0.0(react@18.3.1)
use-sync-external-store: 1.6.0(react@19.2.4) use-sync-external-store: 1.6.0(react@18.3.1)
uuid: 11.1.0 uuid: 11.1.0
vite: 6.4.1(@types/node@25.5.0)(jiti@2.6.1)(yaml@2.8.3) vite: 6.4.1(@types/node@25.5.0)(jiti@2.6.1)(yaml@2.8.3)
which: 5.0.0 which: 5.0.0
@ -10744,7 +10737,9 @@ snapshots:
dependencies: dependencies:
xmlchars: 2.2.0 xmlchars: 2.2.0
scheduler@0.27.0: {} scheduler@0.23.2:
dependencies:
loose-envify: 1.4.0
scroll-into-view-if-needed@3.1.0: scroll-into-view-if-needed@3.1.0:
dependencies: dependencies:
@ -10851,14 +10846,14 @@ snapshots:
slate: 0.117.2 slate: 0.117.2
tiny-invariant: 1.3.1 tiny-invariant: 1.3.1
slate-react@0.117.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(slate-dom@0.116.0(slate@0.117.2))(slate@0.117.2): slate-react@0.117.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.116.0(slate@0.117.2))(slate@0.117.2):
dependencies: dependencies:
'@juggle/resize-observer': 3.4.0 '@juggle/resize-observer': 3.4.0
direction: 1.0.4 direction: 1.0.4
is-hotkey: 0.2.0 is-hotkey: 0.2.0
lodash: 4.17.23 lodash: 4.17.23
react: 19.2.4 react: 18.3.1
react-dom: 19.2.4(react@19.2.4) react-dom: 18.3.1(react@18.3.1)
scroll-into-view-if-needed: 3.1.0 scroll-into-view-if-needed: 3.1.0
slate: 0.117.2 slate: 0.117.2
slate-dom: 0.116.0(slate@0.117.2) slate-dom: 0.116.0(slate@0.117.2)
@ -11025,7 +11020,7 @@ snapshots:
style-mod@4.1.3: {} style-mod@4.1.3: {}
styled-components@6.3.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4): styled-components@6.3.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies: dependencies:
'@emotion/is-prop-valid': 1.4.0 '@emotion/is-prop-valid': 1.4.0
'@emotion/unitless': 0.10.0 '@emotion/unitless': 0.10.0
@ -11033,12 +11028,12 @@ snapshots:
css-to-react-native: 3.2.0 css-to-react-native: 3.2.0
csstype: 3.2.3 csstype: 3.2.3
postcss: 8.4.49 postcss: 8.4.49
react: 19.2.4 react: 18.3.1
shallowequal: 1.1.0 shallowequal: 1.1.0
stylis: 4.3.6 stylis: 4.3.6
tslib: 2.8.1 tslib: 2.8.1
optionalDependencies: optionalDependencies:
react-dom: 19.2.4(react@19.2.4) react-dom: 18.3.1(react@18.3.1)
stylis@4.3.6: {} stylis@4.3.6: {}
@ -11331,46 +11326,46 @@ snapshots:
urlpattern-polyfill@10.1.0: {} urlpattern-polyfill@10.1.0: {}
use-callback-ref@1.3.3(@types/react@19.2.14)(react@19.2.4): use-callback-ref@1.3.3(@types/react@19.2.14)(react@18.3.1):
dependencies: dependencies:
react: 19.2.4 react: 18.3.1
tslib: 2.8.1 tslib: 2.8.1
optionalDependencies: optionalDependencies:
'@types/react': 19.2.14 '@types/react': 19.2.14
use-device-pixel-ratio@1.1.2(react@19.2.4): use-device-pixel-ratio@1.1.2(react@18.3.1):
dependencies: dependencies:
react: 19.2.4 react: 18.3.1
use-effect-event@1.0.2(react@19.2.4): use-effect-event@1.0.2(react@18.3.1):
dependencies: dependencies:
react: 19.2.4 react: 18.3.1
use-effect-event@2.0.3(react@19.2.4): use-effect-event@2.0.3(react@18.3.1):
dependencies: dependencies:
react: 19.2.4 react: 18.3.1
use-hot-module-reload@2.0.0(react@19.2.4): use-hot-module-reload@2.0.0(react@18.3.1):
dependencies: dependencies:
react: 19.2.4 react: 18.3.1
use-isomorphic-layout-effect@1.2.1(@types/react@19.2.14)(react@19.2.4): use-isomorphic-layout-effect@1.2.1(@types/react@19.2.14)(react@18.3.1):
dependencies: dependencies:
react: 19.2.4 react: 18.3.1
optionalDependencies: optionalDependencies:
'@types/react': 19.2.14 '@types/react': 19.2.14
use-sidecar@1.1.3(@types/react@19.2.14)(react@19.2.4): use-sidecar@1.1.3(@types/react@19.2.14)(react@18.3.1):
dependencies: dependencies:
detect-node-es: 1.1.0 detect-node-es: 1.1.0
react: 19.2.4 react: 18.3.1
tslib: 2.8.1 tslib: 2.8.1
optionalDependencies: optionalDependencies:
'@types/react': 19.2.14 '@types/react': 19.2.14
use-sync-external-store@1.6.0(react@19.2.4): use-sync-external-store@1.6.0(react@18.3.1):
dependencies: dependencies:
react: 19.2.4 react: 18.3.1
util-deprecate@1.0.2: {} util-deprecate@1.0.2: {}
@ -11598,9 +11593,9 @@ snapshots:
zod@3.25.76: {} zod@3.25.76: {}
zustand@5.0.12(@types/react@19.2.14)(immer@10.2.0)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)): zustand@5.0.12(@types/react@19.2.14)(immer@10.2.0)(react@18.3.1)(use-sync-external-store@1.6.0(react@18.3.1)):
optionalDependencies: optionalDependencies:
'@types/react': 19.2.14 '@types/react': 19.2.14
immer: 10.2.0 immer: 10.2.0
react: 19.2.4 react: 18.3.1
use-sync-external-store: 1.6.0(react@19.2.4) use-sync-external-store: 1.6.0(react@18.3.1)

View File

@ -9,25 +9,6 @@ export default defineConfig({
projectId: 'y821x5qu', projectId: 'y821x5qu',
dataset: 'production', dataset: 'production',
plugins: [ plugins: [structureTool()],
structureTool({
structure: (S) =>
S.list()
.title('Contenu')
.items([
S.listItem()
.title('Page d\'accueil')
.id('homePage')
.child(
S.document()
.schemaType('homePage')
.documentId('homePage')
.title('Page d\'accueil')
),
S.divider(),
S.documentTypeListItem('product').title('Produits'),
]),
}),
],
schema: { types: schemaTypes }, schema: { types: schemaTypes },
}) })

View File

@ -1,167 +0,0 @@
import { defineType, defineField } from 'sanity'
export default defineType({
name: 'homePage',
title: 'Page d\'accueil',
type: 'document',
groups: [
{ name: 'hero', title: 'Hero' },
{ name: 'collection', title: 'Collection' },
{ name: 'contact', title: 'Contact / WhatsApp' },
{ name: 'footer', title: 'Footer' },
{ name: 'seo', title: 'SEO' },
],
fields: [
// ── HERO ──────────────────────────────────────────────────
defineField({
name: 'heroLabel',
title: 'Label hero',
type: 'string',
initialValue: '// ARCHIVE_001 — 2026',
group: 'hero',
}),
defineField({
name: 'heroTitle',
title: 'Titre hero',
type: 'string',
initialValue: 'REBOURS STUDIO',
description: 'Utiliser | pour un retour à la ligne (ex: REBOURS|STUDIO)',
group: 'hero',
validation: (rule) => rule.required(),
}),
defineField({
name: 'heroSubtitle',
title: 'Sous-titre hero',
type: 'text',
rows: 2,
initialValue: 'Mobilier d\'art contemporain.\nSpace Age × Memphis.',
group: 'hero',
}),
defineField({
name: 'heroStatus',
title: 'Status hero',
type: 'text',
rows: 2,
initialValue: 'STATUS: [PROTOTYPE EN COURS]\nCOLLECTION_001 — BIENTÔT DISPONIBLE',
group: 'hero',
}),
defineField({
name: 'heroImage',
title: 'Image hero',
type: 'image',
options: { hotspot: true },
description: 'Image principale du hero. Si vide, utilise l\'image du premier produit.',
group: 'hero',
fields: [
defineField({
name: 'alt',
title: 'Texte alternatif',
type: 'string',
}),
],
}),
// ── COLLECTION ────────────────────────────────────────────
defineField({
name: 'collectionLabel',
title: 'Label collection',
type: 'string',
initialValue: '// COLLECTION_001',
group: 'collection',
}),
defineField({
name: 'collectionCta',
title: 'Texte d\'action collection',
type: 'string',
initialValue: 'CLIQUER POUR OUVRIR',
description: 'Affiché après le nombre d\'objets',
group: 'collection',
}),
// ── CONTACT / WHATSAPP ────────────────────────────────────
defineField({
name: 'contactLabel',
title: 'Label contact',
type: 'string',
initialValue: '// CONTACT',
group: 'contact',
}),
defineField({
name: 'contactTitle',
title: 'Titre contact',
type: 'string',
initialValue: 'UNE QUESTION ? PARLONS-EN',
description: 'Utiliser | pour un retour à la ligne',
group: 'contact',
}),
defineField({
name: 'contactDescription',
title: 'Description contact',
type: 'text',
rows: 2,
initialValue: 'Commandes sur mesure, questions techniques,\nou simplement dire bonjour.',
group: 'contact',
}),
defineField({
name: 'whatsappNumber',
title: 'Numéro WhatsApp',
type: 'string',
initialValue: '33651755191',
description: 'Format international sans + (ex: 33612345678)',
group: 'contact',
validation: (rule) => rule.required(),
}),
defineField({
name: 'whatsappButtonText',
title: 'Texte bouton WhatsApp',
type: 'string',
initialValue: 'CONTACTEZ-NOUS SUR WHATSAPP',
group: 'contact',
}),
defineField({
name: 'contactResponseTime',
title: 'Temps de réponse',
type: 'string',
initialValue: 'RÉPONSE SOUS 24H',
group: 'contact',
}),
// ── FOOTER ────────────────────────────────────────────────
defineField({
name: 'footerText',
title: 'Texte footer',
type: 'string',
initialValue: '© 2026 REBOURS STUDIO — PARIS',
group: 'footer',
}),
defineField({
name: 'instagramUrl',
title: 'Lien Instagram',
type: 'url',
initialValue: 'https://instagram.com/rebour.studio',
group: 'footer',
}),
// ── SEO ───────────────────────────────────────────────────
defineField({
name: 'seoTitle',
title: 'Titre SEO',
type: 'string',
initialValue: 'REBOURS — Mobilier d\'art contemporain | Collection 001',
group: 'seo',
}),
defineField({
name: 'seoDescription',
title: 'Description SEO',
type: 'text',
rows: 3,
initialValue: 'REBOURS Studio crée du mobilier d\'art contemporain inspiré du Space Age et du mouvement Memphis. Pièces uniques fabriquées à Paris. Collection 001 en cours.',
group: 'seo',
}),
],
preview: {
prepare() {
return { title: 'Page d\'accueil' }
},
},
})

View File

@ -1,4 +1,3 @@
import product from './product' import product from './product'
import homePage from './homePage'
export const schemaTypes = [product, homePage] export const schemaTypes = [product]

View File

@ -103,24 +103,18 @@ export default defineType({
rows: 4, rows: 4,
}), }),
defineField({ defineField({
name: 'images', name: 'image',
title: 'Images produit', title: 'Image produit',
type: 'array', type: 'image',
description: 'La première image est utilisée comme image principale', options: { hotspot: true },
validation: (rule) => rule.required().min(1), validation: (rule) => rule.required(),
of: [ fields: [
{ defineField({
type: 'image', name: 'alt',
options: { hotspot: true }, title: 'Texte alternatif',
fields: [ type: 'string',
defineField({ description: 'Description de l\'image pour le SEO et l\'accessibilité',
name: 'alt', }),
title: 'Texte alternatif',
type: 'string',
description: 'Description de l\'image pour le SEO et l\'accessibilité',
}),
],
},
], ],
}), }),
defineField({ defineField({
@ -180,7 +174,7 @@ export default defineType({
select: { select: {
title: 'productDisplayName', title: 'productDisplayName',
subtitle: 'type', subtitle: 'type',
media: 'images.0', media: 'image',
}, },
}, },
}) })

View File

@ -1,54 +0,0 @@
import { createClient } from '@sanity/client'
import dotenv from 'dotenv'
dotenv.config()
const client = createClient({
projectId: process.env.SANITY_PROJECT_ID,
dataset: process.env.SANITY_DATASET || 'production',
apiVersion: '2024-01-01',
useCdn: false,
token: process.env.SANITY_API_TOKEN,
})
const homePage = {
_id: 'homePage',
_type: 'homePage',
// Hero
heroLabel: '// ARCHIVE_001 — 2026',
heroTitle: 'REBOURS|STUDIO',
heroSubtitle: 'Mobilier d\'art contemporain.\nSpace Age × Memphis.',
heroStatus: 'STATUS: [PROTOTYPE EN COURS]\nCOLLECTION_001 — BIENTÔT DISPONIBLE',
// Collection
collectionLabel: '// COLLECTION_001',
collectionCta: 'CLIQUER POUR OUVRIR',
// Contact / WhatsApp
contactLabel: '// CONTACT',
contactTitle: 'UNE QUESTION ?|PARLONS-EN',
contactDescription: 'Commandes sur mesure, questions techniques,\nou simplement dire bonjour.',
whatsappNumber: '33651755191',
whatsappButtonText: 'CONTACTEZ-NOUS SUR WHATSAPP',
contactResponseTime: 'RÉPONSE SOUS 24H',
// Footer
footerText: '© 2026 REBOURS STUDIO — PARIS',
instagramUrl: 'https://instagram.com/rebour.studio',
// SEO
seoTitle: 'REBOURS — Mobilier d\'art contemporain | Collection 001',
seoDescription: 'REBOURS Studio crée du mobilier d\'art contemporain inspiré du Space Age et du mouvement Memphis. Pièces uniques fabriquées à Paris. Collection 001 en cours.',
}
async function seed() {
console.log('Creating homePage document...')
const result = await client.createOrReplace(homePage)
console.log(`✓ homePage created: ${result._id}`)
}
seed().catch((err) => {
console.error('Error:', err.message)
process.exit(1)
})

View File

@ -71,7 +71,7 @@ app.post('/api/checkout', async (request, reply) => {
description, description,
price, price,
currency, currency,
"imageUrl": images[0].asset->url, "imageUrl": image.asset->url,
"slug": slug.current "slug": slug.current
}`, }`,
{ slug } { slug }
@ -131,7 +131,7 @@ app.get('/api/session/:id', async (request) => {
// ── Start ─────────────────────────────────────────────────────────────────── // ── Start ───────────────────────────────────────────────────────────────────
try { try {
await app.listen({ port: process.env.FASTIFY_PORT ?? process.env.PORT ?? 3000, host: '0.0.0.0' }) await app.listen({ port: process.env.PORT ?? 3000, host: '0.0.0.0' })
} catch (err) { } catch (err) {
app.log.error(err) app.log.error(err)
process.exit(1) process.exit(1)

View File

@ -12,8 +12,6 @@ const {
ogImage = 'https://rebours.studio/assets/lamp-violet.jpg', ogImage = 'https://rebours.studio/assets/lamp-violet.jpg',
canonical = 'https://rebours.studio/', canonical = 'https://rebours.studio/',
} = Astro.props; } = Astro.props;
const cssVersion = Date.now();
--- ---
<!DOCTYPE html> <!DOCTYPE html>
@ -58,7 +56,7 @@ const cssVersion = Date.now();
<slot name="head" /> <slot name="head" />
<link rel="stylesheet" href={`/style.css?v=${cssVersion}`}> <link rel="stylesheet" href="/style.css">
</head> </head>
<body> <body>
<slot /> <slot />

View File

@ -5,7 +5,7 @@ export const sanity = createClient({
projectId: import.meta.env.SANITY_PROJECT_ID, projectId: import.meta.env.SANITY_PROJECT_ID,
dataset: import.meta.env.SANITY_DATASET || 'production', dataset: import.meta.env.SANITY_DATASET || 'production',
apiVersion: '2024-01-01', apiVersion: '2024-01-01',
useCdn: false, useCdn: true,
}) })
const builder = imageUrlBuilder(sanity) const builder = imageUrlBuilder(sanity)
@ -29,8 +29,8 @@ const PRODUCT_FIELDS = `
description, description,
specs, specs,
notes, notes,
images, image,
"imageAlt": images[0].alt, "imageAlt": image.alt,
seoTitle, seoTitle,
seoDescription, seoDescription,
price, price,
@ -51,28 +51,3 @@ export async function getProductBySlug(slug) {
{ slug } { slug }
) )
} }
export async function getHomePage() {
return sanity.fetch(
`*[_type == "homePage" && _id == "homePage"][0] {
heroLabel,
heroTitle,
heroSubtitle,
heroStatus,
heroImage,
"heroImageAlt": heroImage.alt,
collectionLabel,
collectionCta,
contactLabel,
contactTitle,
contactDescription,
whatsappNumber,
whatsappButtonText,
contactResponseTime,
footerText,
instagramUrl,
seoTitle,
seoDescription
}`
)
}

View File

@ -1,21 +1,26 @@
--- ---
import Base from '../../layouts/Base.astro'; import Base from '../../layouts/Base.astro';
import { getPublishedProducts, getProductBySlug, urlFor } from '../../lib/sanity.mjs'; import { getPublishedProducts, urlFor } from '../../lib/sanity.mjs';
const { slug } = Astro.params; export async function getStaticPaths() {
const product = await getProductBySlug(slug); const products = await getPublishedProducts();
if (!product) { return products.map(p => ({
return Astro.redirect('/'); params: { slug: p.slug },
props: {
slug: p.slug,
name: p.name,
title: p.seoTitle || `REBOURS — ${p.productDisplayName} | Collection 001`,
description: p.seoDescription || p.description?.substring(0, 155) || '',
ogImage: p.image ? urlFor(p.image).width(1200).url() : '',
productName: p.productDisplayName,
price: p.price ? String(p.price / 100) : null,
availability: p.availability || 'https://schema.org/PreOrder',
},
}));
} }
const name = product.name; const { slug, title, description, ogImage, name, productName, price, availability } = Astro.props;
const title = product.seoTitle || `REBOURS — ${product.productDisplayName} | Collection 001`;
const description = product.seoDescription || product.description?.substring(0, 155) || '';
const ogImage = product.images?.[0] ? urlFor(product.images[0]).width(1200).url() : '';
const productName = product.productDisplayName;
const price = product.price ? String(product.price / 100) : null;
const availability = product.availability || 'https://schema.org/PreOrder';
const allProducts = await getPublishedProducts(); const allProducts = await getPublishedProducts();
@ -76,10 +81,7 @@ const schemaBreadcrumb = {
</div> </div>
<div class="panel-inner"> <div class="panel-inner">
<div class="panel-img-col"> <div class="panel-img-col">
<div class="panel-gallery" id="panel-gallery"> <img id="panel-img" src="" alt="Image produit REBOURS Studio">
<img id="panel-img" src="" alt="Image produit REBOURS Studio">
</div>
<div class="panel-gallery-nav" id="panel-gallery-nav"></div>
</div> </div>
<div class="panel-info-col"> <div class="panel-info-col">
<p class="panel-index" id="panel-index"></p> <p class="panel-index" id="panel-index"></p>
@ -107,11 +109,11 @@ const schemaBreadcrumb = {
<p id="panel-desc" class="panel-desc"></p> <p id="panel-desc" class="panel-desc"></p>
<hr> <hr>
<details class="accordion" open> <details class="accordion">
<summary>SPÉCIFICATIONS TECHNIQUES <span>↓</span></summary> <summary>SPÉCIFICATIONS TECHNIQUES <span>↓</span></summary>
<div class="accordion-body" id="panel-specs"></div> <div class="accordion-body" id="panel-specs"></div>
</details> </details>
<details class="accordion" open> <details class="accordion">
<summary>NOTES DE CONCEPTION <span>↓</span></summary> <summary>NOTES DE CONCEPTION <span>↓</span></summary>
<div class="accordion-body" id="panel-notes"></div> <div class="accordion-body" id="panel-notes"></div>
</details> </details>
@ -171,9 +173,9 @@ const schemaBreadcrumb = {
<p class="hero-sub mono-sm">STATUS: [PROTOTYPE EN COURS]<br>COLLECTION_001 — BIENTÔT DISPONIBLE</p> <p class="hero-sub mono-sm">STATUS: [PROTOTYPE EN COURS]<br>COLLECTION_001 — BIENTÔT DISPONIBLE</p>
</div> </div>
<div class="hero-right"> <div class="hero-right">
{allProducts[0]?.images?.[0] && ( {allProducts[0]?.image && (
<img <img
src={urlFor(allProducts[0].images[0]).width(1024).url()} src={urlFor(allProducts[0].image).width(1024).url()}
alt="REBOURS — Mobilier d'art contemporain, Paris 2026" alt="REBOURS — Mobilier d'art contemporain, Paris 2026"
class="hero-img" class="hero-img"
width="1024" height="1024" width="1024" height="1024"
@ -192,13 +194,8 @@ const schemaBreadcrumb = {
<div class="product-grid"> <div class="product-grid">
{allProducts.map((p, i) => { {allProducts.map((p, i) => {
const mainImg = p.images?.[0]; const imgUrl = p.image ? urlFor(p.image).width(800).url() : '';
const imgUrl = mainImg ? urlFor(mainImg).width(800).url() : '';
const alt = p.imageAlt || `${p.productDisplayName} — mobilier d'art contemporain, REBOURS Studio Paris`; const alt = p.imageAlt || `${p.productDisplayName} — mobilier d'art contemporain, REBOURS Studio Paris`;
const allImgs = (p.images || []).map(img => ({
url: urlFor(img).width(1200).url(),
alt: img.alt || alt,
}));
return ( return (
<article class="product-card" <article class="product-card"
data-index={p.index} data-index={p.index}
@ -210,8 +207,7 @@ const schemaBreadcrumb = {
data-desc={p.description} data-desc={p.description}
data-specs={p.specs || ''} data-specs={p.specs || ''}
data-notes={p.notes || ''} data-notes={p.notes || ''}
data-img={mainImg ? urlFor(mainImg).width(1200).url() : ''} data-img={p.image ? urlFor(p.image).width(1200).url() : ''}
data-images={JSON.stringify(allImgs)}
data-price={p.price ? String(p.price) : ''} data-price={p.price ? String(p.price) : ''}
data-slug={p.slug} data-slug={p.slug}
data-img-alt={alt} data-img-alt={alt}

View File

@ -1,38 +1,12 @@
--- ---
import Base from '../layouts/Base.astro'; import Base from '../layouts/Base.astro';
import { getPublishedProducts, getHomePage, urlFor } from '../lib/sanity.mjs'; import { getPublishedProducts, urlFor } from '../lib/sanity.mjs';
const [products, home] = await Promise.all([getPublishedProducts(), getHomePage()]); const products = await getPublishedProducts();
const heroImg = home?.heroImage const firstImage = products[0]?.image
? urlFor(home.heroImage).width(1024).url() ? urlFor(products[0].image).width(1024).url()
: products[0]?.images?.[0] : '/assets/table-terrazzo.jpg';
? urlFor(products[0].images[0]).width(1024).url()
: '/assets/table-terrazzo.jpg';
const heroImgAlt = home?.heroImageAlt || 'REBOURS — Mobilier d\'art contemporain, Paris 2026';
const heroLabel = home?.heroLabel || '// ARCHIVE_001 — 2026';
const heroTitleParts = (home?.heroTitle || 'REBOURS|STUDIO').split('|');
const heroSubtitle = home?.heroSubtitle || 'Mobilier d\'art contemporain.\nSpace Age × Memphis.';
const heroStatus = home?.heroStatus || 'STATUS: [PROTOTYPE EN COURS]\nCOLLECTION_001 — BIENTÔT DISPONIBLE';
const collectionLabel = home?.collectionLabel || '// COLLECTION_001';
const collectionCta = home?.collectionCta || 'CLIQUER POUR OUVRIR';
const contactLabel = home?.contactLabel || '// CONTACT';
const contactTitleParts = (home?.contactTitle || 'UNE QUESTION ?|PARLONS-EN').split('|');
const contactDesc = home?.contactDescription || 'Commandes sur mesure, questions techniques,\nou simplement dire bonjour.';
const whatsappNumber = home?.whatsappNumber || '33651755191';
const whatsappBtnText = home?.whatsappButtonText || 'CONTACTEZ-NOUS SUR WHATSAPP';
const contactResponseTime = home?.contactResponseTime || 'RÉPONSE SOUS 24H';
const footerText = home?.footerText || '© 2026 REBOURS STUDIO — PARIS';
const instagramUrl = home?.instagramUrl || 'https://instagram.com/rebour.studio';
const seoTitle = home?.seoTitle || 'REBOURS — Mobilier d\'art contemporain | Collection 001';
const seoDesc = home?.seoDescription || 'REBOURS Studio crée du mobilier d\'art contemporain inspiré du Space Age et du mouvement Memphis. Pièces uniques fabriquées à Paris. Collection 001 en cours.';
const firstImage = heroImg;
const schemaOrg = { const schemaOrg = {
"@context": "https://schema.org", "@context": "https://schema.org",
@ -51,7 +25,7 @@ const schemaOrg = {
"@type": "Product", "@type": "Product",
"name": p.productDisplayName, "name": p.productDisplayName,
"description": p.seoDescription || p.description?.substring(0, 155), "description": p.seoDescription || p.description?.substring(0, 155),
"image": p.images?.[0] ? urlFor(p.images[0]).width(1024).url() : undefined, "image": p.image ? urlFor(p.image).width(1024).url() : undefined,
}, },
"price": String(p.price / 100), "price": String(p.price / 100),
"priceCurrency": p.currency || 'EUR', "priceCurrency": p.currency || 'EUR',
@ -62,8 +36,8 @@ const schemaOrg = {
--- ---
<Base <Base
title={seoTitle} title="REBOURS — Mobilier d'art contemporain | Collection 001"
description={seoDesc} description="REBOURS Studio crée du mobilier d'art contemporain inspiré du Space Age et du mouvement Memphis. Pièces uniques fabriquées à Paris. Collection 001 en cours."
canonical="https://rebours.studio/" canonical="https://rebours.studio/"
> >
<Fragment slot="head"> <Fragment slot="head">
@ -80,10 +54,7 @@ const schemaOrg = {
</div> </div>
<div class="panel-inner"> <div class="panel-inner">
<div class="panel-img-col"> <div class="panel-img-col">
<div class="panel-gallery" id="panel-gallery"> <img id="panel-img" src="" alt="Image produit REBOURS Studio">
<img id="panel-img" src="" alt="Image produit REBOURS Studio">
</div>
<div class="panel-gallery-nav" id="panel-gallery-nav"></div>
</div> </div>
<div class="panel-info-col"> <div class="panel-info-col">
<p class="panel-index" id="panel-index"></p> <p class="panel-index" id="panel-index"></p>
@ -111,11 +82,11 @@ const schemaOrg = {
<p id="panel-desc" class="panel-desc"></p> <p id="panel-desc" class="panel-desc"></p>
<hr> <hr>
<details class="accordion" open> <details class="accordion">
<summary>SPÉCIFICATIONS TECHNIQUES <span>↓</span></summary> <summary>SPÉCIFICATIONS TECHNIQUES <span>↓</span></summary>
<div class="accordion-body" id="panel-specs"></div> <div class="accordion-body" id="panel-specs"></div>
</details> </details>
<details class="accordion" open> <details class="accordion">
<summary>NOTES DE CONCEPTION <span>↓</span></summary> <summary>NOTES DE CONCEPTION <span>↓</span></summary>
<div class="accordion-body" id="panel-notes"></div> <div class="accordion-body" id="panel-notes"></div>
</details> </details>
@ -170,15 +141,15 @@ const schemaOrg = {
<!-- HERO --> <!-- HERO -->
<section class="hero" aria-label="Introduction"> <section class="hero" aria-label="Introduction">
<div class="hero-left"> <div class="hero-left">
<p class="label">{heroLabel}</p> <p class="label">// ARCHIVE_001 — 2026</p>
<h1>{heroTitleParts.map((part, i) => <>{i > 0 && <br/>}{part}</>)}</h1> <h1>REBOURS<br>STUDIO</h1>
<p class="hero-sub" set:html={heroSubtitle.replace(/\n/g, '<br>')} /> <p class="hero-sub">Mobilier d'art contemporain.<br>Space Age × Memphis.</p>
<p class="hero-sub mono-sm" set:html={heroStatus.replace(/\n/g, '<br>')} /> <p class="hero-sub mono-sm">STATUS: [PROTOTYPE EN COURS]<br>COLLECTION_001 — BIENTÔT DISPONIBLE</p>
</div> </div>
<div class="hero-right"> <div class="hero-right">
<img <img
src={heroImg} src={firstImage}
alt={heroImgAlt} alt="REBOURS — Mobilier d'art contemporain, Paris 2026"
class="hero-img" class="hero-img"
width="1024" height="1024" width="1024" height="1024"
fetchpriority="high"> fetchpriority="high">
@ -190,19 +161,14 @@ const schemaOrg = {
<!-- COLLECTION GRID --> <!-- COLLECTION GRID -->
<section class="collection" id="collection" aria-label="Collection 001"> <section class="collection" id="collection" aria-label="Collection 001">
<div class="collection-header"> <div class="collection-header">
<p class="label">{collectionLabel}</p> <p class="label">// COLLECTION_001</p>
<span class="label">{products.length} OBJETS — {collectionCta}</span> <span class="label">{products.length} OBJETS — CLIQUER POUR OUVRIR</span>
</div> </div>
<div class="product-grid"> <div class="product-grid">
{products.map((p, i) => { {products.map((p, i) => {
const mainImg = p.images?.[0]; const imgUrl = p.image ? urlFor(p.image).width(800).url() : '';
const imgUrl = mainImg ? urlFor(mainImg).width(800).url() : '';
const alt = p.imageAlt || `${p.productDisplayName} — mobilier d'art contemporain, REBOURS Studio Paris`; const alt = p.imageAlt || `${p.productDisplayName} — mobilier d'art contemporain, REBOURS Studio Paris`;
const allImgs = (p.images || []).map(img => ({
url: urlFor(img).width(1200).url(),
alt: img.alt || alt,
}));
return ( return (
<article class="product-card" <article class="product-card"
data-index={p.index} data-index={p.index}
@ -214,8 +180,7 @@ const schemaOrg = {
data-desc={p.description} data-desc={p.description}
data-specs={p.specs || ''} data-specs={p.specs || ''}
data-notes={p.notes || ''} data-notes={p.notes || ''}
data-img={mainImg ? urlFor(mainImg).width(1200).url() : ''} data-img={p.image ? urlFor(p.image).width(1200).url() : ''}
data-images={JSON.stringify(allImgs)}
data-price={p.price ? String(p.price) : ''} data-price={p.price ? String(p.price) : ''}
data-slug={p.slug} data-slug={p.slug}
data-img-alt={alt} data-img-alt={alt}
@ -237,32 +202,30 @@ const schemaOrg = {
</div> </div>
</section> </section>
<!-- CONTACT WHATSAPP --> <!-- NEWSLETTER -->
<section class="newsletter" id="contact" aria-label="Contact WhatsApp"> <section class="newsletter" id="contact" aria-label="Accès anticipé">
<div class="nl-left"> <div class="nl-left">
<p class="label">{contactLabel}</p> <p class="label">// ACCÈS_ANTICIPÉ</p>
<h2>{contactTitleParts.map((part, i) => <>{i > 0 && <br/>}{part}</>)}</h2> <h2>REJOINDRE<br>L'EXPÉRIENCE</h2>
</div> </div>
<div class="nl-right"> <div class="nl-right">
<div class="nl-form" style="pointer-events: auto;"> <form class="nl-form" onsubmit="event.preventDefault();" aria-label="Inscription newsletter">
<p class="mono-sm" style="line-height: 1.9; margin-bottom: 0.5rem;" set:html={contactDesc.replace(/\n/g, '<br>')} /> <label for="nl-email">EMAIL :</label>
<a href={`https://wa.me/${whatsappNumber}`} target="_blank" rel="noopener" class="whatsapp-btn" aria-label="Nous contacter sur WhatsApp"> <div class="nl-row">
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" style="flex-shrink:0;"> <input type="email" id="nl-email" name="email" placeholder="votre@email.com" autocomplete="email" required>
<path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z"/> <button type="submit">ENVOYER →</button>
</svg> </div>
{whatsappBtnText} <p class="mono-sm"><span class="blink">■</span> CONNECTION_STATUS: PENDING</p>
</a> </form>
<p class="mono-sm"><span class="blink">■</span> {contactResponseTime}</p>
</div>
</div> </div>
</section> </section>
</main> </main>
<footer class="footer"> <footer class="footer">
<span>{footerText}</span> <span>© 2026 REBOURS STUDIO — PARIS</span>
<nav aria-label="Liens secondaires"> <nav aria-label="Liens secondaires">
<a href={instagramUrl} rel="noopener" target="_blank">INSTAGRAM</a> <a href="https://instagram.com/rebour.studio" rel="noopener" target="_blank">INSTAGRAM</a>
&nbsp;/&nbsp; &nbsp;/&nbsp;
<a href="#" class="contact-trigger">CONTACT</a> <a href="#" class="contact-trigger">CONTACT</a>
</nav> </nav>
@ -271,7 +234,7 @@ const schemaOrg = {
</div> </div>
<!-- CONTACT MODAL --> <!-- CONTACT MODAL -->
<div id="contact-modal" class="contact-modal" aria-hidden="true" data-whatsapp={whatsappNumber}> <div id="contact-modal" class="contact-modal" aria-hidden="true">
<div class="contact-modal-backdrop"></div> <div class="contact-modal-backdrop"></div>
<div class="contact-modal-content"> <div class="contact-modal-content">
<div class="contact-modal-header"> <div class="contact-modal-header">
@ -300,7 +263,7 @@ const schemaOrg = {
<button type="submit" class="contact-submit"> <button type="submit" class="contact-submit">
ENVOYER LE MESSAGE → ENVOYER LE MESSAGE →
</button> </button>
<p class="mono-sm contact-note"><span class="blink">■</span> REDIRECTION VERS WHATSAPP</p> <p class="mono-sm contact-note"><span class="blink">■</span> REDIRECTION VERS VOTRE CLIENT MAIL</p>
</form> </form>
</div> </div>
</div> </div>

View File

@ -46,10 +46,10 @@ document.addEventListener('DOMContentLoaded', () => {
const x = e.clientX; const x = e.clientX;
const y = e.clientY; const y = e.clientY;
cursorH.style.left = (x - 16) + 'px'; cursorH.style.left = (x - 20) + 'px';
cursorH.style.top = y + 'px'; cursorH.style.top = y + 'px';
cursorV.style.left = x + 'px'; cursorV.style.left = x + 'px';
cursorV.style.top = (y - 16) + 'px'; cursorV.style.top = (y - 20) + 'px';
cursorCenter.style.left = x + 'px'; cursorCenter.style.left = x + 'px';
cursorCenter.style.top = y + 'px'; cursorCenter.style.top = y + 'px';
cursorCoords.style.left = (x + 16) + 'px'; cursorCoords.style.left = (x + 16) + 'px';
@ -85,12 +85,6 @@ document.addEventListener('DOMContentLoaded', () => {
attachCursorHover(document.querySelectorAll( attachCursorHover(document.querySelectorAll(
'a, button, input, .product-card, summary, .panel-close' 'a, button, input, .product-card, summary, .panel-close'
)); ));
// WhatsApp hover — green center dot
document.querySelectorAll('.whatsapp-btn').forEach(el => {
el.addEventListener('mouseenter', () => cursorCenter.classList.add('cad-whatsapp'));
el.addEventListener('mouseleave', () => cursorCenter.classList.remove('cad-whatsapp'));
});
} }
// ========================================================== // ==========================================================
@ -137,70 +131,8 @@ document.addEventListener('DOMContentLoaded', () => {
// 3. GSAP SCROLL ANIMATIONS — CAD REVEAL // 3. GSAP SCROLL ANIMATIONS — CAD REVEAL
// ========================================================== // ==========================================================
// ---- Header fade in ---- // ---- Hero parallax ----
const header = document.querySelector('.header');
if (header) {
gsap.fromTo(header,
{ opacity: 0, y: -10 },
{ opacity: 1, y: 0, duration: 0.5, ease: 'power2.out' }
);
}
// ---- Hero animations (scroll-triggered, replay in/out) ----
const heroLabel = document.querySelector('.hero-left .label');
const heroH1 = document.querySelector('.hero-left h1');
const heroSubs = document.querySelectorAll('.hero-sub');
const heroImg = document.querySelector('.hero-img'); const heroImg = document.querySelector('.hero-img');
const heroRight = document.querySelector('.hero-right');
const heroTl = gsap.timeline({
defaults: { ease: 'power3.out' },
scrollTrigger: {
trigger: '.hero',
start: 'top 95%',
end: 'bottom 5%',
toggleActions: 'play reverse play reverse',
},
});
if (heroLabel) {
heroTl.fromTo(heroLabel,
{ opacity: 0, x: -20 },
{ opacity: 1, x: 0, duration: 0.6 },
0.1
);
}
if (heroH1) {
heroTl.fromTo(heroH1,
{ opacity: 0, y: 40, clipPath: 'inset(0 0 100% 0)' },
{ opacity: 1, y: 0, clipPath: 'inset(0 0 0% 0)', duration: 1 },
0.2
);
}
heroSubs.forEach((sub, i) => {
heroTl.fromTo(sub,
{ opacity: 0, y: 20 },
{ opacity: 1, y: 0, duration: 0.6 },
0.5 + i * 0.15
);
});
if (heroImg && heroRight) {
heroTl.fromTo(heroRight,
{ clipPath: 'inset(0 0 0 100%)' },
{ clipPath: 'inset(0 0 0 0%)', duration: 1.2, ease: 'power4.inOut' },
0.3
);
heroTl.fromTo(heroImg,
{ scale: 1.15, opacity: 0 },
{ scale: 1, opacity: 0.92, duration: 1.4, ease: 'power2.out' },
0.4
);
}
// Hero parallax on scroll
if (heroImg) { if (heroImg) {
gsap.to(heroImg, { gsap.to(heroImg, {
yPercent: 15, yPercent: 15,
@ -221,88 +153,280 @@ document.addEventListener('DOMContentLoaded', () => {
line.className = 'cad-construction-line'; line.className = 'cad-construction-line';
collectionHeader.appendChild(line); collectionHeader.appendChild(line);
gsap.fromTo(line, gsap.from(line, {
{ scaleX: 0 }, scaleX: 0,
{ transformOrigin: 'left center',
scaleX: 1, duration: 0.8,
transformOrigin: 'left center', ease: 'power2.out',
duration: 0.8, scrollTrigger: {
ease: 'power2.out', trigger: collectionHeader,
scrollTrigger: { start: 'top 85%',
trigger: collectionHeader, },
start: 'top 95%', });
end: 'bottom 5%',
toggleActions: 'play reverse play reverse',
},
}
);
} }
// ---- Product cards: scale + fade reveal on scroll (replays) ---- // ---- Product cards: staggered CAD reveal ----
const cards = document.querySelectorAll('.product-card'); const cards = document.querySelectorAll('.product-card');
cards.forEach((card, i) => { cards.forEach((card, i) => {
const imgWrap = card.querySelector('.card-img-wrap');
const img = card.querySelector('.card-img-wrap img'); const img = card.querySelector('.card-img-wrap img');
const meta = card.querySelector('.card-meta'); const meta = card.querySelector('.card-meta');
const imgWrap = card.querySelector('.card-img-wrap');
if (!img) return; if (!img) return;
// Create a CAD scan line per card
const scanLine = document.createElement('div');
scanLine.className = 'card-scanline';
imgWrap.appendChild(scanLine);
// Create corner marks per card
const cornerOverlay = document.createElement('div');
cornerOverlay.className = 'card-corners';
cornerOverlay.innerHTML =
'<span class="cc cc-tl"></span>' +
'<span class="cc cc-tr"></span>' +
'<span class="cc cc-br"></span>' +
'<span class="cc cc-bl"></span>';
imgWrap.appendChild(cornerOverlay);
// Create coordinate annotation per card
const coordTag = document.createElement('div');
coordTag.className = 'card-coord-tag';
coordTag.textContent = `[${String(i + 1).padStart(3, '0')}] — ${img.naturalWidth || '1024'} × ${img.naturalHeight || '1024'} px`;
imgWrap.appendChild(coordTag);
// GSAP timeline triggered on scroll
const tl = gsap.timeline({ const tl = gsap.timeline({
scrollTrigger: { scrollTrigger: {
trigger: card, trigger: card,
start: 'top 95%', start: 'top 88%',
end: 'bottom 5%', toggleActions: 'play none none none',
toggleActions: 'play reverse play reverse',
}, },
}); });
// Clip-path reveal + scale + fade // 1. Image clip-path reveal (bottom → top scan)
tl.fromTo(imgWrap,
{ clipPath: 'inset(8% 8% 8% 8%)' },
{ clipPath: 'inset(0% 0% 0% 0%)', duration: 0.8, ease: 'power3.out' },
0
);
tl.fromTo(img, tl.fromTo(img,
{ opacity: 0, scale: 1.12 }, { clipPath: 'inset(100% 0 0 0)', filter: 'brightness(1.8) grayscale(100%)' },
{ opacity: 1, scale: 1, duration: 0.9, ease: 'power2.out' }, { clipPath: 'inset(0% 0 0 0)', filter: 'brightness(1) grayscale(15%)',
duration: 0.9, ease: 'power3.out' },
0 0
); );
// 2. Scan line sweeps up
tl.fromTo(scanLine,
{ top: '100%', opacity: 1 },
{ top: '-2%', opacity: 0, duration: 0.9, ease: 'power3.out' },
0
);
// 3. Corner brackets appear
tl.fromTo(cornerOverlay.querySelectorAll('.cc'),
{ opacity: 0, scale: 0.5 },
{ opacity: 1, scale: 1, duration: 0.4, stagger: 0.06, ease: 'back.out(2)' },
0.3
);
// 4. Coordinate tag types in
tl.fromTo(coordTag,
{ opacity: 0, x: -10 },
{ opacity: 1, x: 0, duration: 0.4, ease: 'power2.out' },
0.5
);
// 5. Meta bar slides in
if (meta) { if (meta) {
tl.fromTo(meta, tl.fromTo(meta,
{ opacity: 0, y: 15 }, { opacity: 0, y: 8 },
{ opacity: 1, y: 0, duration: 0.5, ease: 'power2.out' }, { opacity: 1, y: 0, duration: 0.4, ease: 'power2.out' },
0.25 0.4
); );
} }
}); });
// ---- Newsletter section: slide in (replays) ---- // ---- Newsletter section: slide in ----
const nlLeft = document.querySelector('.nl-left'); const nlLeft = document.querySelector('.nl-left');
const nlRight = document.querySelector('.nl-right'); const nlRight = document.querySelector('.nl-right');
if (nlLeft && nlRight) { if (nlLeft && nlRight) {
gsap.fromTo(nlLeft, gsap.from(nlLeft, {
{ opacity: 0, x: -40 }, opacity: 0, x: -30, duration: 0.7, ease: 'power2.out',
{ scrollTrigger: { trigger: '.newsletter', start: 'top 80%' },
opacity: 1, x: 0, duration: 0.7, ease: 'power2.out', });
scrollTrigger: { trigger: '.newsletter', start: 'top 95%', end: 'bottom 5%', toggleActions: 'play reverse play reverse' }, gsap.from(nlRight, {
} opacity: 0, x: 30, duration: 0.7, ease: 'power2.out', delay: 0.15,
); scrollTrigger: { trigger: '.newsletter', start: 'top 80%' },
gsap.fromTo(nlRight, });
{ opacity: 0, x: 40 },
{
opacity: 1, x: 0, duration: 0.7, ease: 'power2.out', delay: 0.15,
scrollTrigger: { trigger: '.newsletter', start: 'top 95%', end: 'bottom 5%', toggleActions: 'play reverse play reverse' },
}
);
} }
// ========================================================== // ==========================================================
// 4. (REMOVED) — no overlay effects on panel image // 4. TECHNICAL DRAWING OVERLAY (panel)
// ========================================================== // ==========================================================
const panelImgCol = document.querySelector('.panel-img-col');
let techOverlay = null;
let techTimeline = null;
function createTechOverlay(card) {
if (techOverlay) techOverlay.remove();
techOverlay = document.createElement('div');
techOverlay.className = 'tech-overlay';
// Corner brackets
['tl', 'tr', 'br', 'bl'].forEach(pos => {
const corner = document.createElement('div');
corner.className = `tech-corner tech-corner--${pos}`;
techOverlay.appendChild(corner);
});
// Center crosshair
const centerH = document.createElement('div');
centerH.className = 'tech-center-h';
const centerV = document.createElement('div');
centerV.className = 'tech-center-v';
techOverlay.appendChild(centerH);
techOverlay.appendChild(centerV);
// Horizontal dimension line
const dimH = document.createElement('div');
dimH.className = 'tech-dim tech-dim--h';
dimH.innerHTML =
'<span class="tech-dim-arrow">◂</span>' +
'<span class="tech-dim-line"></span>' +
'<span class="tech-dim-text">840</span>' +
'<span class="tech-dim-line"></span>' +
'<span class="tech-dim-arrow">▸</span>';
techOverlay.appendChild(dimH);
// Vertical dimension line
const dimV = document.createElement('div');
dimV.className = 'tech-dim tech-dim--v';
dimV.innerHTML =
'<span class="tech-dim-arrow">▴</span>' +
'<span class="tech-dim-line"></span>' +
'<span class="tech-dim-text">420</span>' +
'<span class="tech-dim-line"></span>' +
'<span class="tech-dim-arrow">▾</span>';
techOverlay.appendChild(dimV);
// Reference text
const ref = document.createElement('div');
ref.className = 'tech-ref';
const idx = card ? card.dataset.index : '001';
ref.textContent = `REF: ${idx} — SCALE 1:5 — UNIT: mm`;
techOverlay.appendChild(ref);
// Blueprint grid
const grid = document.createElement('div');
grid.className = 'tech-grid';
techOverlay.appendChild(grid);
// Scan line
const scanline = document.createElement('div');
scanline.className = 'tech-scanline';
techOverlay.appendChild(scanline);
if (panelImgCol) panelImgCol.appendChild(techOverlay);
}
function animateTechOverlay() {
if (!techOverlay) return;
// Kill previous timeline
if (techTimeline) techTimeline.kill();
const corners = techOverlay.querySelectorAll('.tech-corner');
const centerH = techOverlay.querySelector('.tech-center-h');
const centerV = techOverlay.querySelector('.tech-center-v');
const dimH = techOverlay.querySelector('.tech-dim--h');
const dimV = techOverlay.querySelector('.tech-dim--v');
const ref = techOverlay.querySelector('.tech-ref');
const grid = techOverlay.querySelector('.tech-grid');
const scanline = techOverlay.querySelector('.tech-scanline');
techTimeline = gsap.timeline();
// 1. Blueprint grid fades in
techTimeline.fromTo(grid,
{ opacity: 0 },
{ opacity: 1, duration: 0.6, ease: 'power1.in' },
0
);
// 2. Scan line sweeps
techTimeline.fromTo(scanline,
{ top: '0%', opacity: 1 },
{ top: '100%', opacity: 0, duration: 1.1, ease: 'power2.inOut' },
0
);
// 3. Corner brackets draw in (scale from corner)
corners.forEach((c, i) => {
const origins = ['top left', 'top right', 'bottom right', 'bottom left'];
techTimeline.fromTo(c,
{ opacity: 0, scale: 0, borderColor: 'rgba(232,168,0,0)' },
{ opacity: 1, scale: 1, borderColor: 'rgba(232,168,0,0.6)',
transformOrigin: origins[i],
duration: 0.4, ease: 'back.out(1.5)' },
0.15 + i * 0.08
);
});
// 4. Center crosshair extends
techTimeline.fromTo(centerH,
{ scaleX: 0, opacity: 0 },
{ scaleX: 1, opacity: 1, duration: 0.5, ease: 'power2.out' },
0.3
);
techTimeline.fromTo(centerV,
{ scaleY: 0, opacity: 0 },
{ scaleY: 1, opacity: 1, duration: 0.5, ease: 'power2.out' },
0.35
);
// 5. Dimension lines extend
techTimeline.fromTo(dimH,
{ opacity: 0, scaleX: 0 },
{ opacity: 1, scaleX: 1, transformOrigin: 'center center',
duration: 0.6, ease: 'power2.out' },
0.4
);
techTimeline.fromTo(dimV,
{ opacity: 0, scaleY: 0 },
{ opacity: 1, scaleY: 1, transformOrigin: 'center center',
duration: 0.6, ease: 'power2.out' },
0.45
);
// 6. Reference text types in
techTimeline.fromTo(ref,
{ opacity: 0, x: -15 },
{ opacity: 1, x: 0, duration: 0.4, ease: 'power2.out' },
0.6
);
// 7. Panel image scan reveal
const panelImg = document.getElementById('panel-img');
if (panelImg) {
techTimeline.fromTo(panelImg,
{ clipPath: 'inset(0 0 100% 0)', filter: 'brightness(1.8) grayscale(100%)' },
{ clipPath: 'inset(0 0 0% 0)', filter: 'brightness(1) contrast(1) grayscale(0%)',
duration: 1, ease: 'power3.out' },
0.05
);
}
}
function hideTechOverlay() {
if (techTimeline) techTimeline.kill();
if (techOverlay) {
gsap.to(techOverlay, { opacity: 0, duration: 0.3, onComplete: () => {
if (techOverlay) techOverlay.remove();
techOverlay = null;
}});
}
}
// ========================================================== // ==========================================================
// 5. PRODUCT PANEL // 5. PRODUCT PANEL
// ========================================================== // ==========================================================
@ -312,23 +436,18 @@ document.addEventListener('DOMContentLoaded', () => {
const panelCards = document.querySelectorAll('.product-card'); const panelCards = document.querySelectorAll('.product-card');
const fields = { const fields = {
img: document.getElementById('panel-img'), img: document.getElementById('panel-img'),
gallery: document.getElementById('panel-gallery'), index: document.getElementById('panel-index'),
galleryNav: document.getElementById('panel-gallery-nav'), name: document.getElementById('panel-name'),
index: document.getElementById('panel-index'), type: document.getElementById('panel-type'),
name: document.getElementById('panel-name'), mat: document.getElementById('panel-mat'),
type: document.getElementById('panel-type'), year: document.getElementById('panel-year'),
mat: document.getElementById('panel-mat'), status: document.getElementById('panel-status'),
year: document.getElementById('panel-year'), desc: document.getElementById('panel-desc'),
status: document.getElementById('panel-status'), specs: document.getElementById('panel-specs'),
desc: document.getElementById('panel-desc'), notes: document.getElementById('panel-notes'),
specs: document.getElementById('panel-specs'),
notes: document.getElementById('panel-notes'),
}; };
let currentGalleryIndex = 0;
let currentGalleryImages = [];
// ---- CHECKOUT LOGIC ---- // ---- CHECKOUT LOGIC ----
const checkoutSection = document.getElementById('checkout-section'); const checkoutSection = document.getElementById('checkout-section');
const checkoutToggleBtn = document.getElementById('checkout-toggle-btn'); const checkoutToggleBtn = document.getElementById('checkout-toggle-btn');
@ -389,67 +508,9 @@ document.addEventListener('DOMContentLoaded', () => {
.replace(/[^a-z0-9-]/g, ''); .replace(/[^a-z0-9-]/g, '');
} }
function showGalleryImage(index) {
if (!currentGalleryImages.length) return;
currentGalleryIndex = index;
fields.img.src = currentGalleryImages[index].url;
fields.img.alt = currentGalleryImages[index].alt;
// Update nav dots
fields.galleryNav.querySelectorAll('.gallery-dot').forEach((dot, i) => {
dot.classList.toggle('active', i === index);
});
}
function openPanel(card, pushState = true) { function openPanel(card, pushState = true) {
// Gallery setup fields.img.src = card.dataset.img;
try { fields.img.alt = card.dataset.imgAlt || card.dataset.name;
currentGalleryImages = JSON.parse(card.dataset.images || '[]');
} catch { currentGalleryImages = []; }
if (currentGalleryImages.length > 0) {
currentGalleryIndex = 0;
fields.img.src = currentGalleryImages[0].url;
fields.img.alt = currentGalleryImages[0].alt;
} else {
fields.img.src = card.dataset.img;
fields.img.alt = card.dataset.imgAlt || card.dataset.name;
}
// Build nav dots
fields.galleryNav.innerHTML = '';
if (currentGalleryImages.length > 1) {
fields.galleryNav.style.display = 'flex';
currentGalleryImages.forEach((_, i) => {
const dot = document.createElement('button');
dot.className = 'gallery-dot' + (i === 0 ? ' active' : '');
dot.setAttribute('aria-label', `Image ${i + 1}`);
dot.addEventListener('click', () => showGalleryImage(i));
fields.galleryNav.appendChild(dot);
});
// Arrow buttons
const prevBtn = document.createElement('button');
prevBtn.className = 'gallery-arrow gallery-prev';
prevBtn.textContent = '←';
prevBtn.setAttribute('aria-label', 'Image précédente');
prevBtn.addEventListener('click', () => {
showGalleryImage((currentGalleryIndex - 1 + currentGalleryImages.length) % currentGalleryImages.length);
});
const nextBtn = document.createElement('button');
nextBtn.className = 'gallery-arrow gallery-next';
nextBtn.textContent = '→';
nextBtn.setAttribute('aria-label', 'Image suivante');
nextBtn.addEventListener('click', () => {
showGalleryImage((currentGalleryIndex + 1) % currentGalleryImages.length);
});
fields.galleryNav.prepend(prevBtn);
fields.galleryNav.appendChild(nextBtn);
} else {
fields.galleryNav.style.display = 'none';
}
fields.index.textContent = card.dataset.index; fields.index.textContent = card.dataset.index;
fields.name.textContent = card.dataset.name; fields.name.textContent = card.dataset.name;
fields.type.textContent = card.dataset.type; fields.type.textContent = card.dataset.type;
@ -486,7 +547,7 @@ document.addEventListener('DOMContentLoaded', () => {
checkoutSubmitBtn.textContent = 'PROCÉDER AU PAIEMENT →'; checkoutSubmitBtn.textContent = 'PROCÉDER AU PAIEMENT →';
checkoutForm.reset(); checkoutForm.reset();
panel.querySelectorAll('details').forEach(d => d.setAttribute('open', '')); panel.querySelectorAll('details').forEach(d => d.removeAttribute('open'));
panel.classList.add('is-open'); panel.classList.add('is-open');
panel.setAttribute('aria-hidden', 'false'); panel.setAttribute('aria-hidden', 'false');
document.body.style.overflow = 'hidden'; document.body.style.overflow = 'hidden';
@ -495,6 +556,10 @@ document.addEventListener('DOMContentLoaded', () => {
'summary, .panel-close, .checkout-btn, .checkout-submit' 'summary, .panel-close, .checkout-btn, .checkout-submit'
)); ));
// Technical drawing overlay (delayed for panel slide-in)
createTechOverlay(card);
setTimeout(() => animateTechOverlay(), 350);
if (pushState) { if (pushState) {
const cardSlug = card.dataset.slug || toSlug(card.dataset.name); const cardSlug = card.dataset.slug || toSlug(card.dataset.name);
history.pushState({ slug: cardSlug }, '', `/collection/${cardSlug}`); history.pushState({ slug: cardSlug }, '', `/collection/${cardSlug}`);
@ -502,6 +567,7 @@ document.addEventListener('DOMContentLoaded', () => {
} }
function closePanel(pushState = true) { function closePanel(pushState = true) {
hideTechOverlay();
panel.classList.remove('is-open'); panel.classList.remove('is-open');
panel.setAttribute('aria-hidden', 'true'); panel.setAttribute('aria-hidden', 'true');
document.body.style.overflow = ''; document.body.style.overflow = '';
@ -661,7 +727,7 @@ document.addEventListener('DOMContentLoaded', () => {
} }
}); });
// Form → WhatsApp // Form → mailto
if (contactForm) { if (contactForm) {
contactForm.addEventListener('submit', (e) => { contactForm.addEventListener('submit', (e) => {
e.preventDefault(); e.preventDefault();
@ -670,9 +736,9 @@ document.addEventListener('DOMContentLoaded', () => {
const subject = document.getElementById('contact-subject').value.trim() || 'Contact depuis rebours.studio'; const subject = document.getElementById('contact-subject').value.trim() || 'Contact depuis rebours.studio';
const message = document.getElementById('contact-message').value.trim(); const message = document.getElementById('contact-message').value.trim();
const waNumber = contactModal.dataset.whatsapp || '33651755191'; const body = `${message}\n\n${name}\n${email}`;
const text = `*${subject}*\n\n${message}\n\n${name}\n${email}`; const mailto = `mailto:contact@rebours.fr?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`;
window.open(`https://wa.me/${waNumber}?text=${encodeURIComponent(text)}`, '_blank'); window.location.href = mailto;
}); });
} }