Ir al contenido
DocuGraph

DocuGraph

Frontend Spec: Interactive 2D Directed Acyclic Graph Canvas Composable (GraphCanvas)

Estado de fase (MASTER-00): MVP — alcance del MVP Shipaton sujeto a resultados de Gate 0 y PoC. Gobernanza: MASTER-00.

Frontend Spec: Interactive 2D Directed Acyclic Graph Canvas Composable (GraphCanvas)

Sección titulada «Frontend Spec: Interactive 2D Directed Acyclic Graph Canvas Composable (GraphCanvas)»

This specification details the core 2D graph visualizer (GraphCanvas) in :docugraph-app. It renders nodes and directed relationship edges retrieved via BE-03 buildGraph on a hardware-accelerated Compose Canvas. It supports smooth pan and pinch-zoom gestures (0.25x to 3.0x scale), spatial viewport pruning for 60 FPS performance, force-directed layout positioning, filter search queries, and node selection callbacks to launch FE-04 NodeInspector.

package io.docugraph.app.ui.graph
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@Composable
fun GraphCanvas(
projectId: String,
viewModel: GraphViewModel,
onNodeSelected: (nodeId: String) -> Unit,
modifier: Modifier = Modifier
)
package io.docugraph.app.ui.graph
import androidx.compose.ui.geometry.Offset
import io.docugraph.backend.models.graph.GraphEdgeDto
import io.docugraph.backend.models.graph.GraphNodeDto
data class ViewportTransform(
val panOffset: Offset = Offset.Zero,
val zoomScale: Float = 1.0f
)
data class RenderNode(
val data: GraphNodeDto,
val position: Offset,
val radius: Float = 24.0f,
val isHighlighted: Boolean = false
)
data class GraphCanvasUiState(
val nodesMap: Map<String, RenderNode> = emptyMap(),
val edges: List<GraphEdgeDto> = emptyList(),
val transform: ViewportTransform = ViewportTransform(),
val selectedNodeId: String? = null,
val searchQuery: String = "",
val isLoading: Boolean = false
)

4. Compose Custom Canvas Hierarchy & Gesture Handling

Sección titulada «4. Compose Custom Canvas Hierarchy & Gesture Handling»
Box (Modifier.fillMaxSize().clipToBounds())
├── Canvas (Modifier.fillMaxSize().pointerInput { detectTransformGestures & detectTapGestures })
│ ├── withTransform (translate(panX, panY), scale(zoomScale))
│ │ ├── Draw Edges: for edge in visibleEdges -> drawLine / drawPath (Directed Arrows)
│ │ └── Draw Nodes: for node in visibleNodes -> drawCircle / drawText (Node Label & Color by document_type)
└── Overlay Controls (Aligned to Top/Bottom):
├── TopSearchBar (OutlinedTextField search filter query)
└── BottomToolbar (Zoom In Button, Zoom Out Button, Reset Viewport Button)

5. Force-Directed Layout & Viewport Pruning (60 FPS Performance)

Sección titulada «5. Force-Directed Layout & Viewport Pruning (60 FPS Performance)»
  1. Force-Directed Layout Algorithm:
    • Repulsive Coulomb Force between nodes: $F_r = k^2 / d$
    • Attractive Hooke Spring Force along directed edges: $F_a = d^2 / k$
    • Iterate layout physics for 50 ticks upon loading GraphDataResponse.
  2. Spatial Viewport Pruning:
    • Calculate bounding box of current screen viewport transformed by (panOffset, zoomScale).
    • Skip rendering edges and nodes whose spatial coordinates fall outside visible screen bounds.
Document Type Primary Node Color Accent / Border Color
user_story #4CAF50 (Green) #2E7D32
backend_task_spec #2196F3 (Blue) #1565C0
frontend_component_spec #9C27B0 (Purple) #6A1B9A
database_schema #FF9800 (Orange) #E65100
mcp_specification #E91E63 (Pink) #AD1457
  • Performance Test: Render 500 nodes on desktop and mobile target, ensure frame render time $<16$ms (60 FPS).
  • UiTest: Drag canvas (verify panOffset update), pinch-zoom (verify scale clamped between 0.25x and 3.0x), tap node (verify onNodeSelected invoked with node ID).