284284 flex-direction : column;
285285 border : 1px solid var (--border );
286286 cursor : pointer;
287+ position : relative;
287288 }
288289
289290 .server-card : hover {
392393 margin-top : auto;
393394 margin-bottom : 0 ;
394395 }
396+
397+ .install-button {
398+ background-color : var (--button-bg );
399+ color : white;
400+ border : none;
401+ border-radius : 4px ;
402+ padding : 6px 12px ;
403+ font-size : 0.8rem ;
404+ cursor : pointer;
405+ display : flex;
406+ align-items : center;
407+ gap : 4px ;
408+ transition : background-color 0.2s ;
409+ text-decoration : none;
410+ width : fit-content;
411+ position : absolute;
412+ bottom : 10px ;
413+ right : 1.25rem ;
414+ z-index : 10 ;
415+ }
416+
417+ .install-button : hover {
418+ background-color : var (--button-hover );
419+ }
420+
421+ .install-button svg {
422+ width : 14px ;
423+ height : 14px ;
424+ }
395425
396426 .server-card .meta .author {
397427 color : var (--accent-highlight );
@@ -1473,13 +1503,49 @@ <h3>Raw Manifest:</h3>
14731503 </ div >
14741504 </ div >
14751505
1506+ <!-- Load Lucien servers configuration -->
1507+
14761508 < script >
1509+ { % include lucien - servers . js % }
14771510 // Server data - to be loaded from the combined JSON file (as a map with server names as keys)
14781511 let serversMap = { } ;
14791512 let serversArray = [ ] ; // Also keep an array version for rendering
14801513
14811514 // Fetch all servers data
14821515 const serverDataUrl = '/api/servers.json' ;
1516+ const searchParams = new URLSearchParams ( window . location . search ) ;
1517+ const sortRule = {
1518+ 'isPinned' : 0 ,
1519+ 'isOfficial' : 1 ,
1520+ 'others' : 2 ,
1521+ }
1522+ const isLucien = searchParams . get ( 'source' ) === 'lucien'
1523+ const isStaging = Boolean ( searchParams . get ( 'isStaging' ) )
1524+ const sourceProtocol = ( isLucien && isStaging ) ? 'lucien-staging' : 'lucien'
1525+
1526+ // Use the Lucien specified servers from the external JS file
1527+ const defaultSpecifiedServers = [ ]
1528+ if ( isLucien ) {
1529+ // Get servers based on whether we're in staging or production
1530+ defaultSpecifiedServers . push ( ...lucienSpecifiedServers ) ;
1531+ }
1532+
1533+ function serverSortRule ( serverA , serverB ) {
1534+
1535+ function determineServerType ( server ) {
1536+ if ( defaultSpecifiedServers . includes ( server . name ) ) {
1537+ return 'isPinned' ;
1538+ }
1539+ if ( server . is_official ) {
1540+ return 'isOfficial' ;
1541+ }
1542+ return 'others' ;
1543+ }
1544+
1545+ const serverAType = determineServerType ( serverA ) ;
1546+ const serverBType = determineServerType ( serverB ) ;
1547+ return sortRule [ serverAType ] - sortRule [ serverBType ] ;
1548+ }
14831549
14841550 // First load GitHub star counts, then fetch server data
14851551 loadGitHubStarCounts ( )
@@ -1493,6 +1559,8 @@ <h3>Raw Manifest:</h3>
14931559 . then ( data => {
14941560 serversMap = data ;
14951561 serversArray = Object . values ( serversMap ) ;
1562+ // sort servers by sortRule
1563+ serversArray . sort ( serverSortRule ) ;
14961564
14971565 // Generate tag filters dynamically from server data
14981566 generateTagFilters ( serversArray ) ;
@@ -2006,6 +2074,47 @@ <h3>Raw Manifest:</h3>
20062074 // Use the star count element created in the header
20072075
20082076 serverContent . appendChild ( meta ) ;
2077+
2078+ // Add install button with click function for deeplink
2079+ const installButton = document . createElement ( 'button' ) ;
2080+ installButton . className = 'install-button' ;
2081+ installButton . type = 'button' ;
2082+
2083+ // Add download icon
2084+ installButton . innerHTML = `
2085+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
2086+ <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
2087+ <polyline points="7 10 12 15 17 10"></polyline>
2088+ <line x1="12" y1="15" x2="12" y2="3"></line>
2089+ </svg>
2090+ Install
2091+ ` ;
2092+
2093+ // Handle click event to build search params and trigger deeplink
2094+ installButton . addEventListener ( 'click' , ( event ) => {
2095+ // Prevent the modal from opening
2096+ event . stopPropagation ( ) ;
2097+
2098+ // Build search params with server install info
2099+ const params = new URLSearchParams ( ) ;
2100+ params . append ( 'name' , server . name ) ;
2101+ params . append ( 'action' , 'install' ) ;
2102+
2103+ const installInfo = Object . values ( server . installations ) [ 0 ] ;
2104+ // No remote for now, only stdio
2105+ params . append ( 'command' , installInfo . command ) ;
2106+ params . append ( 'args' , installInfo . args . join ( ' ' ) ) ;
2107+ if ( installInfo . env ) {
2108+ params . append ( 'env' , JSON . stringify ( installInfo . env ) ) ;
2109+ }
2110+
2111+ console . log ( params )
2112+ // Create and trigger the deeplink
2113+ const deeplink = `${ sourceProtocol } ://mcpm?${ params . toString ( ) } ` ;
2114+ window . location . href = deeplink ;
2115+ } ) ;
2116+
2117+ serverContent . appendChild ( installButton ) ;
20092118
20102119 const linksContainer = document . createElement ( 'div' ) ;
20112120 linksContainer . className = 'server-links' ;
0 commit comments