44 */
55
66import { GUI } from 'three/addons/libs/lil-gui.module.min.js' ;
7- import { saveSlicingSettings , loadSlicingSettings } from './state.js' ;
7+ import { saveSlicingSettings , loadSlicingSettings , saveFolderStates , loadFolderStates } from './state.js' ;
88
99/**
1010 * Create the legends for movement types and axes.
@@ -166,7 +166,15 @@ export function createSlicingGUI(sliceCallback, useDefaults = false, rotateCallb
166166
167167 slicingGUI = new GUI ( { title : 'Slicer' } ) ;
168168
169- let h = slicingGUI . addFolder ( 'Model Rotation' ) ;
169+ // Build a named folder map to avoid relying on lil-gui private properties.
170+ // Each entry tracks the folder reference and its current open state.
171+ const folderMap = { } ;
172+ const trackFolder = ( title , folder , defaultOpen ) => {
173+ folderMap [ title ] = { folder, isOpen : defaultOpen } ;
174+ return folder ;
175+ } ;
176+
177+ let h = trackFolder ( 'Model Rotation' , slicingGUI . addFolder ( 'Model Rotation' ) , true ) ;
170178 h . add ( params , 'rotationX' , - 180 , 180 , 1 ) . name ( 'Rotation X (°)' ) . onChange ( ( value ) => {
171179 saveSlicingSettings ( params ) ;
172180 if ( rotateCallback ) rotateCallback ( 'x' , value ) ;
@@ -187,26 +195,26 @@ export function createSlicingGUI(sliceCallback, useDefaults = false, rotateCallb
187195 rotateCallback ( 'z' , params . rotationZ ) ;
188196 }
189197
190- h = slicingGUI . addFolder ( 'Printer & Filament' ) ;
198+ h = trackFolder ( 'Printer & Filament' , slicingGUI . addFolder ( 'Printer & Filament' ) , true ) ;
191199 h . add ( params , 'printer' , PRINTER_OPTIONS ) . name ( 'Printer' ) . onChange ( ( ) => saveSlicingSettings ( params ) ) ;
192200 h . add ( params , 'filament' , FILAMENT_OPTIONS ) . name ( 'Filament' ) . onChange ( ( ) => saveSlicingSettings ( params ) ) ;
193201 h . add ( params , 'nozzleTemperature' , 150 , 300 , 5 ) . name ( 'Nozzle Temp (°C)' ) . onFinishChange ( ( ) => saveSlicingSettings ( params ) ) ;
194202 h . add ( params , 'bedTemperature' , 0 , 120 , 5 ) . name ( 'Bed Temp (°C)' ) . onFinishChange ( ( ) => saveSlicingSettings ( params ) ) ;
195203 h . add ( params , 'fanSpeed' , 0 , 100 , 5 ) . name ( 'Fan Speed (%)' ) . onFinishChange ( ( ) => saveSlicingSettings ( params ) ) ;
196204
197- h = slicingGUI . addFolder ( 'Slicer Settings' ) ;
205+ h = trackFolder ( 'Slicer Settings' , slicingGUI . addFolder ( 'Slicer Settings' ) , true ) ;
198206 h . add ( params , 'shellWallThickness' , 0.4 , 2.0 , 0.4 ) . name ( 'Shell Wall Thickness (mm)' ) . onFinishChange ( ( ) => saveSlicingSettings ( params ) ) ;
199207 h . add ( params , 'shellSkinThickness' , 0.4 , 2.0 , 0.4 ) . name ( 'Shell Skin Thickness (mm)' ) . onFinishChange ( ( ) => saveSlicingSettings ( params ) ) ;
200208 h . add ( params , 'layerHeight' , 0.1 , 0.4 , 0.05 ) . name ( 'Layer Height (mm)' ) . onFinishChange ( ( ) => saveSlicingSettings ( params ) ) ;
201209 h . add ( params , 'infillDensity' , 0 , 100 , 5 ) . name ( 'Infill Density (%)' ) . onFinishChange ( ( ) => saveSlicingSettings ( params ) ) ;
202210 h . add ( params , 'infillPattern' , INFILL_PATTERN_OPTIONS ) . name ( 'Infill Pattern' ) . onChange ( ( ) => saveSlicingSettings ( params ) ) ;
203211
204- h = slicingGUI . addFolder ( 'Adhesion' ) ;
212+ h = trackFolder ( 'Adhesion' , slicingGUI . addFolder ( 'Adhesion' ) , false ) ;
205213 h . add ( params , 'adhesionEnabled' ) . name ( 'Adhesion Enabled' ) . onChange ( ( ) => saveSlicingSettings ( params ) ) ;
206214 h . add ( params , 'adhesionType' , [ 'skirt' , 'brim' , 'raft' ] ) . name ( 'Adhesion Type' ) . onChange ( ( ) => saveSlicingSettings ( params ) ) ;
207215 h . close ( ) ;
208216
209- h = slicingGUI . addFolder ( 'Support' ) ;
217+ h = trackFolder ( 'Support' , slicingGUI . addFolder ( 'Support' ) , false ) ;
210218 h . add ( params , 'supportEnabled' ) . name ( 'Support Enabled' ) . onChange ( ( ) => saveSlicingSettings ( params ) ) ;
211219 h . add ( params , 'supportType' , [ 'normal' , 'tree' ] ) . name ( 'Support Type' ) . onChange ( ( ) => saveSlicingSettings ( params ) ) ;
212220 h . add ( params , 'supportPlacement' , [ 'buildPlate' , 'everywhere' ] ) . name ( 'Support Placement' ) . onChange ( ( ) => saveSlicingSettings ( params ) ) ;
@@ -216,6 +224,28 @@ export function createSlicingGUI(sliceCallback, useDefaults = false, rotateCallb
216224 slicingGUI . add ( params , 'slice' ) . name ( 'Slice' ) ;
217225 slicingGUI . open ( ) ;
218226
227+ // Collect the current open/closed state of all folders.
228+ const collectFolderStates = ( ) =>
229+ Object . fromEntries ( Object . entries ( folderMap ) . map ( ( [ title , entry ] ) => [ title , entry . isOpen ] ) ) ;
230+
231+ // Apply saved folder states (skipped when resetting to defaults), then
232+ // attach a click listener to each folder title to persist state on toggle.
233+ const savedFolderStates = useDefaults ? null : loadFolderStates ( ) ;
234+ for ( const [ title , entry ] of Object . entries ( folderMap ) ) {
235+ if ( savedFolderStates && title in savedFolderStates ) {
236+ entry . isOpen = savedFolderStates [ title ] ;
237+ if ( entry . isOpen ) {
238+ entry . folder . open ( ) ;
239+ } else {
240+ entry . folder . close ( ) ;
241+ }
242+ }
243+ entry . folder . $title . addEventListener ( 'click' , ( ) => {
244+ entry . isOpen = ! entry . isOpen ;
245+ saveFolderStates ( collectFolderStates ( ) ) ;
246+ } ) ;
247+ }
248+
219249 // Store params on the GUI instance for access in sliceModel
220250 slicingGUI . userData = params ;
221251
0 commit comments