@@ -135,7 +135,7 @@ const StandaloneCodeBlock = ({
135135declare global {
136136 interface ImportMeta {
137137 env : {
138- EXAMPLES : string [ ] ;
138+ EXAMPLES : { name : string ; version : string } [ ] ;
139139 SSG_PREVIEWS : Record < string , string > ;
140140 } ;
141141 }
@@ -152,21 +152,25 @@ type PackagesSource = 'build' | 'fetching' | 'live' | 'error';
152152function useExamplePackages ( ) {
153153 // Initialize immediately from build-time data — zero latency on first render.
154154 // Build-time names like "vue-basic" must map back to their npm scope.
155+ // Each entry now carries the version that was pinned at build time so the
156+ // version dropdown is populated immediately (before the live npm fetch).
155157 const [ packages , setPackages ] = useState < NpmPackageInfo [ ] > ( ( ) =>
156- ( import . meta. env . EXAMPLES ?? [ 'hello-world' ] ) . map ( ( name : string ) => {
157- const scopeConfig =
158- EXAMPLE_SCOPES . find ( ( s ) => s . prefix && name . startsWith ( s . prefix ) ) ??
159- EXAMPLE_SCOPES [ 0 ] ;
160- const rawName = scopeConfig . prefix
161- ? name . slice ( scopeConfig . prefix . length )
162- : name ;
163- return {
164- name : `${ scopeConfig . scope } ${ rawName } ` ,
165- shortName : name ,
166- scope : scopeConfig ,
167- version : '' ,
168- } ;
169- } ) ,
158+ ( import . meta. env . EXAMPLES ?? [ { name : 'hello-world' , version : '' } ] ) . map (
159+ ( { name, version } : { name : string ; version : string } ) => {
160+ const scopeConfig =
161+ EXAMPLE_SCOPES . find ( ( s ) => s . prefix && name . startsWith ( s . prefix ) ) ??
162+ EXAMPLE_SCOPES [ 0 ] ;
163+ const rawName = scopeConfig . prefix
164+ ? name . slice ( scopeConfig . prefix . length )
165+ : name ;
166+ return {
167+ name : `${ scopeConfig . scope } ${ rawName } ` ,
168+ shortName : name ,
169+ scope : scopeConfig ,
170+ version,
171+ } ;
172+ } ,
173+ ) ,
170174 ) ;
171175 const [ source , setSource ] = useState < PackagesSource > ( 'build' ) ;
172176
@@ -191,6 +195,7 @@ function usePackageVersions(packageName: string) {
191195 const [ loading , setLoading ] = useState ( false ) ;
192196 useEffect ( ( ) => {
193197 if ( ! packageName ) return ;
198+ setVersions ( [ ] ) ;
194199 setLoading ( true ) ;
195200 fetchPackageVersions ( packageName )
196201 . then ( setVersions )
@@ -474,7 +479,7 @@ function ColumnResizer({
474479 const startW = widthRef . current ! ;
475480 const sign = reverse ? - 1 : 1 ;
476481 const onPointerMove = ( ev : PointerEvent ) => {
477- onWidthChange ( Math . max ( 80 , startW + ( ev . clientX - startX ) * sign ) ) ;
482+ onWidthChange ( Math . max ( 160 , startW + ( ev . clientX - startX ) * sign ) ) ;
478483 } ;
479484 const onPointerUp = ( ) => {
480485 el . removeEventListener ( 'pointermove' , onPointerMove ) ;
@@ -613,7 +618,13 @@ function App() {
613618 const [ defaultFile , setDefaultFile ] = useState (
614619 initial . file ?? ( ( initial . example ?? 'hello-world' ) . startsWith ( 'vue-' ) ? 'src/App.vue' : 'src/App.tsx' ) ,
615620 ) ;
616- const [ version , setVersion ] = useState < string | undefined > ( initial . version ) ;
621+ const [ version , setVersion ] = useState < string | undefined > (
622+ initial . version ??
623+ ( ( import . meta. env . EXAMPLES ?? [ ] ) . find (
624+ ( e : { name : string ; version : string } ) =>
625+ e . name === ( initial . example ?? 'hello-world' ) ,
626+ ) ?. version || undefined ) ,
627+ ) ;
617628 const [ copied , setCopied ] = useState ( false ) ;
618629 const [ exampleSearch , setExampleSearch ] = useState ( '' ) ;
619630 const [ entrySearch , setEntrySearch ] = useState ( '' ) ;
@@ -629,20 +640,30 @@ function App() {
629640 const currentPkg = examplePackages . find ( ( p ) => p . shortName === example ) ;
630641 const currentPkgName = currentPkg ?. name ?? `@lynx-example/${ example } ` ;
631642
643+ // The version that was pinned when this site was built (from build-time EXAMPLES).
644+ const getBuildVersion = useCallback (
645+ ( name : string ) =>
646+ ( import . meta. env . EXAMPLES ?? [ ] ) . find (
647+ ( e : { name : string ; version : string } ) => e . name === name ,
648+ ) ?. version ?? '' ,
649+ [ ] ,
650+ ) ;
651+ const buildVersion = getBuildVersion ( example ) ;
652+
632653 const { versions : packageVersions } = usePackageVersions ( currentPkgName ) ;
633654
634655 // Auto-select the newest concrete version once packageVersions loads.
635656 // Never pass the virtual 'latest' tag to jsdelivr; the data API requires real semver.
636657 const effectiveVersion =
637658 version ?? currentPkg ?. version ?? packageVersions [ 0 ] ?. version ;
638659
639- // Once packageVersions is available, pin `version` state to a real semver
640- // so the dropdown shows a selected value and the URL gets a concrete version.
660+ // Fallback: if no build version is known for this example (e.g. a newly
661+ // published package not yet in the build), auto-select the newest live version.
641662 useEffect ( ( ) => {
642- if ( ! version && packageVersions . length > 0 ) {
663+ if ( ! version && ! buildVersion && packageVersions . length > 0 ) {
643664 setVersion ( packageVersions [ 0 ] . version ) ;
644665 }
645- } , [ version , packageVersions ] ) ;
666+ } , [ version , buildVersion , packageVersions ] ) ;
646667
647668 // Metadata & entry state
648669 const [ metadata , setMetadata ] = useState < Record < string , any > | null > ( null ) ;
@@ -661,15 +682,18 @@ function App() {
661682 const [ metadataHtml , setMetadataHtml ] = useState ( '' ) ;
662683
663684 // Resizable column widths
664- const col1Ref = useRef ( 180 ) ;
665- const col2Ref = useRef ( 180 ) ;
666- const col4Ref = useRef ( 300 ) ;
667- const [ col1W , setCol1W ] = useState ( 180 ) ;
668- const [ col2W , setCol2W ] = useState ( 180 ) ;
669- const [ col4W , setCol4W ] = useState ( 300 ) ;
685+ const col1Ref = useRef ( 220 ) ;
686+ const col2Ref = useRef ( 220 ) ;
687+ const col3Ref = useRef ( 220 ) ;
688+ const col4Ref = useRef ( 220 ) ;
689+ const [ col1W , setCol1W ] = useState ( 220 ) ;
690+ const [ col2W , setCol2W ] = useState ( 220 ) ;
691+ const [ col3W , setCol3W ] = useState ( 220 ) ;
692+ const [ col4W , setCol4W ] = useState ( 220 ) ;
670693
671694 const setCol1 = useCallback ( ( w : number ) => { col1Ref . current = w ; setCol1W ( w ) ; } , [ ] ) ;
672695 const setCol2 = useCallback ( ( w : number ) => { col2Ref . current = w ; setCol2W ( w ) ; } , [ ] ) ;
696+ const setCol3 = useCallback ( ( w : number ) => { col3Ref . current = w ; setCol3W ( w ) ; } , [ ] ) ;
673697 const setCol4 = useCallback ( ( w : number ) => { col4Ref . current = w ; setCol4W ( w ) ; } , [ ] ) ;
674698
675699 const jsxString = useMemo (
@@ -1050,7 +1074,7 @@ function App() {
10501074 data-active = { example === name }
10511075 onClick = { ( ) => {
10521076 setExample ( name ) ;
1053- setVersion ( undefined ) ;
1077+ setVersion ( getBuildVersion ( name ) || undefined ) ;
10541078 setDefaultFile (
10551079 source === 'vue' ? 'src/App.vue' : 'src/App.tsx' ,
10561080 ) ;
@@ -1133,13 +1157,14 @@ function App() {
11331157 style = { {
11341158 ...panelLabelStyle ,
11351159 padding : '0 4px' ,
1136- marginBottom : 2 ,
1160+ marginBottom : 4 ,
11371161 display : 'flex' ,
11381162 alignItems : 'center' ,
1139- gap : 6 ,
1163+ gap : 4 ,
1164+ flexWrap : 'wrap' ,
11401165 } }
11411166 >
1142- < span > Entries</ span >
1167+ < span style = { { flexShrink : 0 } } > Entries</ span >
11431168 < input
11441169 type = "text"
11451170 value = { entrySearch }
@@ -1155,22 +1180,9 @@ function App() {
11551180 fontSize : 10 ,
11561181 fontFamily : 'inherit' ,
11571182 outline : 'none' ,
1158- minWidth : 0 ,
1183+ minWidth : 40 ,
11591184 } }
11601185 />
1161- </ div >
1162- < div
1163- style = { {
1164- padding : '0 4px' ,
1165- marginBottom : 4 ,
1166- display : 'flex' ,
1167- alignItems : 'center' ,
1168- gap : 4 ,
1169- } }
1170- >
1171- < span style = { { fontSize : 10 , color : 'var(--sb-text-dim)' , flexShrink : 0 } } >
1172- Version
1173- </ span >
11741186 < select
11751187 value = { version ?? '' }
11761188 onChange = { ( e ) => setVersion ( e . target . value || undefined ) }
@@ -1179,14 +1191,15 @@ function App() {
11791191 fontSize : 10 ,
11801192 padding : '1px 20px 1px 6px' ,
11811193 borderRadius : 4 ,
1194+ flexShrink : 0 ,
11821195 } }
11831196 >
11841197 { packageVersions . length === 0 && (
11851198 < option value = "" disabled > Loading…</ option >
11861199 ) }
11871200 { packageVersions . map ( ( v ) => (
11881201 < option key = { v . version } value = { v . version } >
1189- { v . version }
1202+ { v . version } { v . version === buildVersion ? ' (build)' : '' }
11901203 </ option >
11911204 ) ) }
11921205 </ select >
@@ -1241,8 +1254,8 @@ function App() {
12411254 { /* Col 3: controls */ }
12421255 < div
12431256 style = { {
1244- flex : '1 1 0' ,
1245- minWidth : 120 ,
1257+ flex : `0 0 ${ col3W } px` ,
1258+ minWidth : 160 ,
12461259 padding : '10px 16px' ,
12471260 overflow : 'hidden' ,
12481261 display : 'grid' ,
@@ -1306,9 +1319,9 @@ function App() {
13061319 />
13071320 </ div >
13081321
1309- < ColumnResizer widthRef = { col4Ref } onWidthChange = { setCol4 } reverse />
1322+ < ColumnResizer widthRef = { col3Ref } onWidthChange = { setCol3 } />
13101323
1311- { /* Right : metadata JSON */ }
1324+ { /* Col 4 : metadata JSON */ }
13121325 < div
13131326 style = { {
13141327 flex : `0 0 ${ col4W } px` ,
@@ -1375,45 +1388,18 @@ function App() {
13751388 </ div >
13761389
13771390 { /* ── Go component(s) — Desktop + Mobile ── */ }
1391+ { /* App-level concern: don't render Go until we have a resolved CDN version.
1392+ This avoids the useCdn=false → useCdn=true double-mount when
1393+ effectiveVersion transitions from undefined to a real semver. */ }
13781394 < main >
13791395 < PreviewErrorBoundary >
13801396 < GoConfigProvider config = { goConfig } >
1381- < div className = "dual-view" >
1382- { /* Desktop */ }
1383- < div style = { { flex : '1 1 500px' , minWidth : 0 } } >
1384- < Go
1385- key = { `desktop-${ example } -${ selectedEntry } -${ defaultTab } -${ effectiveVersion } ` }
1386- example = { example }
1387- defaultFile = { defaultFile }
1388- defaultTab = { defaultTab }
1389- defaultEntryFile = { defaultEntryFile || undefined }
1390- entry = { entryFilter || undefined }
1391- highlight = { highlight || undefined }
1392- img = { img || undefined }
1393- schema = { schema || undefined }
1394- version = { effectiveVersion }
1395- />
1396- < div className = "figure-caption" > Desktop</ div >
1397- </ div >
1398- { /* Mobile — fixed 320×660 */ }
1399- < div
1400- className = "mobile-preview"
1401- style = { {
1402- flex : '0 0 320px' ,
1403- maxWidth : 320 ,
1404- overflow : 'hidden' ,
1405- containerType : 'inline-size' as any ,
1406- } }
1407- >
1408- < div
1409- style = { {
1410- height : 660 ,
1411- overflow : 'hidden' ,
1412- borderRadius : 16 ,
1413- } }
1414- >
1397+ { effectiveVersion ? (
1398+ < div className = "dual-view" >
1399+ { /* Desktop */ }
1400+ < div style = { { flex : '1 1 500px' , minWidth : 0 } } >
14151401 < Go
1416- key = { `mobile -${ example } -${ selectedEntry } -${ defaultTab } -${ effectiveVersion } ` }
1402+ key = { `desktop -${ example } -${ selectedEntry } -${ defaultTab } -${ effectiveVersion } ` }
14171403 example = { example }
14181404 defaultFile = { defaultFile }
14191405 defaultTab = { defaultTab }
@@ -1424,10 +1410,48 @@ function App() {
14241410 schema = { schema || undefined }
14251411 version = { effectiveVersion }
14261412 />
1413+ < div className = "figure-caption" > Desktop</ div >
1414+ </ div >
1415+ { /* Mobile — fixed 320×660 */ }
1416+ < div
1417+ className = "mobile-preview"
1418+ style = { {
1419+ flex : '0 0 320px' ,
1420+ maxWidth : 320 ,
1421+ overflow : 'hidden' ,
1422+ containerType : 'inline-size' as any ,
1423+ } }
1424+ >
1425+ < div
1426+ style = { {
1427+ height : 660 ,
1428+ overflow : 'hidden' ,
1429+ borderRadius : 16 ,
1430+ } }
1431+ >
1432+ < Go
1433+ key = { `mobile-${ example } -${ selectedEntry } -${ defaultTab } -${ effectiveVersion } ` }
1434+ example = { example }
1435+ defaultFile = { defaultFile }
1436+ defaultTab = { defaultTab }
1437+ defaultEntryFile = { defaultEntryFile || undefined }
1438+ entry = { entryFilter || undefined }
1439+ highlight = { highlight || undefined }
1440+ img = { img || undefined }
1441+ schema = { schema || undefined }
1442+ version = { effectiveVersion }
1443+ />
1444+ </ div >
1445+ < div className = "figure-caption" > Mobile (320 × 660)</ div >
14271446 </ div >
1428- < div className = "figure-caption" > Mobile (320 × 660)</ div >
14291447 </ div >
1430- </ div >
1448+ ) : (
1449+ < div className = "dual-view" style = { { alignItems : 'center' , justifyContent : 'center' , minHeight : 400 } } >
1450+ < span style = { { color : 'var(--semi-color-text-2)' , fontSize : 13 } } >
1451+ Resolving package version…
1452+ </ span >
1453+ </ div >
1454+ ) }
14311455 </ GoConfigProvider >
14321456 </ PreviewErrorBoundary >
14331457 </ main >
0 commit comments