@@ -303,6 +303,7 @@ function saveWidgetState(el) {
303303 height : el . style ?. height || null ,
304304 display : el . style ?. display || '' ,
305305 minimized : el . classList ?. contains ( 'minimized' ) || false ,
306+ maximized : el . dataset ?. maximized === 'true' ,
306307 } ;
307308 _writeStateMap ( map ) ;
308309}
@@ -330,6 +331,12 @@ function restoreWidgetState(el) {
330331 if ( st . height ) el . style . height = st . height ;
331332 if ( st . display !== undefined ) el . style . display = st . display ;
332333 if ( st . minimized ) el . classList . add ( 'minimized' ) ; else el . classList . remove ( 'minimized' ) ;
334+ if ( st . maximized ) {
335+ el . dataset . maximized = 'true' ;
336+ applyMaximizedWidgetBounds ( el ) ;
337+ } else {
338+ delete el . dataset . maximized ;
339+ }
333340 return true ;
334341 }
335342 return false ;
@@ -343,6 +350,93 @@ function centerWidget(el) {
343350 el . style . top = `${ top } px` ;
344351 } catch { }
345352}
353+
354+ function captureWidgetRestoreState ( el ) {
355+ if ( ! el ) return ;
356+ try {
357+ const root = getScaleRoot ( ) ;
358+ const rootRect = root ? root . getBoundingClientRect ( ) : { left : 0 , top : 0 } ;
359+ const scale = getScaleFactor ( root ) || 1 ;
360+ const rect = el . getBoundingClientRect ( ) ;
361+ const computedLeft = `${ Math . round ( ( rect . left - rootRect . left ) / scale ) } px` ;
362+ const computedTop = `${ Math . round ( ( rect . top - rootRect . top ) / scale ) } px` ;
363+ const computedWidth = `${ Math . round ( rect . width / scale ) } px` ;
364+ const computedHeight = `${ Math . round ( rect . height / scale ) } px` ;
365+ el . dataset . restoreLeft = el . style . left || computedLeft ;
366+ el . dataset . restoreTop = el . style . top || computedTop ;
367+ el . dataset . restoreWidth = el . style . width || computedWidth ;
368+ el . dataset . restoreHeight = el . style . height || computedHeight ;
369+ el . dataset . restoreRight = el . style . right || '' ;
370+ el . dataset . restoreBottom = el . style . bottom || '' ;
371+ } catch { }
372+ }
373+
374+ function applyMaximizedWidgetBounds ( el ) {
375+ if ( ! el ) return ;
376+ try {
377+ const root = getScaleRoot ( ) ;
378+ const scale = getScaleFactor ( root ) || 1 ;
379+ const viewportWidth = Math . max ( 320 , Math . round ( ( window . innerWidth - 24 ) / scale ) ) ;
380+ const viewportHeight = Math . max ( 220 , Math . round ( ( window . innerHeight - 58 ) / scale ) ) ;
381+ el . style . left = '12px' ;
382+ el . style . top = '46px' ;
383+ el . style . right = 'auto' ;
384+ el . style . bottom = 'auto' ;
385+ el . style . width = `${ viewportWidth } px` ;
386+ el . style . height = `${ viewportHeight } px` ;
387+ } catch { }
388+ }
389+
390+ function restoreWidgetBounds ( el ) {
391+ if ( ! el ) return ;
392+ try {
393+ el . style . left = el . dataset . restoreLeft || '' ;
394+ el . style . top = el . dataset . restoreTop || '' ;
395+ el . style . width = el . dataset . restoreWidth || '' ;
396+ el . style . height = el . dataset . restoreHeight || '' ;
397+ el . style . right = el . dataset . restoreRight || '' ;
398+ el . style . bottom = el . dataset . restoreBottom || '' ;
399+ } catch { }
400+ }
401+
402+ function maximizeWidget ( el ) {
403+ if ( ! el || isAnchoredWidget ( el ) ) return false ;
404+ try {
405+ if ( el . dataset . maximized === 'true' ) {
406+ applyMaximizedWidgetBounds ( el ) ;
407+ saveWidgetState ( el ) ;
408+ return true ;
409+ }
410+ captureWidgetRestoreState ( el ) ;
411+ el . dataset . maximized = 'true' ;
412+ el . classList . remove ( 'minimized' ) ;
413+ applyMaximizedWidgetBounds ( el ) ;
414+ focusWidget ( el ) ;
415+ saveWidgetState ( el ) ;
416+ return true ;
417+ } catch {
418+ return false ;
419+ }
420+ }
421+
422+ function restoreWidget ( el ) {
423+ if ( ! el || isAnchoredWidget ( el ) ) return false ;
424+ try {
425+ delete el . dataset . maximized ;
426+ restoreWidgetBounds ( el ) ;
427+ ensureInViewport ( el ) ;
428+ focusWidget ( el ) ;
429+ saveWidgetState ( el ) ;
430+ return true ;
431+ } catch {
432+ return false ;
433+ }
434+ }
435+
436+ function toggleWidgetMaximized ( el , force ) {
437+ const next = typeof force === 'boolean' ? force : el ?. dataset ?. maximized !== 'true' ;
438+ return next ? maximizeWidget ( el ) : restoreWidget ( el ) ;
439+ }
346440function persistOnChanges ( el ) {
347441 try {
348442 const mo = new MutationObserver ( ( ) => saveWidgetState ( el ) ) ;
@@ -557,12 +651,22 @@ function installWidgetResizers(el) {
557651 // dataset-based constraints during mount, after the empty shell exists.
558652 try {
559653 const header = el . querySelector ( '.header' ) ;
560- const headerW = header ? Math . ceil ( header . scrollWidth ) : 0 ;
654+ const title = header ? header . querySelector ( '.title' ) : null ;
655+ const controls = header ? header . querySelector ( '.controls' ) : null ;
656+ const headerCs = header ? getComputedStyle ( header ) : null ;
657+ const padL = headerCs ? ( parseFloat ( headerCs . paddingLeft || '0' ) || 0 ) : 0 ;
658+ const padR = headerCs ? ( parseFloat ( headerCs . paddingRight || '0' ) || 0 ) : 0 ;
659+ const gap = headerCs ? ( parseFloat ( headerCs . columnGap || headerCs . gap || '0' ) || 0 ) : 0 ;
660+ const titleW = title ? Math . ceil ( title . scrollWidth || title . getBoundingClientRect ( ) . width || 0 ) : 0 ;
661+ const controlsW = controls ? Math . ceil ( controls . scrollWidth || controls . getBoundingClientRect ( ) . width || 0 ) : 0 ;
662+ const headerW = header ? Math . ceil ( titleW + controlsW + padL + padR + gap + 12 ) : 0 ;
561663 const headerH = header ? Math . ceil ( header . getBoundingClientRect ( ) . height ) : 0 ;
562664 const dataMinW = Number ( el . dataset ?. minWidth || 0 ) ;
563665 const dataMinH = Number ( el . dataset ?. minHeight || 0 ) ;
564- const baseMinW = Math . max ( 160 , headerW + 12 , dataMinW || 0 ) ;
565- const baseMinH = Math . max ( 80 , headerH + 20 , dataMinH || 0 ) ;
666+ const inlineMinW = parseFloat ( el . style . minWidth || '0' ) || 0 ;
667+ const inlineMinH = parseFloat ( el . style . minHeight || '0' ) || 0 ;
668+ const baseMinW = Math . max ( 160 , headerW , dataMinW || 0 , inlineMinW || 0 ) ;
669+ const baseMinH = Math . max ( 80 , headerH + 20 , dataMinH || 0 , inlineMinH || 0 ) ;
566670 el . __minW = baseMinW ;
567671 el . __minH = baseMinH ;
568672 el . __minSizeSet = true ;
@@ -831,6 +935,9 @@ try {
831935 window . ensureWidgetInView = focusWidget ;
832936 window . ChronosLaunchWizard = launchWizard ;
833937 window . ChronosFocusWidget = focusWidget ;
938+ window . ChronosMaximizeWidget = maximizeWidget ;
939+ window . ChronosRestoreWidget = restoreWidget ;
940+ window . ChronosToggleWidgetMaximized = toggleWidgetMaximized ;
834941} catch { }
835942
836943// ---- Generic widget header dragging ----
0 commit comments