A YASGUI plugin for visualizing SPARQL CONSTRUCT and DESCRIBE query results as interactive graphs with nodes (subjects/objects) and edges (predicates).
- 🔷 Interactive Graph Visualization: Automatic force-directed layout with smooth physics-based positioning
- 🎨 Smart Color Coding:
- 🔵 Light Blue (#97C2FC) = URIs
- 🟢 Light Green (#a6c8a6ff) = Literals
- ⚪ Light Grey (#c5c5c5ff) = Blank nodes
- 🟠 Orange (#e15b13ff) = rdf:type objects (classes)
- 🖼️ Node Icons & Images: Render images or emoji/icons on nodes via
schema:image/schema:iconproperties (see Node icons and images) - � Compact Mode: Hide literal and class nodes; show rdf:type and datatype properties in enhanced tooltips instead
- 🔍 Navigation: Mouse wheel zoom, drag to pan, "Zoom to Fit" button
- ✋ Drag & Drop: Reorganize nodes by dragging them to new positions (nodes stay pinned after manual drag)
- � Node Expansion: Double-click any URI node to fetch and merge related triples via DESCRIBE queries (see Expand Nodes with Double Click)
- �💬 Rich Tooltips: Modern HTML tooltips with node type, value, namespace, and datatype information
- 🌓 Theme Support: Automatic light/dark mode detection and dynamic color switching
- ⚡ Performance: Handles up to 1,000 nodes with <2s render time
- ♿ Accessible: WCAG AA color contrast, keyboard navigation support
npm install @matdata/yasgui-graph-pluginimport Yasgui from '@matdata/yasgui';
import GraphPlugin from '@matdata/yasgui-graph-plugin';
Yasgui.Yasr.registerPlugin('Graph', GraphPlugin);
const yasgui = new Yasgui(document.getElementById('yasgui'));<!-- YASGUI -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@matdata/yasgui/build/yasgui.min.css">
<script src="https://cdn.jsdelivr.net/npm/@matdata/yasgui/build/yasgui.min.js"></script>
<!-- Graph Plugin -->
<script src="https://cdn.jsdelivr.net/npm/@matdata/yasgui-graph-plugin/dist/yasgui-graph-plugin.min.js"></script>
<script>
// Plugin auto-registers as 'graph'
const yasgui = new Yasgui(document.getElementById('yasgui'));
</script>See the complete working example in demo/index.html.
const yasgui = new Yasgui(document.getElementById('yasgui'), {
requestConfig: {
endpoint: 'https://dbpedia.org/sparql'
}
});CONSTRUCT Query:
PREFIX ex: <http://example.org/>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
CONSTRUCT {
ex:Alice rdf:type ex:Person .
ex:Alice ex:knows ex:Bob .
ex:Alice ex:name "Alice" .
ex:Bob rdf:type ex:Person .
ex:Bob ex:name "Bob" .
}
WHERE {}DESCRIBE Query:
PREFIX ex: <http://example.org/>
# Returns all triples about the specified resources
DESCRIBE ex:Alice ex:BobAfter running the query, click the "Graph" tab to see the visualization.
- Zoom: Mouse wheel (scroll up = zoom in, scroll down = zoom out)
- Pan: Click and drag the background
- Fit to View: Click the "Zoom to Fit" button to center the entire graph
- Drag Nodes: Click and drag any node to reposition it (nodes are automatically pinned in place after dragging)
- Expand Nodes: 🆕 Double-click any blue URI node to fetch and merge additional RDF triples for that resource (see Node Expansion below)
- Tooltips: Hover over nodes/edges to see rich HTML tooltips with type, value, namespace, and datatype information
| Color | Meaning | Example |
|-------|---------|---------||
| 🔵 Light Blue (#97C2FC) | URI nodes | ex:Person, ex:Alice |
| 🟢 Light Green (#a6c8a6ff) | Literal values | "Alice", "30"^^xsd:integer |
| ⚪ Light Grey (#c5c5c5ff) | Blank nodes | _:b1, _:addr1 |
| 🟠 Orange (#e15b13ff) | rdf:type objects (classes) | ex:Person in ex:Alice rdf:type ex:Person |
The plugin ships with sensible defaults and stores every change in localStorage so settings survive page reloads.
Click the ⚙ Settings button (top-right of the graph) to open the settings panel.
| Setting | Values | Default | Description |
|---|---|---|---|
| Compact mode | on / off | off | Hide literal and class nodes; show rdf:type and datatype properties in tooltips instead |
| Arrow style | Curved / Straight | Curved | Toggle between smooth curved edges and straight lines between nodes |
| Predicate display | Label / Icon / Hidden | Icon | Show the full prefixed URI on edges, a compact symbol/icon, or nothing |
| Show literals | on / off | on | Include or exclude literal value nodes (strings, numbers, dates, …) |
| Show classes | on / off | on | Include or exclude nodes that are objects of rdf:type triples (class nodes) |
| Show blank nodes | on / off | on | Include or exclude blank nodes (_:b0, _:b1, …) |
| Show node labels | on / off | on | Render the prefixed URI / literal text inside each node |
| Enable physics | on / off | on | Keep the force-directed layout simulation running so nodes keep adjusting |
| Node size | Small / Medium / Large | Medium | Set the radius of all nodes |
When Predicate display is set to Icon, each edge displays a compact symbol instead of the full label. Symbols are defined for the 20+ most common predicates:
| Predicate | Symbol |
|---|---|
rdf:type |
a |
rdfs:label |
lbl |
rdfs:comment |
cmt |
rdfs:subClassOf |
⊂ |
rdfs:subPropertyOf |
⊆ |
rdfs:domain |
dom |
rdfs:range |
rng |
rdfs:seeAlso |
see |
rdfs:isDefinedBy |
idb |
owl:sameAs |
≡ |
owl:equivalentClass |
≅ |
owl:inverseOf |
⇄ |
owl:disjointWith |
≠ |
skos:prefLabel |
★ |
skos:altLabel |
☆ |
skos:definition |
def |
skos:broader |
↑ |
skos:narrower |
↓ |
skos:related |
↔ |
skos:note |
note |
skos:exactMatch |
≡ |
skos:closeMatch |
≈ |
dcterms:title |
ttl |
dcterms:description |
dsc |
dcterms:created |
crt |
dcterms:modified |
mod |
dcterms:creator |
by |
dcterms:subject |
sbj |
foaf:name |
nm |
foaf:knows |
⟷ |
foaf:member |
mbr |
schema:name |
nm |
schema:description |
dsc |
For predicates not in the table the full prefixed label is used as a fallback.
Any URI node can display an image or an icon instead of (or in addition to) the default coloured dot by attaching schema:image or schema:icon as a property directly in the SPARQL result.
| Property | Object | Effect |
|---|---|---|
schema:image (https://schema.org/image) |
URL literal or URI | Node is rendered as a circular image |
schema:icon (https://schema.org/icon) |
emoji / short string | The string is used as the node's label |
schema:icon takes priority over schema:image. The corresponding schema:icon/schema:image triples are not rendered as separate nodes or edges, but their values are shown in the node tooltip. Similarly, any rdfs:label triple is consumed to determine the node's displayed label and is never drawn as an edge, even when predicate display is enabled.
CONSTRUCT {
ex:alice schema:image <https://example.com/alice.png> .
ex:alice ex:knows ex:bob .
}
WHERE {}ex:alice will be drawn as a circular photograph.
CONSTRUCT {
ex:alice rdf:type ex:Person .
ex:Person schema:icon "👤" .
}
WHERE {}ex:alice remains a normal dot node in regular mode.
In compact mode the class node (ex:Person) is hidden and Alice's node inherits the 👤 emoji as its label.
When compact mode is enabled, class nodes are hidden and the plugin resolves the image/icon to show on the resource node using the following priority:
schema:image/schema:icondirectly on the resource (highest priority)schema:image/schema:iconon the rdf:type classschema:image/schema:iconon a rdfs:subClassOf superclass (one hop)
You can also pass initial settings when extending the class:
class CustomGraphPlugin extends GraphPlugin {
constructor(yasr) {
super(yasr);
// Override defaults
this.settings.edgeStyle = 'straight';
this.settings.predicateDisplay = 'label';
this.settings.nodeSize = 'large';
}
}
Yasgui.Yasr.registerPlugin('customGraph', CustomGraphPlugin);The graph plugin supports interactive node expansion via double-clicking. This allows you to progressively explore RDF graphs by fetching additional triples for any URI node without redrawing the entire graph.
- Double-click a blue URI node in the graph
- The node's border turns orange and thickens (loading state)
- A
DESCRIBE <uri>query is sent to the SPARQL endpoint - New triples are merged into the existing graph
- Node's border returns to normal width with thicker border (3px) to indicate expansion
- Graph layout and zoom level are preserved
| State | Border | Meaning |
|---|---|---|
| Default | 2px | Node has not been expanded |
| Loading | 4px, orange | DESCRIBE query in progress |
| Expanded | 3px, normal color | Successfully expanded |
| Node Type | Can Expand? | Reason |
|---|---|---|
| 🔵 URI nodes | ✅ Yes | DESCRIBE works on URIs |
| 🟢 Literals | ❌ No | Cannot run DESCRIBE on literal values |
| ⚪ Blank nodes | ❌ No | Blank nodes have no resolvable identity |
Initial Query:
PREFIX ex: <http://example.org/>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
CONSTRUCT {
ex:alice foaf:knows ex:bob .
ex:alice foaf:name "Alice" .
ex:bob foaf:name "Bob" .
}
WHERE {}Initial Graph: 3 nodes (Alice, "Alice", Bob, "Bob"), 2 edges
User Action: Double-click the ex:bob node
What Happens:
- System executes:
DESCRIBE <http://example.org/bob> - Endpoint returns all triples about Bob (from your SPARQL endpoint)
- New nodes and edges appear in the graph
- Graph layout shifts smoothly to accommodate new nodes
ex:bobnode gets a thicker border
Result: You can now see Bob's relationships, properties, and connections without losing your current view
The node expansion feature requires:
- SPARQL 1.1 DESCRIBE support: Your endpoint must support DESCRIBE queries
- Query execution callback: YASR must provide
yasr.executeQuery()for background queries - RDF response format: Endpoint must return results in RDF (JSON-LD, Turtle, N-Triples, etc.)
- Only new triples are added (existing triples are skipped if already in graph)
- Expansion is one-level deep - only triples directly about the URI are added
- For very large result sets (1000+ triples from DESCRIBE), performance may be affected
- Blank nodes returned by DESCRIBE may not connect properly if disconnected from existing nodes
"Nothing happens when I double-click"
- Ensure the node is blue (URI node, not literal or blank node)
- Check browser console for warnings about
yasr.executeQuery - Verify your SPARQL endpoint supports DESCRIBE queries
"Graph becomes slow after many expansions"
- Disable physics simulation in Settings panel for faster UI response
- Consider limiting query results with WHERE clause constraints
- Each expansion adds more triples to the visualization
"New nodes don't appear where I expect"
- The force-directed layout will position new nodes to minimize overlaps
- Disable Physics in Settings to lock positions if desired
- Manually drag new nodes to preferred positions
See demo/expand.html for a working example with mock DESCRIBE responses and detailed logging.
npm install
npm run buildOutput:
dist/yasgui-graph-plugin.esm.js(ES Module for bundlers)dist/yasgui-graph-plugin.cjs.js(CommonJS for Node.js)dist/yasgui-graph-plugin.min.js(IIFE for browsers/unpkg)dist/index.d.ts(TypeScript declarations)
- Build the plugin:
npm run build - Open
demo/index.htmlin a browser (or runnpm run dev) - Try the sample queries in different tabs
npm test # Run Jest unit tests
npm run lint # ESLint check
npm run format # Prettier format- Quickstart Guide - Installation, usage, troubleshooting
- Node Expansion Feature - Complete guide to double-click expansion (FR-001 through FR-009)
- Data Model - Entity definitions and relationships
- Contracts - API specifications for YASR plugin and vis-network integration
- Specification - Complete feature specification
- [Constitution](./. specify/memory/constitution.md) - Project governance and principles
Tested on latest 2 versions of:
- ✅ Chrome
- ✅ Firefox
- ✅ Safari
- ✅ Edge
Requires ES2018+ support and Canvas API.
Contributions welcome! Please follow the project constitution (.specify/memory/constitution.md) for governance principles:
- Plugin-First Architecture - No YASGUI core modifications
- Visualization Quality - Performance (<2s for 1k nodes), accessibility (WCAG AA)
- Configuration Flexibility - Sensible defaults, but customizable
- Browser Compatibility - ES2018+, latest 2 browser versions
- Documentation - Keep docs updated with changes
- Built with vis-network for graph rendering
- Integrates with YASGUI SPARQL editor
- Follows the yasgui-geo plugin pattern
Current Version: 0.1.0 (MVP)
Implemented Features (v0.1.0):
- ✅ Basic graph visualization (US1) - CONSTRUCT/DESCRIBE results as interactive graphs
- ✅ Navigation controls (US2) - Zoom, pan, "Fit to View" button
- ✅ Color-coded nodes - URIs, literals, blank nodes, rdf:type objects
- ✅ Prefix abbreviation - Display prefixed URIs instead of full URLs
- ✅ Blank node support - Handle anonymous RDF nodes
- ✅ Drag & repositioning - Manually adjust node positions
- ✅ Rich tooltips - Hover for detailed node/edge information
- ✅ Theme support - Light/dark mode detection and switching
- ✅ Settings panel - Configurable display options with localStorage persistence
- ✅ Node icons & images - Display images via schema:image property
- ✅ Compact mode - Hide literals and classes for cleaner visualization
- ✅ Double-click expansion (US5) - Fetch and merge related triples via DESCRIBE queries (see Expand Nodes with Double Click)
- Verify plugin is registered correctly
- Check browser console for errors
- Ensure you're running a CONSTRUCT or DESCRIBE query
- Ensure query type is CONSTRUCT or DESCRIBE
- Confirm query returns triples (check "Table" or "Response" tab)
- Verify results have RDF structure
- Limit results to <1000 nodes for best performance
- Disable physics after initial layout
- Consider using LIMIT clause in SPARQL query
For more help, see Quickstart Guide - Troubleshooting.
git clone https://github.com/yourusername/yasgui-graph-plugin.git
cd yasgui-graph-plugin
npm installThe project supports live development - edit source files and see changes immediately without rebuilding:
-
Start a local dev server (any HTTP server will work):
# Using Python python -m http.server 8000 # Using Node.js (http-server) npx http-server -p 8000 # Using VS Code Live Server extension # Right-click demo/index.html → "Open with Live Server"
-
Open demo in browser:
http://localhost:8000/demo/index.html -
Edit source files (e.g.,
src/colorUtils.js):export function getNodeColor(node) { // Change colors here if (node.isBlankNode) return '#FF00FF'; // Magenta // ... }
-
Refresh browser - changes appear immediately! ⚡
The demo automatically loads ES modules directly from src/ in development mode, so no rebuild is needed.
Build all distribution formats:
npm run buildOutputs:
dist/yasgui-graph-plugin.esm.js- ES Module format (bundled with vis-network)dist/yasgui-graph-plugin.cjs.js- CommonJS format (bundled with vis-network)dist/yasgui-graph-plugin.min.js- IIFE browser bundle (bundled with vis-network)dist/index.d.ts- TypeScript type declarations
npm test # Run all tests
npm run lint # Check code style
npm run format # Auto-fix formatting