diff --git a/desktop/plugins/public/ui-debugger/components/PerfStats.tsx b/desktop/plugins/public/ui-debugger/components/PerfStats.tsx new file mode 100644 index 000000000..1064764c6 --- /dev/null +++ b/desktop/plugins/public/ui-debugger/components/PerfStats.tsx @@ -0,0 +1,73 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {PerfStatsEvent} from '../types'; +import React from 'react'; +import {DataSource, DataTable, DataTableColumn} from 'flipper-plugin'; + +export function PerfStats(props: {events: DataSource}) { + return ( + dataSource={props.events} columns={columns} /> + ); +} + +function formatDiff(start: number, end: number): string { + const ms = end - start; + return `${ms.toFixed(0)}ms`; +} + +const columns: DataTableColumn[] = [ + { + key: 'txId', + title: 'TXID', + }, + { + key: 'observerType', + title: 'Type', + }, + { + key: 'nodesCount', + title: 'Total nodes', + }, + { + key: 'start', + title: 'Start', + onRender: (row: PerfStatsEvent) => { + return new Date(row.start).toISOString(); + }, + }, + { + key: 'traversalComplete', + title: 'Traversal time (Main thread)', + onRender: (row: PerfStatsEvent) => { + return formatDiff(row.start, row.traversalComplete); + }, + }, + { + key: 'queuingComplete', + title: 'Queuing time', + onRender: (row: PerfStatsEvent) => { + return formatDiff(row.traversalComplete, row.queuingComplete); + }, + }, + { + key: 'serializationComplete', + title: 'Serialization time', + onRender: (row: PerfStatsEvent) => { + return formatDiff(row.queuingComplete, row.serializationComplete); + }, + }, + { + key: 'socketComplete', + title: 'Socket send time', + onRender: (row: PerfStatsEvent) => { + return formatDiff(row.serializationComplete, row.socketComplete); + }, + }, +]; diff --git a/desktop/plugins/public/ui-debugger/components/Tree.tsx b/desktop/plugins/public/ui-debugger/components/Tree.tsx new file mode 100644 index 000000000..04a801eee --- /dev/null +++ b/desktop/plugins/public/ui-debugger/components/Tree.tsx @@ -0,0 +1,62 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {Id, UINode} from '../types'; +import {DataNode} from 'antd/es/tree'; +import {Tree as AntTree, Typography} from 'antd'; +import {DownOutlined} from '@ant-design/icons'; +import React from 'react'; + +export function Tree(props: { + rootId: Id; + nodes: Map; + setSelectedNode: (id: Id) => void; +}) { + const [antTree, inactive] = nodesToAntTree(props.rootId, props.nodes); + + return ( + { + props.setSelectedNode(selected[0] as Id); + }} + defaultExpandAll + expandedKeys={[...props.nodes.keys()].filter( + (key) => !inactive.includes(key), + )} + switcherIcon={} + treeData={[antTree]} + /> + ); +} + +function nodesToAntTree(root: Id, nodes: Map): [DataNode, Id[]] { + const inactive: Id[] = []; + + function uiNodeToAntNode(id: Id): DataNode { + const node = nodes.get(id); + + if (node?.activeChild) { + for (const child of node.children) { + if (child !== node?.activeChild) { + inactive.push(child); + } + } + } + + return { + key: id, + title: node?.name, + children: node?.children.map((id) => uiNodeToAntNode(id)), + }; + } + + return [uiNodeToAntNode(root), inactive]; +} diff --git a/desktop/plugins/public/ui-debugger/components/main.tsx b/desktop/plugins/public/ui-debugger/components/main.tsx index f00c6e64c..4ad28e148 100644 --- a/desktop/plugins/public/ui-debugger/components/main.tsx +++ b/desktop/plugins/public/ui-debugger/components/main.tsx @@ -8,111 +8,28 @@ */ import React, {useState} from 'react'; -import {PerfStatsEvent, plugin} from '../index'; +import {plugin} from '../index'; import { DataInspector, - DataTable, - DataTableColumn, DetailSidebar, Layout, usePlugin, useValue, } from 'flipper-plugin'; -import {Tree, Typography} from 'antd'; -import type {DataNode} from 'antd/es/tree'; -import {DownOutlined} from '@ant-design/icons'; +import {Typography} from 'antd'; + import {useHotkeys} from 'react-hotkeys-hook'; import {Id, UINode} from '../types'; - -function nodesToAntTree(root: Id, nodes: Map): [DataNode, Id[]] { - const inactive: Id[] = []; - - function uiNodeToAntNode(id: Id): DataNode { - const node = nodes.get(id); - - if (node?.activeChild) { - for (const child of node.children) { - if (child !== node?.activeChild) { - inactive.push(child); - } - } - } - - return { - key: id, - title: node?.name, - children: node?.children.map((id) => uiNodeToAntNode(id)), - }; - } - - return [uiNodeToAntNode(root), inactive]; -} - -function formatDiff(start: number, end: number): string { - const ms = end - start; - return `${ms.toFixed(0)}ms`; -} - -export const columns: DataTableColumn[] = [ - { - key: 'txId', - title: 'TXID', - }, - { - key: 'observerType', - title: 'Type', - }, - { - key: 'nodesCount', - title: 'Total nodes', - }, - { - key: 'start', - title: 'Start', - onRender: (row: PerfStatsEvent) => { - console.log(row.start); - return new Date(row.start).toISOString(); - }, - }, - { - key: 'traversalComplete', - title: 'Traversal time (Main thread)', - onRender: (row: PerfStatsEvent) => { - return formatDiff(row.start, row.traversalComplete); - }, - }, - { - key: 'queuingComplete', - title: 'Queuing time', - onRender: (row: PerfStatsEvent) => { - return formatDiff(row.traversalComplete, row.queuingComplete); - }, - }, - { - key: 'serializationComplete', - title: 'Serialization time', - onRender: (row: PerfStatsEvent) => { - return formatDiff(row.queuingComplete, row.serializationComplete); - }, - }, - { - key: 'socketComplete', - title: 'Socket send time', - onRender: (row: PerfStatsEvent) => { - return formatDiff(row.serializationComplete, row.socketComplete); - }, - }, -]; +import {PerfStats} from './PerfStats'; +import {Tree} from './Tree'; export function Component() { const instance = usePlugin(plugin); const rootId = useValue(instance.rootId); - const nodes = useValue(instance.nodes); + const nodes: Map = useValue(instance.nodes); const [showPerfStats, setShowPerfStats] = useState(false); - const [selectedNode, setSelectedNode] = useState( - undefined, - ); + const [selectedNode, setSelectedNode] = useState(undefined); useHotkeys('ctrl+i', () => setShowPerfStats((show) => !show)); @@ -125,38 +42,23 @@ export function Component() { Attributes Inspector - + ); } - if (showPerfStats) - return ( - - dataSource={instance.perfEvents} - columns={columns} - /> - ); + if (showPerfStats) return ; if (rootId) { - const [antTree, inactive] = nodesToAntTree(rootId, nodes); return ( <> { - setSelectedNode(selected[0] as string); - }} - defaultExpandAll - expandedKeys={[...nodes.keys()].filter( - (key) => !inactive.includes(key), - )} - switcherIcon={} - treeData={[antTree]} + setSelectedNode={setSelectedNode} + nodes={nodes} + rootId={rootId} /> {selectedNode && renderAttributesInspector(nodes.get(selectedNode))} diff --git a/desktop/plugins/public/ui-debugger/index.tsx b/desktop/plugins/public/ui-debugger/index.tsx index 896b27d16..382e4b237 100644 --- a/desktop/plugins/public/ui-debugger/index.tsx +++ b/desktop/plugins/public/ui-debugger/index.tsx @@ -8,25 +8,7 @@ */ import {PluginClient, createState, createDataSource} from 'flipper-plugin'; -import {Id, UINode} from './types'; - -export type PerfStatsEvent = { - txId: number; - observerType: string; - start: number; - traversalComplete: number; - serializationComplete: number; - queuingComplete: number; - socketComplete: number; - nodesCount: number; -}; - -type Events = { - init: {rootId: string}; - nativeScan: {txId: number; nodes: UINode[]}; - subtreeUpdate: {txId: number; nodes: UINode[]}; - perfStats: PerfStatsEvent; -}; +import {Events, Id, PerfStatsEvent, UINode} from './types'; export function plugin(client: PluginClient) { const rootId = createState(undefined); diff --git a/desktop/plugins/public/ui-debugger/types.tsx b/desktop/plugins/public/ui-debugger/types.tsx index e079f7280..b64d463e2 100644 --- a/desktop/plugins/public/ui-debugger/types.tsx +++ b/desktop/plugins/public/ui-debugger/types.tsx @@ -7,6 +7,45 @@ * @format */ +export type Events = { + init: {rootId: string}; + nativeScan: {txId: number; nodes: UINode[]}; + subtreeUpdate: {txId: number; nodes: UINode[]}; + perfStats: PerfStatsEvent; +}; + +export type PerfStatsEvent = { + txId: number; + observerType: string; + start: number; + traversalComplete: number; + serializationComplete: number; + queuingComplete: number; + socketComplete: number; + nodesCount: number; +}; + +export type UINode = { + id: Id; + name: string; + attributes: Record; + children: Id[]; + bounds?: Bounds; + tags: Tag[]; + activeChild?: Id; +}; + +export type Bounds = { + x: number; + y: number; + width: number; + height: number; +}; + +export type Id = string; + +export type Tag = 'Native' | 'Declarative' | 'Android' | 'Litho '; + export type Inspectable = | InspectableObject | InspectableText @@ -35,12 +74,3 @@ export type InspectableObject = { type: 'object'; fields: Record; }; - -export type Id = string; -export type UINode = { - id: Id; - name: string; - attributes: Record; - children: Id[]; - activeChild?: Id; -};