Skip to content

Commit a173a9b

Browse files
authored
Merge pull request #1343 from square/wenli/workflow-viewer-open-nodes
Improve workflow visualizer functionality
2 parents f0ca5cb + 1c09fd5 commit a173a9b

File tree

16 files changed

+2541
-197
lines changed

16 files changed

+2541
-197
lines changed

workflow-trace-viewer/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@ It can be run via Gradle using:
1010
./gradlew :workflow-trace-viewer:run
1111
```
1212

13+
### Terminology
14+
15+
**Trace**: A trace is a file — made up of frames — that contains the execution history of a Workflow. It includes information about render passes, how states have changed within workflows, and the specific props being passed through. The data collected to generate these should be in chronological order, and allows developers to step through the process easily.
16+
17+
**Frame**: Essentially a "snapshot" of the current "state" of the whole Workflow tree. It contains relevant information about the changes in workflow states and how props are passed throughout.
18+
19+
- Note that "snapshot" and "state" are different from `snapshotState` and `State`, which are idiomatic to the Workflow library.
20+
1321
### External Libraries
1422

1523
[FileKit](https://github.com/vinceglb/FileKit) is an external library made to apply file operations on Kotlin and KMP projects. It's purpose in this app is to allow developers to upload their own json trace files. The motivation for its use is to quickly implement a file picker.

workflow-trace-viewer/api/workflow-trace-viewer.api

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,47 +9,62 @@ public final class com/squareup/workflow1/traceviewer/ComposableSingletons$MainK
99
public final fun getLambda-1$wf1_workflow_trace_viewer ()Lkotlin/jvm/functions/Function3;
1010
}
1111

12-
public final class com/squareup/workflow1/traceviewer/ComposableSingletons$UploadFileKt {
13-
public static final field INSTANCE Lcom/squareup/workflow1/traceviewer/ComposableSingletons$UploadFileKt;
12+
public final class com/squareup/workflow1/traceviewer/MainKt {
13+
public static final fun main ()V
14+
public static synthetic fun main ([Ljava/lang/String;)V
15+
}
16+
17+
public final class com/squareup/workflow1/traceviewer/model/Node {
18+
public static final field $stable I
19+
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;)V
20+
public final fun getChildren ()Ljava/util/List;
21+
public final fun getId ()Ljava/lang/String;
22+
public final fun getName ()Ljava/lang/String;
23+
}
24+
25+
public final class com/squareup/workflow1/traceviewer/ui/FrameSelectTabKt {
26+
public static final fun FrameSelectTab (Ljava/util/List;ILkotlin/jvm/functions/Function1;Landroidx/compose/ui/Modifier;Landroidx/compose/runtime/Composer;II)V
27+
}
28+
29+
public final class com/squareup/workflow1/traceviewer/ui/WorkflowInfoPanelKt {
30+
public static final fun RightInfoPanel (Lcom/squareup/workflow1/traceviewer/model/Node;Landroidx/compose/ui/Modifier;Landroidx/compose/runtime/Composer;II)V
31+
}
32+
33+
public final class com/squareup/workflow1/traceviewer/ui/WorkflowTreeKt {
34+
public static final fun RenderDiagram (Lio/github/vinceglb/filekit/PlatformFile;ILkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Modifier;Landroidx/compose/runtime/Composer;II)V
35+
}
36+
37+
public final class com/squareup/workflow1/traceviewer/util/ComposableSingletons$UploadFileKt {
38+
public static final field INSTANCE Lcom/squareup/workflow1/traceviewer/util/ComposableSingletons$UploadFileKt;
1439
public static field lambda-1 Lkotlin/jvm/functions/Function3;
1540
public fun <init> ()V
1641
public final fun getLambda-1$wf1_workflow_trace_viewer ()Lkotlin/jvm/functions/Function3;
1742
}
1843

19-
public final class com/squareup/workflow1/traceviewer/MainKt {
20-
public static final fun main ()V
21-
public static synthetic fun main ([Ljava/lang/String;)V
44+
public final class com/squareup/workflow1/traceviewer/util/JsonParserKt {
45+
public static final fun parseTrace (Lio/github/vinceglb/filekit/PlatformFile;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
2246
}
2347

24-
public final class com/squareup/workflow1/traceviewer/SandboxBackgroundKt {
25-
public static final fun SandboxBackground (Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V
48+
public abstract interface class com/squareup/workflow1/traceviewer/util/ParseResult {
2649
}
2750

28-
public final class com/squareup/workflow1/traceviewer/UploadFileKt {
29-
public static final fun UploadFile (Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Modifier;Landroidx/compose/runtime/Composer;II)V
51+
public final class com/squareup/workflow1/traceviewer/util/ParseResult$Failure : com/squareup/workflow1/traceviewer/util/ParseResult {
52+
public static final field $stable I
53+
public fun <init> (Ljava/lang/Throwable;)V
54+
public final fun getError ()Ljava/lang/Throwable;
3055
}
3156

32-
public final class com/squareup/workflow1/traceviewer/WorkflowJsonParserKt {
33-
public static final fun parseTrace (Ljava/lang/String;)Lcom/squareup/workflow1/traceviewer/WorkflowNode;
57+
public final class com/squareup/workflow1/traceviewer/util/ParseResult$Success : com/squareup/workflow1/traceviewer/util/ParseResult {
58+
public static final field $stable I
59+
public fun <init> (Ljava/util/List;)V
60+
public final fun getTrace ()Ljava/util/List;
3461
}
3562

36-
public final class com/squareup/workflow1/traceviewer/WorkflowNode {
37-
public static final field $stable I
38-
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;)V
39-
public final fun component1 ()Ljava/lang/String;
40-
public final fun component2 ()Ljava/lang/String;
41-
public final fun component3 ()Ljava/util/List;
42-
public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;)Lcom/squareup/workflow1/traceviewer/WorkflowNode;
43-
public static synthetic fun copy$default (Lcom/squareup/workflow1/traceviewer/WorkflowNode;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;ILjava/lang/Object;)Lcom/squareup/workflow1/traceviewer/WorkflowNode;
44-
public fun equals (Ljava/lang/Object;)Z
45-
public final fun getChildren ()Ljava/util/List;
46-
public final fun getId ()Ljava/lang/String;
47-
public final fun getName ()Ljava/lang/String;
48-
public fun hashCode ()I
49-
public fun toString ()Ljava/lang/String;
63+
public final class com/squareup/workflow1/traceviewer/util/SandboxBackgroundKt {
64+
public static final fun SandboxBackground (Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V
5065
}
5166

52-
public final class com/squareup/workflow1/traceviewer/WorkflowTreeKt {
53-
public static final fun DrawWorkflowTree (Lcom/squareup/workflow1/traceviewer/WorkflowNode;Landroidx/compose/ui/Modifier;Landroidx/compose/runtime/Composer;II)V
67+
public final class com/squareup/workflow1/traceviewer/util/UploadFileKt {
68+
public static final fun UploadFile (Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Modifier;Landroidx/compose/runtime/Composer;II)V
5469
}
5570

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
package com.squareup.workflow1.traceviewer
22

33
import androidx.compose.foundation.layout.Box
4-
import androidx.compose.material.Text
54
import androidx.compose.runtime.Composable
6-
import androidx.compose.runtime.LaunchedEffect
75
import androidx.compose.runtime.getValue
6+
import androidx.compose.runtime.mutableIntStateOf
87
import androidx.compose.runtime.mutableStateOf
98
import androidx.compose.runtime.remember
109
import androidx.compose.runtime.setValue
10+
import androidx.compose.ui.Alignment
1111
import androidx.compose.ui.Modifier
12+
import com.squareup.workflow1.traceviewer.model.Node
13+
import com.squareup.workflow1.traceviewer.ui.FrameSelectTab
14+
import com.squareup.workflow1.traceviewer.ui.RenderDiagram
15+
import com.squareup.workflow1.traceviewer.ui.RightInfoPanel
16+
import com.squareup.workflow1.traceviewer.util.SandboxBackground
17+
import com.squareup.workflow1.traceviewer.util.UploadFile
1218
import io.github.vinceglb.filekit.PlatformFile
13-
import io.github.vinceglb.filekit.readString
1419

1520
/**
1621
* Main composable that provides the different layers of UI.
@@ -19,28 +24,43 @@ import io.github.vinceglb.filekit.readString
1924
public fun App(
2025
modifier: Modifier = Modifier
2126
) {
22-
Box {
23-
var selectedFile by remember { mutableStateOf<PlatformFile?>(null) }
27+
var selectedTraceFile by remember { mutableStateOf<PlatformFile?>(null) }
28+
var selectedNode by remember { mutableStateOf<Node?>(null) }
29+
var workflowFrames by remember { mutableStateOf<List<Node>>(emptyList()) }
30+
var frameIndex by remember { mutableIntStateOf(0) }
2431

25-
if (selectedFile != null) {
26-
SandboxBackground { WorkflowContent(selectedFile!!) }
32+
Box(
33+
modifier = modifier
34+
) {
35+
// Main content
36+
if (selectedTraceFile != null) {
37+
SandboxBackground {
38+
RenderDiagram(
39+
traceFile = selectedTraceFile!!,
40+
frameInd = frameIndex,
41+
onFileParse = { workflowFrames = it },
42+
onNodeSelect = { selectedNode = it }
43+
)
44+
}
2745
}
2846

29-
UploadFile(onFileSelect = { selectedFile = it })
30-
}
31-
}
47+
FrameSelectTab(
48+
frames = workflowFrames,
49+
currentIndex = frameIndex,
50+
onIndexChange = { frameIndex = it },
51+
modifier = Modifier.align(Alignment.TopCenter)
52+
)
3253

33-
@Composable
34-
private fun WorkflowContent(file: PlatformFile) {
35-
var jsonString by remember { mutableStateOf<String?>(null) }
36-
LaunchedEffect(file) {
37-
jsonString = file.readString()
38-
}
39-
val root = jsonString?.let { parseTrace(it) }
54+
RightInfoPanel(selectedNode)
4055

41-
if (root != null) {
42-
DrawWorkflowTree(root)
43-
} else {
44-
Text("Empty data or failed to parse data") // TODO: proper handling of error
56+
// The states are reset when a new file is selected.
57+
UploadFile(
58+
onFileSelect = {
59+
selectedTraceFile = it
60+
selectedNode = null
61+
frameIndex = 0
62+
},
63+
modifier = Modifier.align(Alignment.BottomStart)
64+
)
4565
}
4666
}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package com.squareup.workflow1.traceviewer
22

3+
import androidx.compose.foundation.layout.fillMaxSize
4+
import androidx.compose.ui.Modifier
35
import androidx.compose.ui.window.singleWindowApplication
46

57
/**
68
* Main entry point for the desktop application, see [README.md] for more details.
79
*/
810
fun main() {
911
singleWindowApplication(title = "Workflow Trace Viewer") {
10-
App()
12+
App(Modifier.fillMaxSize())
1113
}
1214
}

workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/WorkflowJsonParser.kt

Lines changed: 0 additions & 29 deletions
This file was deleted.

workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/WorkflowTree.kt

Lines changed: 0 additions & 87 deletions
This file was deleted.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.squareup.workflow1.traceviewer.model
2+
3+
/**
4+
* Since the logic of Workflow is hierarchical (where each workflow may have parent workflows and/or
5+
* children workflows, a tree structure is most appropriate for representing the data rather than
6+
* using flat data structures like an array.
7+
*
8+
* TBD what more metadata should be involved with each node, e.g. (props, states, # of render passes)
9+
*/
10+
public class Node(
11+
val id: String,
12+
val name: String,
13+
val children: List<Node>
14+
)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.squareup.workflow1.traceviewer.ui
2+
3+
import androidx.compose.foundation.clickable
4+
import androidx.compose.foundation.layout.padding
5+
import androidx.compose.foundation.lazy.LazyRow
6+
import androidx.compose.foundation.lazy.rememberLazyListState
7+
import androidx.compose.foundation.shape.RoundedCornerShape
8+
import androidx.compose.material.Surface
9+
import androidx.compose.material.Text
10+
import androidx.compose.runtime.Composable
11+
import androidx.compose.ui.Modifier
12+
import androidx.compose.ui.draw.clip
13+
import androidx.compose.ui.graphics.Color
14+
import androidx.compose.ui.unit.dp
15+
import com.squareup.workflow1.traceviewer.model.Node
16+
17+
/**
18+
* A trace tab selector that allows devs to switch between different states within the provided trace.
19+
*/
20+
@Composable
21+
public fun FrameSelectTab(
22+
frames: List<Node>,
23+
currentIndex: Int,
24+
onIndexChange: (Int) -> Unit,
25+
modifier: Modifier = Modifier
26+
) {
27+
val state = rememberLazyListState()
28+
29+
Surface(
30+
modifier = modifier
31+
.padding(4.dp),
32+
color = Color.White,
33+
) {
34+
LazyRow(
35+
modifier = Modifier
36+
.padding(8.dp),
37+
state = state
38+
) {
39+
items(frames.size) { index ->
40+
Text(
41+
text = "State ${index + 1}",
42+
color = if (index == currentIndex) Color.Black else Color.LightGray,
43+
modifier = Modifier
44+
.clip(RoundedCornerShape(16.dp))
45+
.clickable { onIndexChange(index) }
46+
.padding(10.dp)
47+
)
48+
}
49+
}
50+
}
51+
}

0 commit comments

Comments
 (0)