@@ -139,6 +139,21 @@ <h4 class="mb-3 text-xs font-semibold uppercase tracking-wide text-gray-400">Cat
139139
140140const esc = s => ( s ?? "" ) . toString ( ) . replace ( / [ & < > " ' ] / g, c => ( { '&' :'&' , '<' :'<' , '>' :'>' , '"' :'"' , "'" :''' } [ c ] ) ) ;
141141const hostOf = p => { const u = ( p . repoUrl || p . gitUrl || '' ) . toLowerCase ( ) ; return u . includes ( 'gitlab' ) ?'GitLab' :u . includes ( 'github' ) ?'GitHub' :'' ; } ;
142+ // curated = hosted under the Basis org (github.com/BasisVR/…); anything else is community.
143+ const 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' ; } ;
144+ async function fetchLatestVersion ( repoUrl , gitUrl ) {
145+ const src = gitUrl || repoUrl || '' ;
146+ const m = src . match ( / g i t h u b \. c o m [ / : ] + ( [ ^ / ? # ] + ) \/ ( [ ^ / ? # . ] + ) / i) ;
147+ if ( ! m ) return null ;
148+ const pm = src . match ( / [ ? & ] p a t h = ( [ ^ & # ] + ) / ) ;
149+ const path = pm ? decodeURIComponent ( pm [ 1 ] ) . replace ( / ^ \/ + | \/ + $ / g, '' ) + '/' : '' ;
150+ try {
151+ const res = await fetch ( `https://raw.githubusercontent.com/${ m [ 1 ] } /${ m [ 2 ] } /HEAD/${ path } package.json` ) ;
152+ if ( ! res . ok ) return null ;
153+ const j = await res . json ( ) ;
154+ return j . version || null ;
155+ } catch { return null ; }
156+ }
142157const fmtNum = n => n >= 1e9 ?( n / 1e9 ) . toFixed ( 1 ) . replace ( / \. 0 $ / , '' ) + 'B' :n >= 1e6 ?( n / 1e6 ) . toFixed ( 1 ) . replace ( / \. 0 $ / , '' ) + 'M' :n >= 1e3 ?( n / 1e3 ) . toFixed ( 1 ) . replace ( / \. 0 $ / , '' ) + 'K' :'' + n ;
143158const badgeCls = s => s === 'curated' ? 'bg-brand/15 text-brand ring-1 ring-brand/30'
144159 : s === 'community' ? 'bg-purple-500/15 text-purple-600 dark:text-purple-400 ring-1 ring-purple-500/30'
@@ -323,8 +338,9 @@ <h2 class="flex flex-wrap items-center gap-2 text-xl font-bold">${esc(p.name)} <
323338 <label class="${ field } "><span class="${ label } ">Category</span><input name="category" class="${ input } " placeholder="Tools"></label>
324339 </div>
325340 <div class="grid gap-4 sm:grid-cols-2">
326- <label class="${ field } "><span class="${ label } ">Source</span><select name="source" class="${ input } "><option value="community">community</option><option value="curated">curated</option></select></label>
327- <label class="${ field } "><span class="${ label } ">Version</span><input name="version" class="${ input } " placeholder="1.0.0"></label>
341+ <label class="${ field } "><span class="${ label } ">Source (auto)</span>
342+ <div class="${ input } flex items-center justify-between"><span id="srcAuto" class="font-semibold">community</span><span id="srcHint" class="text-xs text-gray-400">hosted elsewhere</span></div></label>
343+ <label class="${ field } "><span class="${ label } ">Version <span id="verHint" class="text-xs text-gray-400"></span></span><input name="version" class="${ input } " placeholder="auto-detected from releases"></label>
328344 </div>
329345 <label class="${ field } "><span class="${ label } ">Git URL (UPM) *</span><input name="gitUrl" required class="${ input } " placeholder="https://github.com/you/repo.git?path=Packages/com.you.x or https://gitlab.com/…"></label>
330346 <label class="${ field } "><span class="${ label } ">Repository URL</span><input name="repoUrl" class="${ input } " placeholder="https://github.com/you/repo or https://gitlab.com/you/repo"></label>
@@ -333,7 +349,27 @@ <h2 class="flex flex-wrap items-center gap-2 text-xl font-bold">${esc(p.name)} <
333349 </form>
334350 </div>` ;
335351 m . classList . remove ( 'hidden' ) ;
336- document . getElementById ( 'submitForm' ) . onsubmit = submitPackage ;
352+ const f = document . getElementById ( 'submitForm' ) ;
353+ f . onsubmit = submitPackage ;
354+ let verTimer ;
355+ const updateAuto = ( ) => {
356+ const repo = f . elements . repoUrl . value , git = f . elements . gitUrl . value ;
357+ const s = deriveSource ( repo , git ) ;
358+ document . getElementById ( 'srcAuto' ) . textContent = s ;
359+ document . getElementById ( 'srcHint' ) . textContent = s === 'curated' ? 'under github.com/BasisVR' : 'hosted elsewhere' ;
360+ clearTimeout ( verTimer ) ;
361+ verTimer = setTimeout ( async ( ) => {
362+ if ( ! repo && ! git ) { document . getElementById ( 'verHint' ) . textContent = '' ; return ; }
363+ document . getElementById ( 'verHint' ) . textContent = '(checking package.json…)' ;
364+ const v = await fetchLatestVersion ( repo , git ) ;
365+ document . getElementById ( 'verHint' ) . textContent = v ? '(from package.json)' : '(not detected)' ;
366+ if ( v && ! f . elements . version . dataset . touched ) f . elements . version . value = v ;
367+ } , 500 ) ;
368+ } ;
369+ f . elements . gitUrl . oninput = updateAuto ;
370+ f . elements . repoUrl . oninput = updateAuto ;
371+ f . elements . version . oninput = ( ) => { f . elements . version . dataset . touched = '1' ; } ;
372+ updateAuto ( ) ;
337373}
338374
339375function submitPackage ( e ) {
@@ -343,7 +379,7 @@ <h2 class="flex flex-wrap items-center gap-2 text-xl font-bold">${esc(p.name)} <
343379 id :g ( 'id' ) , name :g ( 'name' ) , description :g ( 'description' ) ,
344380 author :g ( 'author' ) || 'Community' , category :g ( 'category' ) || 'Misc' ,
345381 tags : g ( 'tags' ) ? g ( 'tags' ) . split ( ',' ) . map ( t => t . trim ( ) ) . filter ( Boolean ) : [ ] ,
346- source :g ( 'source' ) || 'community' , gitUrl :g ( 'gitUrl' ) || null , repoUrl :g ( 'repoUrl' ) || null ,
382+ source :deriveSource ( g ( 'repoUrl' ) , g ( 'gitUrl' ) ) , gitUrl :g ( 'gitUrl' ) || null , repoUrl :g ( 'repoUrl' ) || null ,
347383 unity :g ( 'unity' ) || null , version :g ( 'version' ) || '1.0.0'
348384 } ;
349385 if ( ! pkg . id || ! pkg . name ) { toast ( 'Package id and name are required' , 'err' ) ; return ; }
0 commit comments