284
284
flex-direction : column;
285
285
border : 1px solid var (--border );
286
286
cursor : pointer;
287
+ position : relative;
287
288
}
288
289
289
290
.server-card : hover {
392
393
margin-top : auto;
393
394
margin-bottom : 0 ;
394
395
}
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
+ }
395
425
396
426
.server-card .meta .author {
397
427
color : var (--accent-highlight );
@@ -1473,13 +1503,48 @@ <h3>Raw Manifest:</h3>
1473
1503
</ div >
1474
1504
</ div >
1475
1505
1506
+ <!-- Load Lucien servers configuration -->
1507
+
1476
1508
< script >
1509
+ { % include lucien - servers . js % }
1477
1510
// Server data - to be loaded from the combined JSON file (as a map with server names as keys)
1478
1511
let serversMap = { } ;
1479
1512
let serversArray = [ ] ; // Also keep an array version for rendering
1480
1513
1481
1514
// Fetch all servers data
1482
1515
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 sourceProtocol = searchParams . get ( 'protocol' ) || 'lucien'
1524
+
1525
+ // Use the Lucien specified servers from the external JS file
1526
+ const defaultSpecifiedServers = [ ]
1527
+ if ( isLucien ) {
1528
+ // Get servers based on whether we're in staging or production
1529
+ defaultSpecifiedServers . push ( ...lucienSpecifiedServers ) ;
1530
+ }
1531
+
1532
+ function serverSortRule ( serverA , serverB ) {
1533
+
1534
+ function determineServerType ( server ) {
1535
+ if ( defaultSpecifiedServers . includes ( server . name ) ) {
1536
+ return 'isPinned' ;
1537
+ }
1538
+ if ( server . is_official ) {
1539
+ return 'isOfficial' ;
1540
+ }
1541
+ return 'others' ;
1542
+ }
1543
+
1544
+ const serverAType = determineServerType ( serverA ) ;
1545
+ const serverBType = determineServerType ( serverB ) ;
1546
+ return sortRule [ serverAType ] - sortRule [ serverBType ] ;
1547
+ }
1483
1548
1484
1549
// First load GitHub star counts, then fetch server data
1485
1550
loadGitHubStarCounts ( )
@@ -1493,6 +1558,8 @@ <h3>Raw Manifest:</h3>
1493
1558
. then ( data => {
1494
1559
serversMap = data ;
1495
1560
serversArray = Object . values ( serversMap ) ;
1561
+ // sort servers by sortRule
1562
+ serversArray . sort ( serverSortRule ) ;
1496
1563
1497
1564
// Generate tag filters dynamically from server data
1498
1565
generateTagFilters ( serversArray ) ;
@@ -2006,6 +2073,48 @@ <h3>Raw Manifest:</h3>
2006
2073
// Use the star count element created in the header
2007
2074
2008
2075
serverContent . appendChild ( meta ) ;
2076
+
2077
+ // Add install button with click function for deeplink
2078
+ if ( isLucien ) {
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' , encodeURIComponent ( installInfo . args . join ( ' ' ) ) ) ;
2107
+ if ( installInfo . env ) {
2108
+ params . append ( 'env' , JSON . stringify ( installInfo . env ) ) ;
2109
+ }
2110
+
2111
+ // Create and trigger the deeplink
2112
+ const deeplink = `${ sourceProtocol } ://mcpm?${ params . toString ( ) } ` ;
2113
+ window . location . href = deeplink ;
2114
+ } ) ;
2115
+
2116
+ serverContent . appendChild ( installButton ) ;
2117
+ }
2009
2118
2010
2119
const linksContainer = document . createElement ( 'div' ) ;
2011
2120
linksContainer . className = 'server-links' ;
0 commit comments