content: list-card summary projection (summary.sx) + 14 tests (697/697)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 52s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 52s
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,7 +15,7 @@ if [ ! -x "$SX_SERVER" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
SUITES=(block doc render api meta page page-full markdown text section compose tree-edit move clone query toc anchor outline flatten transform normalize find-replace stats table callout media data wire 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 tree-edit move clone query toc anchor outline flatten transform normalize find-replace stats summary table callout media data wire validate store snapshot crdt crdt-store sync md-import md-doc fed)
|
||||
|
||||
OUT_JSON="lib/content/scoreboard.json"
|
||||
OUT_MD="lib/content/scoreboard.md"
|
||||
@@ -58,6 +58,7 @@ run_suite() {
|
||||
(load "lib/content/normalize.sx")
|
||||
(load "lib/content/find-replace.sx")
|
||||
(load "lib/content/stats.sx")
|
||||
(load "lib/content/summary.sx")
|
||||
(load "lib/content/table.sx")
|
||||
(load "lib/content/callout.sx")
|
||||
(load "lib/content/media.sx")
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
"normalize": {"pass": 11, "fail": 0},
|
||||
"find-replace": {"pass": 10, "fail": 0},
|
||||
"stats": {"pass": 17, "fail": 0},
|
||||
"summary": {"pass": 14, "fail": 0},
|
||||
"table": {"pass": 15, "fail": 0},
|
||||
"callout": {"pass": 12, "fail": 0},
|
||||
"media": {"pass": 15, "fail": 0},
|
||||
@@ -38,7 +39,7 @@
|
||||
"md-doc": {"pass": 12, "fail": 0},
|
||||
"fed": {"pass": 20, "fail": 0}
|
||||
},
|
||||
"total_pass": 683,
|
||||
"total_pass": 697,
|
||||
"total_fail": 0,
|
||||
"total": 683
|
||||
"total": 697
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ _Generated by `lib/content/conformance.sh`_
|
||||
| normalize | 11 | 0 | 11 |
|
||||
| find-replace | 10 | 0 | 10 |
|
||||
| stats | 17 | 0 | 17 |
|
||||
| summary | 14 | 0 | 14 |
|
||||
| table | 15 | 0 | 15 |
|
||||
| callout | 12 | 0 | 12 |
|
||||
| media | 15 | 0 | 15 |
|
||||
@@ -41,4 +42,4 @@ _Generated by `lib/content/conformance.sh`_
|
||||
| md-import | 38 | 0 | 38 |
|
||||
| md-doc | 12 | 0 | 12 |
|
||||
| fed | 20 | 0 | 20 |
|
||||
| **Total** | **683** | **0** | **683** |
|
||||
| **Total** | **697** | **0** | **697** |
|
||||
|
||||
26
lib/content/summary.sx
Normal file
26
lib/content/summary.sx
Normal file
@@ -0,0 +1,26 @@
|
||||
;; content-on-sx — list-card summary projection.
|
||||
;;
|
||||
;; content/summary returns a one-call projection for index/listing cards:
|
||||
;; {:id :title :excerpt :words :reading-minutes :cover}
|
||||
;; composing the metadata, text, stats and query layers. `cover` is the first
|
||||
;; image's src (or nil).
|
||||
;;
|
||||
;; Requires (loaded by harness): doc.sx, meta.sx (doc-title), text.sx
|
||||
;; (content/excerpt), stats.sx (word-count/reading), query.sx (select-type).
|
||||
|
||||
(define
|
||||
content/summary-title
|
||||
(fn (doc) (let ((t (doc-title doc))) (if (= t nil) (doc-id doc) t))))
|
||||
|
||||
(define
|
||||
content/cover
|
||||
(fn
|
||||
(doc)
|
||||
(let
|
||||
((imgs (content/select-type doc "image")))
|
||||
(if
|
||||
(= (len imgs) 0)
|
||||
nil
|
||||
(str (blk-get (first imgs) "src"))))))
|
||||
|
||||
(define content/summary (fn (doc) {:id (doc-id doc) :reading-minutes (content/reading-minutes doc) :words (content/word-count doc) :title (content/summary-title doc) :excerpt (content/excerpt doc 160) :cover (content/cover doc)}))
|
||||
74
lib/content/tests/summary.sx
Normal file
74
lib/content/tests/summary.sx
Normal file
@@ -0,0 +1,74 @@
|
||||
;; Extension — list-card summary projection.
|
||||
|
||||
(st-bootstrap-classes!)
|
||||
(content/bootstrap!)
|
||||
(content-bootstrap-text!)
|
||||
|
||||
(define
|
||||
d
|
||||
(doc-with-title
|
||||
(doc-append
|
||||
(doc-append
|
||||
(doc-append (doc-empty "post") (mk-heading "h" 1 "Hello"))
|
||||
(mk-text "p" "one two three four"))
|
||||
(mk-image "img" "/cover.png" "cover"))
|
||||
"My Post"))
|
||||
|
||||
;; image alt ("cover") is part of the plain-text projection, so it counts.
|
||||
(define s (content/summary d))
|
||||
(content-test "summary id" (get s :id) "post")
|
||||
(content-test "summary title" (get s :title) "My Post")
|
||||
(content-test
|
||||
"summary excerpt"
|
||||
(get s :excerpt)
|
||||
"Hello one two three four cover")
|
||||
(content-test "summary words" (get s :words) 6)
|
||||
(content-test "summary reading" (get s :reading-minutes) 1)
|
||||
(content-test "summary cover" (get s :cover) "/cover.png")
|
||||
|
||||
;; ── title falls back to id ──
|
||||
(content-test
|
||||
"summary title fallback"
|
||||
(get
|
||||
(content/summary (doc-append (doc-empty "x") (mk-text "p" "y")))
|
||||
:title)
|
||||
"x")
|
||||
|
||||
;; ── no image → cover nil ──
|
||||
(content-test
|
||||
"no cover"
|
||||
(get
|
||||
(content/summary (doc-append (doc-empty "x") (mk-text "p" "y")))
|
||||
:cover)
|
||||
nil)
|
||||
(content-test "cover helper nil" (content/cover (doc-empty "e")) nil)
|
||||
|
||||
;; ── first image wins as cover ──
|
||||
(define
|
||||
d2
|
||||
(doc-append
|
||||
(doc-append (doc-empty "d") (mk-image "i1" "/a.png" "a"))
|
||||
(mk-image "i2" "/b.png" "b")))
|
||||
(content-test "first image cover" (content/cover d2) "/a.png")
|
||||
|
||||
;; ── empty doc ──
|
||||
(define se (content/summary (doc-empty "e")))
|
||||
(content-test "empty summary words" (get se :words) 0)
|
||||
(content-test "empty summary excerpt" (get se :excerpt) "")
|
||||
(content-test "empty summary cover" (get se :cover) nil)
|
||||
|
||||
;; ── excerpt truncates long content ──
|
||||
(content-test
|
||||
"excerpt truncated"
|
||||
(>
|
||||
(string-length
|
||||
(get
|
||||
(content/summary
|
||||
(doc-append
|
||||
(doc-empty "d")
|
||||
(mk-text
|
||||
"p"
|
||||
"word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word")))
|
||||
:excerpt))
|
||||
100)
|
||||
true)
|
||||
@@ -19,7 +19,7 @@ injected adapter, not core.
|
||||
|
||||
## Status (rolling)
|
||||
|
||||
`bash lib/content/conformance.sh` → **683/683** (Phases 1–4 COMPLETE + ~31 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 + deep editing + flatten + relative reorder, doc stats, table + callout + media blocks, HTML page wrapper + SEO page, doc composition + id-remap, portable data + wire serialization, block query + transforms + find/replace, TOC + anchored headings + outline, normalization)
|
||||
`bash lib/content/conformance.sh` → **697/697** (Phases 1–4 COMPLETE + ~32 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 + deep editing + flatten + relative reorder, doc stats + summary, table + callout + media blocks, HTML page wrapper + SEO page, doc composition + id-remap, portable data + wire serialization, block query + transforms + find/replace, TOC + anchored headings + outline, normalization)
|
||||
|
||||
## Ground rules
|
||||
|
||||
@@ -91,6 +91,7 @@ lib/content/api.sx ── (content/edit) (content/render) (content/history) ─
|
||||
- [x] table block (`table.sx`: CtTable, renders html/sx/text/md, validated)
|
||||
- [x] callout block (`callout.sx`: CtCallout note/warning/tip, renders html/sx/text/md, validated)
|
||||
- [x] media block (`media.sx`: CtMedia video/audio, renders html/sx/text/md, validated)
|
||||
- [x] list-card summary (`summary.sx`: content/summary — title/excerpt/words/reading/cover)
|
||||
- [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] document composition (`compose.sx`: concat/prepend/concat-all/wrap-section)
|
||||
@@ -110,6 +111,10 @@ lib/content/api.sx ── (content/edit) (content/render) (content/history) ─
|
||||
|
||||
## Progress log
|
||||
|
||||
- 2026-06-07 — Extension: list-card summary (`summary.sx`). `content/summary`
|
||||
returns `{:id :title :excerpt :words :reading-minutes :cover}` for index/listing
|
||||
cards, composing metadata + text + stats + query (`content/cover` = first
|
||||
image's src). Title falls back to id. 14 tests; suite 697/697.
|
||||
- 2026-06-07 — Extension: video/audio media block (`media.sx`). `CtMedia` holds
|
||||
kind (video/audio) + src; answers asHTML (`<video/audio src controls>`,
|
||||
escaped), asSx, asText (""), asMarkdown: (`[kind](src)`). `mk-media`/`mk-video`/
|
||||
|
||||
Reference in New Issue
Block a user