Add (param :as type) annotations to defcomp params across all services and templates

Annotates ~500 defcomp params across 62 files: market (5), blog (7), cart (5),
events (3), federation (4), account (3), orders (2), shared templates (11),
sx docs (14), plus remaining spec fn params (z3, test-framework, adapter-dom,
adapter-async, engine, eval). Total annotations in codebase: 1043.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-11 21:01:02 +00:00
parent 98c1023b81
commit 477ce766ff
62 changed files with 537 additions and 502 deletions

View File

@@ -1,10 +1,10 @@
;; Market card components — pure data, no raw! HTML injection
(defcomp ~market-label-overlay (&key src)
(defcomp ~market-label-overlay (&key (src :as string))
(img :src src :alt ""
:class "pointer-events-none absolute inset-0 w-full h-full object-contain object-top"))
(defcomp ~market-card-image (&key image labels brand brand-highlight)
(defcomp ~market-card-image (&key (image :as string) (labels :as list?) (brand :as string) (brand-highlight :as string?))
(div :class "w-full aspect-square bg-stone-100 relative"
(figure :class "inline-block w-full h-full"
(div :class "relative w-full h-full"
@@ -12,35 +12,35 @@
(when labels (map (lambda (src) (~market-label-overlay :src src)) labels)))
(figcaption :class (str "mt-2 text-sm text-center" brand-highlight " text-stone-600") brand))))
(defcomp ~market-card-no-image (&key labels brand)
(defcomp ~market-card-no-image (&key (labels :as list?) (brand :as string))
(div :class "w-full aspect-square bg-stone-100 relative"
(div :class "p-2 flex flex-col items-center justify-center gap-2 text-red-500 h-full relative"
(div :class "text-stone-400 text-xs" "No image")
(when labels (ul :class "flex flex-row gap-1" (map (lambda (l) (li l)) labels)))
(div :class "text-stone-900 text-center line-clamp-3 break-words [overflow-wrap:anywhere]" brand))))
(defcomp ~market-card-sticker (&key src name ring-cls)
(defcomp ~market-card-sticker (&key (src :as string) (name :as string) (ring-cls :as string?))
(img :src src :alt name :class (str "w-6 h-6" ring-cls)))
(defcomp ~market-card-stickers (&key stickers)
(defcomp ~market-card-stickers (&key (stickers :as list))
(div :class "flex flex-row justify-center gap-2 p-2"
(map (lambda (s) (~market-card-sticker :src (get s "src") :name (get s "name") :ring-cls (get s "ring-cls"))) stickers)))
(defcomp ~market-card-highlight (&key pre mid post)
(defcomp ~market-card-highlight (&key (pre :as string) (mid :as string) (post :as string))
(<> pre (mark mid) post))
;; Price — delegates to shared ~price
(defcomp ~market-card-price (&key special-price regular-price)
(defcomp ~market-card-price (&key (special-price :as string?) (regular-price :as string?))
(~price :special-price special-price :regular-price regular-price))
;; Main product card — accepts pure data, composes sub-components
(defcomp ~market-product-card (&key href hx-select
has-like liked slug csrf like-action
image labels brand brand-highlight
special-price regular-price
cart-action quantity cart-href
stickers
title has-highlight search-pre search-mid search-post)
(defcomp ~market-product-card (&key (href :as string) (hx-select :as string)
(has-like :as boolean) (liked :as boolean?) (slug :as string) (csrf :as string) (like-action :as string?)
(image :as string?) (labels :as list?) (brand :as string) (brand-highlight :as string?)
(special-price :as string?) (regular-price :as string?)
(cart-action :as string) (quantity :as number?) (cart-href :as string)
(stickers :as list?)
(title :as string) (has-highlight :as boolean) (search-pre :as string?) (search-mid :as string?) (search-post :as string?))
(div :class "flex flex-col rounded-xl bg-white shadow hover:shadow-md transition overflow-hidden relative"
(when has-like
(~market-like-button :form-id (str "like-" slug) :action like-action :slug slug :csrf csrf
@@ -65,7 +65,7 @@
(~market-card-highlight :pre search-pre :mid search-mid :post search-post)
title)))))
(defcomp ~market-like-button (&key form-id action slug csrf icon-cls)
(defcomp ~market-like-button (&key (form-id :as string) (action :as string) (slug :as string) (csrf :as string) (icon-cls :as string))
(div :class "absolute top-2 right-2 z-10 text-6xl md:text-xl"
(form :id form-id :action action :method "post"
:sx-post action :sx-target (str "#like-" slug) :sx-swap "outerHTML"
@@ -73,22 +73,22 @@
(button :type "submit" :class "cursor-pointer"
(i :class icon-cls :aria-hidden "true")))))
(defcomp ~market-market-card-title-link (&key href name)
(defcomp ~market-market-card-title-link (&key (href :as string) (name :as string))
(a :href href :class "hover:text-emerald-700"
(h2 :class "text-lg font-semibold text-stone-900" name)))
(defcomp ~market-market-card-title (&key name)
(defcomp ~market-market-card-title (&key (name :as string))
(h2 :class "text-lg font-semibold text-stone-900" name))
(defcomp ~market-market-card-desc (&key description)
(defcomp ~market-market-card-desc (&key (description :as string))
(p :class "text-sm text-stone-600 mt-1 line-clamp-2" description))
(defcomp ~market-market-card-badge (&key href title)
(defcomp ~market-market-card-badge (&key (href :as string) (title :as string))
(div :class "flex flex-wrap items-center gap-1.5 mt-3"
(a :href href :class "inline-block px-2 py-0.5 rounded-full text-xs font-medium bg-amber-100 text-amber-800 hover:bg-amber-200"
title)))
(defcomp ~market-market-card (&key title-content desc-content badge-content title desc badge)
(defcomp ~market-market-card (&key (title-content :as list?) (desc-content :as list?) (badge-content :as list?) (title :as string?) (desc :as string?) (badge :as string?))
(article :class "rounded-xl bg-white shadow-sm border border-stone-200 p-5 flex flex-col justify-between hover:border-stone-400 transition-colors"
(div
(if title-content title-content (when title title))
@@ -101,8 +101,8 @@
;; ---------------------------------------------------------------------------
;; Product cards grid with infinite scroll sentinels
(defcomp ~market-product-cards-content (&key products page total-pages next-url
mobile-sentinel-hs desktop-sentinel-hs)
(defcomp ~market-product-cards-content (&key (products :as list) (page :as number) (total-pages :as number) (next-url :as string)
(mobile-sentinel-hs :as string?) (desktop-sentinel-hs :as string?))
(<>
(map (lambda (p)
(~market-product-card
@@ -126,7 +126,7 @@
(~end-of-results))))
;; Single market card from data (handles conditional title/desc/badge)
(defcomp ~market-card-from-data (&key name description href show-badge badge-href badge-title)
(defcomp ~market-card-from-data (&key (name :as string) (description :as string?) (href :as string?) (show-badge :as boolean) (badge-href :as string?) (badge-title :as string?))
(~market-market-card
:title-content (if href
(~market-market-card-title-link :href href :name name)
@@ -137,7 +137,7 @@
(~market-market-card-badge :href badge-href :title badge-title))))
;; Market cards list with infinite scroll sentinel
(defcomp ~market-cards-content (&key markets page has-more next-url)
(defcomp ~market-cards-content (&key (markets :as list) (page :as number) (has-more :as boolean) (next-url :as string))
(<>
(map (lambda (m)
(~market-card-from-data
@@ -149,7 +149,7 @@
(~sentinel-simple :id (str "sentinel-" page) :next-url next-url))))
;; Market landing page content from data
(defcomp ~market-landing-from-data (&key excerpt feature-image html)
(defcomp ~market-landing-from-data (&key (excerpt :as string?) (feature-image :as string?) (html :as string?))
(~market-landing-content :inner
(<> (when excerpt (~market-landing-excerpt :text excerpt))
(when feature-image (~market-landing-image :src feature-image))

View File

@@ -1,6 +1,6 @@
;; Market product detail components
(defcomp ~market-detail-gallery-inner (&key like image alt labels brand)
(defcomp ~market-detail-gallery-inner (&key (like :as list?) (image :as string) (alt :as string) (labels :as list?) (brand :as string))
(<> like
(figure :class "inline-block"
(div :class "relative w-full aspect-square"
@@ -18,79 +18,79 @@
:class "absolute right-2 top-1/2 -translate-y-1/2 z-10 grid place-items-center w-12 h-12 md:w-14 md:h-14 rounded-full bg-white/90 hover:bg-white shadow-lg text-3xl md:text-4xl"
:title "Next" "\u203a")))
(defcomp ~market-detail-gallery (&key inner nav)
(defcomp ~market-detail-gallery (&key (inner :as list) (nav :as list?))
(div :class "relative rounded-xl overflow-hidden bg-stone-100"
inner nav))
(defcomp ~market-detail-thumb (&key title src alt)
(defcomp ~market-detail-thumb (&key (title :as string) (src :as string) (alt :as string))
(<> (button :type "button" :data-thumb ""
:class "shrink-0 rounded-lg overflow-hidden bg-stone-100 hover:opacity-90 ring-offset-2"
:title title
(img :src src :class "h-16 w-16 object-contain" :alt alt :loading "lazy" :decoding "async"))
(span :data-image-src src :class "hidden")))
(defcomp ~market-detail-thumbs (&key thumbs)
(defcomp ~market-detail-thumbs (&key (thumbs :as list))
(div :class "flex flex-row justify-center"
(div :class "mt-3 flex gap-2 overflow-x-auto no-scrollbar" thumbs)))
(defcomp ~market-detail-no-image (&key like)
(defcomp ~market-detail-no-image (&key (like :as list?))
(div :class "relative aspect-square bg-stone-100 rounded-xl flex items-center justify-center text-stone-400"
like "No image"))
(defcomp ~market-detail-sticker (&key src name)
(defcomp ~market-detail-sticker (&key (src :as string) (name :as string))
(img :src src :alt name :class "w-10 h-10"))
(defcomp ~market-detail-stickers (&key items)
(defcomp ~market-detail-stickers (&key (items :as list))
(div :class "p-2 flex flex-row justify-center gap-2" items))
(defcomp ~market-detail-unit-price (&key price)
(defcomp ~market-detail-unit-price (&key (price :as string))
(div (str "Unit price: " price)))
(defcomp ~market-detail-case-size (&key size)
(defcomp ~market-detail-case-size (&key (size :as string))
(div (str "Case size: " size)))
(defcomp ~market-detail-extras (&key inner)
(defcomp ~market-detail-extras (&key (inner :as list))
(div :class "mt-2 space-y-1 text-sm text-stone-600" inner))
(defcomp ~market-detail-desc-short (&key text)
(defcomp ~market-detail-desc-short (&key (text :as string))
(p :class "leading-relaxed text-lg" text))
(defcomp ~market-detail-desc-html (&key html)
(defcomp ~market-detail-desc-html (&key (html :as string))
(div :class "max-w-none text-sm leading-relaxed" (~rich-text :html html)))
(defcomp ~market-detail-desc-wrapper (&key inner)
(defcomp ~market-detail-desc-wrapper (&key (inner :as list))
(div :class "mt-4 text-stone-800 space-y-3" inner))
(defcomp ~market-detail-section (&key title html)
(defcomp ~market-detail-section (&key (title :as string) (html :as string))
(details :class "group rounded-xl border bg-white shadow-sm open:shadow p-0"
(summary :class "cursor-pointer select-none px-4 py-3 flex items-center justify-between"
(span :class "font-medium" title)
(span :class "ml-2 text-xl transition-transform group-open:rotate-180" "\u2304"))
(div :class "px-4 pb-4 max-w-none text-sm leading-relaxed" (~rich-text :html html))))
(defcomp ~market-detail-sections (&key items)
(defcomp ~market-detail-sections (&key (items :as list))
(div :class "mt-8 space-y-3" items))
(defcomp ~market-detail-right-col (&key inner)
(defcomp ~market-detail-right-col (&key (inner :as list))
(div :class "md:col-span-3" inner))
(defcomp ~market-detail-layout (&key gallery stickers details)
(defcomp ~market-detail-layout (&key (gallery :as list) (stickers :as list?) (details :as list))
(<> (div :class "mt-3 grid grid-cols-1 md:grid-cols-5 gap-6" :data-gallery-root ""
(div :class "md:col-span-2" gallery stickers)
details)
(div :class "pb-8")))
(defcomp ~market-landing-excerpt (&key text)
(defcomp ~market-landing-excerpt (&key (text :as string))
(div :class "w-full text-center italic text-3xl p-2" text))
(defcomp ~market-landing-image (&key src)
(defcomp ~market-landing-image (&key (src :as string))
(div :class "mb-3 flex justify-center"
(img :src src :alt "" :class "rounded-lg w-full md:w-3/4 object-cover")))
(defcomp ~market-landing-html (&key html)
(defcomp ~market-landing-html (&key (html :as string))
(div :class "blog-content p-2" (~rich-text :html html)))
(defcomp ~market-landing-content (&key inner)
(defcomp ~market-landing-content (&key (inner :as list))
(<> (article :class "relative w-full" inner) (div :class "pb-8")))
@@ -99,7 +99,7 @@
;; ---------------------------------------------------------------------------
;; Gallery section from pre-computed data
(defcomp ~market-detail-gallery-from-data (&key images labels brand like-data has-nav-buttons thumbs)
(defcomp ~market-detail-gallery-from-data (&key (images :as list?) (labels :as list?) (brand :as string) (like-data :as dict?) (has-nav-buttons :as boolean) (thumbs :as list?))
(let ((like-sx (when like-data
(~market-like-button
:form-id (get like-data "form-id") :action (get like-data "action")
@@ -124,7 +124,7 @@
(~market-detail-no-image :like like-sx))))
;; Right column details from data
(defcomp ~market-detail-info-from-data (&key extras desc-short desc-html sections)
(defcomp ~market-detail-info-from-data (&key (extras :as list?) (desc-short :as string?) (desc-html :as string?) (sections :as list?))
(~market-detail-right-col :inner
(<>
(when extras
@@ -145,9 +145,9 @@
sections)))))))
;; Full product detail layout from data
(defcomp ~market-product-detail-from-data (&key images labels brand like-data
has-nav-buttons thumbs sticker-items
extras desc-short desc-html sections)
(defcomp ~market-product-detail-from-data (&key (images :as list?) (labels :as list?) (brand :as string) (like-data :as dict?)
(has-nav-buttons :as boolean) (thumbs :as list?) (sticker-items :as list?)
(extras :as list?) (desc-short :as string?) (desc-html :as string?) (sections :as list?))
(~market-detail-layout
:gallery (~market-detail-gallery-from-data
:images images :labels labels :brand brand :like-data like-data

View File

@@ -1,21 +1,21 @@
;; Market meta/SEO components
(defcomp ~market-meta-title (&key title)
(defcomp ~market-meta-title (&key (title :as string))
(title title))
(defcomp ~market-meta-description (&key description)
(defcomp ~market-meta-description (&key (description :as string))
(meta :name "description" :content description))
(defcomp ~market-meta-canonical (&key href)
(defcomp ~market-meta-canonical (&key (href :as string))
(link :rel "canonical" :href href))
(defcomp ~market-meta-og (&key property content)
(defcomp ~market-meta-og (&key (property :as string) (content :as string))
(meta :property property :content content))
(defcomp ~market-meta-twitter (&key name content)
(defcomp ~market-meta-twitter (&key (name :as string) (content :as string))
(meta :name name :content content))
(defcomp ~market-meta-jsonld (&key json)
(defcomp ~market-meta-jsonld (&key (json :as string))
(script :type "application/ld+json" (~rich-text :html json)))
@@ -23,9 +23,10 @@
;; Composition: all product meta tags from data
;; ---------------------------------------------------------------------------
(defcomp ~market-product-meta-from-data (&key title description canonical image-url
site-title brand price price-currency
jsonld-json)
(defcomp ~market-product-meta-from-data (&key (title :as string) (description :as string) (canonical :as string?)
(image-url :as string?)
(site-title :as string) (brand :as string?) (price :as string?) (price-currency :as string?)
(jsonld-json :as string))
(<>
(~market-meta-title :title title)
(~market-meta-description :description description)

View File

@@ -1,6 +1,6 @@
;; Market navigation components
(defcomp ~market-category-link (&key href hx-select active select-colours label)
(defcomp ~market-category-link (&key (href :as string) (hx-select :as string) (active :as boolean) (select-colours :as string) (label :as string))
(div :class "relative nav-group"
(a :href href :sx-get href :sx-target "#main-panel"
:sx-select hx-select :sx-swap "outerHTML" :sx-push-url "true"
@@ -8,14 +8,14 @@
:class (str "block px-2 py-1 rounded text-center whitespace-normal break-words leading-snug bg-stone-200 text-black " select-colours)
label)))
(defcomp ~market-desktop-category-nav (&key links admin)
(defcomp ~market-desktop-category-nav (&key (links :as list) (admin :as list?))
(nav :class "hidden md:flex gap-4 text-sm ml-2 w-full justify-end items-center"
links admin))
(defcomp ~market-mobile-nav-wrapper (&key items)
(defcomp ~market-mobile-nav-wrapper (&key (items :as list))
(div :class "px-4 py-2" (div :class "divide-y" items)))
(defcomp ~market-mobile-all-link (&key href hx-select active select-colours)
(defcomp ~market-mobile-all-link (&key (href :as string) (hx-select :as string) (active :as boolean) (select-colours :as string))
(a :role "option" :href href :sx-get href :sx-target "#main-panel"
:sx-select hx-select :sx-swap "outerHTML" :sx-push-url "true"
:aria-selected (if active "true" "false")
@@ -28,7 +28,7 @@
(path :fill-rule "evenodd" :clip-rule "evenodd"
:d "M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z")))
(defcomp ~market-mobile-cat-summary (&key bg-cls href hx-select select-colours cat-name count-label count-str chevron)
(defcomp ~market-mobile-cat-summary (&key (bg-cls :as string) (href :as string) (hx-select :as string) (select-colours :as string) (cat-name :as string) (count-label :as string) (count-str :as string) (chevron :as list))
(summary :class (str "flex items-center justify-between cursor-pointer select-none block rounded-lg px-3 py-3 text-base hover:bg-stone-50" bg-cls)
(a :href href :sx-get href :sx-target "#main-panel"
:sx-select hx-select :sx-swap "outerHTML" :sx-push-url "true"
@@ -37,7 +37,7 @@
(div :aria-label count-label count-str))
chevron))
(defcomp ~market-mobile-sub-link (&key select-colours active href hx-select label count-label count-str)
(defcomp ~market-mobile-sub-link (&key (select-colours :as string) (active :as boolean) (href :as string) (hx-select :as string) (label :as string) (count-label :as string) (count-str :as string))
(a :class (str "snap-start px-2 py-3 rounded " select-colours " flex flex-row gap-2")
:aria-selected (if active "true" "false")
:href href :sx-get href :sx-target "#main-panel"
@@ -45,20 +45,20 @@
(div label)
(div :aria-label count-label count-str)))
(defcomp ~market-mobile-subs-panel (&key links)
(defcomp ~market-mobile-subs-panel (&key (links :as list))
(div :class "pb-3 pl-2"
(div :data-peek-viewport "" :data-peek-size-px "18" :data-peek-edge "bottom" :data-peek-mask "true" :class "m-2 bg-stone-100"
(div :data-peek-inner "" :class "grid grid-cols-1 gap-1 snap-y snap-mandatory pr-1" :aria-label "Subcategories"
links))))
(defcomp ~market-mobile-view-all (&key href hx-select)
(defcomp ~market-mobile-view-all (&key (href :as string) (hx-select :as string))
(div :class "pb-3 pl-2"
(a :class "px-2 py-1 rounded hover:bg-stone-100 block"
:href href :sx-get href :sx-target "#main-panel"
:sx-select hx-select :sx-swap "outerHTML" :sx-push-url "true"
"View all")))
(defcomp ~market-mobile-cat-details (&key open summary subs)
(defcomp ~market-mobile-cat-details (&key (open :as boolean) (summary :as list) (subs :as list))
(details :class "group/cat py-1" :open open
summary subs))
@@ -67,7 +67,7 @@
;; Composition: mobile nav panel from pre-computed category data
;; ---------------------------------------------------------------------------
(defcomp ~market-mobile-nav-from-data (&key categories all-href all-active hx-select select-colours)
(defcomp ~market-mobile-nav-from-data (&key (categories :as list) (all-href :as string) (all-active :as boolean) (hx-select :as string) (select-colours :as string))
(~market-mobile-nav-wrapper :items
(<>
(~market-mobile-all-link :href all-href :hx-select hx-select

View File

@@ -1,36 +1,36 @@
;; Market price display components
(defcomp ~market-price-special (&key price)
(defcomp ~market-price-special (&key (price :as string))
(div :class "text-lg font-semibold text-emerald-700" price))
(defcomp ~market-price-regular-strike (&key price)
(defcomp ~market-price-regular-strike (&key (price :as string))
(div :class "text-sm line-through text-stone-500" price))
(defcomp ~market-price-regular (&key price)
(defcomp ~market-price-regular (&key (price :as string))
(div :class "mt-1 text-lg font-semibold" price))
(defcomp ~market-price-line (&key inner)
(defcomp ~market-price-line (&key (inner :as list))
(div :class "mt-1 flex items-baseline gap-2 justify-center" inner))
(defcomp ~market-header-price-special-label ()
(div :class "text-md font-bold text-emerald-700" "Special price"))
(defcomp ~market-header-price-special (&key price)
(defcomp ~market-header-price-special (&key (price :as string))
(div :class "text-xl font-semibold text-emerald-700" price))
(defcomp ~market-header-price-strike (&key price)
(defcomp ~market-header-price-strike (&key (price :as string))
(div :class "text-base text-md line-through text-stone-500" price))
(defcomp ~market-header-price-regular-label ()
(div :class "hidden md:block text-xl font-bold" "Our price"))
(defcomp ~market-header-price-regular (&key price)
(defcomp ~market-header-price-regular (&key (price :as string))
(div :class "text-xl font-semibold" price))
(defcomp ~market-header-rrp (&key rrp)
(defcomp ~market-header-rrp (&key (rrp :as string))
(div :class "text-base text-stone-400" (span "rrp:") " " (span rrp)))
(defcomp ~market-prices-row (&key inner)
(defcomp ~market-prices-row (&key (inner :as list))
(div :class "flex flex-row items-center justify-between md:gap-2 md:px-2" inner))
@@ -38,8 +38,9 @@
;; Composition: prices header + cart button from data
;; ---------------------------------------------------------------------------
(defcomp ~market-prices-header-from-data (&key cart-id cart-action csrf quantity cart-href
sp-val sp-str rp-val rp-str rrp-str)
(defcomp ~market-prices-header-from-data (&key (cart-id :as string) (cart-action :as string) (csrf :as string) (quantity :as number?)
(cart-href :as string)
(sp-val :as number?) (sp-str :as string?) (rp-val :as number?) (rp-str :as string?) (rrp-str :as string?))
(~market-prices-row :inner
(<>
(if quantity
@@ -57,7 +58,7 @@
(when rrp-str (~market-header-rrp :rrp rrp-str)))))
;; Card price line from data (used in product cards)
(defcomp ~market-card-price-from-data (&key sp-val sp-str rp-val rp-str)
(defcomp ~market-card-price-from-data (&key (sp-val :as number?) (sp-str :as string?) (rp-val :as number?) (rp-str :as string?))
(~market-price-line :inner
(<>
(when sp-val