Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
- **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 `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).
- The extractor entry is a **named template only** (`rdfax:extract-rdfa`, invoked headless as `-it:"Q{https://w3id.org/atomgraph/rdfa-editor/rdfa#}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
Expand Down Expand Up @@ -61,7 +61,7 @@ Run `make sef` after any XSLT change; run the tests after any extractor change.
- **No deviation from W3C specs.** The extractor follows the RDFa 1.1 processing rules exactly. Markup that relies on non-conformant readings (e.g. `about="#part" property="schema:hasPart"` expected to yield an edge) is a markup bug — the conformant containment idiom is `property="…hasPart" resource="#part" typeof="…"`.
- **Content model: XHTML 1.0 Strict + HTML5 figure**, transcribed in `content-model.xsl` — nesting the DTD allows must round-trip intact; nesting it disallows is normalized at boundaries (canonical serialization, paste, load-init) and reported by lint. This changed the **storage contract for blockquote**: it is block-only, so stored bare-text quotes are rewritten to `blockquote > p` once at load/save (LDH content audit advised).
- All `innerHTML` writes of serialized XDM use `serialize(…, map{ 'method': 'html' })` — XML's self-closing `<p/>` reads as an *open* tag to the HTML fragment parser and swallows following siblings.
- The `$base-uri` global param is declared in `RDFa2RDFXML-v3.xsl` **only** — a second declaration in an including module is a static error (XTSE0630). The browser passes the page URI as a template param to `extract-rdfa` instead.
- The `$base-uri` global param is declared in `RDFa2RDFXML-v3.xsl` **only** — a second declaration in an including module is a static error (XTSE0630). The browser passes the page URI as a template param to `rdfax:extract-rdfa` instead.
- The SEF is compiled with `-relocate:on`: relative `doc()`/`document()`/`@document` hrefs resolve against the SEF's **load location** (`dist/`), which is why `generate-sef.sh` copies `vocabs/` to `dist/vocabs/`.
- Form state (`checked`/`value`/`disabled`) is read **and** written via `ixsl:get`/`ixsl:set-property` — the attributes never reflect user input. `ixsl:set-attribute` is used only for RDFa attributes that must serialize into content.
- Vocabularies are plain ontology RDF/XML files in `vocabs/` — no custom manifest format. Adding a vocabulary = drop the file in `vocabs/` and add its href to `$vocab-hrefs`.
Expand Down
6 changes: 3 additions & 3 deletions docs/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ The prototype (through commit `b8466cf`) is functionally rich: structured-block
- Element allowlist question: canonicalization currently passes unknown elements through (shallow-copy) — decide pass-through vs allowlist for embedded content (iframe/object/embed/form should not survive).

### A2. Editing completeness
- **HTML paste** with sanitization: currently plain-text only. We already own the cleanup machinery — parse `clipboardData` `text/html` via `parse-xml`/fragment parsing and run it through `mode="canonical"` + the new sanitization rules; insert clean fragment. (Word/Google-Docs paste is the single biggest prod-usability item.)
- **HTML paste** with sanitization: currently plain-text only. We already own the cleanup machinery — parse `clipboardData` `text/html` via `parse-xml`/fragment parsing and run it through `mode="cm:canonical"` + the new sanitization rules; insert clean fragment. (Word/Google-Docs paste is the single biggest prod-usability item.)
- Nested lists (indent/outdent via Tab/Shift+Tab in `li`), `ul`↔`ol` conversion of an existing list.
- h4–h6 in the block-type select (cheap); `code`/`sub`/`sup` inline toggles (cheap — the `format-inline` machinery is generic).
- Image upload (LDH has upload flows) — URL-only first; wire LDH upload later.
Expand Down Expand Up @@ -47,7 +47,7 @@ Mapped integration surface (all in `LinkedDataHub/src/main/webapp/static/com/ato
6. **Vocabularies** — `/ns?uri=<vocab>&accept=application/rdf+xml` endpoint (constructor.xsl:203-209 pattern) via `ixsl:promise`; typeahead control (LDH typeahead.xsl precedent) replaces the selects.
7. **i18n** — strings into `translations.rdf` (`key('resources', id, document(...))` + `ac:label`).
8. **Feature flag** — decision DEFERRED (user); document options: `lapp:Application` property / XSL param via web.xml / hard cutover.
9. **Build** — modules pulled into the LDH webapp tree at build time and `xsl:import`ed from `client.xsl`; existing pom `xslt3-he … -relocate:on` step compiles everything (pom.xml:39, 383-391). Conflict audit checklist: our unnamed-mode `match="/"` (extract-rdfa) vs LDH root templates, `body` keydown fallback, host-level event templates, `xsl:output`, `id('content')` assumptions.
9. **Build** — modules pulled into the LDH webapp tree at build time and `xsl:import`ed from `client.xsl`; existing pom `xslt3-he … -relocate:on` step compiles everything (pom.xml:39, 383-391). Conflict audit checklist: the extractor entry (named-only `rdfax:extract-rdfa` since the unnamed-mode `match="/"` was dropped) vs LDH root templates, `body` keydown fallback, host-level event templates, `xsl:output`, `id('content')` assumptions.
10. **v6 note** — view mode renders XMLLiteral via identity transform (`imports/default.xsl:1489-1503`); the eventual v6 in-place model (edit the view markup, PUT canonical doc) is the successor to this form-control integration.

## C. Milestones
Expand All @@ -74,7 +74,7 @@ Mapped integration surface (all in `LinkedDataHub/src/main/webapp/static/com/ato
### D2. HTML paste (`src/edit.xsl` onpaste)
- `text/html` non-empty → HTML path; else existing plain-text path.
- Parse: detached `$carrier := createElement('div')` + `innerHTML :=` clipboard HTML (scripts inert when detached; XPath over detached nodes already proven).
- Sanitize: `$clean :=` apply-templates `$carrier/node()` in `mode="canonical"` (now incl. D1 rules).
- Sanitize: `$clean :=` apply-templates `$carrier/node()` in `mode="cm:canonical"` (now incl. D1 rules).
- Wrap stray top-level inline runs: `for-each-group group-adjacent="boolean(self::p|self::h1|…block…)"` → non-block groups wrapped in `<p>`.
- Re-materialize XDM → live DOM: `serialize()` the fragment → `$stage := createElement('div')` + `innerHTML` (safe post-sanitization).
- Insert:
Expand Down
8 changes: 4 additions & 4 deletions docs/ldh/MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ kept the textarea in sync continuously; the replacement syncs once, declarativel
```xml
<xsl:template match="div[contains-token(@class, 'rdfa-editor-content')]" mode="ldh:FormPreSubmit">
<xsl:variable name="canonical" as="node()*">
<xsl:apply-templates select="node()" mode="canonical"/>
<xsl:apply-templates select="node()" mode="cm:canonical"/>
</xsl:variable>
<xsl:for-each select="following-sibling::input[@name = 'ol'][1]">
<ixsl:set-property name="value" select="serialize($canonical, map{ 'method': 'xml' })" object="."/>
Expand All @@ -65,7 +65,7 @@ kept the textarea in sync continuously; the replacement syncs once, declarativel
```

Key points: serialize the container's **children only** (no wrapper element —
parse-rdf-post adds the div); `mode="canonical"` guarantees the stored literal is
parse-rdf-post adds the div); `mode="cm:canonical"` guarantees the stored literal is
sanitized and free of editing ephemera (chrome, contenteditable, classes, on*
handlers, unsafe URLs). SaxonJS 3 applies `ixsl:set-property` immediately
(verified in-browser), so the value is readable by `ldh:parse-rdf-post` within
Expand Down Expand Up @@ -132,8 +132,8 @@ The full integration was proven by compiling LDH's `client.xsl` (from the built
single SEF via `xslt3-he -nogo -ns:##html5 -relocate:on` — no errors, no conflicts.

Audit results:
- Extractor entry is a **named template only** (`extract-rdfa`; the unnamed-mode
`match="/"` was removed; headless tests invoke with `-it:extract-rdfa`). All other
- Extractor entry is a **named template only** (`rdfax:extract-rdfa`; the unnamed-mode
`match="/"` was removed; headless tests invoke with `-it:"Q{https://w3id.org/atomgraph/rdfa-editor/rdfa#}extract-rdfa"`). All other
editor matching lives in named modes (`rdfax:extract`, `canonical`) or `ixsl:*` event
modes.
- Event templates: LDH has no `contenteditable` usage and no `body` keydown template
Expand Down
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
return SaxonJS.transform({
documentPool: pool,
stylesheetLocation: "dist/index.xsl.sef.json",
initialTemplate: "main",
initialTemplate: "Q{https://w3id.org/atomgraph/rdfa-editor#}main",
logLevel: 10
}, "async");
})
Expand Down
3 changes: 2 additions & 1 deletion src/RDFa2RDFXML-v3.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@
'dct': 'http://purl.org/dc/terms/'
}"/>

<xsl:template match="/" name="extract-rdfa">
<!-- named entry only: an unnamed-mode match="/" here would intercept every default-mode dispatch against a document node in a host stylesheet that imports this module (the composition contract in CLAUDE.md) -->
<xsl:template name="rdfax:extract-rdfa">
<xsl:param name="doc" as="document-node()" select="."/>
<xsl:param name="base" as="xs:string" select="($base-uri, string(base-uri($doc)))[1]"/>
<xsl:variable name="effective-base" as="xs:string"
Expand Down
2 changes: 1 addition & 1 deletion src/annotate.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ version="3.0">
rdf:Description per subject (via rdfae:group-triples) for readability -->
<xsl:template match="button[@id = 'parse-rdf']" mode="ixsl:onclick">
<xsl:variable name="rdf" as="element(rdf:RDF)">
<xsl:call-template name="extract-rdfa">
<xsl:call-template name="rdfax:extract-rdfa">
<xsl:with-param name="doc" select="ixsl:page()"/>
<xsl:with-param name="base" select="rdfae:document-uri()"/>
</xsl:call-template>
Expand Down
Loading
Loading