@@ -214,26 +214,10 @@ export default function KeypadFlasherApp() {
214214 muted = { false }
215215 />
216216 < div className = "muted small" style = { { maxWidth : "340px" } } >
217- Hold to see active lighting, release to return to passive. Does not show global brightness. Updates live as you change settings below .
217+ Hold to see active lighting, release to return to passive. Does not show global brightness. Updates live when settings change .
218218 </ div >
219219 </ div >
220220 </ div >
221- < div style = { { display : "flex" , flexDirection : "column" , gap : "4px" , marginTop : "8px" } } >
222- < div style = { { fontWeight : 600 } } > Device lighting</ div >
223- < div className = "muted small" > These settings apply to every LED on the device.</ div >
224- < label className = "muted small" style = { { display : "flex" , alignItems : "center" , justifyContent : "space-between" , gap : "10px" } } >
225- < span > Global brightness</ span >
226- < input
227- type = "range"
228- min = { 0 }
229- max = { 100 }
230- value = { draftLedConfig . brightnessPercent }
231- onChange = { ( e ) => setBrightnessPercent ( Number ( e . target . value ) ) }
232- />
233- < span style = { { minWidth : "36px" , textAlign : "right" } } > { draftLedConfig . brightnessPercent } %</ span >
234- </ label >
235- < div className = "muted small" > Brightness scales both passive and active effects together; use the controls below for per-key tweaks.</ div >
236- </ div >
237221 < div style = { { display : "flex" , flexDirection : "column" , gap : "14px" } } >
238222 < div style = { { display : "flex" , flexDirection : "column" , gap : "2px" } } >
239223 < div style = { { fontWeight : 600 } } > Passive lighting</ div >
@@ -362,6 +346,7 @@ export default function KeypadFlasherApp() {
362346 const [ editorTarget , setEditorTarget ] = useState < EditTarget | null > ( null ) ;
363347 const [ editorBinding , setEditorBinding ] = useState < HidBindingDto | null > ( null ) ;
364348 const [ stepClipboard , setStepClipboard ] = useState < HidStepDto [ ] | null > ( null ) ;
349+ const [ showGlobalLightingModal , setShowGlobalLightingModal ] = useState < boolean > ( false ) ;
365350 const [ showLightingModal , setShowLightingModal ] = useState < boolean > ( false ) ;
366351 const [ focusLedIndex , setFocusLedIndex ] = useState < number | null > ( null ) ;
367352 const [ copiedLedLighting , setCopiedLedLighting ] = useState < { passiveMode : PassiveLedMode ; passive : LedColor ; activeMode : ActiveLedMode ; activeColor : LedColor } | null > ( null ) ;
@@ -913,6 +898,17 @@ export default function KeypadFlasherApp() {
913898 return { r, g, b } ;
914899 } ;
915900
901+ const cloneLedConfig = ( config : LedConfigurationDto ) : LedConfigurationDto => ( {
902+ passiveModes : [ ...config . passiveModes ] ,
903+ passiveColors : [ ...config . passiveColors ] ,
904+ activeModes : [ ...config . activeModes ] ,
905+ activeColors : [ ...config . activeColors ] ,
906+ brightnessPercent : config . brightnessPercent ,
907+ rainbowStepMs : config . rainbowStepMs ,
908+ breathingMinPercent : config . breathingMinPercent ,
909+ breathingStepMs : config . breathingStepMs ,
910+ } ) ;
911+
916912 const ledDisplayName = ( idx : number ) : string => {
917913 const btn = userButtons . find ( ( b ) => b . ledIndex === idx ) ;
918914 if ( btn ) return `Button ${ btn . id + 1 } ` ;
@@ -1021,6 +1017,26 @@ export default function KeypadFlasherApp() {
10211017 }
10221018 } , [ parseImportedConfig , connectedInfo , rememberedBootloaderId , selectedLayout ] ) ;
10231019
1020+ const closeGlobalLightingModal = ( ) => {
1021+ setShowGlobalLightingModal ( false ) ;
1022+ setDraftLedConfig ( null ) ;
1023+ } ;
1024+
1025+ const saveGlobalLightingModal = ( ) => {
1026+ if ( draftLedConfig ) {
1027+ setLedConfig ( draftLedConfig ) ;
1028+ }
1029+ closeGlobalLightingModal ( ) ;
1030+ } ;
1031+
1032+ const openGlobalLightingModal = ( ) => {
1033+ if ( ! ledConfig ) return ;
1034+ setDraftLedConfig ( cloneLedConfig ( ledConfig ) ) ;
1035+ setLightingStatus ( defaultLightingStatus ) ;
1036+ setFocusLedIndex ( null ) ;
1037+ setShowGlobalLightingModal ( true ) ;
1038+ } ;
1039+
10241040 const closeLightingModal = ( ) => {
10251041 setShowLightingModal ( false ) ;
10261042 setDraftLedConfig ( null ) ;
@@ -1035,16 +1051,7 @@ export default function KeypadFlasherApp() {
10351051 } ;
10361052 const openLightingForLed = ( idx : number ) => {
10371053 if ( ! ledConfig || idx < 0 || idx >= ledConfig . passiveColors . length ) return ;
1038- setDraftLedConfig ( {
1039- passiveModes : [ ...ledConfig . passiveModes ] ,
1040- passiveColors : [ ...ledConfig . passiveColors ] ,
1041- activeModes : [ ...ledConfig . activeModes ] ,
1042- activeColors : [ ...ledConfig . activeColors ] ,
1043- brightnessPercent : ledConfig . brightnessPercent ,
1044- rainbowStepMs : ledConfig . rainbowStepMs ,
1045- breathingMinPercent : ledConfig . breathingMinPercent ,
1046- breathingStepMs : ledConfig . breathingStepMs ,
1047- } ) ;
1054+ setDraftLedConfig ( cloneLedConfig ( ledConfig ) ) ;
10481055 setLightingStatus ( defaultLightingStatus ) ;
10491056 setFocusLedIndex ( idx ) ;
10501057 setShowLightingModal ( true ) ;
@@ -1387,6 +1394,8 @@ export default function KeypadFlasherApp() {
13871394 warnSingleChord = { warnSingleChord }
13881395 onEdit = { openEdit }
13891396 onOpenLightingForLed = { openLightingForLed }
1397+ onOpenLightingSettings = { openGlobalLightingModal }
1398+ lightingDisabled = { ! ledConfig || layoutLedCount === 0 }
13901399 onToggleBootloaderOnBoot = { updateBootloaderOnBoot }
13911400 onToggleBootloaderChord = { updateBootloaderChordMember }
13921401 onExportConfig = { openExportModal }
@@ -1458,6 +1467,54 @@ export default function KeypadFlasherApp() {
14581467 </ div >
14591468 ) }
14601469
1470+ { showGlobalLightingModal && (
1471+ < div
1472+ className = "modal-backdrop"
1473+ role = "dialog"
1474+ aria-modal = "true"
1475+ onClick = { ( e ) => {
1476+ if ( modalPointerDownRef . current ) { modalPointerDownRef . current = false ; return ; }
1477+ if ( e . target === e . currentTarget ) closeGlobalLightingModal ( ) ;
1478+ } }
1479+ >
1480+ < div
1481+ className = "modal lighting-modal"
1482+ onClick = { ( e ) => e . stopPropagation ( ) }
1483+ onMouseDown = { ( ) => { modalPointerDownRef . current = true ; } }
1484+ onMouseUp = { ( ) => { modalPointerDownRef . current = false ; } }
1485+ >
1486+ < div className = "modal-header" >
1487+ < div className = "modal-title" > Global lighting</ div >
1488+ </ div >
1489+ < div className = "modal-body" >
1490+ { draftLedConfig ? (
1491+ < div className = "space-y-2" >
1492+ < div className = "muted small" > These settings apply to every button LED on the device.</ div >
1493+ < label className = "muted small" style = { { display : "flex" , alignItems : "center" , justifyContent : "space-between" , gap : "10px" } } >
1494+ < span > Global brightness</ span >
1495+ < input
1496+ type = "range"
1497+ min = { 0 }
1498+ max = { 100 }
1499+ value = { draftLedConfig . brightnessPercent }
1500+ onChange = { ( e ) => setBrightnessPercent ( Number ( e . target . value ) ) }
1501+ />
1502+ < span style = { { minWidth : "36px" , textAlign : "right" } } > { draftLedConfig . brightnessPercent } %</ span >
1503+ </ label >
1504+ < div className = "muted small" > Brightness scales both passive and active effects together.</ div >
1505+ </ div >
1506+ ) : (
1507+ < div className = "muted small" > No lighting configuration available.</ div >
1508+ ) }
1509+ </ div >
1510+ < div className = "modal-actions" >
1511+ < button className = "btn" onClick = { closeGlobalLightingModal } > Cancel</ button >
1512+ < button className = "btn btn-primary" onClick = { saveGlobalLightingModal } disabled = { ! draftLedConfig } > Save</ button >
1513+ </ div >
1514+ </ div >
1515+ </ div >
1516+ ) }
1517+
14611518 { showLightingModal && selectedLayout && (
14621519 < div
14631520 className = "modal-backdrop"
0 commit comments