Saltar a contenido

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)

  1. Force-Directed Layout Algorithm:
  2. Repulsive Coulomb Force between nodes: $F_r = k^2 / d$
  3. Attractive Hooke Spring Force along directed edges: $F_a = d^2 / k$
  4. Iterate layout physics for 50 ticks upon loading GraphDataResponse.
  5. Spatial Viewport Pruning:
  6. Calculate bounding box of current screen viewport transformed by (panOffset, zoomScale).
  7. 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 panOffset update), pinch-zoom (verify scale clamped between 0.25x and 3.0x), tap node (verify onNodeSelected invoked with node ID).