99 <div :class =" [$style.playgroundPaneRoot, $style.playgroundEditorPane]" >
1010 <div :class =" $style.playgroundPaneHeader" >
1111 <div :class =" $style.playgroundResultActionsLeft" >
12- <button :class =" $style.playgroundButton" @click =" replaceWithFizzbuzz" >Fizzbuzz</button >
12+ <button :class =" $style.playgroundButton" :disabled = " editorLoading " @click =" replaceWithFizzbuzz" >Fizzbuzz</button >
1313 </div >
1414 <div :class =" $style.playgroundResultActionsRight" >
1515 <button :class =" $style.playgroundButton" :disabled =" !isRunning" @click =" abort" >Abort</button >
16- <button :class =" [$style.playgroundButton, $style.playgroundButtonPrimary]" @click =" run" >Run</button >
16+ <button :class =" [$style.playgroundButton, $style.playgroundButtonPrimary]" :disabled = " editorLoading " @click =" run" >Run</button >
1717 </div >
1818 </div >
1919
2020 <div :class =" $style.playgroundEditorRoot" >
21- <div :class =" $style.playgroundEditorScroller" >
21+ <div :class =" $style.playgroundEditorScroller" :inert = " editorLoading " >
2222 <div :class =" [$style.highlight, $style.playgroundEditorHighlight]" v-html =" editorHtml" ></div >
2323 <textarea
2424 ref =" inputEl"
3131 :class =" $style.playgroundEditorTextarea"
3232 ></textarea >
3333 </div >
34+ <div v-if =" editorLoading" :class =" $style.playgroundEditorLoading" >
35+ <div >Loading...</div >
36+ </div >
3437 </div >
3538 </div >
3639 <div :class =" $style.playgroundPaneRoot" >
4043 <label for =" output" >Output</label >
4144 <input type =" radio" id =" ast" v-model =" resultTab" value =" ast" >
4245 <label for =" ast" >AST</label >
46+ <input type =" radio" id =" metadata" v-model =" resultTab" value =" metadata" >
47+ <label for =" metadata" >Metadata</label >
4348 </div >
4449 <div :class =" $style.playgroundResultActionsRight" >
4550 <button :class =" [$style.playgroundButton]" @click =" clearLog" >Clear</button >
5964 >{{ log.text }}</div >
6065 </div >
6166 <div v-else-if =" resultTab === 'ast'" :class =" $style.playgroundResultContent" >
62- <div :class =" $style.highlight" v-html =" astHtml" ></div >
67+ <div v-if =" isSyntaxError" class =" danger custom-block" >
68+ <p class =" custom-block-title" >Syntax Error</p >
69+ <p >See Output tab for details</p >
70+ </div >
71+ <div v-else :class =" $style.highlight" v-html =" astHtml" ></div >
72+ </div >
73+ <div v-else-if =" resultTab === 'metadata'" :class =" $style.playgroundResultContent" >
74+ <div v-if =" isSyntaxError" class =" danger custom-block" >
75+ <p class =" custom-block-title" >Syntax Error</p >
76+ <p >See Output tab for details</p >
77+ </div >
78+ <div v-else-if =" metadata" :class =" $style.highlight" v-html =" metadataHtml" ></div >
79+ <div v-else >No metadata</div >
6380 </div >
6481 </div >
6582 </div >
@@ -85,9 +102,10 @@ const fizzbuzz = `for (let i, 100) {
85102\t\t else i
86103} ` ;
87104
88- const resultTab = ref <' output' | ' ast' >(' output' );
105+ const resultTab = ref <' output' | ' ast' | ' metadata ' >(' output' );
89106
90107// #region Editor
108+ const editorLoading = ref (true );
91109const inputEl = useTemplateRef (' inputEl' );
92110const code = ref (fizzbuzz );
93111const editorHtml = ref (' ' );
@@ -159,23 +177,35 @@ const logs = ref<{
159177}[]>([]);
160178const logEl = useTemplateRef (' logEl' );
161179
180+ const isSyntaxError = ref (false );
181+
162182const ast = ref <Ast .Node [] | null >(null );
163183const astHtml = ref (' ' );
164184
185+ const metadata = ref <unknown >(null );
186+ const metadataHtml = ref (' ' );
187+
165188function parse() {
189+ isSyntaxError .value = false ;
190+
166191 if (parser != null ) {
167192 try {
168193 const _ast = parser .parse (code .value );
169194 logs .value = [];
170195 ast .value = _ast ;
196+
197+ const meta = Interpreter .collectMetadata (_ast );
198+ metadata .value = meta ?.get (null ) ?? null ;
171199 } catch (err ) {
172200 if (err instanceof errors .AiScriptError ) {
173201 logs .value = [{
174202 text: ` [SyntaxError] ${err .name }: ${err .message } ` ,
175203 type: ' error' ,
176204 }];
205+ isSyntaxError .value = true ;
177206 }
178207 ast .value = null ;
208+ metadata .value = null ;
179209 }
180210 } else {
181211 ast .value = null ;
@@ -299,6 +329,8 @@ const updateHash = useThrottle((d: HashData) => {
299329// #endregion
300330
301331onMounted (async () => {
332+ const loadStartedAt = Date .now ();
333+
302334 await init ();
303335 initAiScriptEnv ();
304336
@@ -324,7 +356,12 @@ onMounted(async () => {
324356 }, { immediate: true });
325357
326358 watch (ast , async (newAst ) => {
327- if (highlighter && newAst != null ) {
359+ if (highlighter ) {
360+ if (newAst == null ) {
361+ astHtml .value = ' ' ;
362+ return ;
363+ }
364+
328365 astHtml .value = highlighter .codeToHtml (JSON .stringify (newAst , null , 2 ), {
329366 lang: ' json' ,
330367 themes: {
@@ -334,7 +371,31 @@ onMounted(async () => {
334371 defaultColor: false ,
335372 });
336373 }
337- });
374+ }, { immediate: true });
375+
376+ watch (metadata , async (newMetadata ) => {
377+ if (highlighter ) {
378+ if (newMetadata == null ) {
379+ metadataHtml .value = ' ' ;
380+ return ;
381+ }
382+
383+ metadataHtml .value = highlighter .codeToHtml (JSON .stringify (newMetadata , null , 2 ), {
384+ lang: ' json' ,
385+ themes: {
386+ light: ' github-light' ,
387+ dark: ' github-dark' ,
388+ },
389+ defaultColor: false ,
390+ });
391+ }
392+ }, { immediate: true });
393+
394+ const loadEndedAt = Date .now ();
395+
396+ setTimeout (() => {
397+ editorLoading .value = false ;
398+ }, Math .max (0 , 500 - (loadEndedAt - loadStartedAt )));
338399});
339400
340401onUnmounted (() => {
@@ -388,6 +449,19 @@ onUnmounted(() => {
388449 overflow : scroll ;
389450}
390451
452+ .playgroundEditorLoading {
453+ position : absolute ;
454+ top : 0 ;
455+ left : 0 ;
456+ right : 0 ;
457+ bottom : 0 ;
458+ background-color : rgba (255 , 255 , 255 , 0.5 );
459+ z-index : 1 ;
460+ display : flex ;
461+ justify-content : center ;
462+ align-items : center ;
463+ }
464+
391465.playgroundEditorScroller {
392466 position : relative ;
393467 padding : 24px 36px ;
0 commit comments