Skip to content

Embed LDH's RDF-defined blocks as atomic RDFa islands#16

Open
namedgraph wants to merge 2 commits into
masterfrom
feature/ldh-object-blocks
Open

Embed LDH's RDF-defined blocks as atomic RDFa islands#16
namedgraph wants to merge 2 commits into
masterfrom
feature/ldh-object-blocks

Conversation

@namedgraph

Copy link
Copy Markdown
Member

Summary

Blocks defined purely in RDF — ldh:View, ldh:ResultSetChart, ldh:Object — can now be embedded wherever the content model admits a block, per the LDH v6 document format: a div[@about][@typeof] element with its definition triples as span[@property] children (objects via @resource, literals as span text). No wrappers, no rdf:_N sequences, no document→block containment edges — extraction yields the block descriptions, containment is implicit in the content.

The editor treats such a block as an atomic island:

  • never a text host — the placeholder and its spans are locked, tabindex="-1" makes it a focusable navigation island (shared doctrine with block images: arrow-key selection, Backspace/Delete removes it whole, undo/redo chords work while it's focused);
  • a hard boundary for Backspace merges (local:merge-host-in gains a tail-island guard) and for the cross-host delete machine (local:clamped-range moves boundaries out of islands, so a selection takes them whole or not at all);
  • byte-identical round-trip: the visual rendering is injected into an ephemeral div[data-role=rendering] child by the overridable mode="local:render-island" hook and stripped at canonicalization; the extractor skips it.

Core / extension split

  • src/blocks.xsl (core, zero LDH knowledge): $object-block-types param (default empty), the local:island() predicate, the render hook + neutral card, and extension stubs for dialogs / toolbar buttons / slash-menu items.
  • src/ldh-blocks.xsl + entry src/ldh-editor.xsl (xsl:import layering, higher import precedence): the LDH class list, async renderers — ixsl:promise/ixsl:http-request chains fetching the referenced documents by content negotiation on clean trailing-slash URIs (no file extensions anywhere) — and the "Block…" insert dialog wired into the slash menu and toolbar. In production LinkedDataHub, client.xsl plays this module's role, bridging the hook into its block rendering (v6 ldh:Block / v5 ldh:RenderRow — MIGRATION.md §12).

Build & deployment

Two SEFs: the core dist/index.xsl.sef.json is what GitHub Pages deploys (Pages can't content-negotiate); the LDH-extended dist/ldh-editor.xsl.sef.json powers the local /demo/ showcase and the browser fixtures. make up now serves via node serve.mjs, which negotiates application/rdf+xml / application/sparql-results+xml representations of document URIs.

Test plan

  • make test — 50 headless PASS, incl. new extractor/canonical/lint fixtures for the storage form
  • make test-browser — all 14 suites green, incl. new blocks.mjs (43 assertions: hydration, view-source round-trip, island navigation/deletion/boundaries, dialog insertion top-level and nested in a list item, stage-2/sweep delete atomicity, canonical copy, undo + invariants after every mutation)
  • fixture-nesting.html runs the extended SEF, so every existing suite doubles as proof the extension layers without disturbing core behavior
  • Manual: make up/demo/ hydrates a chart and a nested object block over conneg; Source shows the clean v6 placeholder; Extract RDF shows the block descriptions

🤖 Generated with Claude Code

namedgraph and others added 2 commits July 13, 2026 11:24
Blocks defined purely in RDF (ldh:View, ldh:ResultSetChart, ldh:Object)
can now live wherever the content model admits a div, per the LDH v6
document format: a div[@about][@typeof] block element with its
definition triples as span[@Property] children (objects via @resource,
literals as span text) - no wrappers, no rdf:_N sequences, no
document-to-block containment edges. The editor treats such a div as an
atomic island: never a text host, focusable/navigable/deletable like a
block image (shared local:select-island doctrine, undo/redo chords
included), a hard boundary for merges (merge-host-in gains a
tail-island guard - islands hold no hosts, the lookup would leapfrog
them) and for the delete machine (clamped-range moves boundaries out of
islands, so they join selections whole and are only ever removed
whole), and byte-identical on round-trip: the visual rendering is
injected into an ephemeral div[data-role=rendering] child by the
overridable mode="local:render-island" hook and stripped at
canonicalization.

The core (new src/blocks.xsl: $object-block-types param, the
local:island predicate, the render hook + neutral card, extension
stubs) knows no LDH vocabulary. The LDH extension (src/ldh-blocks.xsl,
layered by the src/ldh-editor.xsl entry via xsl:import) supplies the
class list, async renderers - ixsl:promise/ixsl:http-request chains
fetching the referenced documents by content negotiation on clean
trailing-slash URIs (no file extensions anywhere; serve.mjs replaces
python http.server and negotiates rdf+xml / sparql-results+xml
representations) - and the Block... insert dialog wired into the slash
menu and toolbar through the new hook points. In production
LinkedDataHub, client.xsl plays the extension's role, bridging the hook
into its block rendering (MIGRATION.md par. 12).

Two build flavors: the core SEF (dist/index.xsl.sef.json) is what
GitHub Pages deploys - Pages cannot content-negotiate, so the
LDH-extended SEF (dist/ldh-editor.xsl.sef.json) and the demo/ showcase
page stay local-only. New coverage: extractor/canonical/lint fixtures
for the storage form, tests/browser/blocks.mjs against
tests/fixture-blocks.html (hydration, round-trip, navigation,
boundaries, dialog insertion top-level and nested, stage-2/sweep
atomicity, canonical copy, undo); fixture-nesting.html runs the
extended SEF so every core suite doubles as proof the extension layers
non-invasively.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The plan shipped in 0496acf (src/blocks.xsl, src/ldh-blocks.xsl,
src/ldh-editor.xsl, demo/, tests, CLAUDE.md docs); the file is no
longer needed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant