@@ -7,6 +7,15 @@ import { HistoryUI } from './history-ui.js';
77
88class App {
99 constructor ( ) {
10+ this . endpointCookieName = 'moq_test_ui_endpoint_url' ;
11+ this . endpointQueryValue = null ;
12+ this . autoRunRequested = false ;
13+ this . defaultEndpointUrl = '' ;
14+ this . autoRunSelfTestStarted = false ;
15+ this . boundRelayInputIds = new Set ( ) ;
16+
17+ this . initEndpointDefaults ( ) ;
18+
1019 this . tools = [ ] ;
1120 this . selectedTool = null ;
1221 this . currentRunId = null ;
@@ -37,6 +46,124 @@ class App {
3746 this . ws . connect ( ) ;
3847 }
3948
49+ initEndpointDefaults ( ) {
50+ const query = new URLSearchParams ( window . location . search ) ;
51+ const endpoint =
52+ query . get ( 'endpoint' ) ||
53+ query . get ( 'endpoint_url' ) ||
54+ query . get ( 'endpointUrl' ) ||
55+ query . get ( 'relay_url' ) ||
56+ query . get ( 'relayUrl' ) ||
57+ '' ;
58+
59+ const autoRunRaw = query . get ( 'auto_run' ) || query . get ( 'autorun' ) || query . get ( 'autoRun' ) || '' ;
60+
61+ this . endpointQueryValue = endpoint . trim ( ) ;
62+ this . autoRunRequested = this . parseBooleanFlag ( autoRunRaw ) ;
63+
64+ if ( this . autoRunRequested && ! this . endpointQueryValue ) {
65+ console . warn ( 'Ignoring auto-run because no endpoint URL was provided in query parameters.' ) ;
66+ this . autoRunRequested = false ;
67+ }
68+
69+ const cookieEndpoint = this . getCookie ( this . endpointCookieName ) . trim ( ) ;
70+ this . defaultEndpointUrl = this . endpointQueryValue || cookieEndpoint ;
71+ if ( this . endpointQueryValue ) {
72+ this . setEndpointUrl ( this . endpointQueryValue ) ;
73+ }
74+ }
75+
76+ parseBooleanFlag ( value ) {
77+ const normalized = String ( value || '' ) . trim ( ) . toLowerCase ( ) ;
78+ return [ '1' , 'true' , 'yes' , 'y' , 'on' ] . includes ( normalized ) ;
79+ }
80+
81+ getCookie ( name ) {
82+ const prefix = `${ name } =` ;
83+ const parts = document . cookie ? document . cookie . split ( ';' ) : [ ] ;
84+ for ( const part of parts ) {
85+ const trimmed = part . trim ( ) ;
86+ if ( trimmed . startsWith ( prefix ) ) {
87+ return decodeURIComponent ( trimmed . slice ( prefix . length ) ) ;
88+ }
89+ }
90+ return '' ;
91+ }
92+
93+ setCookie ( name , value , days = 365 ) {
94+ const expires = new Date ( Date . now ( ) + days * 24 * 60 * 60 * 1000 ) . toUTCString ( ) ;
95+ document . cookie = `${ name } =${ encodeURIComponent ( value ) } ; expires=${ expires } ; path=/; SameSite=Lax` ;
96+ }
97+
98+ setEndpointUrl ( url ) {
99+ const trimmed = ( url || '' ) . trim ( ) ;
100+ if ( ! trimmed ) return ;
101+ this . defaultEndpointUrl = trimmed ;
102+ this . setCookie ( this . endpointCookieName , trimmed ) ;
103+ this . applyEndpointToRelayInputs ( trimmed ) ;
104+ }
105+
106+ isRelayParam ( param ) {
107+ if ( ! param ) return false ;
108+ const id = String ( param . id || '' ) . toLowerCase ( ) ;
109+ const label = String ( param . label || '' ) . toLowerCase ( ) ;
110+ return id === 'relay_url' || id === 'endpoint_url' || label . includes ( 'relay url' ) ;
111+ }
112+
113+ applyEndpointToRelayInputs ( url ) {
114+ const value = ( url || '' ) . trim ( ) ;
115+ if ( ! value ) return ;
116+
117+ const selfTestRelay = document . getElementById ( 'st-relay-url' ) ;
118+ if ( selfTestRelay ) {
119+ selfTestRelay . value = value ;
120+ }
121+
122+ if ( ! Array . isArray ( this . paramForm ?. params ) || this . paramForm . params . length === 0 ) return ;
123+ for ( const param of this . paramForm . params ) {
124+ if ( ! this . isRelayParam ( param ) ) continue ;
125+ const input = document . getElementById ( `param-${ param . id } ` ) ;
126+ if ( input ) {
127+ input . value = value ;
128+ }
129+ }
130+ }
131+
132+ bindRelayInputSync ( ) {
133+ const selfTestRelay = document . getElementById ( 'st-relay-url' ) ;
134+ if ( selfTestRelay && ! this . boundRelayInputIds . has ( 'st-relay-url' ) ) {
135+ selfTestRelay . addEventListener ( 'change' , ( ) => {
136+ const value = selfTestRelay . value . trim ( ) ;
137+ if ( value ) {
138+ this . setEndpointUrl ( value ) ;
139+ }
140+ } ) ;
141+ this . boundRelayInputIds . add ( 'st-relay-url' ) ;
142+ }
143+
144+ if ( ! Array . isArray ( this . paramForm ?. params ) ) return ;
145+ for ( const param of this . paramForm . params ) {
146+ if ( ! this . isRelayParam ( param ) ) continue ;
147+ const input = document . getElementById ( `param-${ param . id } ` ) ;
148+ if ( ! input ) continue ;
149+ if ( this . boundRelayInputIds . has ( input . id ) ) continue ;
150+ input . addEventListener ( 'change' , ( ) => {
151+ const value = input . value . trim ( ) ;
152+ if ( value ) {
153+ this . setEndpointUrl ( value ) ;
154+ }
155+ } ) ;
156+ this . boundRelayInputIds . add ( input . id ) ;
157+ }
158+ }
159+
160+ maybeAutoRunSelfTest ( ) {
161+ if ( ! this . autoRunRequested || this . autoRunSelfTestStarted ) return ;
162+ this . autoRunSelfTestStarted = true ;
163+ this . switchView ( 'self-test' ) ;
164+ this . startSelfTest ( ) ;
165+ }
166+
40167 bindUI ( ) {
41168 // Navigation
42169 document . querySelectorAll ( '.nav-btn' ) . forEach ( btn => {
@@ -58,6 +185,9 @@ class App {
58185 } ) ;
59186 document . getElementById ( 'btn-self-test-stop' ) . addEventListener ( 'click' , ( ) => this . stopSelfTest ( ) ) ;
60187
188+ this . applyEndpointToRelayInputs ( this . defaultEndpointUrl ) ;
189+ this . bindRelayInputSync ( ) ;
190+
61191 // History
62192 document . getElementById ( 'btn-refresh-history' ) . addEventListener ( 'click' , ( ) => {
63193 this . ws . send ( 'list-results' , { tool : document . getElementById ( 'history-tool-filter' ) . value || undefined } ) ;
@@ -105,6 +235,7 @@ class App {
105235 this . renderToolList ( ) ;
106236 this . selfTestUI . setTools ( this . tools ) ;
107237 this . historyUI . setTools ( this . tools ) ;
238+ this . maybeAutoRunSelfTest ( ) ;
108239 }
109240
110241 renderToolList ( ) {
@@ -163,6 +294,8 @@ class App {
163294 document . getElementById ( 'tool-description' ) . textContent = this . selectedTool . description || '' ;
164295
165296 this . paramForm . render ( this . selectedTool . parameters , document . getElementById ( 'param-form' ) ) ;
297+ this . applyEndpointToRelayInputs ( this . defaultEndpointUrl ) ;
298+ this . bindRelayInputSync ( ) ;
166299 this . outputPanel . setFilters ( this . selectedTool . filters ) ;
167300 this . rendererLoader . unload ( ) ;
168301
@@ -181,6 +314,10 @@ class App {
181314 state . runId = null ;
182315 state . status = 'starting' ;
183316 state . exitCode = null ;
317+ const endpoint = ( params . relay_url || params . endpoint_url || '' ) . trim ( ) ;
318+ if ( endpoint ) {
319+ this . setEndpointUrl ( endpoint ) ;
320+ }
184321
185322 this . ws . send ( 'start-run' , { toolId : this . selectedTool . name , params } ) ;
186323 document . getElementById ( 'output-section' ) . classList . remove ( 'hidden' ) ;
@@ -426,9 +563,12 @@ class App {
426563 }
427564
428565 startSelfTest ( ) {
429- const relayUrl = document . getElementById ( 'st-relay-url' ) . value ;
566+ const relayUrl = document . getElementById ( 'st-relay-url' ) . value . trim ( ) ;
430567 const transport = document . getElementById ( 'st-transport' ) . value ;
431568 const draft = document . getElementById ( 'st-draft' ) . value ;
569+ if ( relayUrl ) {
570+ this . setEndpointUrl ( relayUrl ) ;
571+ }
432572 this . ws . send ( 'start-self-test' , { relayUrl, transport, draft } ) ;
433573 document . getElementById ( 'btn-self-test-run' ) . classList . add ( 'hidden' ) ;
434574 document . getElementById ( 'btn-self-test-stop' ) . classList . remove ( 'hidden' ) ;
0 commit comments