@@ -23,6 +23,9 @@ const defaultRepoUrl = String(repoDefaults.url || '').trim();
2323const defaultRepoDir = String ( repoDefaults . dir || '' ) . trim ( ) ;
2424const defaultRepoBranch = String ( repoDefaults . branch || '' ) . trim ( ) ;
2525const hideRepoInputs = parseBoolean ( repoDefaults . hideInputs , false ) ;
26+ const launchQueryParamsPlaceholder = '__SPRITZ_UI_LAUNCH_QUERY_PARAMS__' ;
27+ const launchConfig = config . launch || { } ;
28+ const launchQueryParams = parseTemplateMap ( launchConfig . queryParams ) ;
2629const authReturnToPlaceholder = '__SPRITZ_RETURN_TO__' ;
2730const noticeEl = document . getElementById ( 'notice' ) ;
2831const listEl = document . getElementById ( 'list' ) ;
@@ -421,6 +424,33 @@ function parseStorageKeys(value) {
421424 . filter ( Boolean ) ;
422425}
423426
427+ function normalizeTemplateMap ( value ) {
428+ if ( ! value || typeof value !== 'object' || Array . isArray ( value ) ) return { } ;
429+ const normalized = { } ;
430+ for ( const [ key , raw ] of Object . entries ( value ) ) {
431+ const name = String ( key || '' ) . trim ( ) ;
432+ if ( ! name ) continue ;
433+ if ( raw === undefined || raw === null ) continue ;
434+ normalized [ name ] = String ( raw ) ;
435+ }
436+ return normalized ;
437+ }
438+
439+ function parseTemplateMap ( value ) {
440+ if ( ! value ) return { } ;
441+ if ( typeof value === 'object' ) {
442+ return normalizeTemplateMap ( value ) ;
443+ }
444+ const trimmed = String ( value ) . trim ( ) ;
445+ if ( ! trimmed || trimmed === launchQueryParamsPlaceholder ) return { } ;
446+ try {
447+ const parsed = JSON . parse ( trimmed ) ;
448+ return normalizeTemplateMap ( parsed ) ;
449+ } catch {
450+ return { } ;
451+ }
452+ }
453+
424454function parsePresets ( raw ) {
425455 if ( Array . isArray ( raw ) ) return raw ;
426456 if ( typeof raw === 'string' ) {
@@ -863,6 +893,51 @@ async function fetchSpritzes() {
863893 }
864894}
865895
896+ function applyTemplatePlaceholders ( template , context ) {
897+ if ( ! template ) return '' ;
898+ return String ( template ) . replace ( / \{ ( [ a - z A - Z 0 - 9 _ ] + ) \} / g, ( _ , key ) => {
899+ const value = context [ key ] ;
900+ if ( value === undefined || value === null ) return '' ;
901+ return String ( value ) ;
902+ } ) ;
903+ }
904+
905+ function buildOpenUrl ( rawUrl , spritz ) {
906+ const input = String ( rawUrl || '' ) . trim ( ) ;
907+ if ( ! input ) return '' ;
908+ let url ;
909+ try {
910+ url = new URL ( input , window . location . href ) ;
911+ } catch {
912+ return input ;
913+ }
914+ const queryEntries = Object . entries ( launchQueryParams ) ;
915+ if ( ! queryEntries . length ) {
916+ return url . href ;
917+ }
918+ const name = String ( spritz ?. metadata ?. name || '' ) . trim ( ) ;
919+ const namespace = String ( spritz ?. metadata ?. namespace || '' ) . trim ( ) ;
920+ const context = {
921+ origin : url . origin ,
922+ host : url . host ,
923+ hostname : url . hostname ,
924+ path : url . pathname ,
925+ query : url . search ,
926+ name,
927+ namespace,
928+ name_encoded : encodeURIComponent ( name ) ,
929+ namespace_encoded : encodeURIComponent ( namespace ) ,
930+ path_encoded : encodeURIComponent ( url . pathname ) ,
931+ ui_origin : window . location . origin ,
932+ ws_origin : url . origin . replace ( / ^ h t t p / i, 'ws' ) ,
933+ } ;
934+ for ( const [ param , template ] of queryEntries ) {
935+ const value = applyTemplatePlaceholders ( template , context ) ;
936+ url . searchParams . set ( param , value ) ;
937+ }
938+ return url . href ;
939+ }
940+
866941function renderList ( items ) {
867942 updateKnownSpritzNames ( items ) ;
868943 if ( ! items . length ) {
@@ -891,7 +966,7 @@ function renderList(items) {
891966 const openBtn = document . createElement ( 'button' ) ;
892967 openBtn . textContent = 'Open' ;
893968 openBtn . onclick = ( ) => {
894- const url = spritz . status ?. url ;
969+ const url = buildOpenUrl ( spritz . status ?. url , spritz ) ;
895970 if ( url ) window . open ( url , '_blank' ) ;
896971 } ;
897972
0 commit comments