@@ -92,9 +92,11 @@ function requestHeatmapValue(globalX, globalY, slice) {
9292 heatmapHoverState . controller . abort ( ) ;
9393 heatmapHoverState . controller = null ;
9494 }
95+ const baseSlice = Number . isFinite ( slice ) ? slice : heatmapState . slice ;
96+ const requestSlice = clampSlice ( baseSlice ) ;
9597 heatmapHoverState . requestX = globalX ;
9698 heatmapHoverState . requestY = globalY ;
97- heatmapHoverState . requestSlice = slice ;
99+ heatmapHoverState . requestSlice = requestSlice ;
98100 heatmapHoverState . data = null ;
99101
100102 const controller = new AbortController ( ) ;
@@ -103,9 +105,7 @@ function requestHeatmapValue(globalX, globalY, slice) {
103105 const params = new URLSearchParams ( ) ;
104106 params . set ( "x" , String ( globalX ) ) ;
105107 params . set ( "y" , String ( globalY ) ) ;
106- if ( heatmapState . layout && typeof heatmapState . layout . depth === "number" && heatmapState . layout . depth > 1 ) {
107- params . set ( "slice" , String ( slice ) ) ;
108- }
108+ params . set ( "slice" , String ( requestSlice ) ) ;
109109 appendModelParam ( params ) ;
110110
111111 const tensorName = encodeURIComponent ( heatmapState . tensor . name ) ;
@@ -123,7 +123,7 @@ function requestHeatmapValue(globalX, globalY, slice) {
123123 return ;
124124 }
125125 heatmapHoverState . controller = null ;
126- if ( heatmapHoverState . requestX !== globalX || heatmapHoverState . requestY !== globalY || heatmapHoverState . requestSlice !== slice ) {
126+ if ( heatmapHoverState . requestX !== globalX || heatmapHoverState . requestY !== globalY || heatmapHoverState . requestSlice !== requestSlice ) {
127127 return ;
128128 }
129129 heatmapHoverState . data = data ;
@@ -134,7 +134,7 @@ function requestHeatmapValue(globalX, globalY, slice) {
134134 return ;
135135 }
136136 heatmapHoverState . controller = null ;
137- if ( heatmapHoverState . requestX !== globalX || heatmapHoverState . requestY !== globalY || heatmapHoverState . requestSlice !== slice ) {
137+ if ( heatmapHoverState . requestX !== globalX || heatmapHoverState . requestY !== globalY || heatmapHoverState . requestSlice !== requestSlice ) {
138138 return ;
139139 }
140140 heatmapHoverState . data = null ;
@@ -153,6 +153,10 @@ function resetHeatmap(message = HEATMAP_DEFAULT_STREAM_MESSAGE) {
153153 heatmapState . controller . abort ( ) ;
154154 heatmapState . controller = null ;
155155 }
156+ if ( heatmapState . sliceController ) {
157+ heatmapState . sliceController . abort ( ) ;
158+ heatmapState . sliceController = null ;
159+ }
156160 hideHeatmapTooltip ( ) ;
157161 const preserveGrid = ! ! heatmapState . gridVisible ;
158162 heatmapState . tensor = null ;
@@ -178,6 +182,7 @@ function resetHeatmap(message = HEATMAP_DEFAULT_STREAM_MESSAGE) {
178182 heatmapState . viewWidth = heatmapCanvas . width ;
179183 heatmapState . viewHeight = heatmapCanvas . height ;
180184 heatmapState . pendingScale = null ;
185+ heatmapState . sliceRequestId = 0 ;
181186 if ( heatmapGainOffsetToggle ) {
182187 heatmapGainOffsetToggle . checked = false ;
183188 }
@@ -532,15 +537,14 @@ async function fetchHeatmapWindow() {
532537 const tensor = heatmapState . tensor ;
533538 const layout = heatmapState . layout || { width : heatmapState . viewWidth , height : heatmapState . viewHeight , depth : 1 } ;
534539 heatmapState . slice = clampSlice ( heatmapState . slice ) ;
540+ const requestedSlice = heatmapState . slice ;
535541
536542 const params = new URLSearchParams ( ) ;
537543 params . set ( "x" , String ( heatmapState . windowX ) ) ;
538544 params . set ( "y" , String ( heatmapState . windowY ) ) ;
539545 params . set ( "width" , String ( heatmapState . viewWidth ) ) ;
540546 params . set ( "height" , String ( heatmapState . viewHeight ) ) ;
541- if ( layout . depth > 1 ) {
542- params . set ( "slice" , String ( heatmapState . slice ) ) ;
543- }
547+ params . set ( "slice" , String ( requestedSlice ) ) ;
544548 appendModelParam ( params ) ;
545549
546550 try {
@@ -577,8 +581,9 @@ async function fetchHeatmapWindow() {
577581 } else {
578582 heatmapState . windowY = clampWindowY ( heatmapState . windowY ) ;
579583 }
584+ let nextSlice = heatmapState . slice ;
580585 if ( typeof origin . slice === "number" ) {
581- heatmapState . slice = clampSlice ( origin . slice ) ;
586+ nextSlice = clampSlice ( origin . slice ) ;
582587 }
583588
584589 const viewport = data . viewport || { } ;
@@ -597,17 +602,29 @@ async function fetchHeatmapWindow() {
597602
598603 heatmapState . windowX = clampWindowX ( heatmapState . windowX ) ;
599604 heatmapState . windowY = clampWindowY ( heatmapState . windowY ) ;
600- heatmapState . slice = clampSlice ( heatmapState . slice ) ;
605+ nextSlice = clampSlice ( nextSlice ) ;
606+ const previousSlice = heatmapState . slice ;
607+ heatmapState . slice = nextSlice ;
608+ const sliceChanged = heatmapState . slice !== previousSlice ;
609+ if ( sliceChanged ) {
610+ if ( heatmapState . sliceController ) {
611+ heatmapState . sliceController . abort ( ) ;
612+ heatmapState . sliceController = null ;
613+ }
614+ heatmapState . sliceRequestId = ( heatmapState . sliceRequestId || 0 ) + 1 ;
615+ heatmapState . sliceMin = undefined ;
616+ heatmapState . sliceMax = undefined ;
617+ }
601618 if ( histogramState . slice !== heatmapState . slice && heatmapState . tensor ) {
602619 histogramState . slice = heatmapState . slice ;
603620 void fetchHistogram ( ) ;
604621 }
622+ if ( sliceChanged ) {
623+ void fetchSliceProperties ( ) ;
624+ }
605625
606626 heatmapState . viewMin = typeof data . min === "number" ? data . min : undefined ;
607627 heatmapState . viewMax = typeof data . max === "number" ? data . max : undefined ;
608- heatmapState . sliceMin = typeof data . sliceMin === "number" ? data . sliceMin : undefined ;
609- heatmapState . sliceMax = typeof data . sliceMax === "number" ? data . sliceMax : undefined ;
610-
611628 if ( ! heatmapState . scaleInitialized ) {
612629 setHeatmapScale ( heatmapState . viewMin , heatmapState . viewMax , { reapply : false , sync : false } ) ;
613630 } else {
@@ -656,6 +673,91 @@ async function fetchHeatmapWindow() {
656673 }
657674}
658675
676+ async function fetchSliceProperties ( options = { } ) {
677+ if ( ! heatmapState . tensor ) {
678+ return null ;
679+ }
680+ if ( ! currentModelPath ) {
681+ return null ;
682+ }
683+
684+ if ( heatmapState . sliceController ) {
685+ heatmapState . sliceController . abort ( ) ;
686+ }
687+
688+ const controller = new AbortController ( ) ;
689+ const requestId = ( heatmapState . sliceRequestId || 0 ) + 1 ;
690+ heatmapState . sliceController = controller ;
691+ heatmapState . sliceRequestId = requestId ;
692+
693+ const params = new URLSearchParams ( ) ;
694+ const requestedSlice = Number . isInteger ( options . slice )
695+ ? clampSlice ( options . slice )
696+ : clampSlice ( heatmapState . slice ) ;
697+ params . set ( "slice" , String ( requestedSlice ) ) ;
698+ appendModelParam ( params ) ;
699+
700+ try {
701+ const res = await fetch ( `api/tensors/${ encodeURIComponent ( heatmapState . tensor . name ) } /slice/properties?${ params . toString ( ) } ` , {
702+ signal : controller . signal ,
703+ } ) ;
704+ if ( ! res . ok ) {
705+ const text = await res . text ( ) ;
706+ throw new Error ( text || `Request failed: ${ res . status } ` ) ;
707+ }
708+ const data = await res . json ( ) ;
709+ if ( controller . signal . aborted || heatmapState . sliceRequestId !== requestId ) {
710+ return null ;
711+ }
712+
713+ if ( heatmapState . sliceController === controller ) {
714+ heatmapState . sliceController = null ;
715+ }
716+
717+ const hasMin = typeof data . min === "number" && Number . isFinite ( data . min ) ;
718+ const hasMax = typeof data . max === "number" && Number . isFinite ( data . max ) ;
719+ heatmapState . sliceMin = hasMin ? data . min : undefined ;
720+ heatmapState . sliceMax = hasMax ? data . max : undefined ;
721+
722+ const valid = typeof data . valid === "number" && data . valid > 0 ? data . valid : 0 ;
723+
724+ if ( options . applyScale ) {
725+ if ( hasMin && hasMax && valid > 0 ) {
726+ setHeatmapScale ( data . min , data . max ) ;
727+ } else {
728+ const reason = valid > 0
729+ ? "Slice scale unavailable."
730+ : "Slice has no finite values to scale." ;
731+ heatmapHeaderMessage = reason ;
732+ updateHeatmapHeader ( ) ;
733+ }
734+ }
735+
736+ syncHeatmapControls ( ) ;
737+ return {
738+ slice : typeof data . slice === "number" ? data . slice : requestedSlice ,
739+ min : heatmapState . sliceMin ,
740+ max : heatmapState . sliceMax ,
741+ valid,
742+ } ;
743+ } catch ( err ) {
744+ if ( controller . signal . aborted || heatmapState . sliceRequestId !== requestId ) {
745+ return null ;
746+ }
747+ if ( heatmapState . sliceController === controller ) {
748+ heatmapState . sliceController = null ;
749+ }
750+ if ( options . applyScale ) {
751+ const message = err instanceof Error ? err . message : String ( err ) ;
752+ heatmapHeaderMessage = `Slice scale request failed: ${ message } ` ;
753+ updateHeatmapHeader ( ) ;
754+ }
755+ console . error ( "Failed to fetch slice properties" , err ) ;
756+ syncHeatmapControls ( ) ;
757+ return null ;
758+ }
759+ }
760+
659761function updateHeatmapImage ( values , minValue , maxValue ) {
660762 ensureHeatmapBuffer ( heatmapState . viewWidth , heatmapState . viewHeight ) ;
661763
@@ -811,6 +913,13 @@ function commitSliceInput() {
811913 heatmapSliceInput . value = String ( value ) ;
812914 const nextSlice = clampSlice ( value - 1 ) ;
813915 if ( nextSlice !== heatmapState . slice ) {
916+ if ( heatmapState . sliceController ) {
917+ heatmapState . sliceController . abort ( ) ;
918+ heatmapState . sliceController = null ;
919+ }
920+ heatmapState . sliceRequestId = ( heatmapState . sliceRequestId || 0 ) + 1 ;
921+ heatmapState . sliceMin = undefined ;
922+ heatmapState . sliceMax = undefined ;
814923 heatmapState . slice = nextSlice ;
815924 histogramState . slice = nextSlice ;
816925 if ( heatmapState . tensor ) {
@@ -888,10 +997,11 @@ if (heatmapSliceButton) {
888997 if ( ! heatmapState . tensor ) {
889998 return ;
890999 }
891- if ( ! Number . isFinite ( heatmapState . sliceMin ) || ! Number . isFinite ( heatmapState . sliceMax ) ) {
1000+ if ( Number . isFinite ( heatmapState . sliceMin ) && Number . isFinite ( heatmapState . sliceMax ) ) {
1001+ setHeatmapScale ( heatmapState . sliceMin , heatmapState . sliceMax ) ;
8921002 return ;
8931003 }
894- setHeatmapScale ( heatmapState . sliceMin , heatmapState . sliceMax ) ;
1004+ void fetchSliceProperties ( { applyScale : true } ) ;
8951005 } ) ;
8961006}
8971007
0 commit comments