@@ -12,6 +12,7 @@ class TrajectoryViewer {
1212 this . initTheme ( ) ;
1313 await this . loadExperiments ( ) ;
1414 this . setupEventListeners ( ) ;
15+ this . initFromURL ( ) ;
1516 this . updateUI ( ) ;
1617 }
1718
@@ -74,6 +75,73 @@ class TrajectoryViewer {
7475
7576 console . log ( 'Discovery complete. Found experiments:' , this . experiments ) ;
7677 }
78+
79+ initFromURL ( ) {
80+ const { experimentName, instanceName } = this . parseURLSegments ( ) ;
81+ if ( experimentName && this . experiments . some ( exp => exp . name === experimentName ) ) {
82+ this . selectExperiment ( experimentName ) . then ( ( ) => {
83+ // If instance is specified in URL and exists, select it
84+ if ( instanceName && this . currentExperiment &&
85+ this . currentExperiment . instances . includes ( instanceName ) ) {
86+ this . selectInstance ( instanceName ) ;
87+ }
88+ } ) ;
89+ }
90+ }
91+
92+ parseURLSegments ( ) {
93+ const path = window . location . pathname ;
94+ // Remove leading slash and extract segments
95+ const segments = path . replace ( / ^ \/ / , '' ) . split ( '/' ) . filter ( s => s ) ;
96+ return {
97+ experimentName : segments [ 0 ] || null ,
98+ instanceName : segments [ 1 ] || null
99+ } ;
100+ }
101+
102+ parseURLForExperiment ( ) {
103+ // Keep this method for backward compatibility
104+ return this . parseURLSegments ( ) . experimentName ;
105+ }
106+
107+ updateURL ( experimentName , instanceName = null ) {
108+ let newPath = '/' ;
109+ if ( experimentName ) {
110+ newPath += experimentName ;
111+ if ( instanceName ) {
112+ newPath += `/${ instanceName } ` ;
113+ }
114+ }
115+
116+ // Update URL without reloading the page
117+ if ( window . location . pathname !== newPath ) {
118+ window . history . pushState (
119+ { experiment : experimentName , instance : instanceName } ,
120+ '' ,
121+ newPath
122+ ) ;
123+ }
124+ }
125+
126+ handlePopState ( event ) {
127+ // Handle browser back/forward navigation
128+ const { experimentName, instanceName } = this . parseURLSegments ( ) ;
129+ if ( experimentName && this . experiments . some ( exp => exp . name === experimentName ) ) {
130+ this . selectExperiment ( experimentName ) . then ( ( ) => {
131+ // If instance is specified in URL and exists, select it
132+ if ( instanceName && this . currentExperiment &&
133+ this . currentExperiment . instances . includes ( instanceName ) ) {
134+ this . selectInstance ( instanceName ) ;
135+ }
136+ } ) ;
137+ } else {
138+ // No experiment in URL, clear selection
139+ this . currentExperiment = null ;
140+ this . currentInstance = null ;
141+ this . trajectoryData = null ;
142+ this . updateUI ( ) ;
143+ }
144+ }
77145
78146 async discoverInstancesForExperiment ( experimentName , instancePatterns ) {
79147 const foundInstances = [ ] ;
@@ -126,6 +194,11 @@ class TrajectoryViewer {
126194 document . getElementById ( 'theme-toggle' ) . addEventListener ( 'click' , ( ) => {
127195 this . toggleTheme ( ) ;
128196 } ) ;
197+
198+ // Browser navigation (back/forward)
199+ window . addEventListener ( 'popstate' , ( event ) => {
200+ this . handlePopState ( event ) ;
201+ } ) ;
129202 }
130203
131204 initTheme ( ) {
@@ -268,12 +341,22 @@ class TrajectoryViewer {
268341 }
269342
270343 async selectExperiment ( experimentName ) {
271- if ( ! experimentName ) return ;
344+ if ( ! experimentName ) {
345+ this . currentExperiment = null ;
346+ this . currentInstance = null ;
347+ this . trajectoryData = null ;
348+ this . updateURL ( '' ) ;
349+ this . updateUI ( ) ;
350+ return ;
351+ }
272352
273353 this . currentExperiment = this . experiments . find ( exp => exp . name === experimentName ) ;
274354 this . currentInstance = null ;
275355 this . trajectoryData = null ;
276356
357+ // Update URL with experiment name
358+ this . updateURL ( experimentName ) ;
359+
277360 // Auto-select first instance if available
278361 if ( this . currentExperiment && this . currentExperiment . instances . length > 0 ) {
279362 await this . selectInstance ( this . currentExperiment . instances [ 0 ] ) ;
@@ -286,6 +369,10 @@ class TrajectoryViewer {
286369 if ( ! instanceName || ! this . currentExperiment ) return ;
287370
288371 this . currentInstance = instanceName ;
372+
373+ // Update URL with both experiment and instance
374+ this . updateURL ( this . currentExperiment . name , instanceName ) ;
375+
289376 await this . loadTrajectoryData ( ) ;
290377 this . updateUI ( ) ;
291378 }
0 commit comments