@@ -22,6 +22,13 @@ import { loadFromUrl, parseNacaFromName } from './urlState';
2222
2323export type RouteTheme = 'dark' | 'light' ;
2424
25+ interface HeavyRouteData {
26+ foil ?: SerializedAirfoilData ;
27+ spacing ?: SpacingKnot [ ] ;
28+ layout ?: IJsonModel ;
29+ filters ?: unknown ;
30+ }
31+
2532interface SerializedAirfoilData {
2633 name : string ;
2734 coordinates : AirfoilPoint [ ] ;
@@ -251,8 +258,9 @@ export function serializeRouteState(snapshot: CanonicalRouteStateSnapshot, baseP
251258 if ( airfoil . spacingPanelMode ) params . set ( 'spacingMode' , airfoil . spacingPanelMode ) ;
252259 if ( airfoil . sspInterpolation ) params . set ( 'sspInterp' , airfoil . sspInterpolation ) ;
253260 if ( airfoil . sspVisualization ) params . set ( 'sspViz' , airfoil . sspVisualization ) ;
254- if ( airfoil . spacingKnots ?. length ) params . set ( 'spacing' , encodeCompressed ( airfoil . spacingKnots ) ) ;
255- if ( airfoil . exactGeometry ) params . set ( 'foil' , encodeCompressed ( airfoil . exactGeometry ) ) ;
261+ const heavy : HeavyRouteData = { } ;
262+ if ( airfoil . spacingKnots ?. length ) heavy . spacing = airfoil . spacingKnots ;
263+ if ( airfoil . exactGeometry ) heavy . foil = airfoil . exactGeometry ;
256264
257265 setBooleanParam ( params , 'grid' , visualization . showGrid ?? DEFAULT_VISUALIZATION_STATE . showGrid , DEFAULT_VISUALIZATION_STATE . showGrid ) ;
258266 setBooleanParam ( params , 'curve' , visualization . showCurve ?? DEFAULT_VISUALIZATION_STATE . showCurve , DEFAULT_VISUALIZATION_STATE . showCurve ) ;
@@ -322,17 +330,21 @@ export function serializeRouteState(snapshot: CanonicalRouteStateSnapshot, baseP
322330 params . set ( 'splom' , ( ui . dataExplorerSplomKeys ?? DEFAULT_ROUTE_UI_STATE . dataExplorerSplomKeys ) . join ( ',' ) ) ;
323331 }
324332 if ( ui . dataExplorerColorBy ) params . set ( 'colorBy' , String ( ui . dataExplorerColorBy ) ) ;
325- if ( ui . dataExplorerFilterModel ) params . set ( ' filters' , encodeCompressed ( ui . dataExplorerFilterModel ) ) ;
333+ if ( ui . dataExplorerFilterModel ) heavy . filters = ui . dataExplorerFilterModel ;
326334 setNumberParam ( params , 'cx' , ui . viewport ?. centerX ?? DEFAULT_ROUTE_UI_STATE . viewport . centerX , DEFAULT_ROUTE_UI_STATE . viewport . centerX ) ;
327335 setNumberParam ( params , 'cy' , ui . viewport ?. centerY ?? DEFAULT_ROUTE_UI_STATE . viewport . centerY , DEFAULT_ROUTE_UI_STATE . viewport . centerY ) ;
328336 setNumberParam ( params , 'zoom' , ui . viewport ?. zoom ?? DEFAULT_ROUTE_UI_STATE . viewport . zoom , DEFAULT_ROUTE_UI_STATE . viewport . zoom ) ;
329337 if ( ! isDefaultLayout ( ui . layoutJson ?? null ) ) {
330- params . set ( ' layout' , encodeCompressed ( ui . layoutJson ?? defaultLayoutJson ) ) ;
338+ heavy . layout = ui . layoutJson ?? defaultLayoutJson ;
331339 }
332340
333341 const pathname = buildPathname ( basePath , snapshot . panel ) ;
334342 const search = params . toString ( ) ;
335- return search ? `${ pathname } ?${ search } ` : pathname ;
343+ const hasHeavy = Object . keys ( heavy ) . length > 0 ;
344+ const fragment = hasHeavy ? `#h=${ encodeCompressed ( heavy ) } ` : '' ;
345+
346+ if ( search ) return `${ pathname } ?${ search } ${ fragment } ` ;
347+ return `${ pathname } ${ fragment } ` ;
336348}
337349
338350function buildLegacyFallback ( basePath : string , hash ?: string ) : RouteStateSnapshot | null {
@@ -404,9 +416,16 @@ export function parseRouteStateFromLocation(
404416 const panel = extractPanelFromPath ( locationLike . pathname ) ?? DEFAULT_ROUTE_UI_STATE . activePanel ;
405417 const params = new URLSearchParams ( locationLike . search ) ;
406418
407- if ( [ ...params . keys ( ) ] . length === 0 ) {
419+ // Decode heavy data from fragment (new format) or fall back to query params (old URLs)
420+ let heavy : HeavyRouteData | null = null ;
421+ const hash = locationLike . hash ;
422+ if ( hash . startsWith ( '#h=' ) ) {
423+ heavy = decodeCompressed < HeavyRouteData > ( hash . slice ( 3 ) ) ;
424+ }
425+
426+ if ( [ ...params . keys ( ) ] . length === 0 && ! heavy ) {
408427 return (
409- buildLegacyFallback ( basePath , locationLike . hash ) ?? {
428+ buildLegacyFallback ( basePath , hash ) ?? {
410429 basePath,
411430 panel,
412431 theme : 'dark' ,
@@ -420,10 +439,10 @@ export function parseRouteStateFromLocation(
420439 ) ;
421440 }
422441
423- const spacingKnots = decodeCompressed < SpacingKnot [ ] > ( params . get ( 'spacing' ) ) ?? undefined ;
424- const exactGeometry = decodeCompressed < SerializedAirfoilData > ( params . get ( 'foil' ) ) ?? undefined ;
425- const layoutJson = decodeCompressed < IJsonModel > ( params . get ( 'layout' ) ) ;
426- const filterModel = decodeCompressed ( params . get ( 'filters' ) ) ;
442+ const spacingKnots = heavy ?. spacing ?? decodeCompressed < SpacingKnot [ ] > ( params . get ( 'spacing' ) ) ?? undefined ;
443+ const exactGeometry = heavy ?. foil ?? decodeCompressed < SerializedAirfoilData > ( params . get ( 'foil' ) ) ?? undefined ;
444+ const layoutJson = heavy ?. layout ?? decodeCompressed < IJsonModel > ( params . get ( 'layout' ) ) ;
445+ const filterModel = heavy ?. filters ?? decodeCompressed ( params . get ( 'filters' ) ) ;
427446
428447 const plotGroup = params . get ( 'plotGroup' ) ;
429448 const dataView = params . get ( 'dataView' ) ;
0 commit comments