@@ -4,86 +4,172 @@ import { useEffect, useRef } from "react";
44import { resetTime , cleanupInstance } from "../constants/Time.js" ;
55import { setCanvasHeight } from "../constants/Utils.js" ;
66
7+ // --------------------------------------------------------------------------
8+ // Turbopack intercepts ALL fetch() calls that originate from within its
9+ // module graph during construction — including the passthrough to realFetch.
10+ // The only reliable fix is to swallow every fetch that fires during
11+ // `new P5Constructor(...)` and let p5 handle the empty responses gracefully.
12+ // The real fetch is restored immediately after the constructor returns.
13+ // --------------------------------------------------------------------------
14+ function withP5FetchGuard ( fn ) {
15+ const realFetch = window . fetch ;
16+
17+ // Swallow ALL fetches during p5 instantiation
18+ window . fetch = function guardedFetch ( ) {
19+ return Promise . resolve (
20+ new Response ( new ArrayBuffer ( 0 ) , {
21+ status : 200 ,
22+ headers : { "Content-Type" : "application/octet-stream" } ,
23+ } )
24+ ) ;
25+ } ;
26+
27+ try {
28+ return fn ( ) ;
29+ } finally {
30+ window . fetch = realFetch ;
31+ }
32+ }
33+
34+ // --------------------------------------------------------------------------
35+ // p5 loader — tries npm import first, falls back to CDN script injection.
36+ // --------------------------------------------------------------------------
37+ const loadP5Library = ( ( ) => {
38+ let cached = null ;
39+
40+ return ( ) =>
41+ new Promise ( async ( resolve , reject ) => {
42+ if ( cached ) return resolve ( cached ) ;
43+
44+ try {
45+ const mod = await import ( "p5" ) ;
46+ const P5 = mod ?. default ?? mod ;
47+ if ( typeof P5 === "function" ) {
48+ cached = P5 ;
49+ return resolve ( cached ) ;
50+ }
51+ } catch ( _ ) { }
52+
53+ if ( typeof window . p5 === "function" ) {
54+ cached = window . p5 ;
55+ return resolve ( cached ) ;
56+ }
57+
58+ const script = document . createElement ( "script" ) ;
59+ script . src =
60+ "https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.1/p5.min.js" ;
61+ script . async = true ;
62+ script . onload = ( ) => {
63+ if ( typeof window . p5 === "function" ) {
64+ cached = window . p5 ;
65+ resolve ( cached ) ;
66+ } else {
67+ reject ( new Error ( "p5 CDN loaded but window.p5 is not a function" ) ) ;
68+ }
69+ } ;
70+ script . onerror = ( ) => reject ( new Error ( "Failed to load p5 from CDN" ) ) ;
71+ document . head . appendChild ( script ) ;
72+ } ) ;
73+ } ) ( ) ;
74+
75+ // --------------------------------------------------------------------------
76+ // Instantiate p5 inside a macrotask so we are outside Turbopack's module
77+ // execution frame, with the fetch guard active for the entire constructor.
78+ // --------------------------------------------------------------------------
79+ function createP5Instance ( P5Constructor , sketchFn , container ) {
80+ return new Promise ( ( resolve , reject ) => {
81+ setTimeout ( ( ) => {
82+ try {
83+ const instance = withP5FetchGuard (
84+ ( ) => new P5Constructor ( sketchFn , container )
85+ ) ;
86+ resolve ( instance ) ;
87+ } catch ( err ) {
88+ reject ( err ) ;
89+ }
90+ } , 0 ) ;
91+ } ) ;
92+ }
93+
94+ // --------------------------------------------------------------------------
95+ // Component
96+ // --------------------------------------------------------------------------
797export default function P5Wrapper ( { sketch, simInfos } ) {
8- // containerRef: the <div> where p5 will attach the canvas
998 const containerRef = useRef ( null ) ;
10- // p5InstanceRef: keeps track of the current p5 instance (so it can be removed on unmount)
1199 const p5InstanceRef = useRef ( null ) ;
12100
13- // Cleanup helper function to safely remove a p5 instance
14101 const safeRemove = ( instance ) => {
15102 if ( ! instance ) return ;
16103 try {
17104 cleanupInstance ( instance ) ;
18105 instance . remove ( ) ;
19- } catch ( err ) { }
106+ } catch ( _ ) { }
20107 } ;
21108
22- // --- LOGICA DI RESIZE CENTRALIZZATA ---
109+ // Centralised resize logic
23110 useEffect ( ( ) => {
24111 const handleResize = ( ) => {
25112 if ( ! containerRef . current || ! p5InstanceRef . current ) return ;
26-
27- // 1. Ottieni le nuove dimensioni dal contenitore DOM
28113 const { clientWidth, clientHeight } = containerRef . current ;
29-
30- // 2. Aggiorna il valore globale della fisica
31114 setCanvasHeight ( clientHeight ) ;
32-
33- // 3. Comunica a p5.js di ridimensionare il canvas
34115 p5InstanceRef . current . resizeCanvas ( clientWidth , clientHeight ) ;
35-
36- // Opzionale: se hai bisogno di resettare qualcosa nello sketch al resize
37116 if ( p5InstanceRef . current . onResize ) {
38117 p5InstanceRef . current . onResize ( clientWidth , clientHeight ) ;
39118 }
40119 } ;
41120
42121 window . addEventListener ( "resize" , handleResize ) ;
43-
44- // Eseguiamo una chiamata iniziale per sincronizzare le dimensioni al montaggio
45122 handleResize ( ) ;
46-
47123 return ( ) => window . removeEventListener ( "resize" , handleResize ) ;
48124 } , [ sketch ] ) ;
49125
126+ // p5 instance lifecycle
50127 useEffect ( ( ) => {
51128 let active = true ;
52- let tempP5Instance = null ;
53129
54- ( async ( ) => {
130+ const loadP5 = async ( ) => {
55131 try {
56- const { default : p5 } = await import ( "p5" ) ;
57- if ( ! active || ! containerRef . current || ! sketch ) return ;
58-
59- safeRemove ( p5InstanceRef . current ) ;
60-
61- tempP5Instance = new p5 ( ( p ) => {
62- p . _instanceId =
63- crypto ?. randomUUID ?. ( ) ?? Math . random ( ) . toString ( 36 ) . slice ( 2 ) ;
132+ if ( typeof window === "undefined" ) return ;
133+ if ( ! containerRef . current || ! sketch ) return ;
64134
65- resetTime ( ) ;
66- sketch ( p ) ;
135+ const P5Constructor = await loadP5Library ( ) ;
136+ if ( ! active ) return ;
67137
68- p . setup = ( ( originalSetup ) => ( ) => {
69- if ( originalSetup ) originalSetup ( ) ;
70- const h = containerRef . current . clientHeight ;
71- const w = containerRef . current . clientWidth ;
72- p . resizeCanvas ( w , h ) ;
73- setCanvasHeight ( h ) ;
74- } ) ( p . setup ) ;
75- } , containerRef . current ) ;
138+ safeRemove ( p5InstanceRef . current ) ;
139+ p5InstanceRef . current = null ;
140+
141+ const instance = await createP5Instance (
142+ P5Constructor ,
143+ ( p ) => {
144+ p . _instanceId =
145+ crypto ?. randomUUID ?. ( ) ?? Math . random ( ) . toString ( 36 ) . slice ( 2 ) ;
146+
147+ resetTime ( ) ;
148+ sketch ( p ) ;
149+
150+ p . setup = ( ( originalSetup ) => ( ) => {
151+ if ( originalSetup ) originalSetup ( ) ;
152+ const h = containerRef . current ?. clientHeight ?? 0 ;
153+ const w = containerRef . current ?. clientWidth ?? 0 ;
154+ p . resizeCanvas ( w , h ) ;
155+ setCanvasHeight ( h ) ;
156+ } ) ( p . setup ) ;
157+ } ,
158+ containerRef . current
159+ ) ;
76160
77161 if ( ! active ) {
78- safeRemove ( tempP5Instance ) ;
162+ safeRemove ( instance ) ;
79163 return ;
80164 }
81165
82- p5InstanceRef . current = tempP5Instance ;
166+ p5InstanceRef . current = instance ;
83167 } catch ( err ) {
84- console . error ( "Failed to load P5:" , err ) ;
168+ console . error ( "Failed to initialize P5 safely :" , err ) ;
85169 }
86- } ) ( ) ;
170+ } ;
171+
172+ loadP5 ( ) ;
87173
88174 return ( ) => {
89175 active = false ;
0 commit comments