@@ -13,9 +13,59 @@ interface StreamingBufferMessageProps {
1313}
1414
1515/**
16- * Wrap any raw JSON object/array blocks in markdown fenced code blocks so the
17- * Details section renders them as formatted code rather than plain text.
18- * Lines that already sit inside an existing fenced block are left alone.
16+ * Format a key from snake_case / camelCase / kebab-case into a readable label.
17+ */
18+ const humanizeKey = ( key : string ) : string => {
19+ if ( ! key ) return key ;
20+ const spaced = key
21+ . replace ( / [ _ - ] + / g, ' ' )
22+ . replace ( / ( [ a - z 0 - 9 ] ) ( [ A - Z ] ) / g, '$1 $2' )
23+ . trim ( ) ;
24+ return spaced . replace ( / \b \w / g, ( c ) => c . toUpperCase ( ) ) ;
25+ } ;
26+
27+ /**
28+ * Render a parsed JSON value as readable Markdown (bullet list of
29+ * "**Key**: value" entries, recursing into nested objects/arrays).
30+ */
31+ const jsonToMarkdown = ( value : any , depth = 0 ) : string => {
32+ const indent = ' ' . repeat ( depth ) ;
33+
34+ if ( value === null || value === undefined ) return `${ indent } _n/a_` ;
35+
36+ if ( Array . isArray ( value ) ) {
37+ if ( value . length === 0 ) return `${ indent } _(none)_` ;
38+ return value
39+ . map ( ( item ) => {
40+ if ( item !== null && typeof item === 'object' ) {
41+ return `${ indent } - \n${ jsonToMarkdown ( item , depth + 1 ) } ` ;
42+ }
43+ return `${ indent } - ${ String ( item ) } ` ;
44+ } )
45+ . join ( '\n' ) ;
46+ }
47+
48+ if ( typeof value === 'object' ) {
49+ const entries = Object . entries ( value ) ;
50+ if ( entries . length === 0 ) return `${ indent } _(empty)_` ;
51+ return entries
52+ . map ( ( [ k , v ] ) => {
53+ const label = humanizeKey ( k ) ;
54+ if ( v !== null && typeof v === 'object' ) {
55+ return `${ indent } - **${ label } :**\n${ jsonToMarkdown ( v , depth + 1 ) } ` ;
56+ }
57+ return `${ indent } - **${ label } :** ${ v === null || v === undefined ? '' : String ( v ) } ` ;
58+ } )
59+ . join ( '\n' ) ;
60+ }
61+
62+ return `${ indent } ${ String ( value ) } ` ;
63+ } ;
64+
65+ /**
66+ * Detect raw JSON blocks in the streaming buffer and replace them with a
67+ * readable Markdown rendering so the Details section doesn't expose raw JSON.
68+ * Lines that already sit inside an existing fenced code block are left alone.
1969 */
2070const formatBufferContent = ( content : string ) : string => {
2171 if ( ! content ) return content ;
@@ -55,9 +105,8 @@ const formatBufferContent = (content: string): string => {
55105 const block = lines . slice ( i , endIdx + 1 ) . join ( '\n' ) ;
56106 try {
57107 const parsed = JSON . parse ( block ) ;
58- out . push ( '```json' ) ;
59- out . push ( JSON . stringify ( parsed , null , 2 ) ) ;
60- out . push ( '```' ) ;
108+ out . push ( jsonToMarkdown ( parsed ) ) ;
109+ out . push ( '' ) ;
61110 i = endIdx + 1 ;
62111 continue ;
63112 } catch {
0 commit comments