diff --git a/CLAUDE.md b/CLAUDE.md index 0c38f70..4351540 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,40 +4,40 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Multi-instance model (M2) -- **Editable regions** are any elements with the `rdfa-editor-content` class token (host-page chrome, canonicalization-stripped). No `id('content')` singletons anywhere: `local:roots()`, `local:root-of()`, `local:active-root()` (selection, then last focused host, then first region) in `edit.xsl`. +- **Editable regions** are any elements with the `rdfa-editor-content` class token (host-page chrome, canonicalization-stripped). No `id('content')` singletons anywhere: `rdfae:roots()`, `rdfae:root-of()`, `rdfae:active-root()` (selection, then last focused host, then first region) in `edit.xsl`. - **Undo is region-keyed**: one global stack, each stash entry carries `data-root` (region index) and restores only its region; caret data is region-relative. Stash ids: `rdfa-editor-undo-storage/-stack/-redo-stack`. - Blocks never move between regions (drop-target resolution is scoped to the dragged block's region). ToC and view-source follow the active region (`tocRoot` remembered at render); lint and find work across all regions. -- **Editor state lives on one window container** `window.rdfaEditor` — a single object created with `Object()` in the `main` template (mirrors LinkedDataHub's `window.LinkedDataHub`), holding all mutable state under bare names (`range`, `activeBlock`, `editingSpan`, …). Every read/write goes through the `local:editor-state()` accessor (`index.xsl`): reads `ixsl:get(local:editor-state(), 'range')`, writes `object="local:editor-state()"`. Browser globals (`scrollX`, `Date`, `location.href`, …) are read straight off `ixsl:window()` and are not editor state. Editor UI containers carry the `rdfa-editor-ui` class and all generic CSS selectors (`.btn-*`, `.modal-*`, `.crumb`, …) are scoped under it — LDH/Bootstrap pages stay unaffected. +- **Editor state lives on one window container** `window.rdfaEditor` — a single object created with `Object()` in the `main` template (mirrors LinkedDataHub's `window.LinkedDataHub`), holding all mutable state under bare names (`range`, `activeBlock`, `editingSpan`, …). Every read/write goes through the `rdfae:editor-state()` accessor (`index.xsl`): reads `ixsl:get(rdfae:editor-state(), 'range')`, writes `object="rdfae:editor-state()"`. Browser globals (`scrollX`, `Date`, `location.href`, …) are read straight off `ixsl:window()` and are not editor state. Editor UI containers carry the `rdfa-editor-ui` class and all generic CSS selectors (`.btn-*`, `.modal-*`, `.crumb`, …) are scoped under it — LDH/Bootstrap pages stay unaffected. - The extractor entry is a **named template only** (`-it:extract-rdfa`; no unnamed-mode `match="/"`) so it composes with host stylesheets' root templates. Integration with LinkedDataHub's client.xsl is compile-proven (see docs/ldh/MIGRATION.md §10). - Links in content: plain click places the caret (links render with a text I-beam, not a pointer, so the affordance matches — they are editable text); Ctrl/Cmd+Click opens the href. The annotation overlay paints the stored selection as `.rdfa-editor-selection-hint` boxes (no content mutation), cleared on hide. ## Overview -RDFa-Editor is a prototype XHTML+RDFa authoring tool using client-side XSLT. Content lives in a `#content` container of structured blocks (p, h1–h3, ul/ol, blockquote, pre, figure, table), always editable Notion-style: typing within blocks, Enter splits, Backspace-at-start merges, toolbar for block types / inline formatting / lists / figures / tables / links, drag-handle reordering (nested blocks included — every `%block` drags, drops land wherever the content model admits it), a `/` slash menu (in an empty block) and markdown shortcuts (`# `, `- `, `1. `, `> `, ` ``` `) that insert/convert blocks per the content model. **Object blocks** (blocks.xsl) embed RDF-defined blocks per the LDH v6 document format (`XHTML-RDFa-as-LDH-v6-Document-Format` wiki) in two kinds: a *defined* block — a `div[@about][@typeof]` whose `@typeof` matches the `$object-block-types` param, fragment `@about` (a document part), its defining triples inline as `span[@property]` children — and a *reference* block — an **empty div whose absolute `@about` names a resource outside the document** (no `ldh:Object` wrapper, no `rdf:value` indirection — naming the resource IS the reference, and the empty div extracts to zero triples). Both are treated as **atomic islands** (never editable inside, focusable/navigable/deletable like block images, hard merge/delete boundaries) and rendered client-side into an ephemeral `div[@data-role='rendering']` child via the overridable `mode="local:render-island"` hook; the LinkedDataHub extension (ldh-blocks.xsl, layered by the ldh-editor.xsl entry via `xsl:import`) supplies `ldh:View`/`ldh:ResultSetChart` renderers plus the reference dereferencer (conneg on the `@about` URI — a DBpedia resource works directly, CORS + 303 to RDF/XML), an insert dialog and slash/toolbar contributions. Nesting follows the **XHTML Strict content model** (see content-model.xsl below): `blockquote` is a block *container* (`blockquote > p+` — bare text inside it is invalid and gets normalized; the toolbar quote toggle wraps/unwraps), `li`/`td`/`th`/`dd`/`figcaption` are `%Flow;` (mixed text AND nested blocks — Tab/Shift+Tab indent/outdent nested lists), `p`/`h1–h6`/`caption`/`dt` are inline-only. Tables are composite blocks (structure locked; `td`/`th`/`caption` editable) inserted via a rows×cols dialog with an optional header row and caption; row/column toolbar controls, Tab/Shift+Tab cell traversal and Enter (step down a column) all grow the grid at its bottom edge. Right-click a text selection to annotate it with RDFa; right-click an existing annotation to edit or remove it. "Extract RDF" shows the page's triples; "Source" shows the canonical XHTML+RDFa serialization (all editing ephemera stripped) in [Exclusive XML Canonicalization](https://www.w3.org/TR/xml-exc-c14n/) form (`

`, sorted attributes/namespace declarations, no indentation — the `rdf:XMLLiteral` value space). +RDFa-Editor is a prototype XHTML+RDFa authoring tool using client-side XSLT. Content lives in a `#content` container of structured blocks (p, h1–h3, ul/ol, blockquote, pre, figure, table), always editable Notion-style: typing within blocks, Enter splits, Backspace-at-start merges, toolbar for block types / inline formatting / lists / figures / tables / links, drag-handle reordering (nested blocks included — every `%block` drags, drops land wherever the content model admits it), a `/` slash menu (in an empty block) and markdown shortcuts (`# `, `- `, `1. `, `> `, ` ``` `) that insert/convert blocks per the content model. **Object blocks** (blocks.xsl) embed RDF-defined blocks per the LDH v6 document format (`XHTML-RDFa-as-LDH-v6-Document-Format` wiki) in two kinds: a *defined* block — a `div[@about][@typeof]` whose `@typeof` matches the `$object-block-types` param, fragment `@about` (a document part), its defining triples inline as `span[@property]` children — and a *reference* block — an **empty div whose absolute `@about` names a resource outside the document** (no `ldh:Object` wrapper, no `rdf:value` indirection — naming the resource IS the reference, and the empty div extracts to zero triples). Both are treated as **atomic islands** (never editable inside, focusable/navigable/deletable like block images, hard merge/delete boundaries) and rendered client-side into an ephemeral `div[@data-role='rendering']` child via the overridable `mode="rdfae:render-island"` hook; the LinkedDataHub extension (ldh-blocks.xsl, layered by the ldh-editor.xsl entry via `xsl:import`) supplies `ldh:View`/`ldh:ResultSetChart` renderers plus the reference dereferencer (conneg on the `@about` URI — a DBpedia resource works directly, CORS + 303 to RDF/XML), an insert dialog and slash/toolbar contributions. Nesting follows the **XHTML Strict content model** (see content-model.xsl below): `blockquote` is a block *container* (`blockquote > p+` — bare text inside it is invalid and gets normalized; the toolbar quote toggle wraps/unwraps), `li`/`td`/`th`/`dd`/`figcaption` are `%Flow;` (mixed text AND nested blocks — Tab/Shift+Tab indent/outdent nested lists), `p`/`h1–h6`/`caption`/`dt` are inline-only. Tables are composite blocks (structure locked; `td`/`th`/`caption` editable) inserted via a rows×cols dialog with an optional header row and caption; row/column toolbar controls, Tab/Shift+Tab cell traversal and Enter (step down a column) all grow the grid at its bottom edge. Right-click a text selection to annotate it with RDFa; right-click an existing annotation to edit or remove it. "Extract RDF" shows the page's triples; "Source" shows the canonical XHTML+RDFa serialization (all editing ephemera stripped) in [Exclusive XML Canonicalization](https://www.w3.org/TR/xml-exc-c14n/) form (`

`, sorted attributes/namespace declarations, no indentation — the `rdf:XMLLiteral` value space). ## Architecture -XSLT 3.0 running on **SaxonJS 3** with its interactive extensions (`ixsl:` namespace). `index.html` loads `lib/SaxonJS3.js` plus `lib/xml-c14n-sync.js` (the exclusive-c14n serializer LinkedDataHub also ships — the "Source" view calls it through `local:canonicalize-xml` in edit.xsl, a port of LDH's `ldh:canonicalize-xml`: `serialize()` → `parse-xml()` bridges XDM to browser DOM, which the lib walks) and executes `dist/index.xsl.sef.json` (initial template `main`); async work uses the **SaxonJS 3 promise API** (`ixsl:promise` + `ixsl:http-request` `=> ixsl:then`, `on-failure`; DOM-mutating callbacks are `ixsl:updating="yes"` functions) — **never the legacy `ixsl:schedule-action`**. The editor UI is fully generated by the stylesheet — the overlay is rendered once at startup (hidden) and only populated/shown/hidden afterwards, so the stylesheet is self-contained and embeddable on any host page (host pages must include both libs). +XSLT 3.0 running on **SaxonJS 3** with its interactive extensions (`ixsl:` namespace). `index.html` loads `lib/SaxonJS3.js` plus `lib/xml-c14n-sync.js` (the exclusive-c14n serializer LinkedDataHub also ships — the "Source" view calls it through `rdfae:canonicalize-xml` in edit.xsl, a port of LDH's `ldh:canonicalize-xml`: `serialize()` → `parse-xml()` bridges XDM to browser DOM, which the lib walks) and executes `dist/index.xsl.sef.json` (initial template `main`); async work uses the **SaxonJS 3 promise API** (`ixsl:promise` + `ixsl:http-request` `=> ixsl:then`, `on-failure`; DOM-mutating callbacks are `ixsl:updating="yes"` functions) — **never the legacy `ixsl:schedule-action`**. The editor UI is fully generated by the stylesheet — the overlay is rendered once at startup (hidden) and only populated/shown/hidden afterwards, so the stylesheet is self-contained and embeddable on any host page (host pages must include both libs). Two build flavors: **core** (`src/index.xsl` → `dist/index.xsl.sef.json`, deployed to GitHub Pages — `deploy-pages.yml` excludes the LDH artifacts) and **LDH-extended** (`src/ldh-editor.xsl` → `dist/ldh-editor.xsl.sef.json`, used by `demo/index.html` and the browser fixtures — local only, its object blocks need the content-negotiating dev server). Modules under `src/`: -- **index.xsl** — the core entry point: `main` template (fully synchronous — the host page preloads the vocabularies into the SaxonJS `documentPool` keyed by page-relative URI, see index.html), editor-state container init, `local:editor-state()`; the module include list. +- **index.xsl** — the core entry point: `main` template (fully synchronous — the host page preloads the vocabularies into the SaxonJS `documentPool` keyed by page-relative URI, see index.html), editor-state container init, `rdfae:editor-state()`; the module include list. - **RDFa2RDFXML-v3.xsl** — RDFa 1.1 → RDF/XML extraction. **Pure XSLT, no `ixsl:`** — it runs headless via `xslt3` for the test suite. Strictly W3C-conformant (RDFa 1.1 processing rules, section 7.5, including chaining via typed resources and `rdfa:usesVocabulary`). Covers `@about`/`@typeof`/`@property`/`@content`/`@datatype`/`@resource`/`@href`/`@src`/`@prefix` (and `xmlns:*`)/`@vocab` with bare terms, `@lang`+`@xml:lang` inheritance, base-URI resolution (`about=""` = the document, `` honored), and exclusion of `head`/`script`/`style`/`[data-role='rendering']` subtrees. Out of scope (documented future work): `@rel`/`@rev`, `@inlist`, safe CURIEs, `@datetime`/`