;; Phase 1 — ordered block document: apply edit ops, structural moves. ;; Every op returns a NEW document; the input is never mutated. (st-bootstrap-classes!) (content-bootstrap-blocks!) (content-bootstrap-doc!) (define h (mk-heading "h" 1 "Title")) (define p1 (mk-text "p1" "First")) (define p2 (mk-text "p2" "Second")) (define img (mk-image "img" "/c.png" "cat")) ;; ── empty + construction ── (define d0 (doc-empty "doc1")) (content-test "empty id" (doc-id d0) "doc1") (content-test "empty type" (doc-type d0) "document") (content-test "empty count" (doc-count d0) 0) (content-test "doc? on doc" (doc? d0) true) (content-test "doc? on block" (doc? h) false) ;; ── append + order ── (define d1 (doc-append (doc-append (doc-append d0 h) p1) p2)) (content-test "append count" (doc-count d1) 3) (content-test "append order" (doc-ids d1) (list "h" "p1" "p2")) (content-test "append types" (doc-types d1) (list "heading" "text" "text")) (content-test "block-at 0" (blk-id (doc-block-at d1 0)) "h") ;; ── append is immutable ── (content-test "append leaves original" (doc-count d0) 0) ;; ── find / index / has ── (content-test "find p1" (blk-id (doc-find d1 "p1")) "p1") (content-test "find missing" (doc-find d1 "nope") nil) (content-test "index-of p2" (doc-index-of d1 "p2") 2) (content-test "index-of missing" (doc-index-of d1 "nope") -1) (content-test "has? yes" (doc-has? d1 "h") true) (content-test "has? no" (doc-has? d1 "x") false) ;; ── insert-after ── (define d2 (doc-insert-after d1 img "h")) (content-test "insert-after order" (doc-ids d2) (list "h" "img" "p1" "p2")) (content-test "insert-after prepend" (doc-ids (doc-insert-after d1 img nil)) (list "img" "h" "p1" "p2")) (content-test "insert-after missing appends" (doc-ids (doc-insert-after d1 img "zzz")) (list "h" "p1" "p2" "img")) (content-test "insert-after immutable" (doc-ids d1) (list "h" "p1" "p2")) ;; ── insert-at ── (content-test "insert-at 0" (doc-ids (doc-insert-at d1 img 0)) (list "img" "h" "p1" "p2")) (content-test "insert-at 1" (doc-ids (doc-insert-at d1 img 1)) (list "h" "img" "p1" "p2")) ;; ── update (copy-on-write block) ── (define d3 (doc-update d1 "p1" "text" "Edited")) (content-test "update value" (str (blk-send (doc-find d3 "p1") "text")) "Edited") (content-test "update keeps order" (doc-ids d3) (list "h" "p1" "p2")) (content-test "update immutable" (str (blk-send (doc-find d1 "p1") "text")) "First") ;; ── delete ── (define d4 (doc-delete d1 "p1")) (content-test "delete order" (doc-ids d4) (list "h" "p2")) (content-test "delete count" (doc-count d4) 2) (content-test "delete immutable" (doc-count d1) 3) (content-test "delete missing no-op" (doc-ids (doc-delete d1 "x")) (list "h" "p1" "p2")) ;; ── move ── (content-test "move p2 to front" (doc-ids (doc-move d1 "p2" 0)) (list "p2" "h" "p1")) (content-test "move h to end" (doc-ids (doc-move d1 "h" 2)) (list "p1" "p2" "h")) (content-test "move missing no-op" (doc-ids (doc-move d1 "x" 0)) (list "h" "p1" "p2")) (content-test "move immutable" (doc-ids d1) (list "h" "p1" "p2")) ;; ── op constructors + interpreter ── (content-test "op-insert apply" (doc-ids (doc-apply d1 (op-insert img "h"))) (list "h" "img" "p1" "p2")) (content-test "op-delete apply" (doc-ids (doc-apply d1 (op-delete "h"))) (list "p1" "p2")) (content-test "op-move apply" (doc-ids (doc-apply d1 (op-move "p2" 0))) (list "p2" "h" "p1")) (content-test "op-update apply" (str (blk-send (doc-find (doc-apply d1 (op-update "p1" "text" "X")) "p1") "text")) "X") ;; ── apply-all: a stream of ops ── (define ops (list (op-insert img "h") (op-delete "p1") (op-move "p2" 0))) (content-test "apply-all" (doc-ids (doc-apply-all d1 ops)) (list "p2" "h" "img")) (content-test "apply-all immutable" (doc-ids d1) (list "h" "p1" "p2")) (content-test "apply-all empty" (doc-ids (doc-apply-all d1 (list))) (list "h" "p1" "p2"))