6565.output-panel { display : none; padding : 0.5rem 0 ; }
6666.output-panel .active { display : block; }
6767# ast { font-size : 0.875rem ; color : var (--muted ); white-space : pre; line-height : 1.5 ; }
68+ # ast .op { color : # e3a857 ; }
69+ # ast .lit { color : # a5d6ff ; }
70+ # ast .node { cursor : pointer; position : relative; }
71+ # ast .node : hover : not (: has (.node : hover )) .bracket { color : var (--accent ); }
72+ # ast .node : hover : not (: has (.node : hover )) > .line { background : var (--accent ); }
73+ # ast .line { position : absolute; left : calc (0.25ch + 2px ); top : 1.5em ; bottom : calc (1.1em + 4px ); width : 1px ; background : var (--border ); opacity : 0.4 ; pointer-events : none; }
74+ # ast .node .collapsed > .args { display : none; }
75+ # ast .node .collapsed > .line { display : none; }
76+ # ast .node .collapsed > .ellipsis { display : inline; }
77+ # ast .ellipsis { display : none; color : var (--muted ); }
6878
6979/* Eval section */
7080.eval-content { padding : 0.5rem 0 ; display : flex; flex-direction : column; gap : 0.5rem ; font-size : 0.875rem ; }
@@ -222,12 +232,17 @@ <h1><a href="https://github.com/dy/subscript" class="logo-link" target="_blank"
222232
223233 < div class ="output-section ">
224234 < div class ="output-tabs ">
225- < button class ="output-tab active " data-tab ="eval "> Eval</ button >
226- < button class ="output-tab " data-tab ="tree "> Tree</ button >
235+ < button class ="output-tab active " data-tab ="tree "> Tree</ button >
236+ < button class ="output-tab " data-tab ="eval "> Eval</ button >
237+ </ div >
238+
239+ <!-- Tree panel -->
240+ < div class ="output-panel active " id ="treePanel ">
241+ < pre id ="ast " data-testid ="ast "> </ pre >
227242 </ div >
228243
229244 <!-- Eval panel -->
230- < div class ="output-panel active " id ="evalPanel ">
245+ < div class ="output-panel " id ="evalPanel ">
231246 < div class ="eval-content ">
232247 < div class ="eval-field ">
233248 < span class ="label "> Context</ span >
@@ -247,11 +262,6 @@ <h1><a href="https://github.com/dy/subscript" class="logo-link" target="_blank"
247262 </ div >
248263 </ div >
249264 </ div >
250-
251- <!-- Tree panel -->
252- < div class ="output-panel " id ="treePanel ">
253- < pre id ="ast "> </ pre >
254- </ div >
255265 </ div >
256266 </ main >
257267
@@ -942,7 +952,7 @@ <h1><a href="https://github.com/dy/subscript" class="logo-link" target="_blank"
942952 editorEl . classList . remove ( 'has-error' )
943953 if ( ast !== undefined ) {
944954 currentAST = ast
945- astEl . textContent = formatAST ( ast )
955+ astEl . innerHTML = formatAST ( ast )
946956 errorEl . textContent = ''
947957 }
948958 if ( result !== undefined ) {
@@ -974,27 +984,31 @@ <h1><a href="https://github.com/dy/subscript" class="logo-link" target="_blank"
974984
975985// AST formatter
976986function formatAST ( node , depth = 0 ) {
977- if ( node === undefined ) return 'undefined'
978- if ( node === null ) return 'null'
979- if ( ! Array . isArray ( node ) ) return JSON . stringify ( node )
987+ const esc = s => s . replace ( / & / g, '&' ) . replace ( / < / g, '<' ) . replace ( / > / g, '>' )
988+ const cls = v => typeof v === 'string' && / ^ [ + \- * \/ % & | ^ ~ ! < > = ? . , : ; @ # ( ) \[ \] { } ] | ^ ( \| \| | & & | \? \? | \* \* | = > | \. \. \. | i f | e l s e | f o r | w h i l e | d o | s w i t c h | c a s e | b r e a k | c o n t i n u e | r e t u r n | t h r o w | t r y | c a t c h | f i n a l l y | f u n c t i o n | c l a s s | l e t | c o n s t | v a r | n e w | d e l e t e | t y p e o f | v o i d | i n | o f | i n s t a n c e o f | a s y n c | a w a i t | y i e l d | i m p o r t | e x p o r t | d e f a u l t | e x t e n d s | s t a t i c | g e t | s e t ) $ / . test ( v ) ? 'op' : 'lit'
989+ const fmt = v => `<span class="${ cls ( v ) } ">${ esc ( JSON . stringify ( v ) ) } </span>`
990+ const stop = `onclick="event.stopPropagation()"`
991+
992+ if ( node === undefined ) return '<span class="lit">undefined</span>'
993+ if ( node === null ) return '<span class="lit">null</span>'
994+ if ( ! Array . isArray ( node ) ) return fmt ( node )
980995 if ( node . length === 0 ) return '[]'
981996
982997 const indent = ' ' . repeat ( depth )
983998 const inner = ' ' . repeat ( depth + 1 )
984999
985- if ( node [ 0 ] === undefined && node . length === 2 ) return `[ , ${ JSON . stringify ( node [ 1 ] ) } ] `
1000+ if ( node [ 0 ] === undefined && node . length === 2 ) return `<span class="node" ${ stop } ><span class="bracket">[</span> , ${ fmt ( node [ 1 ] ) } <span class="bracket">]</span></span> `
9861001
987- const simple = node . every ( ( n , i ) => ! Array . isArray ( n ) || ( i > 0 && JSON . stringify ( n ) . length < 20 ) )
988- if ( simple ) {
989- const inline = `[${ node . map ( ( n , i ) => i === 0 && n === undefined ? '' : ( Array . isArray ( n ) ? formatAST ( n , 0 ) : JSON . stringify ( n ) ) ) . join ( ', ' ) } ]`
990- if ( inline . length < 60 ) return inline
1002+ // Inline if all args are plain (no nested arrays) or all nested are short
1003+ const short = n => ! Array . isArray ( n ) || JSON . stringify ( n ) . length < 30
1004+ if ( node . every ( short ) ) {
1005+ const parts = node . map ( ( n , i ) => i === 0 && n === undefined ? '' : ( Array . isArray ( n ) ? formatAST ( n , 0 ) : fmt ( n ) ) ) . join ( ', ' )
1006+ return `<span class="node" ${ stop } ><span class="bracket">[</span>${ parts } <span class="bracket">]</span></span>`
9911007 }
9921008
993- const parts = node . map ( ( n , i ) => {
994- if ( i === 0 && n === undefined ) return ''
995- return Array . isArray ( n ) ? formatAST ( n , depth + 1 ) : JSON . stringify ( n )
996- } )
997- return `[${ parts [ 0 ] } ,\n${ inner } ${ parts . slice ( 1 ) . join ( `,\n${ inner } ` ) } \n${ indent } ]`
1009+ const op = node [ 0 ] !== undefined ? fmt ( node [ 0 ] ) : ''
1010+ const args = node . slice ( 1 ) . map ( n => Array . isArray ( n ) ? formatAST ( n , depth + 1 ) : fmt ( n ) )
1011+ return `<span class="node" onclick="this.classList.toggle('collapsed');event.stopPropagation()"><span class="bracket">[</span>${ op } <span class="ellipsis">…</span><span class="args">,\n${ inner } ${ args . join ( `,\n${ inner } ` ) } \n${ indent } </span><span class="bracket">]</span><span class="line"></span></span>`
9981012}
9991013
10001014// Compact JSON formatter - multiline but minimal
0 commit comments