@@ -142,6 +142,11 @@ export interface CompositionState {
142142 flipBinT3: number [];
143143 flipBinF3: number [];
144144 stutterPanCHs: number [];
145+ // Backwards-compatible divisions alias
146+ divisions: number ;
147+
148+ // Sections config for testing
149+ SECTIONS? : { min: number ; max: number };
145150
146151 // Logging
147152 LOG: string ;
@@ -268,7 +273,10 @@ export class CompositionStateService implements CompositionState {
268273 velocity = 99 ;
269274 flipBinT3: number [] = [];
270275 flipBinF3: number [] = [];
271- stutterPanCHs: number [] = [];
276+ stutterPanCHs: number [] = []; // Backwards-compatible divisions alias
277+ divisions = 4 ;
278+ // Sections config for testing (allows tests to seed section ranges into state)
279+ SECTIONS = { min: 1 , max: 1 };
272280
273281 // Logging
274282 LOG = ' none' ;
@@ -277,58 +285,81 @@ export class CompositionStateService implements CompositionState {
277285 * Sync state with globalThis
278286 */
279287 syncToGlobal() {
280- const g = globalThis as any ;
281- g .sectionIndex = this .sectionIndex ;
282- g .totalSections = this .totalSections ;
283- g .sectionStart = this .sectionStart ;
284- g .phraseIndex = this .phraseIndex ;
285- g .phrasesPerSection = this .phrasesPerSection ;
286- g .phraseStart = this .phraseStart ;
287- g .measureIndex = this .measureIndex ;
288- g .measureStart = this .measureStart ;
289- g .beatIndex = this .beatIndex ;
290- g .numerator = this .numerator ;
291- g .denominator = this .denominator ;
292- g .beatCount = this .beatCount ;
293- g .beatStart = this .beatStart ;
294- g .divsPerBeat = this .divsPerBeat ;
295- g .divIndex = this .divIndex ;
296- g .divStart = this .divStart ;
297- g .subdivsPerDiv = this .subdivsPerDiv ;
298- g .subdivIndex = this .subdivIndex ;
299- g .subdivStart = this .subdivStart ;
300- g .subsubdivIndex = this .subsubdivIndex ;
301- g .composer = this .composer ;
302- g .activeMotif = this .activeMotif ;
303- g .BPM = this .BPM ;
304- g .beatRhythm = this .beatRhythm ;
305- g .divRhythm = this .divRhythm ;
306- g .subdivRhythm = this .subdivRhythm ;
307- g .LOG = this .LOG ;
288+ // Sync to PolychronContext.state and PolychronContext.test for legacy compatibility without globals
289+ const poly = getPolychronContext ();
290+ poly .state = poly .state || {} as any ;
291+ poly .test = poly .test || {} as any ;
292+
293+ // State vars
294+ poly .state .sectionIndex = this .sectionIndex ;
295+ poly .state .totalSections = this .totalSections ;
296+ poly .state .sectionStart = this .sectionStart ;
297+ poly .state .phraseIndex = this .phraseIndex ;
298+ poly .state .phrasesPerSection = this .phrasesPerSection ;
299+ poly .state .phraseStart = this .phraseStart ;
300+ poly .state .measureIndex = this .measureIndex ;
301+ poly .state .measureStart = this .measureStart ;
302+ poly .state .beatIndex = this .beatIndex ;
303+ poly .state .numerator = this .numerator ;
304+ poly .state .denominator = this .denominator ;
305+ poly .state .beatCount = this .beatCount ;
306+ poly .state .beatStart = this .beatStart ;
307+ poly .state .divsPerBeat = this .divsPerBeat ;
308+ poly .state .divisions = this .divisions ;
309+ poly .state .divIndex = this .divIndex ;
310+ poly .state .divStart = this .divStart ;
311+ poly .state .subdivsPerDiv = this .subdivsPerDiv ;
312+ poly .state .subdivIndex = this .subdivIndex ;
313+ poly .state .subdivStart = this .subdivStart ;
314+ poly .state .subsubdivIndex = this .subsubdivIndex ;
315+ poly .state .composer = this .composer ;
316+ poly .state .activeMotif = this .activeMotif ;
317+ poly .state .BPM = this .BPM ;
318+ poly .state .beatRhythm = this .beatRhythm ;
319+ poly .state .divRhythm = this .divRhythm ;
320+ poly .state .subdivRhythm = this .subdivRhythm ;
321+ poly .state .measuresPerPhrase = this .measuresPerPhrase ;
322+ poly .state .SECTIONS = this .SECTIONS ;
323+
324+ // Test namespace for logging/legacy read
325+ poly .test .LOG = this .LOG ;
326+
327+ // Do NOT write to globalThis; only keep DI-friendly namespaces in sync
328+ poly .state = poly .state || {} as any ; // ensure state exists
308329 }
309330
310331 /**
311332 * Sync state from globalThis (for test setup)
312333 */
313334 syncFromGlobal() {
314- const g = globalThis as any ;
315- if (g .sectionIndex !== undefined ) this .sectionIndex = g .sectionIndex ;
316- if (g .totalSections !== undefined ) this .totalSections = g .totalSections ;
317- if (g .phraseIndex !== undefined ) this .phraseIndex = g .phraseIndex ;
318- if (g .measureIndex !== undefined ) this .measureIndex = g .measureIndex ;
319- if (g .beatIndex !== undefined ) this .beatIndex = g .beatIndex ;
320- if (g .numerator !== undefined ) this .numerator = g .numerator ;
321- if (g .denominator !== undefined ) this .denominator = g .denominator ;
322- if (g .beatCount !== undefined ) this .beatCount = g .beatCount ;
323- if (g .divIndex !== undefined ) this .divIndex = g .divIndex ;
324- if (g .subdivIndex !== undefined ) this .subdivIndex = g .subdivIndex ;
325- if (g .subsubdivIndex !== undefined ) this .subsubdivIndex = g .subsubdivIndex ;
326- if (g .composer !== undefined ) this .composer = g .composer ;
327- if (g .activeMotif !== undefined ) this .activeMotif = g .activeMotif ;
328- if (g .BPM !== undefined ) this .BPM = g .BPM ;
329- if (g .flipBinT3 !== undefined ) this .flipBinT3 = g .flipBinT3 ;
330- if (g .flipBinF3 !== undefined ) this .flipBinF3 = g .flipBinF3 ;
331- if (g .LOG !== undefined ) this .LOG = g .LOG ;
335+ const poly = getPolychronContext ();
336+ const gState = poly .state || {} as any ;
337+ const gTest = poly .test || {} as any ;
338+
339+ // First read from the DI-friendly namespaces
340+ if (gState .sectionIndex !== undefined ) this .sectionIndex = gState .sectionIndex ;
341+ if (gState .totalSections !== undefined ) this .totalSections = gState .totalSections ;
342+ if (gState .phraseIndex !== undefined ) this .phraseIndex = gState .phraseIndex ;
343+ if (gState .measureIndex !== undefined ) this .measureIndex = gState .measureIndex ;
344+ if (gState .beatIndex !== undefined ) this .beatIndex = gState .beatIndex ;
345+ if (gState .numerator !== undefined ) this .numerator = gState .numerator ;
346+ if (gState .denominator !== undefined ) this .denominator = gState .denominator ;
347+ if (gState .beatCount !== undefined ) this .beatCount = gState .beatCount ;
348+ if (gState .divIndex !== undefined ) this .divIndex = gState .divIndex ;
349+ if (gState .divisions !== undefined ) this .divisions = gState .divisions ;
350+ if (gState .subdivIndex !== undefined ) this .subdivIndex = gState .subdivIndex ;
351+ if (gState .subsubdivIndex !== undefined ) this .subsubdivIndex = gState .subsubdivIndex ;
352+ if (gState .composer !== undefined ) this .composer = gState .composer ;
353+ if (gState .activeMotif !== undefined ) this .activeMotif = gState .activeMotif ;
354+ if (gState .BPM !== undefined ) this .BPM = gState .BPM ;
355+ if (gState .measuresPerPhrase !== undefined ) this .measuresPerPhrase = gState .measuresPerPhrase ;
356+ if (gState .SECTIONS !== undefined ) this .SECTIONS = gState .SECTIONS ;
357+ if (gState .flipBinT3 !== undefined ) this .flipBinT3 = gState .flipBinT3 ;
358+ if (gState .flipBinF3 !== undefined ) this .flipBinF3 = gState .flipBinF3 ;
359+ if (gTest .LOG !== undefined ) this .LOG = gTest .LOG ;
360+
361+ // Do not read from globalThis; DI namespaces are authoritative (poly.state & poly.test)
362+ // (no-op - already read from poly.state and poly.test above)
332363 }
333364
334365 /**
@@ -362,34 +393,47 @@ Mirror state to/from `globalThis` for certain initialization flows.
362393
363394``` typescript
364395syncToGlobal () {
365- const g = globalThis as any ;
366- g .sectionIndex = this .sectionIndex ;
367- g .totalSections = this .totalSections ;
368- g .sectionStart = this .sectionStart ;
369- g .phraseIndex = this .phraseIndex ;
370- g .phrasesPerSection = this .phrasesPerSection ;
371- g .phraseStart = this .phraseStart ;
372- g .measureIndex = this .measureIndex ;
373- g .measureStart = this .measureStart ;
374- g .beatIndex = this .beatIndex ;
375- g .numerator = this .numerator ;
376- g .denominator = this .denominator ;
377- g .beatCount = this .beatCount ;
378- g .beatStart = this .beatStart ;
379- g .divsPerBeat = this .divsPerBeat ;
380- g .divIndex = this .divIndex ;
381- g .divStart = this .divStart ;
382- g .subdivsPerDiv = this .subdivsPerDiv ;
383- g .subdivIndex = this .subdivIndex ;
384- g .subdivStart = this .subdivStart ;
385- g .subsubdivIndex = this .subsubdivIndex ;
386- g .composer = this .composer ;
387- g .activeMotif = this .activeMotif ;
388- g .BPM = this .BPM ;
389- g .beatRhythm = this .beatRhythm ;
390- g .divRhythm = this .divRhythm ;
391- g .subdivRhythm = this .subdivRhythm ;
392- g .LOG = this .LOG ;
396+ // Sync to PolychronContext.state and PolychronContext.test for legacy compatibility without globals
397+ const poly = getPolychronContext ();
398+ poly .state = poly .state || {} as any ;
399+ poly .test = poly .test || {} as any ;
400+
401+ // State vars
402+ poly .state .sectionIndex = this .sectionIndex ;
403+ poly .state .totalSections = this .totalSections ;
404+ poly .state .sectionStart = this .sectionStart ;
405+ poly .state .phraseIndex = this .phraseIndex ;
406+ poly .state .phrasesPerSection = this .phrasesPerSection ;
407+ poly .state .phraseStart = this .phraseStart ;
408+ poly .state .measureIndex = this .measureIndex ;
409+ poly .state .measureStart = this .measureStart ;
410+ poly .state .beatIndex = this .beatIndex ;
411+ poly .state .numerator = this .numerator ;
412+ poly .state .denominator = this .denominator ;
413+ poly .state .beatCount = this .beatCount ;
414+ poly .state .beatStart = this .beatStart ;
415+ poly .state .divsPerBeat = this .divsPerBeat ;
416+ poly .state .divisions = this .divisions ;
417+ poly .state .divIndex = this .divIndex ;
418+ poly .state .divStart = this .divStart ;
419+ poly .state .subdivsPerDiv = this .subdivsPerDiv ;
420+ poly .state .subdivIndex = this .subdivIndex ;
421+ poly .state .subdivStart = this .subdivStart ;
422+ poly .state .subsubdivIndex = this .subsubdivIndex ;
423+ poly .state .composer = this .composer ;
424+ poly .state .activeMotif = this .activeMotif ;
425+ poly .state .BPM = this .BPM ;
426+ poly .state .beatRhythm = this .beatRhythm ;
427+ poly .state .divRhythm = this .divRhythm ;
428+ poly .state .subdivRhythm = this .subdivRhythm ;
429+ poly .state .measuresPerPhrase = this .measuresPerPhrase ;
430+ poly .state .SECTIONS = this .SECTIONS ;
431+
432+ // Test namespace for logging/legacy read
433+ poly .test .LOG = this .LOG ;
434+
435+ // Do NOT write to globalThis; only keep DI-friendly namespaces in sync
436+ poly .state = poly .state || {} as any ; // ensure state exists
393437 }
394438```
395439
@@ -399,24 +443,34 @@ syncToGlobal() {
399443
400444``` typescript
401445syncFromGlobal () {
402- const g = globalThis as any ;
403- if (g .sectionIndex !== undefined ) this .sectionIndex = g .sectionIndex ;
404- if (g .totalSections !== undefined ) this .totalSections = g .totalSections ;
405- if (g .phraseIndex !== undefined ) this .phraseIndex = g .phraseIndex ;
406- if (g .measureIndex !== undefined ) this .measureIndex = g .measureIndex ;
407- if (g .beatIndex !== undefined ) this .beatIndex = g .beatIndex ;
408- if (g .numerator !== undefined ) this .numerator = g .numerator ;
409- if (g .denominator !== undefined ) this .denominator = g .denominator ;
410- if (g .beatCount !== undefined ) this .beatCount = g .beatCount ;
411- if (g .divIndex !== undefined ) this .divIndex = g .divIndex ;
412- if (g .subdivIndex !== undefined ) this .subdivIndex = g .subdivIndex ;
413- if (g .subsubdivIndex !== undefined ) this .subsubdivIndex = g .subsubdivIndex ;
414- if (g .composer !== undefined ) this .composer = g .composer ;
415- if (g .activeMotif !== undefined ) this .activeMotif = g .activeMotif ;
416- if (g .BPM !== undefined ) this .BPM = g .BPM ;
417- if (g .flipBinT3 !== undefined ) this .flipBinT3 = g .flipBinT3 ;
418- if (g .flipBinF3 !== undefined ) this .flipBinF3 = g .flipBinF3 ;
419- if (g .LOG !== undefined ) this .LOG = g .LOG ;
446+ const poly = getPolychronContext ();
447+ const gState = poly .state || {} as any ;
448+ const gTest = poly .test || {} as any ;
449+
450+ // First read from the DI-friendly namespaces
451+ if (gState .sectionIndex !== undefined ) this .sectionIndex = gState .sectionIndex ;
452+ if (gState .totalSections !== undefined ) this .totalSections = gState .totalSections ;
453+ if (gState .phraseIndex !== undefined ) this .phraseIndex = gState .phraseIndex ;
454+ if (gState .measureIndex !== undefined ) this .measureIndex = gState .measureIndex ;
455+ if (gState .beatIndex !== undefined ) this .beatIndex = gState .beatIndex ;
456+ if (gState .numerator !== undefined ) this .numerator = gState .numerator ;
457+ if (gState .denominator !== undefined ) this .denominator = gState .denominator ;
458+ if (gState .beatCount !== undefined ) this .beatCount = gState .beatCount ;
459+ if (gState .divIndex !== undefined ) this .divIndex = gState .divIndex ;
460+ if (gState .divisions !== undefined ) this .divisions = gState .divisions ;
461+ if (gState .subdivIndex !== undefined ) this .subdivIndex = gState .subdivIndex ;
462+ if (gState .subsubdivIndex !== undefined ) this .subsubdivIndex = gState .subsubdivIndex ;
463+ if (gState .composer !== undefined ) this .composer = gState .composer ;
464+ if (gState .activeMotif !== undefined ) this .activeMotif = gState .activeMotif ;
465+ if (gState .BPM !== undefined ) this .BPM = gState .BPM ;
466+ if (gState .measuresPerPhrase !== undefined ) this .measuresPerPhrase = gState .measuresPerPhrase ;
467+ if (gState .SECTIONS !== undefined ) this .SECTIONS = gState .SECTIONS ;
468+ if (gState .flipBinT3 !== undefined ) this .flipBinT3 = gState .flipBinT3 ;
469+ if (gState .flipBinF3 !== undefined ) this .flipBinF3 = gState .flipBinF3 ;
470+ if (gTest .LOG !== undefined ) this .LOG = gTest .LOG ;
471+
472+ // Do not read from globalThis; DI namespaces are authoritative (poly.state & poly.test)
473+ // (no-op - already read from poly.state and poly.test above)
420474 }
421475```
422476
0 commit comments