Builder « tout est vide » : diagnostic
2026-09-26 (releve le 2026-09-27). Constat : le Builder paraissait vide et mal fonctionner (UI/UX, moteur, logique). Item : backlog/ai-platform/3069. La carte du chrome livre la veille : 2026-09-26-builder-chrome.md.
2026-09-26 (releve le 2026-09-27). Constat : le Builder paraissait vide et mal fonctionner (UI/UX, moteur, logique). Item :
backlog/ai-platform/3069. La carte du chrome livre la veille :2026-09-26-builder-chrome.md.
Ce document trace le chemin des donnees du montage jusqu'aux pixels, puis
classe les causes par preuve. Les extraits de logs viennent du projet
Vercel boostecom.app (production, lecture seule, fenetre de 72 h au
2026-09-27T00:40Z). Aucun secret n'y figure.
1. Le chemin, du montage aux pixels
mode-views.tsx (kept layer "builder")
└─ OnlookBuilder (onlook-builder.tsx)
└─ ProjectProviders → Main (upstream/.../_components/main.tsx, la couture)
├─ useStartProject : GET builder-canvas → applyFrames(5 cadres)
│ url = /api/stores/<id>/builder-frame?file=templates/<x>.json
├─ pages.scanPages : GET builder-library → arbre Pages / Sections + theme
└─ BuilderChrome (chrome/)
├─ Canvas → FrameView → FrameComponent (canvas/frame/view.tsx)
│ <iframe src=builder-frame…> sandbox=allow-same-origin allow-scripts…
│ builder-frame GET ─307→ /api/preview/<id>/<chemin>?editor=1&file=…&preview_theme_id=…
│ proxy : fetch https://<Store.domain><chemin>, scripts coupes par CSP nonce,
│ injecte builderStampScript (shopify-document.ts) dans <head>
│ cadre ─ready→ parent ─hello→ cadre ─dom{blocks}→ parent
│ → frameEvent.handleDomProcessed → ast.mappings → Calques
│ clic : GestureScreen → view.getElementAtLoc → contentDocument.elementFromPoint
│ → closest('[data-oid]') → elements.click → rect pointille (BaseRect, selector-style.ts)
├─ Calques : LayersTab (vendore) sur ast.mappings.filteredLayers
├─ Pages / Sections : PagesTab (vendore) sur pages.tree
└─ Mirror : use-mirror-session → /mirror/frame (sandbox opaque) + /mirror/sections
Ce que le chemin n'utilise pas, verifie : le script de preload d'Onlook
(ONLOOK_PRELOAD_SCRIPT_SRC, CDN jsdelivr) et l'enfant penpal. Un cadre
dont l'URL contient /builder-frame est « local » (view.tsx:283-286) :
ses methodes lisent iframe.contentDocument (localViewMethods,
view.tsx:109-133) et onLoad ne tente jamais penpal sans runtime
(view.tsx:525-534). Les shims code-provider (qui jette) et trpc
(qui repond vide) ne sont atteints par aucun chemin du canevas. Le
preload n'est donc pas la cause, et le reparer ne changerait rien.
2. Ce que la production montre
| Requete | 72 h | Lecture |
|---|---|---|
GET builder-frame | 65 × 307, 9 × 200 | chaque cadre de template redirige vers le proxy |
GET/PUT builder-canvas | 88 × 200 | cadres et viewport persistes, rien ne casse |
GET builder-library | 15 × 200 | la bibliotheque repond toujours 200, meme en echec |
GET /api/preview/cmohv107…/… | 296 × 200 | le proxy sert les pages : /, /products/lapaisante-bague-anti-stress, /pages/contact, /search a 23:19:16 |
Aucune erreur d'execution sur builder-* dans la fenetre, hormis deux
P2002 builderFrame.createMany du 2026-09-24/25, deja corriges par
skipDuplicates (builder-canvas/route.ts:104-118). Le serveur livre
donc tout ce qu'on lui demande : le vide est dans ce qu'il livre et dans
la facon dont le client le lit.
Le magasin du proprietaire (storeId cmohv107f0007xvt61bkbsior) a pour
domaine cutsdemo.com (log tracking-scan.scan_failed, url =
https://cutsdemo.com, meme storeId). La page d'accueil de ce domaine,
lue le 2026-09-27 :
TriplePixelData={TripleName:"c0ffee.myshopify.com",…,plat:"SHOPIFY",isHeadless:true};- feuilles servies depuis
cdn.shopify.com/oxygen-v2/…etwindow.__reactRouterContext.streamController…; - 0 element
id="shopify-section-…"sur 476 Ko de HTML.
cutsdemo.com est un storefront headless (Hydrogen, heberge sur
Oxygen), pas le theme Online Store que le Builder edite.
3. Les causes, classees
C1. Le canevas rend le domaine de la boutique, pas le theme edite (prouve en production)
builder-frame redirige vers le proxy de la Preview
(builder-frame/route.ts:395-406, previewEditorPath dans
shopify-document.ts:288-303). Le proxy va chercher
https://<Store.domain> (api/preview/[storeId]/[[...path]]/route.ts:848)
et lui passe preview_theme_id. Sur une boutique dont le domaine sert
l'Online Store, c'est juste. Sur cutsdemo.com, Hydrogen ignore
preview_theme_id et rend son application React : builderStampScript
ne trouve aucun [id^='shopify-section-'], le message dom part avec
zero bloc, les Calques n'ont qu'une racine, et aucun noeud ne porte
data-oid. Un clic tombe alors dans le repli de readFrameElement
(view.tsx:119-121) : le noeud touche, oid: null, domId = son nom de
balise ("div"). L'inspecteur s'ouvre sur un « div » dont les ecritures
visent [data-oid="div"]. Le canevas montre une page, mais ni le theme
qu'on edite, ni rien qu'on puisse selectionner : c'est le « tout est
vide ». Et une section Mirror installee dans le theme n'apparaitra jamais
sur ce domaine.
C2. Le marquage des sections court contre le parseur (prouve par le code, toutes boutiques)
Le proxy injecte builderStampScript juste apres <head>
(route.ts:1120-1135). Le script poste ready tout de suite et ne
marque les sections qu'a la reception de hello
(shopify-document.ts:339-356), un aller-retour par le parent
(view.tsx:400-410). Le cadre est de meme origine, donc sur le meme fil :
le hello peut etre traite pendant que le parseur n'a pas atteint le
<body>. stamp() ne tourne qu'une fois : les sections parsees apres
restent sans data-oid, absentes des Calques et non selectionnables. Sur
une page d'accueil de 200 a 500 Ko, c'est le cas courant, pas l'exception.
Meme script, second defaut : il obeissait a tout postMessage, de
n'importe quelle source, et postait vers *. Un script plus complet
(storefrontEditorScript) existait, mais seul mountStorefrontDocument,
que plus rien n'appelait, l'utilisait.
Hors de ce chemin, et non corrige ici : « Modifier le texte sur le
canevas » n'atteint pas un cadre local. text.start demande
isChildTextEditable, que localViewMethods laisse a la valeur de repli
false (view.tsx:33-75) : l'edition s'arrete avant le premier message.
C3. Un echec est peint comme un contenu (prouve par le code)
Chaque echec de builder-frame rend html([]) (route.ts:274, 284, 286, 304, 306, 330, 347) : renderBuilderDocument sans bloc peint une fausse
section « Template / index / This theme has no sections in
templates/index.json. » (shopify-document.ts:1076), en anglais. Shopify
non connecte, jeton expire, aucun theme, portee manquante : quatre causes,
une seule image, qui ressemble a du contenu. builder-library repond 200
avec Funnel et Models seulement quand le pont echoue
(builder-library/route.ts:56-63) et avale l'echec de la liste des themes
(79-102) : l'onglet Sections affiche « No pages found » sans dire
pourquoi.
C4. Aucun etat de chargement, vide ou erreur (prouve par le code)
useStartProjectrenvoie toujoursisProjectReady: true, error: null(use-start-project.tsx:141) : le canevas est vide tant quebuilder-canvasn'a pas repondu, puis blanc pendant que cinq pages de boutique se rendent en parallele par le proxy.- Pages et Sections sont le
PagesTabd'Onlook : « Search pages », « No pages found », « Create a new page » en anglais, et le bouton de creation appellecreatePageInSandboxsur un bac a sable qui n'existe pas (page-tab/index.tsx:191-222,pages/index.ts:199-213). - Les Calques n'ont pas d'etat : un cadre qui n'a encore rien dit et un cadre sans section donnent le meme panneau vide.
C5. Le theme choisi peut etre impossible a previsualiser (risque, non observe)
pickBuilderTheme prend le premier role different de MAIN
(builder-library.ts:39-41), donc aussi ARCHIVED ou LOCKED, que
Shopify refuse de previsualiser, et un theme DEVELOPMENT ephemere de la
CLI avant un vrai brouillon UNPUBLISHED.
C6. Mirror dit ses erreurs en code brut (prouve par le code)
use-mirror-session.ts affiche out.json.message ?? out.json.error
(messages serveur en anglais, ou un code comme rights_not_attested) et
install.refused avec le code de refus. Une erreur du cadre source n'a
pas de « Reessayer » (mirror-stage.tsx:80-84). Sans brouillon, l'onglet
dit d'aller creer une copie ailleurs alors qu'une route sure le fait deja
(POST /api/stores/[storeId]/branches { create: true }, utilisee par le
Worktree). Les trois temps (Source, Resultat, Installer) sont implicites.
4. Ce qui est corrige, et ou
| Cause | Correctif |
|---|---|
| C1 | builder-frame choisit l'hote qui rend le theme (themeRenderSource) : le proxy quand Store.domain est le domaine principal de l'Online Store, sinon un rendu serveur depuis ce domaine principal (shop.primaryDomain), monte avec le script d'edition sous CSP a nonce. builder-library renvoie source et le chrome dit « storefront headless » quand c'est le cas. |
| C2 | Un seul script d'edition (builderFrameScript) : il n'annonce ready et ne marque qu'apres DOMContentLoaded, repond a chaque hello avec les memes ids, n'obeit qu'a son parent, poste vers sa propre origine, et dit l'etat de la page (ok, password, no_sections). Un clic hors de toute section selectionne le cadre, plus jamais un noeud sans oid (view.tsx). |
| C3 | Un echec rend un document d'etat (stateDocument) qui porte un code, jamais une fausse section. builder-library renvoie status. |
| C4 | Le chrome lit l'etat de chaque cadre (use-frame-states.ts), de la bibliotheque et du canevas, et rend un squelette, un vide qui dit pourquoi avec l'action qui corrige, ou une erreur avec « Reessayer ». Pages et Sections sont a nous (chrome/library/), traduits. |
| C5 | pickBuilderTheme classe UNPUBLISHED, puis DEVELOPMENT, puis DEMO, ecarte ARCHIVED et LOCKED, et retombe sur MAIN. |
| C6 | Codes d'erreur Mirror traduits, « Reessayer » sur le cadre, « Creer un brouillon du theme » sans brouillon, et trois etapes explicites. |
5. Ce qui n'est pas verifie
- Le rendu serveur depuis
shop.primaryDomainn'a pas ete execute contre la vraie boutique : pas d'acces Admin depuis cette session. Si Shopify renvoiecutsdemo.comcomme domaine principal de l'Online Store, la regle garde le proxy et le chrome affiche l'etat « aucune section de theme » avec l'explication headless, pas le theme. - Une Online Store protegee par mot de passe, rendue cote serveur, montre l'etat « mot de passe » avec le lien vers les preferences Shopify ; le mot de passe saisi dans la Preview ne s'applique qu'au chemin proxy.
- Aucun navigateur n'a ete lance (pas de
.env, la seule base est la production) : la course du parseur (C2) est prouvee par lecture et par un harnais happy-dom (builder-frame-script.test.ts, qui echoue sur l'ancien script), pas par une capture. - L'edition de texte sur le canevas (voir C2) reste inatteignable pour un cadre local ; un item de suivi est a ouvrir.