|
| 1 | +# Tasks: SPARQL CONSTRUCT Graph Visualization |
| 2 | + |
| 3 | +**Feature**: `001-construct-graph-viz` |
| 4 | +**Branch**: `001-construct-graph-viz` |
| 5 | +**Input**: Design documents from `/specs/001-construct-graph-viz/` |
| 6 | + |
| 7 | +**Tests**: Tests are NOT explicitly requested in the specification. Manual browser testing per constitution principle IV. |
| 8 | + |
| 9 | +**Organization**: Tasks are grouped by user story (P1-P5) to enable independent implementation and testing of each story. |
| 10 | + |
| 11 | +## Format: `- [ ] [ID] [P?] [Story?] Description` |
| 12 | + |
| 13 | +- **[P]**: Can run in parallel (different files, no dependencies) |
| 14 | +- **[Story]**: User story label (US1, US2, etc.) - included for user story phases only |
| 15 | +- All paths are absolute from repository root |
| 16 | + |
| 17 | +## Phase 1: Setup (Shared Infrastructure) |
| 18 | + |
| 19 | +**Purpose**: Project initialization and build configuration |
| 20 | + |
| 21 | +- [ ] T001 Initialize package.json with dependencies: vis-network ^9.1.9, @zazuko/yasgui ^4.x (peerDependency), esbuild (devDependency) |
| 22 | +- [ ] T002 [P] Create .eslintrc.js with ES2018+ rules for code quality validation |
| 23 | +- [ ] T003 [P] Create .prettierrc with formatting rules (2-space indent, single quotes, no trailing commas) |
| 24 | +- [ ] T004 Create esbuild.config.js to bundle src/index.js → dist/yasgui-graph-plugin.min.js (UMD format, global `GraphPlugin`) |
| 25 | +- [ ] T005 [P] Create src/ directory structure: GraphPlugin.js, parsers.js, transformers.js, prefixUtils.js, colorUtils.js, networkConfig.js, index.js |
| 26 | +- [ ] T006 [P] Create example/ directory with index.html and queries.js for manual testing |
| 27 | + |
| 28 | +--- |
| 29 | + |
| 30 | +## Phase 2: Foundational (Blocking Prerequisites) |
| 31 | + |
| 32 | +**Purpose**: Core utilities and base plugin structure that ALL user stories depend on |
| 33 | + |
| 34 | +**⚠️ CRITICAL**: No user story work can begin until this phase is complete |
| 35 | + |
| 36 | +- [ ] T007 [P] Implement extractPrefixes(yasrResults) in src/prefixUtils.js to extract namespace mappings from SPARQL results metadata |
| 37 | +- [ ] T008 [P] Implement applyPrefix(uri, prefixMap) in src/prefixUtils.js to convert full URI to prefixed form (e.g., http://example.org/Person → ex:Person) |
| 38 | +- [ ] T009 [P] Implement truncateLabel(text, maxLength=50) in src/prefixUtils.js to truncate long labels with ellipsis while preserving full value |
| 39 | +- [ ] T010 [P] Implement getNodeColor(node, triples) in src/colorUtils.js to determine color: #FFFF00 (blank nodes), #808080 (literals), #00FF00 (rdf:type objects), #0000FF (other URIs) |
| 40 | +- [ ] T011 [P] Implement getDefaultNetworkOptions() in src/networkConfig.js returning vis-network config: physics enabled with 200 max iterations, interactions (drag/zoom/pan/hover), autoResize, 100% width/height |
| 41 | +- [ ] T012 Create GraphPlugin class skeleton in src/GraphPlugin.js with constructor(yasr), priority=20, label='Graph', and placeholder methods: canHandleResults(), draw(), getIcon() |
| 42 | +- [ ] T013 Implement GraphPlugin.canHandleResults() to detect CONSTRUCT results by checking for subject/predicate/object variables in this.yasr.results |
| 43 | +- [ ] T014 Implement GraphPlugin.getIcon() to return Font Awesome icon element (fa-project-diagram) for YASR tab |
| 44 | +- [ ] T015 Export GraphPlugin and auto-register with Yasgui.Yasr.registerPlugin('graph', GraphPlugin) in src/index.js (UMD compatibility) |
| 45 | + |
| 46 | +**Checkpoint**: Foundation ready - plugin skeleton complete, utilities available, user story implementation can now begin |
| 47 | + |
| 48 | +--- |
| 49 | + |
| 50 | +## Phase 3: User Story 1 - View Basic Graph Structure (Priority: P1) 🎯 MVP |
| 51 | + |
| 52 | +**Goal**: Parse CONSTRUCT results and render interactive graph with nodes (subjects/objects) and edges (predicates) using automatic layout |
| 53 | + |
| 54 | +**Independent Test**: Execute any CONSTRUCT query with connected triples, verify nodes appear with correct labels and colors, edges connect properly, automatic spacing works for disconnected components, prefixes applied to labels |
| 55 | + |
| 56 | +### Implementation for User Story 1 |
| 57 | + |
| 58 | +- [ ] T016 [P] [US1] Implement parseConstructResults(yasrResults) in src/parsers.js to extract RDFTriple[] from bindings format (subject/predicate/object with type/value/datatype) |
| 59 | +- [ ] T017 [P] [US1] Implement createNodeMap(triples) helper in src/transformers.js to deduplicate subjects/objects into Map<string, GraphNode> with id, uri, label, color, type, fullValue |
| 60 | +- [ ] T018 [P] [US1] Implement createEdgesArray(triples, nodeMap) helper in src/transformers.js to create GraphEdge[] with from/to node IDs, predicate label, arrows:'to' |
| 61 | +- [ ] T019 [US1] Implement triplesToGraph(triples, prefixMap) in src/transformers.js orchestrating createNodeMap() and createEdgesArray() to return {nodes: GraphNode[], edges: GraphEdge[]} |
| 62 | +- [ ] T020 [US1] Implement GraphPlugin.draw() method: clear this.yasr.resultsEl, parse triples via parseConstructResults(), extract prefixes via extractPrefixes(), transform via triplesToGraph() |
| 63 | +- [ ] T021 [US1] In GraphPlugin.draw(), create container div with 100% width/height, append to this.yasr.resultsEl |
| 64 | +- [ ] T022 [US1] In GraphPlugin.draw(), create vis.DataSet for nodes and edges, instantiate vis.Network with container, data, and options from getDefaultNetworkOptions() |
| 65 | +- [ ] T023 [US1] In GraphPlugin.draw(), handle stabilizationIterationsDone event to disable physics after layout completes (performance optimization per constitution) |
| 66 | +- [ ] T024 [US1] In GraphPlugin.draw(), add error handling to display empty state message if zero triples, or error message if parsing fails |
| 67 | +- [ ] T025 [US1] Handle blank nodes in createNodeMap(): detect _:b* identifiers, assign yellow color #FFFF00, generate label "_:b1" etc. |
| 68 | +- [ ] T026 [US1] Handle multiple predicates between same nodes in createEdgesArray(): create separate edge for each predicate, vis-network will curve them automatically |
| 69 | +- [ ] T027 [US1] Apply prefix abbreviation to node labels in createNodeMap() using applyPrefix(), truncate long labels using truncateLabel() |
| 70 | +- [ ] T028 [US1] Apply prefix abbreviation to edge labels in createEdgesArray() using applyPrefix() |
| 71 | + |
| 72 | +**Checkpoint**: User Story 1 complete - basic graph visualization working with correct colors, labels, and layout |
| 73 | + |
| 74 | +--- |
| 75 | + |
| 76 | +## Phase 4: User Story 2 - Navigate and Explore Graph (Priority: P2) |
| 77 | + |
| 78 | +**Goal**: Enable zoom (mouse wheel), pan (drag background), and "zoom to extents" control for exploring different parts of the graph |
| 79 | + |
| 80 | +**Independent Test**: Render any graph from US1, verify mouse wheel zooms in/out centered on cursor, click-drag pans viewport smoothly, "fit to view" centers entire graph, graph remains responsive and fills YASR container |
| 81 | + |
| 82 | +### Implementation for User Story 2 |
| 83 | + |
| 84 | +- [ ] T029 [US2] Verify vis-network zoom interaction: mouse wheel scrolling already works via getDefaultNetworkOptions() interaction.zoomView:true (no code changes needed) |
| 85 | +- [ ] T030 [US2] Verify vis-network pan interaction: background drag already works via getDefaultNetworkOptions() interaction.dragView:true (no code changes needed) |
| 86 | +- [ ] T031 [US2] Add "zoom to extents" button to container in GraphPlugin.draw(): create button element with onclick calling this.network.fit() |
| 87 | +- [ ] T032 [US2] Style "zoom to extents" button with CSS: position absolute top-right corner, padding, border, background, hover state |
| 88 | +- [ ] T033 [US2] Verify responsive layout: confirm getDefaultNetworkOptions() includes autoResize:true and width/height:'100%' (already configured in T011) |
| 89 | +- [ ] T034 [US2] Test smooth rendering during interactions: verify FR-018 (<200ms response) by observing zoom/pan operations in example/index.html |
| 90 | + |
| 91 | +**Checkpoint**: User Story 2 complete - navigation controls working, smooth interactions verified |
| 92 | + |
| 93 | +--- |
| 94 | + |
| 95 | +## Phase 5: User Story 3 - Reorganize Layout (Priority: P3) |
| 96 | + |
| 97 | +**Goal**: Allow users to drag individual nodes to new positions with edges updating in real-time and positions persisting during zoom/pan |
| 98 | + |
| 99 | +**Independent Test**: Render any graph, drag nodes to new positions, verify edges follow smoothly, zoom/pan maintains relative positions, no automatic snapping occurs |
| 100 | + |
| 101 | +### Implementation for User Story 3 |
| 102 | + |
| 103 | +- [ ] T035 [US3] Verify vis-network node dragging: already enabled via getDefaultNetworkOptions() interaction.dragNodes:true (no code changes needed) |
| 104 | +- [ ] T036 [US3] Verify edge updates during drag: vis-network automatically updates edges in real-time (built-in behavior, no code changes needed) |
| 105 | +- [ ] T037 [US3] Verify position persistence: vis-network maintains node positions in DataSet during zoom/pan (built-in behavior, no code changes needed) |
| 106 | +- [ ] T038 [US3] Test manual positioning: verify no snapping or collision prevention in example/index.html (FR-019 requirement) |
| 107 | + |
| 108 | +**Checkpoint**: User Story 3 complete - manual node positioning working as expected |
| 109 | + |
| 110 | +--- |
| 111 | + |
| 112 | +## Phase 6: User Story 4 - Inspect Node and Edge Details (Priority: P4) |
| 113 | + |
| 114 | +**Goal**: Show tooltips on hover (300ms delay) with full URI/literal details including datatypes for nodes and predicate URIs for edges |
| 115 | + |
| 116 | +**Independent Test**: Render graph with URIs and literals, hover over nodes and edges, verify tooltips appear after 300ms showing full prefixed URI/literal with datatype/predicate, tooltips disappear on mouse leave |
| 117 | + |
| 118 | +### Implementation for User Story 4 |
| 119 | + |
| 120 | +- [ ] T039 [P] [US4] Add title attribute to nodes in createNodeMap() in src/transformers.js: for URIs show full prefixed URI, for literals show value with datatype (e.g., "123"^^xsd:integer), for blank nodes show identifier |
| 121 | +- [ ] T040 [P] [US4] Add title attribute to edges in createEdgesArray() in src/transformers.js: show full prefixed predicate URI |
| 122 | +- [ ] T041 [US4] Configure tooltip delay in getDefaultNetworkOptions() in src/networkConfig.js: add tooltipDelay property set to 300ms for nodes and edges |
| 123 | +- [ ] T042 [US4] Verify tooltip behavior in example/index.html: confirm 300ms delay before showing, auto-hide on mouse leave (vis-network built-in behavior) |
| 124 | + |
| 125 | +**Checkpoint**: User Story 4 complete - tooltips working with correct information and timing |
| 126 | + |
| 127 | +--- |
| 128 | + |
| 129 | +## Phase 7: User Story 5 - Export Visualization (Priority: P5) |
| 130 | + |
| 131 | +**Goal**: Provide "save as image" control to export visible viewport content at current zoom/pan level as PNG file with readable labels |
| 132 | + |
| 133 | +**Independent Test**: Render and navigate to specific view, click export button, verify downloaded PNG matches current viewport exactly (not entire graph), image has sufficient resolution for readable labels |
| 134 | + |
| 135 | +### Implementation for User Story 5 |
| 136 | + |
| 137 | +- [ ] T043 [US5] Add "save as image" button to container in GraphPlugin.draw(): create button element next to "zoom to extents" button |
| 138 | +- [ ] T044 [US5] Implement GraphPlugin.download(filename) method: get canvas element from this.network.canvas.frame.canvas |
| 139 | +- [ ] T045 [US5] In GraphPlugin.download(), use canvas.toBlob() to export PNG of current viewport (not full graph), trigger browser download with filename |
| 140 | +- [ ] T046 [US5] Wire "save as image" button onclick to call this.download('graph-export') in GraphPlugin.draw() |
| 141 | +- [ ] T047 [US5] Style "save as image" button with CSS: position absolute top-right, left of "zoom to extents", consistent styling |
| 142 | +- [ ] T048 [US5] Test export in example/index.html: zoom/pan to specific view, export, verify PNG shows only visible viewport at current zoom level |
| 143 | + |
| 144 | +**Checkpoint**: User Story 5 complete - image export working for viewport content |
| 145 | + |
| 146 | +--- |
| 147 | + |
| 148 | +## Phase 8: Polish & Cross-Cutting Concerns |
| 149 | + |
| 150 | +**Purpose**: Edge cases, error handling, performance optimization, and final documentation |
| 151 | + |
| 152 | +- [ ] T049 [P] Handle empty results edge case: enhance GraphPlugin.draw() empty state message to match FR-029 "No graph data to visualize" |
| 153 | +- [ ] T050 [P] Handle single triple edge case: verify graph renders two nodes with one edge centered in viewport |
| 154 | +- [ ] T051 [P] Handle self-referencing triples: verify vis-network renders loop edges correctly (built-in support) |
| 155 | +- [ ] T052 [P] Handle very long URIs: ensure truncateLabel() in prefixUtils.js truncates with ellipsis at 50 chars (already implemented in T009) |
| 156 | +- [ ] T053 [P] Handle very long literals: ensure truncateLabel() in prefixUtils.js truncates with ellipsis at 50 chars (already implemented in T009) |
| 157 | +- [ ] T054 [P] Handle duplicate triples deduplication: verify createEdgesArray() in transformers.js only creates one edge per unique subject-predicate-object (Map/Set based) |
| 158 | +- [ ] T055 Add performance warning for large graphs: in GraphPlugin.draw(), if triples.length > 1000, log console warning about potential slowness |
| 159 | +- [ ] T056 Handle browser resize: verify vis-network autoResize option handles window resize automatically (configured in T011) |
| 160 | +- [ ] T057 Add CSS styling for plugin container in src/GraphPlugin.js: container height 600px or 100% of parent, overflow hidden, position relative |
| 161 | +- [ ] T058 Create example/queries.js with sample CONSTRUCT queries: basic graph, ontology visualization, DBpedia example, large graph (100+ triples) |
| 162 | +- [ ] T059 Create example/index.html with YASGUI instance, plugin registration, endpoint configuration, and query examples |
| 163 | +- [ ] T060 Update README.md with installation instructions, usage examples, configuration options, and link to quickstart.md |
| 164 | +- [ ] T061 Build dist/yasgui-graph-plugin.min.js using esbuild: run build script, verify UMD bundle, test global GraphPlugin export |
| 165 | +- [ ] T062 Manual browser testing in Chrome: execute all example queries, verify all 5 user stories work correctly |
| 166 | +- [ ] T063 Manual browser testing in Firefox: execute all example queries, verify all 5 user stories work correctly |
| 167 | +- [ ] T064 Manual browser testing in Safari: execute all example queries, verify all 5 user stories work correctly |
| 168 | +- [ ] T065 Manual browser testing in Edge: execute all example queries, verify all 5 user stories work correctly |
| 169 | + |
| 170 | +**Checkpoint**: All edge cases handled, performance optimized, manual testing complete across all browsers |
| 171 | + |
| 172 | +--- |
| 173 | + |
| 174 | +## Implementation Strategy |
| 175 | + |
| 176 | +### MVP Scope (Recommended First Delivery) |
| 177 | + |
| 178 | +**Phase 1 + Phase 2 + Phase 3 (User Story 1)** = Minimal viable graph visualization |
| 179 | + |
| 180 | +This provides: |
| 181 | +- ✅ Basic graph rendering with automatic layout |
| 182 | +- ✅ Color-coded nodes (literals/blank nodes/URIs/types) |
| 183 | +- ✅ Prefixed labels |
| 184 | +- ✅ Edge connections with predicates |
| 185 | +- ✅ Plugin registration with YASR |
| 186 | + |
| 187 | +**Delivery**: Can be released as v0.1.0 after completing T001-T028 |
| 188 | + |
| 189 | +### Incremental Additions |
| 190 | + |
| 191 | +- **v0.2.0**: Add Phase 4 (User Story 2 - Navigation) for zoom/pan controls |
| 192 | +- **v0.3.0**: Add Phase 5 (User Story 3 - Reorganize) for node dragging |
| 193 | +- **v0.4.0**: Add Phase 6 (User Story 4 - Tooltips) for detail inspection |
| 194 | +- **v0.5.0**: Add Phase 7 (User Story 5 - Export) for image export |
| 195 | +- **v1.0.0**: Complete Phase 8 (Polish) for production readiness |
| 196 | + |
| 197 | +### Parallel Execution Opportunities |
| 198 | + |
| 199 | +**Within Setup Phase (Phase 1)**: |
| 200 | +- T002 (eslint), T003 (prettier), T005 (directories), T006 (examples) can run in parallel after T001 |
| 201 | + |
| 202 | +**Within Foundational Phase (Phase 2)**: |
| 203 | +- T007-T011 (all utility functions) can run in parallel independently |
| 204 | +- T012-T015 (plugin skeleton) must run sequentially |
| 205 | + |
| 206 | +**Within User Story 1 (Phase 3)**: |
| 207 | +- T016-T018 (parsers/transformers helpers) can run in parallel |
| 208 | +- T025-T028 (edge case handlers) can run in parallel after T016-T024 complete |
| 209 | + |
| 210 | +**Within User Story 4 (Phase 6)**: |
| 211 | +- T039-T040 (tooltip content) can run in parallel |
| 212 | + |
| 213 | +**Within Polish Phase (Phase 8)**: |
| 214 | +- T049-T054 (edge case handlers) can run in parallel |
| 215 | +- T062-T065 (browser testing) can run in parallel |
| 216 | + |
| 217 | +**Example Parallel Execution for User Story 1**: |
| 218 | +``` |
| 219 | +┌─ T016 [parsers.js] ────┐ |
| 220 | +├─ T017 [transformers.js] ┼─→ T019 → T020-T024 (draw() implementation) → T027-T028 |
| 221 | +└─ T018 [transformers.js] ┘ ↑ |
| 222 | + │ |
| 223 | + ┌─ T025 (blank nodes) ─────────┤ |
| 224 | + ├─ T026 (multi predicates) ────┤ |
| 225 | + └─ T027-T028 (labels) ─────────┘ |
| 226 | +``` |
| 227 | + |
| 228 | +--- |
| 229 | + |
| 230 | +## Dependencies (User Story Completion Order) |
| 231 | + |
| 232 | +``` |
| 233 | +Phase 1 (Setup) |
| 234 | + ↓ |
| 235 | +Phase 2 (Foundational) ← BLOCKING: Must complete before any user story |
| 236 | + ↓ |
| 237 | +Phase 3 (US1: View Graph) ← MVP: Core visualization |
| 238 | + ↓ |
| 239 | +Phase 4 (US2: Navigate) ← Depends on US1 (need graph to navigate) |
| 240 | + ↓ |
| 241 | +Phase 5 (US3: Reorganize) ← Depends on US1 (need nodes to drag) |
| 242 | + ↓ |
| 243 | +Phase 6 (US4: Tooltips) ← Depends on US1 (need elements to hover) |
| 244 | + ↓ |
| 245 | +Phase 7 (US5: Export) ← Depends on US1, US2 (need viewport to export) |
| 246 | + ↓ |
| 247 | +Phase 8 (Polish) ← Final integration and testing |
| 248 | +``` |
| 249 | + |
| 250 | +**Critical Path**: T001 → T007-T015 (foundational) → T016-T028 (US1) → T029-T048 (US2-US5) → T049-T065 (polish) |
| 251 | + |
| 252 | +**No circular dependencies**: Each user story builds on US1 but stories are otherwise independent |
| 253 | + |
| 254 | +--- |
| 255 | + |
| 256 | +## Task Summary |
| 257 | + |
| 258 | +- **Total Tasks**: 65 |
| 259 | +- **Phase 1 (Setup)**: 6 tasks |
| 260 | +- **Phase 2 (Foundational)**: 9 tasks (BLOCKING) |
| 261 | +- **Phase 3 (US1 - View)**: 13 tasks (MVP) |
| 262 | +- **Phase 4 (US2 - Navigate)**: 6 tasks |
| 263 | +- **Phase 5 (US3 - Reorganize)**: 4 tasks |
| 264 | +- **Phase 6 (US4 - Tooltips)**: 4 tasks |
| 265 | +- **Phase 7 (US5 - Export)**: 6 tasks |
| 266 | +- **Phase 8 (Polish)**: 17 tasks |
| 267 | + |
| 268 | +**Parallel Opportunities**: 22 tasks marked [P] can run in parallel (34% of total) |
| 269 | + |
| 270 | +**Independent Test Criteria**: |
| 271 | +- US1: Execute CONSTRUCT query, verify graph renders with correct colors/labels/layout |
| 272 | +- US2: Zoom/pan/fit controls work smoothly on any graph |
| 273 | +- US3: Drag nodes, positions persist during navigation |
| 274 | +- US4: Hover shows tooltips after 300ms with full details |
| 275 | +- US5: Export button downloads PNG of visible viewport |
| 276 | + |
| 277 | +**Format Validation**: ✅ All tasks follow checklist format `- [ ] [ID] [P?] [Story?] Description with file path` |
| 278 | + |
| 279 | +--- |
| 280 | + |
| 281 | +## Notes |
| 282 | + |
| 283 | +- **No tests**: Manual browser testing per constitution principle IV (Browser Compatibility) |
| 284 | +- **vis-network features**: Many US2/US3 requirements already provided by vis-network built-in functionality (zoom, pan, drag, tooltips), tasks verify configuration |
| 285 | +- **Incremental delivery**: Each user story phase is independently testable and deliverable |
| 286 | +- **MVP focus**: Phase 1 + 2 + 3 (T001-T028) provides minimal viable product |
| 287 | +- **Constitution compliance**: All tasks align with 5 constitution principles (plugin-first, quality, flexibility, compatibility, documentation) |
0 commit comments