Skip to content

Commit 65440b3

Browse files
committed
Documentation updates
1 parent 0ad30ba commit 65440b3

2 files changed

Lines changed: 81 additions & 98 deletions

File tree

CLAUDE.md

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -46,23 +46,33 @@ This application is unique: all logic lives in XSLT, not JavaScript. The archite
4646
- Compiled XSLT (`dist/graph-client.xsl.sef.json`)
4747

4848
2. **src/graph-client.xsl** - Main XSLT stylesheet that:
49-
- Initializes the 3D force graph on page load
49+
- Initializes the 3D force graph on page load with LinkedDataHub demo SKOS concept
5050
- Handles all UI events (clicks, double-clicks, hovers)
51-
- Loads RDF documents via HTTP
51+
- Loads RDF documents via HTTP through CORS proxy (corsproxy.io)
52+
- Maintains merged global RDF document and loaded-uris tracking array
53+
- Expands stub nodes on double-click (creates minimal descriptions for unloaded resources)
5254
- Manipulates the DOM
53-
- Imports the other two stylesheets
55+
- Imports the other three stylesheets
5456

5557
3. **src/3d-force-graph.xsl** - 3D graph initialization and configuration:
5658
- Creates the ForceGraph3D instance
5759
- Sets up node/link rendering (colors, labels, Three.js objects)
5860
- Registers JavaScript event handlers that dispatch CustomEvents back to XSLT
5961
- Converts RDF/XML to graph JSON format
62+
- Deterministic node colors hashed from rdf:type URIs
6063

6164
4. **src/normalize-rdfxml.xsl** - RDF/XML normalization pipeline:
6265
- Three-pass normalization: syntax → flattening → URI resolution
6366
- Converts all RDF syntax variants to canonical form
6467
- Resolves relative URIs to absolute URIs
6568

69+
5. **src/merge-rdfxml.xsl** - RDF document merging:
70+
- Merges new RDF descriptions into existing global document
71+
- Adds new resources and properties to existing resources
72+
- Uses ldh:MergeRDF mode templates
73+
74+
6. **styles.css** - External stylesheet for UI components
75+
6676
### Event Flow
6777

6878
```
@@ -81,14 +91,26 @@ XSLT calls graph.graphData() to update visualization
8191

8292
### RDF Loading and Graph Updates
8393

84-
When a URI is loaded (on page load or via double-click):
94+
When a URI is loaded (on page load, via UI input, or double-click):
8595

8696
1. XSLT template `load-and-update-graph` is called with a document URI
87-
2. Uses `ixsl:http-request()` with `'pool': 'xml'` to fetch and cache RDF/XML
88-
3. RDF goes through 3-pass normalization (normalize-rdfxml.xsl)
89-
4. Normalized RDF is converted to graph JSON using `ldh:ForceGraph3D-convert-data` mode templates
90-
5. Graph data structure: `{ nodes: [{id, label, color, ...}], links: [{source, target, label, ...}] }`
91-
6. XSLT calls `graph.graphData(newData)` to update the 3D visualization
97+
2. URI is wrapped with CORS proxy: `https://corsproxy.io/?url=<encoded-uri>`
98+
3. Uses `ixsl:http-request()` with `'pool': 'xml'` to fetch and cache RDF/XML
99+
4. RDF goes through 3-pass normalization (normalize-rdfxml.xsl)
100+
5. Document URI is added to `window.LinkedDataHub.loaded-uris` array (tracks HTTP-loaded resources)
101+
6. Normalized RDF is merged into global `window.LinkedDataHub.document` (merge-rdfxml.xsl)
102+
7. Merged document is converted to graph JSON using `ldh:ForceGraph3D-convert-data` mode templates
103+
8. Graph data structure: `{ nodes: [{id, label, color, ...}], links: [{source, target, label, ...}] }`
104+
9. XSLT calls `graph.graphData(newData)` to completely replace the visualization
105+
106+
### Stub Node Expansion
107+
108+
When double-clicking a node that was HTTP-loaded (its URI is in `loaded-uris`):
109+
110+
1. Find unresolved object URIs from the node's properties (URIs not in current document)
111+
2. Create minimal stub descriptions: `<rdf:Description rdf:about="uri"><rdfs:label>fragment</rdfs:label></rdf:Description>`
112+
3. Merge stubs into global document and update visualization
113+
4. Stub nodes appear in gray - double-clicking them loads real data via HTTP
92114

93115
### Key XSLT/IXSL Features
94116

@@ -106,10 +128,14 @@ This codebase heavily uses SaxonJS interactive XSLT features:
106128

107129
### State Management
108130

109-
Graph state is stored in `window.LinkedDataHub.graphs[graph-id]` and contains:
110-
- `graph` - The ForceGraph3D instance
111-
- `currentDocumentURI` - URI of currently loaded RDF document
112-
- `resources` - Cached RDF document (optional)
131+
Application state is stored in `window.LinkedDataHub`:
132+
133+
**Graph instances** (`window.LinkedDataHub.graphs[graph-id]`):
134+
- `instance` - The ForceGraph3D instance
135+
136+
**Global RDF state** (`window.LinkedDataHub`):
137+
- `document` - Merged RDF document containing all loaded data
138+
- `loaded-uris` - JavaScript Array of URIs loaded via HTTP (distinguishes real resources from stubs)
113139

114140
Access via helper functions in graph-client.xsl:
115141
- `local:get-graphs()` - Returns the graphs object
@@ -174,13 +200,14 @@ The SaxonJS transform uses `logLevel: 10` for verbose logging.
174200

175201
## Testing with Sample Data
176202

177-
The `examples/` directory contains sample RDF files. To test with different RDF:
203+
The application loads `https://linkeddatahub.com/demo/skos/concepts/concept17128/` by default.
204+
205+
To test with different RDF:
178206

179-
1. Add an `.rdf` file to `examples/`
180-
2. Change the `document-uri` parameter in the `main` template in `src/graph-client.xsl`
181-
3. Recompile and refresh
207+
1. Change the `document-uri` parameter in the `main` template in `src/graph-client.xsl`
208+
2. Recompile with `./generate-sef.sh` and refresh
182209

183-
Or enter any HTTP(S) URL in the UI input field. The remote server must send CORS headers.
210+
Or enter any HTTP(S) URL in the UI input field. All URLs are automatically proxied through `https://corsproxy.io/?url=` to handle CORS.
184211

185212
## Common Patterns
186213

README.md

Lines changed: 36 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
# RDF Graph Browser
1+
# 3D Linked Data
22

33
A browser-based RDF graph visualization tool that combines:
44
- **3d-force-graph** - 3D force-directed graph visualization using WebGL/three.js
55
- **SaxonJS 3.0** - Client-side XSLT 3.0 processor
6-
- **RDF/XML** - Semantic web data format
6+
- **RDF/XML** - Semantic Web data format
7+
8+
> **⚠️ Important Limitation:** Currently, only documents that return `application/rdf+xml` content type can be loaded. Other RDF formats (Turtle, JSON-LD, N-Triples) are not yet supported.
79
810
## Features
911

@@ -16,19 +18,19 @@ A browser-based RDF graph visualization tool that combines:
1618
## Project Structure
1719

1820
```
19-
RDF-Graph-Browser/
21+
3D-Linked-Data/
2022
├── index.html # Main HTML page
23+
├── styles.css # External stylesheet
2124
├── generate-sef.sh # Script to compile XSLT to SEF
2225
├── lib/ # External libraries
2326
│ └── SaxonJS3.js # SaxonJS 3.0 library
2427
├── src/ # XSLT source files
2528
│ ├── graph-client.xsl # Main XSLT client with event handlers
2629
│ ├── 3d-force-graph.xsl # 3D Force Graph initialization
27-
│ └── normalize-rdfxml.xsl # RDF/XML normalization
28-
├── dist/ # Compiled output
29-
│ └── graph-client.xsl.sef.json # Compiled SEF for SaxonJS
30-
└── examples/ # Sample RDF data
31-
└── example.rdf # Example RDF document
30+
│ ├── normalize-rdfxml.xsl # RDF/XML normalization
31+
│ └── merge-rdfxml.xsl # RDF document merging
32+
└── dist/ # Compiled output
33+
└── graph-client.xsl.sef.json # Compiled SEF for SaxonJS
3234
```
3335

3436
## Dependencies
@@ -64,15 +66,16 @@ If you modify the XSLT, regenerate the SEF file:
6466

6567
### Loading RDF Documents
6668

67-
1. **Default**: On page load, `examples/example.rdf` is loaded automatically
68-
2. **Custom URL**: Enter any HTTP(S) URL in the input field and click "Go" or press Enter
69-
3. **Navigate**: Double-click any node with an HTTP(S) URI to load that resource
69+
1. **Default**: On page load, `https://linkeddatahub.com/demo/skos/concepts/concept17128/` is loaded automatically via CORS proxy
70+
2. **Custom URL**: Enter any HTTP(S) URL in the input field and click "Go" or press Enter (automatically proxied)
71+
3. **Navigate**: Double-click any HTTP(S) node to either:
72+
- Load that resource via HTTP (if not already loaded)
73+
- Expand its linked resources as stub nodes (if already loaded)
7074

7175
### Interactive Features
7276

7377
- **Single-click node** - View resource details in info panel
7478
- **Double-click node** - Load and visualize that node's RDF document
75-
- **Right-click node** - Context menu (future feature)
7679
- **Hover node** - Show tooltip with node label and type
7780
- **Drag node** - Reposition nodes in 3D space
7881
- **Rotate view** - Click and drag background to rotate
@@ -91,50 +94,32 @@ All events and operations are logged to the browser console using `<xsl:message>
9194

9295
### XSLT-Driven Design
9396

94-
All application logic is implemented in XSLT 3.0 running in the browser via SaxonJS:
97+
Unlike typical web applications where JavaScript handles all logic, this application uses XSLT 3.0 for everything:
9598

96-
```
97-
┌─────────────────────────────────────────────────┐
98-
│ Browser Window │
99-
│ │
100-
│ ┌──────────────┐ ┌─────────────────┐ │
101-
│ │ WebGL │ │ HTML/DOM │ │
102-
│ │ Canvas │◄────────┤ UI Elements │ │
103-
│ │ (3D graph) │ │ Info Panel │ │
104-
│ └──────────────┘ └─────────────────┘ │
105-
│ ▲ ▲ │
106-
│ │ │ │
107-
│ │ ┌────────────────────────────────┐ │
108-
│ └──┤ SaxonJS XSLT 3.0 Processor │ │
109-
│ │ │ │
110-
│ │ • graph-client.xsl │ │
111-
│ │ - Event handlers │ │
112-
│ │ - RDF loading │ │
113-
│ │ - DOM manipulation │ │
114-
│ │ │ │
115-
│ │ • 3d-force-graph.xsl │ │
116-
│ │ - Graph initialization │ │
117-
│ │ - Event bridge via │ │
118-
│ │ CustomEvents │ │
119-
│ │ │ │
120-
│ │ • normalize-rdfxml.xsl │ │
121-
│ │ - RDF/XML normalization │ │
122-
│ │ - URI resolution │ │
123-
│ └────────────────────────────────┘ │
124-
│ ▲ │
125-
│ │ │
126-
│ ┌───────────────┐ │
127-
│ │ HTTP Fetch │ │
128-
│ │ RDF/XML docs │ │
129-
│ │ (with CORS) │ │
130-
│ └───────────────┘ │
131-
└─────────────────────────────────────────────────┘
132-
```
99+
**What XSLT does (not JavaScript):**
100+
- Loads RDF documents via HTTP
101+
- Parses and normalizes RDF/XML
102+
- Merges RDF data from multiple sources
103+
- Handles UI events (clicks, double-clicks, hovers)
104+
- Updates the DOM (info panel, tooltips)
105+
- Converts RDF to graph visualization data
106+
- Controls the 3D Force Graph
107+
108+
**How it works:**
109+
- XSLT source files in `src/` are compiled to SEF format by `generate-sef.sh`
110+
- SaxonJS loads the pre-compiled SEF and executes XSLT in the browser
111+
- XSLT uses SaxonJS interactive extensions (`ixsl:*`) to interact with JavaScript, DOM, and HTTP APIs
112+
113+
**Why this approach:**
114+
- Declarative programming model for semantic data processing
115+
- Built-in RDF/XML handling without parsing libraries
116+
- Pattern matching via templates and modes
117+
- XPath 3.1 for querying RDF structures
133118

134119
### Event Flow
135120

136121
1. **Page Load**
137-
- SaxonJS loads and compiles SEF
122+
- SaxonJS loads pre-compiled SEF file
138123
- XSLT `main` template initializes 3D Force Graph
139124
- Default RDF document is loaded via `ixsl:http-request()`
140125

@@ -188,35 +173,6 @@ The XSLT pipeline converts RDF/XML to the format expected by 3d-force-graph:
188173
### SEF file not found
189174
Run `./generate-sef.sh` to compile the XSLT to SEF format
190175

191-
### 3d-force-graph not loading
192-
Check browser console - CDN might be blocked. Download library locally if needed.
193-
194-
### CORS errors when loading RDF
195-
The remote server must send proper CORS headers:
196-
- `Access-Control-Allow-Origin: *`
197-
- `Access-Control-Allow-Methods: GET`
198-
- `Access-Control-Allow-Headers: Accept`
199-
200-
For testing, you can use LinkedDataHub which has CORS enabled.
201-
202-
### Fragment URIs cause errors
203-
Fragment identifiers (e.g., `http://example.org/data#Resource`) are automatically stripped before document loading. The fragment is used to look up the specific resource within the loaded document.
204-
205-
### Empty or malformed RDF
206-
Check browser console for parsing errors. The document must be valid RDF/XML.
207-
208-
## Browser Compatibility
209-
210-
Tested on:
211-
- Chrome/Edge (recommended)
212-
- Firefox
213-
- Safari
214-
215-
Requires:
216-
- WebGL support for 3D rendering
217-
- ES6+ JavaScript features
218-
- Fetch API
219-
220176
## License
221177

222178
This project uses:

0 commit comments

Comments
 (0)