2023-05-17 01:56:27 +00:00
/ * T O D O :
- history
- find example and correctly handle "merge" messages
- sort and filter table
- loading icons
- expand referenced object in place
- paste bytes - > analyze
- edit fields / files ?
- different menu for message / enum / extend / literal
2023-05-19 20:17:28 +00:00
- display integers in base16 and base10
- search bytes ?
- highlight results in inspector
2023-05-17 01:56:27 +00:00
* /
import { useState , useEffect , useRef } from 'react' ;
import type { MouseEvent as ReactMouseEvent , ChangeEventHandler , ChangeEvent } from 'react' ;
import './App.css' ;
import { ObjectInspector , ObjectLabel , ObjectName , ObjectValue } from 'react-inspector' ;
import { PanelGroup , Panel , PanelResizeHandle } from 'react-resizable-panels' ;
2023-05-19 20:17:28 +00:00
import { process , read , ParsedFile , TableItem } from './iwa' ;
2023-05-17 01:56:27 +00:00
import { parse_protos , ProtoMap } from './messages' ;
import type { $_TSP_MessageInfo , $_TSP_Reference } from './messages' ;
import { Menu , Item , Separator , useContextMenu } from 'react-contexify' ;
import 'react-contexify/dist/ReactContexify.css' ;
import { ToastContainer , toast } from 'react-toastify' ;
import 'react-toastify/dist/ReactToastify.css' ;
//#region Xxd
//import { vsprintf } from 'printj';
/ * c o n s t X = " % 0 2 h h x " , Y = X + X + " " ;
const FMT = [ . . . Array . from ( { length :16 } ) . map ( ( _ , i ) = >
Y . repeat ( i >> 1 ) + ( i % 2 ? X : " " ) + " " + " " . repeat ( 7 - ( i >> 1 ) ) + "|" + "%c" . repeat ( i ) + " " . repeat ( 16 - i ) + "|\n"
) , Y . repeat ( 8 ) + "|" + "%c" . repeat ( 16 ) + "|\n" ] ;
const xxd = ( u8 : Uint8Array ) : string = > {
let out : string [ ] = [ ] ;
for ( let i = 0 ; i < u8 . length ; i += 16 ) {
let d = [ . . . u8 . slice ( i , i + 16 ) ] ;
out . push ( vsprintf ( ` %04x: ${ FMT [ d . length ] } ` , [ i , . . . d , . . . d . map ( x = > String . fromCharCode ( x ) . replace ( /[^\x20-\x7E]/g , "." ) ) ] ) )
}
return out . join ( "" ) ;
}
type XxdProps = {
data? : Uint8Array ;
} ;
function Xxd ( { data } : XxdProps ) {
return ( < pre > { data && xxd ( data ) } < / pre > ) ;
} * /
//<div className="proto"><Xxd data={id && file.space[+id]?.[0]?.data || void 0} /></div>
//#endregion
//#region TableView
type TableViewProps = {
id? : string ;
data : any [ ] ;
cols : string [ ] ;
2023-05-19 20:17:28 +00:00
filter ? : ( row : any , R : number ) = > boolean ;
2023-05-17 01:56:27 +00:00
rowclick ? : ( row : any , R : number , e : ReactMouseEvent < HTMLTableRowElement , MouseEvent > ) = > void ;
cellclick ? : ( value : any , R : number , C : number , e : ReactMouseEvent < HTMLTableCellElement , MouseEvent > ) = > void ;
} ;
2023-05-19 20:17:28 +00:00
function TableView ( { id , data , cols , filter , rowclick , cellclick } : TableViewProps ) {
2023-05-17 01:56:27 +00:00
return (
< table >
< thead >
< tr > { cols . map ( ( c , idx ) = > (
< th key = { idx } > { c } < / th >
) ) } < / tr >
< / thead >
2023-05-19 20:17:28 +00:00
< tbody > { data . filter ( filter || ( ( ) = > true ) ) . map ( ( row , R ) = > (
2023-05-17 01:56:27 +00:00
< tr id = { ` tr- ${ R } ` } key = { R }
{ . . . ( rowclick ? { onClick : ( e ) = > { e . preventDefault ( ) ; e . stopPropagation ( ) ; rowclick ( row , R , e ) } } : { } ) }
{ . . . ( row [ "id" ] == id ? { style : { backgroundColor : "#646cff" , color : "#FFFFFF" } } : { } ) }
> { /* TODO: forward-ref? */ }
{ cols . map ( ( c , C ) = > ( < td key = { ` ${ R } - ${ C } ` }
{ . . . ( typeof row [ c ] == "string" ? { className : "left" } : { } ) }
{ . . . ( typeof row [ c ] == "number" ? { className : "right" } : { } ) }
{ . . . ( cellclick ? { onClick : ( e ) = > { e . preventDefault ( ) ; e . stopPropagation ( ) ; cellclick ( row , R , C , e ) } } : { } ) }
> { row [ c ] ? ? "" } < / td > ) ) }
< / tr >
) ) } < / tbody >
< / table >
) ;
}
//#endregion
//#region ContextMenu
interface ContextMenuProps {
ID : string ;
menuType : string ;
menuField : string ;
menuId : string ;
onClickId ? : ( { props } : any ) = > void ;
onClickCopyByteArray ? : ( { props } : any ) = > void ;
onClickCopyJSON : ( { props } : any ) = > void ;
showProtoDef : ( { props } : any ) = > void ;
}
const ContextMenu = ( { ID , menuType , menuField , menuId , onClickId , onClickCopyByteArray , onClickCopyJSON , showProtoDef } : ContextMenuProps ) = > (
< Menu id = { ID } >
{ menuField && ( < Item disabled > < b > { menuField } < / b > < / Item > ) }
< Item disabled > < b > { menuType } < / b > < / Item >
< Item hidden = { ( ) = > menuType != ".TSP.Reference" } onClick = { onClickId } > Go to { menuId } < / Item >
< Separator / >
< Item onClick = { onClickCopyByteArray } > Copy byte array < / Item >
< Item onClick = { onClickCopyJSON } > Copy JSON < / Item >
< Item onClick = { showProtoDef } > Show Definition < / Item >
< / Menu > ) ;
//#endregion
function App() {
/* selected message ID */
const [ id , setId ] = useState < string > ( "0" ) ;
/* parsed file */
const [ file , setFile ] = useState < ParsedFile > ( { space : { } , tbl : [ ] , type : "N" } ) ;
/* current object */
const [ obj , setObj ] = useState < any > ( { } ) ;
/* current meta */
const [ meta , setMeta ] = useState < $_TSP_MessageInfo > ( { } as any ) ;
2023-05-19 20:17:28 +00:00
/* current deps */
const [ deps , setDeps ] = useState < any [ ] > ( [ ] ) ;
2023-05-17 01:56:27 +00:00
/* protobuf definitions */
const [ protos , setProtos ] = useState < ProtoMap > ( { } ) ;
/* selected message type */
const [ sel , setSel ] = useState < string > ( "" ) ;
/* current protobuf definition */
const [ __html , setProto ] = useState < string > ( "Select a Row" ) ;
/* "dirty" if inspecting a subfield */
const [ dirty , setDirty ] = useState < boolean > ( false ) ;
2023-05-19 20:17:28 +00:00
/* loading */
const [ loading , setLoading ] = useState < boolean > ( true ) ;
/* search */
const [ search , setSearch ] = useState < string > ( "" ) ;
2023-05-17 01:56:27 +00:00
/* history stack */
const [ stack , setStack ] = useState < string [ ] > ( [ ] ) ;
/* react-contexify */
const MENU_ID = "insp-menu" ;
const { show } = useContextMenu ( { id : MENU_ID } ) ;
const [ menuField , setMenuField ] = useState < string > ( "" ) ;
const [ menuType , setMenuType ] = useState < string > ( "" ) ;
const [ menuId , setMenuId ] = useState < string > ( "" ) ;
const tblRef = useRef < HTMLDivElement > ( null ) ;
2023-05-19 20:17:28 +00:00
/* parse if message has not been parsed */
const preparse = ( id : any , message : string , f : ParsedFile = file , p : ProtoMap = protos ) = > {
const item = f . space [ + id ] [ 0 ] ;
if ( ! item . parsed ) {
item . parsed = process ( item . data , message , p ) ;
item . pre = JSON . stringify ( item . parsed , ( _ , v ) = > typeof v == "bigint" ? v . toString ( ) : v instanceof Uint8Array ? [ . . . v ] : v ) ;
}
if ( ! item . parsedmeta ) {
const m : $_TSP_MessageInfo = item . parsedmeta = process ( item . rawmeta , ".TSP.MessageInfo" , p ) ;
/* .TSP.MessageInfo */
if ( m . object_references ) m . $object_references = m . object_references . map ( ( n : BigInt ) = > {
/* create a fake reference for the inspector */
var o : $_TSP_Reference = ( { identifier : n } ) ;
Object . defineProperty ( o , "PB_TYPE" , { value : ".TSP.Reference" , enumerable : false } ) ;
return o ;
} ) ;
if ( m . field_infos ) m . field_infos . forEach ( ( fi ) = > {
/* .TSP.FieldInfo */
if ( fi . object_references ) fi . $object_references = fi . object_references . map ( ( n : BigInt ) = > {
/* create a fake reference for the inspector */
var o : $_TSP_Reference = ( { identifier : n } ) ;
Object . defineProperty ( o , "PB_TYPE" , { value : ".TSP.Reference" , enumerable : false } ) ;
return o ;
} ) ;
} ) ;
}
} ;
2023-05-17 01:56:27 +00:00
/* update selection based on table row */
2023-05-19 20:17:28 +00:00
const doitRow = ( row : TableItem , R : number , reset? : boolean ) = > {
2023-05-17 01:56:27 +00:00
let obj : any , meta : $_TSP_MessageInfo ;
try {
2023-05-19 20:17:28 +00:00
preparse ( row . id , row . message ) ;
const item = file . space [ + row . id ] [ 0 ] ;
obj = item . parsed ;
meta = item . parsedmeta as $_TSP_MessageInfo ;
2023-05-17 01:56:27 +00:00
} catch ( e ) {
console . error ( row , e ) ; toast . error ( ` Could not parse ${ row . id } ( ${ row . type } ) ` , { position : toast.POSITION.TOP_CENTER } ) ; return ;
}
if ( reset == true ) setStack ( [ ] ) ; else if ( typeof reset != "undefined" ) setStack ( [ . . . stack , id ] ) ;
setSel ( row . message ) ;
setProto ( protos [ row . message ] || "" ) ;
setDirty ( false ) ;
setObj ( obj ) ;
setMeta ( meta ) ;
2023-05-19 20:17:28 +00:00
setDeps ( file . tbl . filter ( r = > ( ( file . space [ + r . id ] [ 0 ] . parsedmeta ? . object_references || [ ] ) ? . indexOf ( BigInt ( row . id ) ) > - 1 ) ) . map ( v = > {
/* this copy uses non-enumerable fields */
var o = { } ; Object . entries ( v ) . forEach ( ( [ k , v ] ) = > Object . defineProperty ( o , k , { enumerable : false , value : v } ) ) ; return o ;
} ) ) ;
2023-05-17 01:56:27 +00:00
setId ( String ( row . id ) ) ;
var rowelt = document . getElementById ( ` tr- ${ R } ` ) ;
var top = rowelt ? . offsetTop || 0 ;
if ( tblRef . current ) {
let tbl = tblRef . current ;
if ( top > tbl . scrollTop + tbl . clientHeight - ( rowelt ? . clientHeight || 0 ) || top < tbl . scrollTop + ( rowelt ? . clientHeight || 0 ) ) tbl . scrollTop = Math . max ( 0 , top - tbl . clientHeight / 2 - ( rowelt ? . clientHeight || 0 ) / 2 ) ;
}
} ;
/* click event handler for the messages table */
const rowclick = ( row : any , R : number ) = > { doitRow ( row , R , true ) ; } ;
/* helper for .TSP.Reference */
const gotoRef = ( id : string ) = > {
var R = file . tbl . findIndex ( t = > + t . id == + id ) ;
if ( R == - 1 ) throw new Error ( ` Message ${ id } not found ` ) ;
doitRow ( file . tbl [ R ] , R , false ) ;
} ;
/* helper for selecting sub-proto */
const selectProto = ( type : string ) = > { setProto ( protos [ type ] || "" ) ; setDirty ( type != sel ) ; } ;
/* go back to the previous message */
const pop = ( ) = > {
const oldId = stack . pop ( ) || "0" ;
setStack ( [ . . . stack ] ) ;
var R = file . tbl . findIndex ( t = > + t . id == + oldId ) ;
if ( R == - 1 ) throw new Error ( ` Message ${ oldId } not found ` ) ;
doitRow ( file . tbl [ R ] , R ) ;
} ;
2023-05-19 20:17:28 +00:00
/* filter message table */
const filter = ( row : any ) = > {
if ( ! search ) return true ;
preparse ( row . id , row . message ) ;
return ( file . space [ + row . id ] [ 0 ] . pre || "" ) . toLowerCase ( ) . indexOf ( search . toLowerCase ( ) ) > - 1 ;
//return row.message == ".TN.SheetArchive";
}
/* load file */
const process_ab = ( ab : ArrayBuffer , p : ProtoMap = protos ) = > {
const f = read ( ab ) ;
f . tbl . forEach ( row = > preparse ( row . id , row . message , f , p ) ) ;
setFile ( f ) ;
} ;
/* on page load, get protobuf definitions and process the test file */
2023-05-17 01:56:27 +00:00
useEffect ( ( ) = > {
2023-05-19 20:17:28 +00:00
const id = toast . loading ( ` Processing test.numbers ` ) ;
2023-05-17 01:56:27 +00:00
let ignore = false ;
( async ( ) = > {
2023-05-19 20:17:28 +00:00
await new Promise ( ( res ) = > setTimeout ( res , 100 ) ) ; // "sleep" to give toast a chance to shine
2023-05-17 01:56:27 +00:00
const protos = await ( await fetch ( "protos" ) ) . text ( ) ;
const testfile = await ( await fetch ( "test.numbers" ) ) . arrayBuffer ( ) ;
if ( ignore ) return ;
2023-05-19 20:17:28 +00:00
const p = parse_protos ( protos ) ;
setProtos ( p ) ;
process_ab ( testfile , p ) ;
toast . dismiss ( id ) ;
setLoading ( false ) ;
2023-05-17 01:56:27 +00:00
} ) ( ) ;
2023-05-19 20:17:28 +00:00
return ( ) = > { ignore = true ; toast . dismiss ( id ) ; }
2023-05-17 01:56:27 +00:00
} , [ ] ) ;
useEffect ( ( ) = > { if ( file . tbl [ 0 ] ) doitRow ( file . tbl [ 0 ] , 0 , true ) ; } , [ file ] ) ;
const onChange : ChangeEventHandler < HTMLInputElement > = async ( e : ChangeEvent < HTMLInputElement > ) = > {
2023-05-19 20:17:28 +00:00
setLoading ( true ) ;
if ( ! e . target . files ) { toast . error ( "must select a file!" ) ; throw new Error ( "No file selected!" ) ; setLoading ( false ) ; }
try {
const id = toast . loading ( ` Processing ${ e . target . files [ 0 ] . name } ` ) ;
await new Promise ( ( res ) = > setTimeout ( res , 100 ) ) ; // "sleep" to give toast a chance to shine
process_ab ( await e . target . files ? . [ 0 ] . arrayBuffer ( ) ) ;
toast . dismiss ( id ) ;
} catch ( e ) {
2023-05-17 01:56:27 +00:00
if ( ( e as any ) . message ? . includes ( "Failed to read archive" ) ) toast . error ( "Please select an iWork file" , { position : toast.POSITION.TOP_CENTER } ) ;
else toast . error ( e && ( e as any ) . message || e , { position : toast.POSITION.TOP_CENTER } ) ;
2023-05-19 20:17:28 +00:00
console . error ( e ) ;
} finally {
e . target . value = "" ;
setLoading ( false ) ;
2023-05-17 01:56:27 +00:00
}
}
/* Menu machinations */
interface MenuProps {
type : string ;
field? : string ;
id : string ;
data : any ;
}
function displayMenu ( event : ReactMouseEvent , props? : MenuProps ) {
setMenuField ( props ? . field || "" ) ;
setMenuType ( props ? . type || "" ) ;
if ( props ? . id ) setMenuId ( String ( props . id ) ) ;
show ( { event , props } ) ;
}
function onClickId ( ) { gotoRef ( menuId ) ; }
function onClickCopyByteArray ( { props } : { props : MenuProps } ) {
var _data = props ? . data ? . PB_RAW ? . data || ( + props . id == + id ) && file . space [ + id ] [ 0 ] . data ;
if ( ! _data ) throw new Error ( "Could not find raw data" ) ;
navigator . clipboard . writeText ( "[" + [ . . . _data ] . map ( x = > "0x" + x . toString ( 16 ) . toUpperCase ( ) . padStart ( 2 , "0" ) ) . join ( ", " ) + "]" ) ;
}
function onClickCopyJSON ( { props } : { props : MenuProps } ) {
if ( ! props ? . data ) throw new Error ( "Could not find raw data" ) ;
navigator . clipboard . writeText ( JSON . stringify ( props . data , ( _ , v ) = > typeof v == "bigint" ? v . toString ( ) : v instanceof Uint8Array ? [ . . . v ] : v ) ) ;
}
function showProtoDef ( { props } : { props : MenuProps } ) { selectProto ( props . type ) ; }
type NodeRendererProps = {
depth : number ;
name : string ;
data : any ;
isNonenumerable : boolean ;
expanded : boolean ;
}
const nodeRenderer = ( { depth , name , data , isNonenumerable } : NodeRendererProps ) = > {
if ( depth === 0 ) return ( < b > Message { id } < a onClick = { ( ) = > { selectProto ( sel ) ; } } onContextMenu = { e = > { displayMenu ( e , { type : sel , id , data } ) } } > < b > [ { sel } ] < / b > < / a > < / b > ) ;
if ( typeof data == "bigint" && name . includes ( "identifier" ) ) return ( < >
< ObjectName name = { name } / > : < ObjectValue object = { data } / > - & gt ; < a onClick = { ( ) = > { gotoRef ( String ( data ) ) } } > < b > { String ( data ) } < / b > < / a >
< / > ) ;
if ( data . PB_TYPE ) {
const frag = ( < a onClick = { ( ) = > { selectProto ( data . PB_TYPE ) ; } } >
{ data . PB_ENUM && < ObjectValue object = { data . value } / > }
< b > { data . PB_ENUM && < > = { data . PB_ENUM } < / > } [ { data . PB_TYPE } ] < / b >
< / a > ) ;
if ( data . PB_TYPE == ".TSP.Reference" ) {
let id = String ( data ? . identifier ) ;
return ( < span onContextMenu = { ( e ) = > { displayMenu ( e , { type : data . PB_TYPE , id , data , field : data.PB_FIELD } ) } } >
< ObjectName name = { name } / > : < b > { frag } - & gt ; < a onClick = { ( ) = > { gotoRef ( id ) } } > < b > { id } < / b > < / a > < / b >
< / span > ) ;
}
return ( < span onContextMenu = { ( e ) = > { displayMenu ( e , { type : data . PB_TYPE , id , data , field : data.PB_FIELD } ) } } >
< ObjectName name = { name } / > : { frag }
< / span > ) ;
}
return ( < ObjectLabel name = { name } data = { data } isNonenumerable = { isNonenumerable } / > ) ;
} ;
const metaRenderer = ( { depth , name , data , isNonenumerable } : NodeRendererProps ) = > {
if ( depth === 0 ) return ( < b > Metadata < / b > ) ;
if ( typeof data == "bigint" && name . includes ( "identifier" ) ) return ( < >
< ObjectName name = { name } / > : < ObjectValue object = { data } / > - & gt ; < a onClick = { ( ) = > { gotoRef ( String ( data ) ) } } > < b > { String ( data ) } < / b > < / a >
< / > ) ;
if ( data . PB_TYPE ) {
const frag = ( < a onClick = { ( ) = > { selectProto ( data . PB_TYPE ) ; } } >
{ data . PB_ENUM && < ObjectValue object = { data . value } / > }
< b > { data . PB_ENUM && < > = { data . PB_ENUM } < / > } [ { data . PB_TYPE } ] < / b >
< / a > ) ;
if ( data . PB_TYPE == ".TSP.Reference" ) {
let id = String ( data ? . identifier ) ;
return ( < span onContextMenu = { ( e ) = > { displayMenu ( e , { type : data . PB_TYPE , id , data , field : data.PB_FIELD } ) } } >
< ObjectName name = { name } / > : < b > { frag } - & gt ; < a onClick = { ( ) = > { gotoRef ( id ) } } > < b > { id } < / b > < / a > < / b >
< / span > ) ;
}
return ( < span onContextMenu = { ( e ) = > { displayMenu ( e , { type : data . PB_TYPE , id , data , field : data.PB_FIELD } ) } } >
< ObjectName name = { name } / > : { frag }
< / span > ) ;
}
return ( < ObjectLabel name = { name } data = { data } isNonenumerable = { isNonenumerable } / > ) ;
} ;
2023-05-19 20:17:28 +00:00
const depsRenderer = ( { depth , data , isNonenumerable } : NodeRendererProps ) = > {
if ( depth === 0 ) return ( < b > Dependents < / b > ) ;
return ( < a onClick = { ( ) = > gotoRef ( data . id ) } > < ObjectLabel name = { data . id } data = { data . message } isNonenumerable = { isNonenumerable } / > < / a > ) ;
} ;
2023-05-17 01:56:27 +00:00
return ( < >
{ /* header */ }
2023-05-19 20:17:28 +00:00
< div id = "header" className = "header" style = { { display : "grid" , gridTemplateColumns : "repeat(3, 1fr)" , alignItems : "center" } } >
< div style = { { display : "flex" , justifyContent : "flex-start" } } > < input type = "file" id = "file" onChange = { onChange } disabled = { loading } accept = ".numbers,.key,.pages" / > < / div >
< div > < b > < a href = "https://sheetjs.com" > SheetJS < / a > IWA Inspector < / b > < / div >
< div style = { { display : "flex" , justifyContent : "flex-end" } } > < input type = "text" value = { search } placeholder = "search for text" onChange = { ( e ) = > setSearch ( e . target . value ) } / > < / div >
< / div >
2023-05-17 01:56:27 +00:00
< div className = "page" >
< PanelGroup direction = 'vertical' >
< Panel defaultSize = { 25 } > < div className = "overflow" ref = { tblRef } >
{ /* message table */ }
2023-05-19 20:17:28 +00:00
< TableView data = { file . tbl } cols = { [ "id" , "type" , "message" , "path" ] } filter = { filter } id = { id } rowclick = { rowclick } / >
2023-05-17 01:56:27 +00:00
< / div > < / Panel >
< PanelResizeHandle style = { { height : "3px" , backgroundColor : "#EEEEEE" } } / >
2023-05-19 20:17:28 +00:00
< Panel > < div className = "overflow" style = { { display : "grid" , gridTemplateColumns : "1fr" , gridTemplateRows : "max-content 1fr" } } >
2023-05-17 01:56:27 +00:00
{ /* selected message bar */ }
< div style = { { boxShadow : "0 2px 2px -1px rgba(0, 0, 0, 0.4)" , height : "24px" } } > { sel ? ( < >
< b > Selected message { ( stack . length > 4 ? [ . . . stack . slice ( 0 , 2 ) , "..." , . . . stack . slice ( - 1 ) ] : stack ) . map ( i = > i + " > " ) . join ( "" ) } { id } ( { sel } ) { file . space [ + id ] ? . [ 0 ] ? . data ? . length || 0 } bytes { stack . length && < a onClick = { pop } > Return to { stack [ stack . length - 1 ] } < / a > || "" } < / b >
< / > ) : "Select a message to see the contents" } < / div >
2023-05-19 20:17:28 +00:00
< div className = 'overflow' > < PanelGroup direction = 'horizontal' >
2023-05-17 01:56:27 +00:00
< Panel > < div className = 'overflow' >
{ /* proto definition */ }
< pre style = { { textAlign : "left" } } >
{ dirty && ( < > < a onClick = { ( ) = > { selectProto ( sel ) ; } } > Return to { sel } < / a > < br / > < br / > < / > ) }
{ __html . split ( "\n" ) . map ( ( r , idx ) = > ( < > { ! r . match ( /(optional|repeated|required) \./ ) ? (
< span key = { idx } > { r } < / span >
) : (
< a key = { idx } onClick = { ( ) = > { selectProto ( r . trim ( ) . split ( " " ) [ 1 ] ) ; } } > { r } < / a >
) } < br / > < / > ) ) }
< / pre >
< / div > < / Panel >
< PanelResizeHandle style = { { width : "2px" , backgroundColor : "#EEEEEE" } } / >
2023-05-19 20:17:28 +00:00
< Panel > < div className = "overflow" style = { { textAlign : "left" , marginTop : "5px" , paddingLeft : "5px" } } >
2023-05-17 01:56:27 +00:00
{ /* inspector */ }
< b > Message < / b >
< ObjectInspector data = { obj } expandLevel = { 1 } nodeRenderer = { nodeRenderer } / >
< b > Meta < / b >
< ObjectInspector data = { meta } expandLevel = { 1 } nodeRenderer = { metaRenderer } / >
2023-05-19 20:17:28 +00:00
{ deps . length && < ObjectInspector data = { deps } expandLevel = { 1 } nodeRenderer = { depsRenderer } / > || void 0 }
2023-05-17 01:56:27 +00:00
< div style = { { height : "13px" } } > < / div >
< / div > < / Panel >
< / PanelGroup > < / div >
< / div > < / Panel >
< / PanelGroup >
{ /* Menu */ }
< ContextMenu ID = { MENU_ID } menuField = { menuField } menuType = { menuType } menuId = { menuId } onClickId = { onClickId } onClickCopyByteArray = { onClickCopyByteArray } onClickCopyJSON = { onClickCopyJSON } showProtoDef = { showProtoDef } / >
{ /* Toast */ }
< ToastContainer / >
< / div >
< / > ) ;
}
2023-05-19 20:17:28 +00:00
export default App ;