11const qs = ( selector , root = document ) => root . querySelector ( selector ) ;
2- const qsa = ( selector , root = document ) => Array . from ( root . querySelectorAll ( selector ) ) ;
32
43function getBasePath ( ) {
54 if ( typeof window . SOLUNA_BASE === "string" && window . SOLUNA_BASE . length > 0 ) {
@@ -12,134 +11,27 @@ function getBasePath() {
1211 if ( segments . length === 0 ) return "" ;
1312 if ( segments . length === 1 ) return `/${ segments [ 0 ] } ` ;
1413 const last = segments [ segments . length - 1 ] ;
15- if ( [ "examples" , "docs" , "play" ] . includes ( last ) ) {
16- return `/${ segments . slice ( 0 , - 1 ) . join ( "/" ) } ` ;
14+ const penultimate = segments [ segments . length - 2 ] ;
15+ if ( [ "examples" , "docs" ] . includes ( last ) ) {
16+ const base = segments . slice ( 0 , - 1 ) . join ( "/" ) ;
17+ return base ? `/${ base } ` : "" ;
1718 }
18- return `/${ segments . join ( "/" ) } ` ;
19- }
20-
21- async function loadJson ( url ) {
22- const response = await fetch ( url ) ;
23- if ( ! response . ok ) {
24- throw new Error ( `Failed to load ${ url } ` ) ;
25- }
26- return response . json ( ) ;
27- }
28-
29- async function resolveExamples ( ) {
30- const base = getBasePath ( ) ;
31- let examples = window . SOLUNA_EXAMPLES || [ ] ;
32- if ( Array . isArray ( examples ) ) return examples ;
33- if ( examples && Array . isArray ( examples . examples ) ) return examples . examples ;
34- try {
35- const data = await loadJson ( `${ base } /data/examples.json` ) ;
36- if ( data && Array . isArray ( data . examples ) ) return data . examples ;
37- } catch ( err ) {
38- console . warn ( "Failed to load examples.json" , err ) ;
19+ if ( [ "examples" , "docs" ] . includes ( penultimate ) ) {
20+ const base = segments . slice ( 0 , - 2 ) . join ( "/" ) ;
21+ return base ? `/${ base } ` : "" ;
3922 }
40- return [ ] ;
41- }
42-
43- async function resolveDocs ( ) {
44- const base = getBasePath ( ) ;
45- let docs = window . SOLUNA_DOCS || [ ] ;
46- if ( Array . isArray ( docs ) ) return docs ;
47- if ( docs && Array . isArray ( docs . modules ) ) return docs . modules ;
48- try {
49- const data = await loadJson ( `${ base } /data/docs.json` ) ;
50- if ( data && Array . isArray ( data . modules ) ) return data . modules ;
51- } catch ( err ) {
52- console . warn ( "Failed to load docs.json" , err ) ;
53- }
54- return [ ] ;
55- }
56-
57- function renderExamples ( target , examples , limit ) {
58- if ( ! target ) return ;
59- if ( ! Array . isArray ( examples ) ) {
60- examples = [ ] ;
61- }
62- const base = getBasePath ( ) ;
63- target . innerHTML = "" ;
64- const list = typeof limit === "number" ? examples . slice ( 0 , limit ) : examples ;
65- list . forEach ( ( example ) => {
66- const card = document . createElement ( "a" ) ;
67- card . className = "card" ;
68- card . href = `${ base } /play/?example=${ encodeURIComponent ( example . id ) } ` ;
69- card . innerHTML = `
70- <div class="card-meta">Example</div>
71- <h3 class="card-title">${ example . title } </h3>
72- <p class="card-desc">Entry: ${ example . entry } </p>
73- ` ;
74- target . appendChild ( card ) ;
75- } ) ;
76- }
77-
78- function renderDocsGrid ( target , modules ) {
79- if ( ! target ) return ;
80- const base = getBasePath ( ) ;
81- target . innerHTML = "" ;
82- modules . forEach ( ( mod ) => {
83- const card = document . createElement ( "a" ) ;
84- card . className = "card" ;
85- card . href = `${ base } /docs/#${ encodeURIComponent ( mod . module ) } ` ;
86- card . innerHTML = `
87- <div class="card-meta">Module</div>
88- <h3 class="card-title">${ mod . title } </h3>
89- <p class="card-desc">Blocks: ${ mod . blocks . length } </p>
90- ` ;
91- target . appendChild ( card ) ;
92- } ) ;
93- }
94-
95- function setupExampleSearch ( input , target , examples ) {
96- if ( ! input || ! target ) return ;
97- input . addEventListener ( "input" , ( ) => {
98- const query = input . value . trim ( ) . toLowerCase ( ) ;
99- const filtered = examples . filter ( ( example ) => {
100- const haystack = `${ example . id } ${ example . title } ${ example . entry } ` . toLowerCase ( ) ;
101- return haystack . includes ( query ) ;
102- } ) ;
103- renderExamples ( target , filtered ) ;
104- } ) ;
105- }
106-
107- function setupDocsSearch ( input ) {
108- if ( ! input ) return ;
109- const sections = qsa ( ".docs-section" ) ;
110- input . addEventListener ( "input" , ( ) => {
111- const query = input . value . trim ( ) . toLowerCase ( ) ;
112- sections . forEach ( ( section ) => {
113- const title = ( section . dataset . title || "" ) . toLowerCase ( ) ;
114- const module = ( section . dataset . module || "" ) . toLowerCase ( ) ;
115- const match = title . includes ( query ) || module . includes ( query ) ;
116- section . classList . toggle ( "is-hidden" , query . length > 0 && ! match ) ;
117- } ) ;
118- } ) ;
119- }
120-
121- async function initHome ( ) {
122- const examples = await resolveExamples ( ) ;
123- const docs = await resolveDocs ( ) ;
124- const grid = qs ( "[data-examples-grid]" ) ;
125- const limit = grid ? Number ( grid . dataset . limit || "" ) : undefined ;
126- renderExamples ( grid , examples , Number . isFinite ( limit ) ? limit : undefined ) ;
127- renderDocsGrid ( qs ( "[data-docs-grid]" ) , docs ) ;
128- const exampleCount = qs ( "[data-example-count]" ) ;
129- if ( exampleCount ) exampleCount . textContent = String ( examples . length ) ;
130- const docsCount = qs ( "[data-docs-count]" ) ;
131- if ( docsCount ) docsCount . textContent = String ( docs . length ) ;
132- }
133-
134- async function initExamples ( ) {
135- const examples = await resolveExamples ( ) ;
136- const grid = qs ( "[data-examples-grid]" ) ;
137- renderExamples ( grid , examples ) ;
138- setupExampleSearch ( qs ( "[data-example-search]" ) , grid , examples ) ;
23+ return `/${ segments . join ( "/" ) } ` ;
13924}
14025
141- async function initDocs ( ) {
142- setupDocsSearch ( qs ( "[data-docs-search]" ) ) ;
26+ function getExampleId ( ) {
27+ const path = window . location . pathname || "/" ;
28+ const trimmed = path . replace ( / \/ i n d e x \. h t m l $ / , "" ) . replace ( / \/ $ / , "" ) ;
29+ if ( trimmed === "" ) return null ;
30+ const segments = trimmed . split ( "/" ) . filter ( Boolean ) ;
31+ if ( segments . length === 0 ) return null ;
32+ const last = segments [ segments . length - 1 ] ;
33+ if ( last === "examples" ) return null ;
34+ return last ;
14335}
14436
14537function createZip ( entries ) {
@@ -272,33 +164,10 @@ function loadRuntimeScript(src) {
272164}
273165
274166async function initPlay ( ) {
275- const examples = await resolveExamples ( ) ;
276- if ( examples . length === 0 ) return ;
167+ const exampleId = getExampleId ( ) ;
168+ if ( ! exampleId ) return ;
277169 const base = getBasePath ( ) ;
278170
279- const params = new URLSearchParams ( window . location . search ) ;
280- const initialId = params . get ( "example" ) || examples [ 0 ] . id ;
281- const selected = examples . find ( ( item ) => item . id === initialId ) || examples [ 0 ] ;
282-
283- const tabs = qs ( "[data-play-tabs]" ) ;
284- if ( tabs ) {
285- tabs . innerHTML = "" ;
286- examples . forEach ( ( example ) => {
287- const tab = document . createElement ( "button" ) ;
288- tab . className = "play-tab" ;
289- tab . textContent = example . title ;
290- if ( example . id === selected . id ) {
291- tab . classList . add ( "active" ) ;
292- }
293- tab . addEventListener ( "click" , ( ) => {
294- const nextUrl = new URL ( window . location . href ) ;
295- nextUrl . searchParams . set ( "example" , example . id ) ;
296- window . location . href = nextUrl . toString ( ) ;
297- } ) ;
298- tabs . appendChild ( tab ) ;
299- } ) ;
300- }
301-
302171 const codeTarget = qs ( "#code-content" ) ;
303172 const consoleTarget = qs ( "#console-output" ) ;
304173 const appendConsole = ( text , isError ) => {
@@ -325,7 +194,7 @@ async function initPlay() {
325194
326195 let sourceText = "" ;
327196 try {
328- sourceText = await loadText ( `${ base } /runtime/test/${ selected . id } .lua` ) ;
197+ sourceText = await loadText ( `${ base } /runtime/test/${ exampleId } .lua` ) ;
329198 if ( codeTarget ) codeTarget . textContent = sourceText ;
330199 } catch ( err ) {
331200 setStatus ( "Failed to load example source." ) ;
@@ -411,9 +280,5 @@ async function initPlay() {
411280}
412281
413282document . addEventListener ( "DOMContentLoaded" , ( ) => {
414- const page = document . body . dataset . page ;
415- if ( page === "home" ) initHome ( ) ;
416- if ( page === "examples" ) initExamples ( ) ;
417- if ( page === "docs" ) initDocs ( ) ;
418- if ( page === "play" ) initPlay ( ) ;
283+ initPlay ( ) ;
419284} ) ;
0 commit comments