From 39c3def2e762b548a3ea4f318270bddf77173fb7 Mon Sep 17 00:00:00 2001 From: giles Date: Wed, 1 Jul 2026 10:08:12 +0000 Subject: [PATCH] host: composition editor for and/or/each + relative-addressed refs (resolve-in-context) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The block editor now edits the object's ONE root composition (:body) as three block kinds — CARD (a ref leaf, the "and"/content), CONDITIONAL (alt+when, the "or": render the first branch whose live-context condition holds), and REPEATER (each: render a template per graph query). The render-fold already interprets seq/alt/when/each/ref, so authored compositions render for free; this adds the editing model + UI. ADDRESSING (per the design discussion — refs are IPNS-like, not frozen CIDs): refs are RELATIVE-STORED + RESOLVE-IN-CONTEXT. A :body stores (ref "body__b0") (field-relative); the render context carries the CONTAINER (the object being rendered) and the resolver combines them -> the card's storage slug ____. So a body is portable (doesn't pin the container's name), and editing a card updates everything that refs it for free (no cascade). A cross-domain ref is absolute with an authority ("market:…"); the resolver dispatches on the prefix (local today, fetch_data/AP later). A compat shim resolves an older absolute ref directly. (Snapshot-to-absolute-CID stays a future on-demand op; the CID — hash(record incl :body) — is the immutable layer over this naming layer.) MODEL: host/blog--{card-slug,resolve-ref,slug->ref,new-card!,node-kind,node-refs,node-pred, node-each-type,cond->pred,pred->ckey}; block-add!/add-cond!/add-each!; index-addressed block-move-idx!/remove-idx!/set-cond! (alt/each aren't single refs). UI: host/blog--block-row renders by kind (card / "if → … else → …" / "for each → …") with a condition ) from a predicate — the inverse of host/blog--cond->pred. +(define host/blog--pred->ckey + (fn (pred) + (if (= (type-of pred) "list") + (let ((op (str (first pred)))) + (cond + ((= op "has") "auth") + ((and (= op "eq") (= (str (first (rest pred))) "device") (= (str (first (rest (rest pred)))) "mobile")) "device:mobile") + ((and (= op "eq") (= (str (first (rest pred))) "device")) "device:desktop") + ((and (= op "eq") (= (str (first (rest pred))) "locale")) "locale:fr") + (else "auth"))) + "auth"))) + +;; add a CARD block to `field`: (ref ). Returns the new card's storage slug. +(define host/blog-block-add! + (fn (slug ctype fields) + (let ((cslug (host/blog--new-card! slug "body" ctype fields))) + (begin (host/blog--append-node! slug (list (quote ref) (host/blog--slug->ref slug cslug))) cslug)))) +;; add a CONDITIONAL (or) block: (alt (when (ref A)) (else (ref B))) — A/B relative refs. +(define host/blog-block-add-cond! + (fn (slug ckey) + (let ((a (host/blog--slug->ref slug (host/blog--new-card! slug "body" "card-text" {"text" "shown when the condition holds"}))) + (b (host/blog--slug->ref slug (host/blog--new-card! slug "body" "card-text" {"text" "shown otherwise"})))) + (host/blog--append-node! slug + (list (quote alt) + (list (quote when) (host/blog--cond->pred ckey) (list (quote ref) a)) + (list (quote else) (list (quote ref) b))))))) +;; add a REPEATER (each) block: (each (query is-a TYPE) (ref