@@ -12,6 +12,7 @@ class TrajectoryViewer {
12
12
this . initTheme ( ) ;
13
13
await this . loadExperiments ( ) ;
14
14
this . setupEventListeners ( ) ;
15
+ this . initFromURL ( ) ;
15
16
this . updateUI ( ) ;
16
17
}
17
18
@@ -74,6 +75,73 @@ class TrajectoryViewer {
74
75
75
76
console . log ( 'Discovery complete. Found experiments:' , this . experiments ) ;
76
77
}
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
+ }
77
145
78
146
async discoverInstancesForExperiment ( experimentName , instancePatterns ) {
79
147
const foundInstances = [ ] ;
@@ -126,6 +194,11 @@ class TrajectoryViewer {
126
194
document . getElementById ( 'theme-toggle' ) . addEventListener ( 'click' , ( ) => {
127
195
this . toggleTheme ( ) ;
128
196
} ) ;
197
+
198
+ // Browser navigation (back/forward)
199
+ window . addEventListener ( 'popstate' , ( event ) => {
200
+ this . handlePopState ( event ) ;
201
+ } ) ;
129
202
}
130
203
131
204
initTheme ( ) {
@@ -268,12 +341,22 @@ class TrajectoryViewer {
268
341
}
269
342
270
343
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
+ }
272
352
273
353
this . currentExperiment = this . experiments . find ( exp => exp . name === experimentName ) ;
274
354
this . currentInstance = null ;
275
355
this . trajectoryData = null ;
276
356
357
+ // Update URL with experiment name
358
+ this . updateURL ( experimentName ) ;
359
+
277
360
// Auto-select first instance if available
278
361
if ( this . currentExperiment && this . currentExperiment . instances . length > 0 ) {
279
362
await this . selectInstance ( this . currentExperiment . instances [ 0 ] ) ;
@@ -286,6 +369,10 @@ class TrajectoryViewer {
286
369
if ( ! instanceName || ! this . currentExperiment ) return ;
287
370
288
371
this . currentInstance = instanceName ;
372
+
373
+ // Update URL with both experiment and instance
374
+ this . updateURL ( this . currentExperiment . name , instanceName ) ;
375
+
289
376
await this . loadTrajectoryData ( ) ;
290
377
this . updateUI ( ) ;
291
378
}
0 commit comments