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)
1. Overview & Purpose
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.
2. Component Signature & Props
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
)
3. UI State Model & Data Classes
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
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)
- 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. - 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.
6. Document Type Color Scheme
| 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 |
7. Verification & Test Plan
- Performance Test: Render 500 nodes on desktop and mobile target, ensure frame render time $<16$ms (60 FPS).
- UiTest: Drag canvas (verify
panOffsetupdate), pinch-zoom (verify scale clamped between 0.25x and 3.0x), tap node (verifyonNodeSelectedinvoked with node ID).