content: document composition (compose.sx) + 17 tests (502/502)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 59s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 59s
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
42
lib/content/compose.sx
Normal file
42
lib/content/compose.sx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
;; content-on-sx — document composition.
|
||||||
|
;;
|
||||||
|
;; Combine documents (header + body + footer, templates, partials) into a new
|
||||||
|
;; document. The result keeps the FIRST document's id and metadata; blocks are
|
||||||
|
;; concatenated. Immutable — inputs are untouched. Block-id collisions across
|
||||||
|
;; combined docs are the caller's concern (content/validate flags duplicates).
|
||||||
|
;;
|
||||||
|
;; Requires (loaded by harness): doc.sx.
|
||||||
|
|
||||||
|
(define
|
||||||
|
content/concat
|
||||||
|
(fn (a b) (doc-with-blocks a (append (doc-blocks a) (doc-blocks b)))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
content/prepend
|
||||||
|
(fn (a b) (doc-with-blocks a (append (doc-blocks b) (doc-blocks a)))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
content/-concat-fold
|
||||||
|
(fn
|
||||||
|
(acc more)
|
||||||
|
(if
|
||||||
|
(= (len more) 0)
|
||||||
|
acc
|
||||||
|
(content/-concat-fold (content/concat acc (first more)) (rest more)))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
content/concat-all
|
||||||
|
(fn
|
||||||
|
(docs)
|
||||||
|
(if
|
||||||
|
(= (len docs) 0)
|
||||||
|
(doc-empty "merged")
|
||||||
|
(content/-concat-fold (first docs) (rest docs)))))
|
||||||
|
|
||||||
|
;; wrap a document's blocks inside a single section (collapse to a subtree).
|
||||||
|
;; Requires section.sx (mk-section) when used.
|
||||||
|
(define
|
||||||
|
content/wrap-section
|
||||||
|
(fn
|
||||||
|
(doc section-id)
|
||||||
|
(doc-with-blocks doc (list (mk-section section-id (doc-blocks doc))))))
|
||||||
@@ -15,7 +15,7 @@ if [ ! -x "$SX_SERVER" ]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
SUITES=(block doc render api meta page page-full markdown text section stats table validate store snapshot crdt crdt-store sync md-import md-doc fed)
|
SUITES=(block doc render api meta page page-full markdown text section compose stats table validate store snapshot crdt crdt-store sync md-import md-doc fed)
|
||||||
|
|
||||||
OUT_JSON="lib/content/scoreboard.json"
|
OUT_JSON="lib/content/scoreboard.json"
|
||||||
OUT_MD="lib/content/scoreboard.md"
|
OUT_MD="lib/content/scoreboard.md"
|
||||||
@@ -45,6 +45,7 @@ run_suite() {
|
|||||||
(load "lib/content/meta.sx")
|
(load "lib/content/meta.sx")
|
||||||
(load "lib/content/text.sx")
|
(load "lib/content/text.sx")
|
||||||
(load "lib/content/section.sx")
|
(load "lib/content/section.sx")
|
||||||
|
(load "lib/content/compose.sx")
|
||||||
(load "lib/content/stats.sx")
|
(load "lib/content/stats.sx")
|
||||||
(load "lib/content/table.sx")
|
(load "lib/content/table.sx")
|
||||||
(load "lib/content/page.sx")
|
(load "lib/content/page.sx")
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
"markdown": {"pass": 20, "fail": 0},
|
"markdown": {"pass": 20, "fail": 0},
|
||||||
"text": {"pass": 20, "fail": 0},
|
"text": {"pass": 20, "fail": 0},
|
||||||
"section": {"pass": 25, "fail": 0},
|
"section": {"pass": 25, "fail": 0},
|
||||||
|
"compose": {"pass": 17, "fail": 0},
|
||||||
"stats": {"pass": 17, "fail": 0},
|
"stats": {"pass": 17, "fail": 0},
|
||||||
"table": {"pass": 15, "fail": 0},
|
"table": {"pass": 15, "fail": 0},
|
||||||
"validate": {"pass": 23, "fail": 0},
|
"validate": {"pass": 23, "fail": 0},
|
||||||
@@ -22,7 +23,7 @@
|
|||||||
"md-doc": {"pass": 12, "fail": 0},
|
"md-doc": {"pass": 12, "fail": 0},
|
||||||
"fed": {"pass": 20, "fail": 0}
|
"fed": {"pass": 20, "fail": 0}
|
||||||
},
|
},
|
||||||
"total_pass": 485,
|
"total_pass": 502,
|
||||||
"total_fail": 0,
|
"total_fail": 0,
|
||||||
"total": 485
|
"total": 502
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ _Generated by `lib/content/conformance.sh`_
|
|||||||
| markdown | 20 | 0 | 20 |
|
| markdown | 20 | 0 | 20 |
|
||||||
| text | 20 | 0 | 20 |
|
| text | 20 | 0 | 20 |
|
||||||
| section | 25 | 0 | 25 |
|
| section | 25 | 0 | 25 |
|
||||||
|
| compose | 17 | 0 | 17 |
|
||||||
| stats | 17 | 0 | 17 |
|
| stats | 17 | 0 | 17 |
|
||||||
| table | 15 | 0 | 15 |
|
| table | 15 | 0 | 15 |
|
||||||
| validate | 23 | 0 | 23 |
|
| validate | 23 | 0 | 23 |
|
||||||
@@ -25,4 +26,4 @@ _Generated by `lib/content/conformance.sh`_
|
|||||||
| md-import | 38 | 0 | 38 |
|
| md-import | 38 | 0 | 38 |
|
||||||
| md-doc | 12 | 0 | 12 |
|
| md-doc | 12 | 0 | 12 |
|
||||||
| fed | 20 | 0 | 20 |
|
| fed | 20 | 0 | 20 |
|
||||||
| **Total** | **485** | **0** | **485** |
|
| **Total** | **502** | **0** | **502** |
|
||||||
|
|||||||
76
lib/content/tests/compose.sx
Normal file
76
lib/content/tests/compose.sx
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
;; Extension — document composition.
|
||||||
|
|
||||||
|
(st-bootstrap-classes!)
|
||||||
|
(content/bootstrap!)
|
||||||
|
(content-bootstrap-section!)
|
||||||
|
|
||||||
|
(define
|
||||||
|
a
|
||||||
|
(doc-with-title
|
||||||
|
(doc-append (doc-empty "a") (mk-heading "h" 1 "A"))
|
||||||
|
"Doc A"))
|
||||||
|
(define
|
||||||
|
b
|
||||||
|
(doc-append
|
||||||
|
(doc-append (doc-empty "b") (mk-text "p" "B1"))
|
||||||
|
(mk-text "q" "B2")))
|
||||||
|
|
||||||
|
;; ── concat ──
|
||||||
|
(define ab (content/concat a b))
|
||||||
|
(content-test "concat ids" (doc-ids ab) (list "h" "p" "q"))
|
||||||
|
(content-test "concat keeps first id" (doc-id ab) "a")
|
||||||
|
(content-test "concat keeps first title" (doc-title ab) "Doc A")
|
||||||
|
(content-test "concat immutable a" (doc-ids a) (list "h"))
|
||||||
|
(content-test "concat immutable b" (doc-ids b) (list "p" "q"))
|
||||||
|
|
||||||
|
;; ── prepend ──
|
||||||
|
(define ba (content/prepend a b))
|
||||||
|
(content-test "prepend ids" (doc-ids ba) (list "p" "q" "h"))
|
||||||
|
(content-test "prepend keeps a id" (doc-id ba) "a")
|
||||||
|
|
||||||
|
;; ── concat with empty ──
|
||||||
|
(content-test
|
||||||
|
"concat empty right"
|
||||||
|
(doc-ids (content/concat a (doc-empty "e")))
|
||||||
|
(list "h"))
|
||||||
|
(content-test
|
||||||
|
"concat empty left"
|
||||||
|
(doc-ids (content/concat (doc-empty "e") b))
|
||||||
|
(list "p" "q"))
|
||||||
|
|
||||||
|
;; ── concat-all ──
|
||||||
|
(define c (doc-append (doc-empty "c") (mk-divider "d")))
|
||||||
|
(content-test
|
||||||
|
"concat-all order"
|
||||||
|
(doc-ids (content/concat-all (list a b c)))
|
||||||
|
(list "h" "p" "q" "d"))
|
||||||
|
(content-test
|
||||||
|
"concat-all keeps first id"
|
||||||
|
(doc-id (content/concat-all (list a b c)))
|
||||||
|
"a")
|
||||||
|
(content-test
|
||||||
|
"concat-all single"
|
||||||
|
(doc-ids (content/concat-all (list a)))
|
||||||
|
(list "h"))
|
||||||
|
(content-test
|
||||||
|
"concat-all empty"
|
||||||
|
(doc-ids (content/concat-all (list)))
|
||||||
|
(list))
|
||||||
|
|
||||||
|
;; ── render of composed doc ──
|
||||||
|
(content-test
|
||||||
|
"composed renders"
|
||||||
|
(asHTML (content/concat a b))
|
||||||
|
"<h1>A</h1><p>B1</p><p>B2</p>")
|
||||||
|
|
||||||
|
;; ── wrap-section collapses blocks into a subtree ──
|
||||||
|
(define w (content/wrap-section ab "sec"))
|
||||||
|
(content-test "wrap top-level is one section" (doc-ids w) (list "sec"))
|
||||||
|
(content-test
|
||||||
|
"wrap children preserved"
|
||||||
|
(doc-tree-ids w)
|
||||||
|
(list "sec" "h" "p" "q"))
|
||||||
|
(content-test
|
||||||
|
"wrap renders nested"
|
||||||
|
(asHTML w)
|
||||||
|
"<section><h1>A</h1><p>B1</p><p>B2</p></section>")
|
||||||
@@ -19,7 +19,7 @@ injected adapter, not core.
|
|||||||
|
|
||||||
## Status (rolling)
|
## Status (rolling)
|
||||||
|
|
||||||
`bash lib/content/conformance.sh` → **485/485** (Phases 1–4 COMPLETE + extensions: HTML/SX escaping, Markdown render + import/export incl. tables & frontmatter (full round-trip), CRDT replication, tree-aware validation, snapshot cache, doc metadata, plain-text render, nested block trees, doc stats, table block, HTML page wrapper + SEO page)
|
`bash lib/content/conformance.sh` → **502/502** (Phases 1–4 COMPLETE + extensions: HTML/SX escaping, Markdown render + import/export incl. tables & frontmatter (full round-trip), CRDT replication, tree-aware validation, snapshot cache, doc metadata, plain-text render, nested block trees, doc stats, table block, HTML page wrapper + SEO page, doc composition)
|
||||||
|
|
||||||
## Ground rules
|
## Ground rules
|
||||||
|
|
||||||
@@ -91,9 +91,15 @@ lib/content/api.sx ── (content/edit) (content/render) (content/history) ─
|
|||||||
- [x] table block (`table.sx`: CtTable, renders html/sx/text/md, validated)
|
- [x] table block (`table.sx`: CtTable, renders html/sx/text/md, validated)
|
||||||
- [x] HTML page wrapper (`page.sx`: content/page, escaped title from metadata)
|
- [x] HTML page wrapper (`page.sx`: content/page, escaped title from metadata)
|
||||||
- [x] SEO page (`page-full.sx`: content/page-full, lang + meta description from excerpt)
|
- [x] SEO page (`page-full.sx`: content/page-full, lang + meta description from excerpt)
|
||||||
|
- [x] document composition (`compose.sx`: concat/prepend/concat-all/wrap-section)
|
||||||
|
|
||||||
## Progress log
|
## Progress log
|
||||||
|
|
||||||
|
- 2026-06-07 — Extension: document composition (`compose.sx`). `content/concat`
|
||||||
|
/ `content/prepend` / `content/concat-all` combine documents (keeping the
|
||||||
|
first's id + metadata, concatenating blocks, immutable); `content/wrap-section`
|
||||||
|
collapses a doc's blocks into a single nested section. For assembling pages
|
||||||
|
from header/body/footer parts and templates. 17 tests; suite 502/502.
|
||||||
- 2026-06-07 — Extension: SEO-complete page (`page-full.sx`). `content/page-full`
|
- 2026-06-07 — Extension: SEO-complete page (`page-full.sx`). `content/page-full`
|
||||||
extends content/page with `<html lang="en">` and a `<meta name="description">`
|
extends content/page with `<html lang="en">` and a `<meta name="description">`
|
||||||
drawn from the document excerpt (plain text, escaped, 160 chars), composing the
|
drawn from the document excerpt (plain text, escaped, 160 chars), composing the
|
||||||
|
|||||||
Reference in New Issue
Block a user