@@ -30,6 +30,9 @@ function JsonContent({ json, label, expandRoot, pageLimit }: JsonProps): ReactNo
3030 } else if ( json instanceof Date ) {
3131 const key = label ? < span className = { styles . key } > { label } : </ span > : ''
3232 div = < > { key } < span className = { styles . string } > { `"${ json . toISOString ( ) } "` } </ span > </ >
33+ } else if ( json instanceof ArrayBuffer || json instanceof Uint8Array ) {
34+ const bytes = json instanceof ArrayBuffer ? new Uint8Array ( json ) : json
35+ div = < ByteArray bytes = { bytes } label = { label } expandRoot = { expandRoot } />
3336 } else if ( typeof json === 'object' && json !== null ) {
3437 div = < JsonObject label = { label } obj = { json } expandRoot = { expandRoot } pageLimit = { pageLimit } />
3538 } else {
@@ -54,6 +57,44 @@ function JsonContent({ json, label, expandRoot, pageLimit }: JsonProps): ReactNo
5457 return div
5558}
5659
60+ function formatHexDump ( bytes : Uint8Array ) : { hex : string , ascii : string } [ ] {
61+ const lines : { hex : string , ascii : string } [ ] = [ ]
62+ for ( let i = 0 ; i < bytes . length ; i += 16 ) {
63+ const slice = bytes . slice ( i , i + 16 )
64+ const hex = Array . from ( slice ) . map ( b => b . toString ( 16 ) . padStart ( 2 , '0' ) ) . join ( ' ' )
65+ const ascii = Array . from ( slice ) . map ( b => b >= 0x20 && b <= 0x7e ? String . fromCharCode ( b ) : '.' ) . join ( '' )
66+ lines . push ( { hex : hex . padEnd ( 47 ) , ascii } )
67+ }
68+ return lines
69+ }
70+
71+ function ByteArray ( { bytes, label, expandRoot } : { bytes : Uint8Array , label ?: string , expandRoot ?: boolean } ) : ReactNode {
72+ const [ collapsed , setCollapsed ] = useState ( ! expandRoot )
73+ const key = label ? < span className = { styles . key } > { label } : </ span > : ''
74+ const summary = `${ bytes . constructor . name } (${ bytes . length } )`
75+
76+ if ( collapsed ) {
77+ return < div role = "treeitem" className = { styles . clickable } aria-expanded = "false" onClick = { ( ) => { setCollapsed ( false ) } } >
78+ { key }
79+ < span className = { styles . comment } > { summary } </ span >
80+ </ div >
81+ }
82+
83+ const lines = formatHexDump ( bytes )
84+ return < >
85+ < div role = "treeitem" className = { styles . clickable } aria-expanded = "true" onClick = { ( ) => { setCollapsed ( true ) } } >
86+ { key }
87+ < span className = { styles . comment } > { summary } </ span >
88+ </ div >
89+ < pre className = { styles . hexDump } >
90+ { lines . map ( ( line , i ) => {
91+ const offset = ( i * 16 ) . toString ( 16 ) . padStart ( 8 , '0' )
92+ return < div key = { i } > < span className = { styles . comment } > { offset } </ span > < span className = { styles . number } > { line . hex } </ span > < span className = { styles . string } > { line . ascii } </ span > </ div >
93+ } ) }
94+ </ pre >
95+ </ >
96+ }
97+
5798function CollapsedArray ( { array } : { array : unknown [ ] } ) : ReactNode {
5899 const { elementRef, width } = useWidth < HTMLSpanElement > ( )
59100 const maxCharacterCount = Math . max ( 20 , Math . floor ( width / 8 ) )
0 commit comments