From b6bac19c20e8ae61afdc058355728d0abd066656 Mon Sep 17 00:00:00 2001 From: Wenli Cai Date: Tue, 17 Jun 2025 16:33:51 -0400 Subject: [PATCH 1/6] Include new fields in Node class from json and display them --- .../workflow1/traceviewer/model/Node.kt | 21 ++++++++++++++--- .../traceviewer/ui/WorkflowInfoPanel.kt | 23 +++++++++++++++---- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/model/Node.kt b/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/model/Node.kt index 412eb5a7c5..5e5471e0f1 100644 --- a/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/model/Node.kt +++ b/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/model/Node.kt @@ -8,7 +8,22 @@ package com.squareup.workflow1.traceviewer.model * TBD what more metadata should be involved with each node, e.g. (props, states, # of render passes) */ public class Node( - val id: String, val name: String, - val children: List -) + val parent: String, + val props: Any? = null, + val state: Any? = null, + val renderings: Any? = null, + val children: List, + val id: String +) { + override fun toString(): String { + return "Node(name='$name', parent='$parent', children=${children.size})" + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Node) return false + return this.id == other.id + } + override fun hashCode(): Int { return id.hashCode() } +} diff --git a/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/ui/WorkflowInfoPanel.kt b/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/ui/WorkflowInfoPanel.kt index f7c959abe2..82317b00e2 100644 --- a/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/ui/WorkflowInfoPanel.kt +++ b/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/ui/WorkflowInfoPanel.kt @@ -22,6 +22,8 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.squareup.workflow1.traceviewer.model.Node @@ -83,11 +85,22 @@ private fun NodePanelDetails( return@Column } - Text("only visible with a node selected") - Text( - text = "This is a node panel for ${node.name}", - fontSize = 20.sp, - modifier = Modifier.padding(8.dp) + val textModifier = Modifier.padding(8.dp) + val textStyle = TextStyle(fontSize = 16.sp, textAlign = TextAlign.Center) + val fields = mapOf( + "Name" to node.name, + "ID" to node.id, + "Props" to node.props.toString(), + "State" to node.state.toString(), + "Renderings" to node.renderings.toString() ) + + fields.forEach { (label, value) -> + Text( + text = "$label: $value", + modifier = textModifier, + style = textStyle + ) + } } } From 3cfb832dc98962cd992ec2257c730a3906180a97 Mon Sep 17 00:00:00 2001 From: Wenli Cai Date: Mon, 7 Jul 2025 13:19:44 -0400 Subject: [PATCH 2/6] Change JSON parsing Since ActionLogger will only be responsible for logging, the parsing part will be done here, so it involves slightly more complicated logic by building the tree structure from the ground up --- .../traceviewer/ui/FrameSelectTab.kt | 2 +- .../workflow1/traceviewer/ui/WorkflowTree.kt | 2 - .../workflow1/traceviewer/util/JsonParser.kt | 57 ++++++++++++++++--- 3 files changed, 50 insertions(+), 11 deletions(-) diff --git a/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/ui/FrameSelectTab.kt b/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/ui/FrameSelectTab.kt index 0dccfa4e8b..fbc8a08a4e 100644 --- a/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/ui/FrameSelectTab.kt +++ b/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/ui/FrameSelectTab.kt @@ -38,7 +38,7 @@ public fun FrameSelectTab( ) { items(frames.size) { index -> Text( - text = "State ${index + 1}", + text = "Frame ${index + 1}", color = if (index == currentIndex) Color.Black else Color.LightGray, modifier = Modifier .clip(RoundedCornerShape(16.dp)) diff --git a/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/ui/WorkflowTree.kt b/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/ui/WorkflowTree.kt index 59277e82f0..e2b12817f4 100644 --- a/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/ui/WorkflowTree.kt +++ b/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/ui/WorkflowTree.kt @@ -64,8 +64,6 @@ public fun RenderDiagram( if (!isLoading) { DrawTree(frames[frameInd], onNodeSelect) } - - // TODO: catch errors and display UI here } /** diff --git a/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/util/JsonParser.kt b/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/util/JsonParser.kt index 6c1b7e2575..c4ae28c8d0 100644 --- a/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/util/JsonParser.kt +++ b/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/util/JsonParser.kt @@ -9,7 +9,8 @@ import io.github.vinceglb.filekit.PlatformFile import io.github.vinceglb.filekit.readString /** - * Parses a given file's JSON String into [Node] with Moshi adapters. + * Parses a given file's JSON String into a list of [Node]s with Moshi adapters. Each of these nodes + * count as the root of a tree which forms a Frame. * * @return A [ParseResult] representing result of parsing, either an error related to the * format of the JSON, or a success and a parsed trace. @@ -19,18 +20,58 @@ public suspend fun parseTrace( ): ParseResult { return try { val jsonString = file.readString() - val moshi = Moshi.Builder() - .add(KotlinJsonAdapterFactory()) - .build() - val workflowList = Types.newParameterizedType(List::class.java, Node::class.java) - val workflowAdapter: JsonAdapter> = moshi.adapter(workflowList) - val trace = workflowAdapter.fromJson(jsonString) - ParseResult.Success(trace) + val workflowAdapter = createMoshiAdapter() + val unParsedTrace = workflowAdapter.fromJson(jsonString) + + val parsedTrace = mutableListOf() + unParsedTrace?.forEach { renderPass -> + val parsed = getFrameFromRenderPass(renderPass) + parsedTrace.add(parsed) + } + + ParseResult.Success(parsedTrace) } catch (e: Exception) { ParseResult.Failure(e) } } +/** + * Creates a Moshi adapter for parsing the JSON trace file. + */ +private fun createMoshiAdapter(): JsonAdapter>> { + val moshi = Moshi.Builder() + .add(KotlinJsonAdapterFactory()) + .build() + val workflowList = Types.newParameterizedType(List::class.java, + Types.newParameterizedType(List::class.java, Node::class.java)) + val adapter: JsonAdapter>> = moshi.adapter(workflowList) + return adapter +} + +/** + * We take an unparsed render pass and build up a tree structure from it to form a Frame. + */ +private fun getFrameFromRenderPass(renderPass: List): Node { + val childrenByParent = renderPass.groupBy { it.parent } + val root = childrenByParent["root"]?.single() + return buildTree(root!!, childrenByParent) +} + +/** + * Recursively builds a tree using each node's children. + */ +private fun buildTree(node: Node, childrenByParent: Map>): Node { + val children = (childrenByParent[node.name] ?: emptyList()) + return Node( + name = node.name, + parent = node.parent, + props = node.props, + state = node.state, + children = children.map { buildTree(it, childrenByParent) }, + id = node.id + ) +} + sealed interface ParseResult { class Success(val trace: List?) : ParseResult class Failure(val error: Throwable) : ParseResult From 7164671d78688ccb364ae698d361b85030eb55db Mon Sep 17 00:00:00 2001 From: Wenli Cai Date: Tue, 8 Jul 2025 10:42:15 -0400 Subject: [PATCH 3/6] Resolve ktlint violations --- .../kotlin/com/squareup/workflow1/traceviewer/model/Node.kt | 4 +++- .../com/squareup/workflow1/traceviewer/util/JsonParser.kt | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/model/Node.kt b/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/model/Node.kt index 5e5471e0f1..8d06037927 100644 --- a/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/model/Node.kt +++ b/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/model/Node.kt @@ -25,5 +25,7 @@ public class Node( if (other !is Node) return false return this.id == other.id } - override fun hashCode(): Int { return id.hashCode() } + override fun hashCode(): Int { + return id.hashCode() + } } diff --git a/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/util/JsonParser.kt b/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/util/JsonParser.kt index c4ae28c8d0..bdfe7564bd 100644 --- a/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/util/JsonParser.kt +++ b/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/util/JsonParser.kt @@ -42,8 +42,10 @@ private fun createMoshiAdapter(): JsonAdapter>> { val moshi = Moshi.Builder() .add(KotlinJsonAdapterFactory()) .build() - val workflowList = Types.newParameterizedType(List::class.java, - Types.newParameterizedType(List::class.java, Node::class.java)) + val workflowList = Types.newParameterizedType( + List::class.java, + Types.newParameterizedType(List::class.java, Node::class.java) + ) val adapter: JsonAdapter>> = moshi.adapter(workflowList) return adapter } From 6718c232d39e91fc57a602d01869fbe5478de05d Mon Sep 17 00:00:00 2001 From: Wenli Cai Date: Wed, 9 Jul 2025 15:47:13 -0400 Subject: [PATCH 4/6] Use workflow node sessionId to filter through parent/child structure when building tree --- .../workflow1/traceviewer/model/Node.kt | 3 ++- .../workflow1/traceviewer/util/JsonParser.kt | 17 +++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/model/Node.kt b/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/model/Node.kt index 8d06037927..2e5b7a7fcb 100644 --- a/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/model/Node.kt +++ b/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/model/Node.kt @@ -9,12 +9,13 @@ package com.squareup.workflow1.traceviewer.model */ public class Node( val name: String, + val id: String, val parent: String, + val parentId: String, val props: Any? = null, val state: Any? = null, val renderings: Any? = null, val children: List, - val id: String ) { override fun toString(): String { return "Node(name='$name', parent='$parent', children=${children.size})" diff --git a/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/util/JsonParser.kt b/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/util/JsonParser.kt index bdfe7564bd..841daac1c0 100644 --- a/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/util/JsonParser.kt +++ b/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/util/JsonParser.kt @@ -8,6 +8,12 @@ import com.squareup.workflow1.traceviewer.model.Node import io.github.vinceglb.filekit.PlatformFile import io.github.vinceglb.filekit.readString +/* + The root workflow Node uses an ID of 0, and since we are filtering childrenByParent by the + parentId, the root node has a parent of -1 ID. This is reflected seen inside android-register + */ +const val ROOT_ID: String = "-1" + /** * Parses a given file's JSON String into a list of [Node]s with Moshi adapters. Each of these nodes * count as the root of a tree which forms a Frame. @@ -52,10 +58,12 @@ private fun createMoshiAdapter(): JsonAdapter>> { /** * We take an unparsed render pass and build up a tree structure from it to form a Frame. + * + * @return Node the root node of the tree for that specific frame. */ private fun getFrameFromRenderPass(renderPass: List): Node { - val childrenByParent = renderPass.groupBy { it.parent } - val root = childrenByParent["root"]?.single() + val childrenByParent: Map> = renderPass.groupBy { it.parentId } + val root = childrenByParent[ROOT_ID]?.single() return buildTree(root!!, childrenByParent) } @@ -63,14 +71,15 @@ private fun getFrameFromRenderPass(renderPass: List): Node { * Recursively builds a tree using each node's children. */ private fun buildTree(node: Node, childrenByParent: Map>): Node { - val children = (childrenByParent[node.name] ?: emptyList()) + val children = (childrenByParent[node.id] ?: emptyList()) return Node( name = node.name, + id = node.id, parent = node.parent, + parentId = node.parentId, props = node.props, state = node.state, children = children.map { buildTree(it, childrenByParent) }, - id = node.id ) } From 54bb33b5f9835171111bd13ab1f7ea3b778295c1 Mon Sep 17 00:00:00 2001 From: Wenli Cai Date: Wed, 9 Jul 2025 15:48:54 -0400 Subject: [PATCH 5/6] Fix PR comments --- .../squareup/workflow1/traceviewer/util/JsonParser.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/util/JsonParser.kt b/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/util/JsonParser.kt index 841daac1c0..9c2aea472c 100644 --- a/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/util/JsonParser.kt +++ b/workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/util/JsonParser.kt @@ -27,15 +27,15 @@ public suspend fun parseTrace( return try { val jsonString = file.readString() val workflowAdapter = createMoshiAdapter() - val unParsedTrace = workflowAdapter.fromJson(jsonString) + val parsedRenderPasses = workflowAdapter.fromJson(jsonString) - val parsedTrace = mutableListOf() - unParsedTrace?.forEach { renderPass -> + val parsedFrames = mutableListOf() + parsedRenderPasses?.forEach { renderPass -> val parsed = getFrameFromRenderPass(renderPass) - parsedTrace.add(parsed) + parsedFrames.add(parsed) } - ParseResult.Success(parsedTrace) + ParseResult.Success(parsedFrames) } catch (e: Exception) { ParseResult.Failure(e) } From 2f4f69a44720bfe2a6757ec51aa9a23b18909877 Mon Sep 17 00:00:00 2001 From: wenli-cai <213805610+wenli-cai@users.noreply.github.com> Date: Wed, 9 Jul 2025 19:53:38 +0000 Subject: [PATCH 6/6] Apply changes from apiDump Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- workflow-trace-viewer/api/workflow-trace-viewer.api | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/workflow-trace-viewer/api/workflow-trace-viewer.api b/workflow-trace-viewer/api/workflow-trace-viewer.api index c06abc9a6b..ee30c6ebaa 100644 --- a/workflow-trace-viewer/api/workflow-trace-viewer.api +++ b/workflow-trace-viewer/api/workflow-trace-viewer.api @@ -16,10 +16,19 @@ public final class com/squareup/workflow1/traceviewer/MainKt { public final class com/squareup/workflow1/traceviewer/model/Node { public static final field $stable I - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/util/List;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun equals (Ljava/lang/Object;)Z public final fun getChildren ()Ljava/util/List; public final fun getId ()Ljava/lang/String; public final fun getName ()Ljava/lang/String; + public final fun getParent ()Ljava/lang/String; + public final fun getParentId ()Ljava/lang/String; + public final fun getProps ()Ljava/lang/Object; + public final fun getRenderings ()Ljava/lang/Object; + public final fun getState ()Ljava/lang/Object; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; } public final class com/squareup/workflow1/traceviewer/ui/FrameSelectTabKt { @@ -42,6 +51,7 @@ public final class com/squareup/workflow1/traceviewer/util/ComposableSingletons$ } public final class com/squareup/workflow1/traceviewer/util/JsonParserKt { + public static final field ROOT_ID Ljava/lang/String; public static final fun parseTrace (Lio/github/vinceglb/filekit/PlatformFile;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; }