@@ -153,6 +153,8 @@ <h4 class="mb-2 text-xs font-semibold uppercase tracking-wide text-gray-400">Bun
153153let RAW = [ ] , ALL = [ ] , RAWB = [ ] , bundlesLoaded = false ;
154154
155155const esc = s => ( s ?? "" ) . toString ( ) . replace ( / [ & < > " ' ] / g, c => ( { '&' :'&' , '<' :'<' , '>' :'>' , '"' :'"' , "'" :''' } [ c ] ) ) ;
156+ // Only ever put http/https links in an href — an escaped "javascript:…" is still a clickable script.
157+ const safeUrl = u => { try { const x = new URL ( u , location . href ) ; return ( x . protocol === 'http:' || x . protocol === 'https:' ) ? x . href : '#' ; } catch { return '#' ; } } ;
156158const hostOf = p => { const u = ( p . repoUrl || p . gitUrl || '' ) . toLowerCase ( ) ; return u . includes ( 'gitlab' ) ?'GitLab' :u . includes ( 'github' ) ?'GitHub' :'' ; } ;
157159// curated = hosted under the Basis org (github.com/BasisVR/…); anything else is community.
158160const deriveSource = ( repoUrl , gitUrl ) => { const m = ( repoUrl || gitUrl || '' ) . toLowerCase ( ) . match ( / g i t h u b \. c o m [ / : ] + ( [ ^ / ? # ] + ) / ) ; return ( m && m [ 1 ] === 'basisvr' ) ? 'curated' : 'community' ; } ;
@@ -341,7 +343,7 @@ <h4 class="mb-2 text-xs font-semibold uppercase tracking-wide text-gray-400">Bun
341343 <div class="min-w-0">
342344 <h3 class="flex flex-wrap items-center gap-2 font-semibold">${ esc ( p . name ) }
343345 <span class="rounded-full px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide ${ badgeCls ( p . source ) } ">${ esc ( p . source ) } </span></h3>
344- <p class="text-xs text-gray-500 dark:text-gray-400">by ${ esc ( p . author ) } ${ hostOf ( p ) ?' · ' + hostOf ( p ) :'' } </p>
346+ <p class="text-xs text-gray-500 dark:text-gray-400">by ${ esc ( p . author ) } ${ hostOf ( p ) ?' · ' + hostOf ( p ) :'' } ${ p . basisVersion ? ' · Basis ' + esc ( p . basisVersion ) : '' } </p>
345347 </div>
346348 </div>
347349 <p class="mt-3 line-clamp-2 text-sm text-gray-600 dark:text-gray-300">${ esc ( p . description ) } </p>
@@ -385,7 +387,7 @@ <h5 class="mb-2 flex items-center gap-2 font-semibold"><span class="${num}">2</s
385387 <div class="inline-flex h-14 w-14 shrink-0 items-center justify-center overflow-hidden rounded-2xl bg-gray-100 text-3xl ring-1 ring-gray-200 dark:bg-white/5 dark:ring-white/10">${ imgOrIcon ( p ) } </div>
386388 <div class="min-w-0 flex-1">
387389 <h2 class="flex flex-wrap items-center gap-2 text-xl font-bold">${ esc ( p . name ) } <span class="rounded-full px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide ${ badgeCls ( p . source ) } ">${ esc ( p . source ) } </span></h2>
388- <p class="mt-1 text-sm text-gray-500 dark:text-gray-400">by ${ p . authorUrl ?`<a href="${ esc ( p . authorUrl ) } " target="_blank" class="underline hover:text-brand">${ esc ( p . author ) } </a>` :esc ( p . author ) } · ${ esc ( p . category ) } ${ hostOf ( p ) ?' · ' + hostOf ( p ) :'' } </p>
390+ <p class="mt-1 text-sm text-gray-500 dark:text-gray-400">by ${ p . authorUrl ?`<a href="${ esc ( safeUrl ( p . authorUrl ) ) } " target="_blank" rel="noopener noreferrer " class="underline hover:text-brand">${ esc ( p . author ) } </a>` :esc ( p . author ) } · ${ esc ( p . category ) } ${ hostOf ( p ) ?' · ' + hostOf ( p ) :'' } </p>
389391 </div>
390392 <button onclick="closeModals()" class="text-2xl leading-none text-gray-400 hover:text-gray-700 dark:hover:text-gray-200">×</button>
391393 </div>
@@ -395,6 +397,7 @@ <h2 class="flex flex-wrap items-center gap-2 text-xl font-bold">${esc(p.name)} <
395397 <span>⭐ <b class="text-gray-900 dark:text-white">${ fmtNum ( p . stars ) } </b> stars</span>
396398 <span>⑂ <b class="text-gray-900 dark:text-white">${ fmtNum ( p . forks ) } </b> forks</span>
397399 <span><b class="text-gray-900 dark:text-white">v${ esc ( p . version ) } </b></span>
400+ ${ p . basisVersion ?`<span title="The Basis version this package was built and tested against">Built for Basis <b class="text-gray-900 dark:text-white">${ esc ( p . basisVersion ) } </b></span>` :'' }
398401 ${ p . unity ?`<span>Unity <b class="text-gray-900 dark:text-white">${ esc ( p . unity ) } </b></span>` :'' }
399402 ${ p . updated ?`<span>Updated <b class="text-gray-900 dark:text-white">${ esc ( p . updated ) } </b></span>` :'' }
400403 </div>
@@ -411,7 +414,9 @@ <h2 class="flex flex-wrap items-center gap-2 text-xl font-bold">${esc(p.name)} <
411414 <div class="space-y-3">${ installMethods ( p ) } </div>
412415 </div>
413416 <div class="flex flex-wrap gap-2">
414- ${ p . repoUrl ?`<a href="${ esc ( p . repoUrl ) } " target="_blank" class="rounded-xl border border-gray-200 px-3 py-1.5 text-sm hover:border-brand hover:text-brand dark:border-white/10">${ hostOf ( p ) ?'View on ' + hostOf ( p ) :'Repository' } ↗</a>` :'' }
417+ ${ p . repoUrl ?`<a href="${ esc ( safeUrl ( p . repoUrl ) ) } " target="_blank" rel="noopener noreferrer" class="rounded-xl border border-gray-200 px-3 py-1.5 text-sm hover:border-brand hover:text-brand dark:border-white/10">${ hostOf ( p ) ?'View on ' + hostOf ( p ) :'Repository' } ↗</a>` :'' }
418+ ${ p . discord ?`<a href="${ esc ( safeUrl ( p . discord ) ) } " target="_blank" rel="noopener noreferrer" class="inline-flex items-center gap-1.5 rounded-xl border border-gray-200 px-3 py-1.5 text-sm hover:border-brand hover:text-brand dark:border-white/10"><svg class="h-4 w-4" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M20.32 4.37A19.8 19.8 0 0 0 15.45 3c-.2.36-.43.83-.6 1.22a18.3 18.3 0 0 0-5.7 0A12.6 12.6 0 0 0 8.54 3a19.7 19.7 0 0 0-4.89 1.37C.53 9.05-.32 13.58.1 18.06a19.9 19.9 0 0 0 6.02 3.04c.49-.66.92-1.36 1.29-2.1-.71-.27-1.38-.6-2.01-.99.17-.12.34-.25.5-.38a14.2 14.2 0 0 0 12.2 0c.16.13.33.26.5.38-.63.39-1.31.72-2.02.99.37.74.8 1.44 1.29 2.1a19.8 19.8 0 0 0 6.02-3.04c.5-5.18-.85-9.67-3.56-13.69ZM8.02 15.33c-1.18 0-2.16-1.08-2.16-2.42 0-1.33.95-2.42 2.16-2.42 1.21 0 2.18 1.09 2.16 2.42 0 1.34-.95 2.42-2.16 2.42Zm7.97 0c-1.18 0-2.16-1.08-2.16-2.42 0-1.33.96-2.42 2.16-2.42 1.21 0 2.18 1.09 2.16 2.42 0 1.34-.95 2.42-2.16 2.42Z"/></svg>Discord ↗</a>` :'' }
419+ ${ p . donate ?`<a href="${ esc ( safeUrl ( p . donate ) ) } " target="_blank" rel="noopener noreferrer" class="inline-flex items-center gap-1.5 rounded-xl border border-brand/30 bg-brand/5 px-3 py-1.5 text-sm text-brand hover:bg-brand/10 dark:border-brand/40">♥ Donate ↗</a>` :'' }
415420 </div>
416421 </div>
417422 </div>` ;
@@ -455,7 +460,7 @@ <h3 class="flex flex-wrap items-center gap-2 font-semibold">${esc(b.name)}
455460 <div class="inline-flex h-14 w-14 shrink-0 items-center justify-center overflow-hidden rounded-2xl bg-gray-100 text-3xl ring-1 ring-gray-200 dark:bg-white/5 dark:ring-white/10">${ imgOrIcon ( { image :b . image , icon :b . icon || '🧩' } ) } </div>
456461 <div class="min-w-0 flex-1">
457462 <h2 class="text-xl font-bold">${ esc ( b . name ) } </h2>
458- <p class="mt-1 text-sm text-gray-500 dark:text-gray-400">by ${ b . authorUrl ?`<a href="${ esc ( b . authorUrl ) } " target="_blank" class="underline hover:text-brand">${ esc ( b . author || '—' ) } </a>` :esc ( b . author || '—' ) } · Basis ${ basis } ${ b . unity ?' · Unity ' + esc ( b . unity ) :'' } </p>
463+ <p class="mt-1 text-sm text-gray-500 dark:text-gray-400">by ${ b . authorUrl ?`<a href="${ esc ( safeUrl ( b . authorUrl ) ) } " target="_blank" rel="noopener noreferrer " class="underline hover:text-brand">${ esc ( b . author || '—' ) } </a>` :esc ( b . author || '—' ) } · Basis ${ basis } ${ b . unity ?' · Unity ' + esc ( b . unity ) :'' } </p>
459464 </div>
460465 <button onclick="closeModals()" class="text-2xl leading-none text-gray-400 hover:text-gray-700 dark:hover:text-gray-200">×</button>
461466 </div>
@@ -562,6 +567,14 @@ <h2 class="text-xl font-bold">${esc(b.name)}</h2>
562567 <label class="${ SUB_field } "><span class="${ SUB_label } ">Git URL (UPM) *</span><input name="gitUrl" required class="${ SUB_input } " value="${ esc ( submitState . git ) } "></label>
563568 <label class="${ SUB_field } "><span class="${ SUB_label } ">Repository URL</span><input name="repoUrl" class="${ SUB_input } " value="${ esc ( submitState . repo ) } "></label>
564569 <label class="${ SUB_field } "><span class="${ SUB_label } ">Tags (comma-separated)</span><input name="tags" class="${ SUB_input } " value="${ esc ( tags ) } " placeholder="tools, editor"></label>
570+ <div class="grid gap-4 sm:grid-cols-2">
571+ <label class="${ SUB_field } "><span class="${ SUB_label } ">Built against Basis version</span><input name="basisVersion" class="${ SUB_input } " placeholder="main or 1.2.0"></label>
572+ <label class="${ SUB_field } "><span class="${ SUB_label } ">Unity version</span><input name="unity" class="${ SUB_input } " value="${ esc ( p . unity || '' ) } " placeholder="6000.0"></label>
573+ </div>
574+ <div class="grid gap-4 sm:grid-cols-2">
575+ <label class="${ SUB_field } "><span class="${ SUB_label } ">Discord invite <span class="text-xs text-gray-400">optional</span></span><input name="discord" class="${ SUB_input } " placeholder="https://discord.gg/…"></label>
576+ <label class="${ SUB_field } "><span class="${ SUB_label } ">Donation link <span class="text-xs text-gray-400">optional</span></span><input name="donate" class="${ SUB_input } " placeholder="https://ko-fi.com/…"></label>
577+ </div>
565578 ${ ( p . dependencies && Object . keys ( p . dependencies ) . length ) ? `<div class="${ SUB_field } "><span class="${ SUB_label } ">Dependencies <span class="text-xs text-gray-400">from package.json — recorded automatically</span></span><div class="flex flex-wrap gap-1.5">${ Object . entries ( p . dependencies ) . map ( ( [ k , v ] ) => `<span class="rounded-full bg-gray-100 px-2 py-0.5 text-[11px] text-gray-600 dark:bg-white/5 dark:text-gray-300">${ esc ( k ) } ${ esc ( v ) } </span>` ) . join ( '' ) } </div></div>` : '' }
566579
567580 <div class="mb-4 rounded-xl border border-gray-200 bg-gray-50 p-4 dark:border-white/10 dark:bg-white/5">
@@ -608,7 +621,8 @@ <h2 class="text-xl font-bold">${esc(b.name)}</h2>
608621 tags : g ( 'tags' ) ? g ( 'tags' ) . split ( ',' ) . map ( t => t . trim ( ) ) . filter ( Boolean ) : [ ] ,
609622 source :deriveSource ( g ( 'repoUrl' ) , g ( 'gitUrl' ) ) , gitUrl :g ( 'gitUrl' ) || null , repoUrl :g ( 'repoUrl' ) || null ,
610623 image :resolveImageUrl ( g ( 'image' ) , g ( 'repoUrl' ) , g ( 'gitUrl' ) ) || null ,
611- unity :g ( 'unity' ) || null , version :g ( 'version' ) || '1.0.0'
624+ unity :g ( 'unity' ) || null , version :g ( 'version' ) || '1.0.0' ,
625+ basisVersion :g ( 'basisVersion' ) || null , discord :g ( 'discord' ) || null , donate :g ( 'donate' ) || null
612626 } ;
613627 const deps = submitState . pkg && submitState . pkg . dependencies ;
614628 if ( deps && Object . keys ( deps ) . length ) pkg . dependencies = deps ;
0 commit comments