Summary
Add a bundled RDF plugin that renders individual RDF terms (URI, literal, blank node) in human-readable RDF surface syntax — not icon-embellished shortcuts.
- Plugin namespace:
https://iolanta.tech/rdf
- v0 output datatype:
https://iolanta.tech/rdf/term (CLI: --as rdf/term)
- Primary consumer:
mermaid/rdf delegates node/edge label text to this facet
Motivation
mermaid/rdf currently renders:
- blank nodes with a ⬜ prefix
- typed/language literals with emoji icons (
🔢 42, 🇺🇸 Hello) instead of ^^ / @ syntax
- URI nodes as bare path strings (
example.org/alice)
We want RDF displayed as RDF — reusable beyond Mermaid.
Output semantics (title vs rdf/term)
--as |
Question it answers |
Literal example |
title |
What do humans call this? |
Hello (lexical value; lang used to pick labels, not shown) |
mermaid/rdf (today) |
What RDF term is this? (diagram) |
🇺🇸 Hello, 🔢 42 — icon workaround for Mermaid label safety |
rdf/term (new) |
What is this term as RDF syntax? |
"Hello"@en, "42"^^xsd:integer |
The title/flag inconsistency today is accidental (separate code paths: TitleFacet vs raw_literal_title). This plugin makes the contract explicit.
Term rendering rules
| Term |
Current mermaid/rdf |
New rdf/term facet |
URIRef |
bare path string |
<https://…> or QName when prefixes allow |
Literal (plain) |
lexical value |
"value" |
Literal (typed) |
🔢 42 |
"42"^^xsd:integer |
Literal (lang) |
🇺🇸 Hello |
"Hello"@en |
BNode |
⬜ _:profile |
_:… via BNode.n3() (no icon) |
Implementation should extend render_node() in iolanta/models.py (add BNode branch via n3()); facet delegates there.
Architecture
iolanta/rdf/ # bundled plugin (mirrors iolanta/mermaid/)
term/
facet.py # RdfTermFacet(Facet[str])
rdf_term.yamlld # OutputDatatype https://iolanta.tech/rdf/term
docs/rdf/
index.md # plugin landing, $id: https://iolanta.tech/rdf
iolanta/mermaid/raw_rdf/ # delegate mermaid_title to rdf/term
pyproject.toml # entry-point: rdf-term-facet
tests/test_rdf_term.py
tests/test_mermaid.py # rewrite rdf assertions when delegation lands
Future (out of v0 scope): https://iolanta.tech/rdf/turtle for full-graph serialization.
CLI note: decode_datatype("rdf") → datatypes/rdf, not iolanta.tech/rdf. Use rdf/term shorthand or full IRI.
Concise overlap notes
| Cluster |
Action |
raw_literal_title, icons in mermaid/rdf |
Replace via delegation to rdf/term |
render_node() in models.py |
Extend as canonical term serializer |
| kglint JSON |
Keep separate (lint, not display syntax) |
| generic vs raw Mermaid |
Keep pedagogical layering |
| duplicate rdfs:comment in rdf.md + yamlld |
Trim on docs update |
Scope (v0)
Out of scope (v0)
- Full-graph Turtle serialization (
rdf/turtle)
- Changes to generic
--as mermaid literal icons
- kglint changes
Implementation order
- Extend
render_node() with BNode
RdfTermFacet + metadata + entry-point + unit tests
- Delegate from
RawRDFMermaid; update mermaid RDF tests + docs example
- Plugin docs at
docs/rdf/index.md
Test plan
- Unit tests per term kind (URI, plain/typed/lang literal, blank node)
- Facet selection tests (AGENTS.md A07 — test that
rdf/term facet is selected, not only string output)
- Updated
test_mermaid.py rdf assertions (Turtle markers present, icons absent in raw RDF path)
iolanta docs/mermaid/rdf-example.yamlld --as mermaid/rdf | mmdc -i - -o /dev/null
direnv exec . j fmt && j lint on touched files
Summary
Add a bundled RDF plugin that renders individual RDF terms (URI, literal, blank node) in human-readable RDF surface syntax — not icon-embellished shortcuts.
https://iolanta.tech/rdfhttps://iolanta.tech/rdf/term(CLI:--as rdf/term)mermaid/rdfdelegates node/edge label text to this facetMotivation
mermaid/rdfcurrently renders:🔢 42,🇺🇸 Hello) instead of^^/@syntaxexample.org/alice)We want RDF displayed as RDF — reusable beyond Mermaid.
Output semantics (title vs rdf/term)
--astitleHello(lexical value; lang used to pick labels, not shown)mermaid/rdf(today)🇺🇸 Hello,🔢 42— icon workaround for Mermaid label safetyrdf/term(new)"Hello"@en,"42"^^xsd:integerThe title/flag inconsistency today is accidental (separate code paths:
TitleFacetvsraw_literal_title). This plugin makes the contract explicit.Term rendering rules
rdf/termfacetURIRef<https://…>or QName when prefixes allowLiteral(plain)"value"Literal(typed)🔢 42"42"^^xsd:integerLiteral(lang)🇺🇸 Hello"Hello"@enBNode⬜ _:profile_:…viaBNode.n3()(no icon)Implementation should extend
render_node()iniolanta/models.py(add BNode branch vian3()); facet delegates there.Architecture
Future (out of v0 scope):
https://iolanta.tech/rdf/turtlefor full-graph serialization.CLI note:
decode_datatype("rdf")→datatypes/rdf, notiolanta.tech/rdf. Userdf/termshorthand or full IRI.Concise overlap notes
raw_literal_title, icons in mermaid/rdfrdf/termrender_node()in models.pyScope (v0)
iolanta/rdf/, docs atdocs/rdf/)RdfTermFacet+https://iolanta.tech/rdf/termoutput datatyperender_node()for blank nodes--as rdf/termdocs/rdf/RawRDFMermaiddelegatesmermaid_titletordf/termtests/test_mermaid.pyanddocs/mermaid/rdf-example.mmd"…"^^xsd:…inside node labels (today's tests forbid this)Out of scope (v0)
rdf/turtle)--as mermaidliteral iconsImplementation order
render_node()with BNodeRdfTermFacet+ metadata + entry-point + unit testsRawRDFMermaid; update mermaid RDF tests + docs exampledocs/rdf/index.mdTest plan
rdf/termfacet is selected, not only string output)test_mermaid.pyrdf assertions (Turtle markers present, icons absent in raw RDF path)iolanta docs/mermaid/rdf-example.yamlld --as mermaid/rdf | mmdc -i - -o /dev/nulldirenv exec . j fmt && j linton touched files