@@ -33,29 +33,44 @@ export const MODULES = [
3333 Inverter , CvMixer , Attenuator , Attenuverter , Offset , AudioMixer ,
3434] ;
3535
36- // Palette order. Membership = inclusion; index = display order. Modules absent
37- // from this array don't appear in the palette. `output` is intentionally
38- // excluded — every patch has exactly one Output module which the store
39- // guarantees, so it isn't user-addable.
40- const PALETTE_ORDER = [
41- "oscillator" , "filter" , "audiomixer" , "amp" , "env" , "arenv" , "adenv" , "lfo" ,
42- "keyboard" , "trigger" , "clock" , "drumseq" , "counter" , "counter3" , "multiplexer" , "mux8" , "quantizer" ,
43- "inverter" , "attenuator" , "attenuverter" , "cvmixer" , "offset" ,
36+ // Palette grouping. Modules are organised into categories; within each group
37+ // the `types` order is the display order, and the groups themselves display
38+ // top-to-bottom in array order. Membership here = palette inclusion: a module
39+ // absent from every group doesn't appear in the palette. `output` is
40+ // intentionally excluded — every patch has exactly one Output module which the
41+ // store guarantees, so it isn't user-addable.
42+ const PALETTE_GROUPS = [
43+ { key : "audio" , label : "Audio" , types : [ "oscillator" , "filter" , "audiomixer" , "amp" ] } ,
44+ { key : "modulation" , label : "Modulation" , types : [ "env" , "arenv" , "adenv" , "lfo" ] } ,
45+ { key : "trigger" , label : "Trigger" , types : [ "keyboard" , "trigger" , "clock" , "drumseq" ] } ,
46+ { key : "logic" , label : "Logic" , types : [ "counter" , "counter3" , "multiplexer" , "mux8" ] } ,
47+ { key : "utility" , label : "Utility" , types : [ "quantizer" , "inverter" , "attenuator" , "attenuverter" , "cvmixer" , "offset" ] } ,
4448] ;
4549
50+ // Flattened palette order, derived from the groups.
51+ const PALETTE_ORDER = PALETTE_GROUPS . flatMap ( ( g ) => g . types ) ;
52+
4653// Validate at import time. A malformed manifest crashes startup loudly
4754// instead of producing silent UI/audio bugs deep in some lookup.
4855for ( const m of MODULES ) validateManifest ( m ) ;
4956if ( new Set ( PALETTE_ORDER ) . size !== PALETTE_ORDER . length ) {
50- throw new Error ( `[modules] PALETTE_ORDER contains duplicates : ${ PALETTE_ORDER . join ( ", " ) } ` ) ;
57+ throw new Error ( `[modules] PALETTE_GROUPS contains duplicate types : ${ PALETTE_ORDER . join ( ", " ) } ` ) ;
5158}
5259for ( const type of PALETTE_ORDER ) {
5360 if ( ! MODULES . some ( ( m ) => m . type === type ) ) {
54- throw new Error ( `[modules] PALETTE_ORDER references unknown module type: ${ type } ` ) ;
61+ throw new Error ( `[modules] PALETTE_GROUPS references unknown module type: ${ type } ` ) ;
5562 }
5663}
5764
5865const _byTypeMap = new Map ( MODULES . map ( ( m ) => [ m . type , m ] ) ) ;
5966
6067export const byType = ( type ) => _byTypeMap . get ( type ) || null ;
6168export const paletteList = ( ) => PALETTE_ORDER . map ( ( type ) => _byTypeMap . get ( type ) ) ;
69+ // Same modules as paletteList(), but bucketed by category for a grouped UI:
70+ // [{ key, label, items: Manifest[] }, …] in display order.
71+ export const paletteGroups = ( ) =>
72+ PALETTE_GROUPS . map ( ( g ) => ( {
73+ key : g . key ,
74+ label : g . label ,
75+ items : g . types . map ( ( type ) => _byTypeMap . get ( type ) ) ,
76+ } ) ) ;
0 commit comments