Files
rose-ash/lib/host/sx/kg-cards.sx
giles 7f87054ec3
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 1m22s
host: load kg-cards components so imported Ghost posts render fully
Imported Ghost posts' sx_content holds (~kg_cards/kg-*) (from the lexical_to_sx converter);
the host's render-page resolves components, but the kg-cards weren't loaded so they
degraded to '(unsupported block)' placeholders. Copied blog/sx/kg_cards.sx ->
lib/host/sx/kg-cards.sx (host self-contained, not coupled to the legacy blog/ Quart dir)
+ added the one host-local dep ~rich-text (was only a test fixture) + registered it in
serve.sh + conformance.sh module lists. Verified: the real 'Free DVD Box Sets!' post now
renders <figure class=kg-card kg-image-card> for all images, zero placeholders.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 15:25:01 +00:00

158 lines
10 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
;; KG card components — Ghost/Koenig-compatible card rendering, copied into the host
;; so it can render imported Ghost posts (sx_content holds (~kg_cards/kg-*) from the
;; lexical_to_sx converter). Produces the same HTML structure as lexical_renderer.py.
;;
;; ~rich-text: the host-local dep these cards need (raw HTML injection). Defined here
;; (it was only a test fixture before) so kg-html/kg-bookmark/etc. resolve in the host.
(defcomp ~rich-text (&key (html :as string)) (raw! html))
;; @css kg-card kg-image-card kg-width-wide kg-width-full kg-gallery-card kg-gallery-container kg-gallery-row kg-gallery-image kg-embed-card kg-bookmark-card kg-bookmark-container kg-bookmark-content kg-bookmark-title kg-bookmark-description kg-bookmark-metadata kg-bookmark-icon kg-bookmark-author kg-bookmark-publisher kg-bookmark-thumbnail kg-callout-card kg-callout-emoji kg-callout-text kg-button-card kg-btn kg-btn-accent kg-toggle-card kg-toggle-heading kg-toggle-heading-text kg-toggle-card-icon kg-toggle-content kg-audio-card kg-audio-thumbnail kg-audio-player-container kg-audio-title kg-audio-player kg-audio-play-icon kg-audio-current-time kg-audio-time kg-audio-seek-slider kg-audio-playback-rate kg-audio-unmute-icon kg-audio-volume-slider kg-video-card kg-video-container kg-file-card kg-file-card-container kg-file-card-contents kg-file-card-title kg-file-card-filesize kg-file-card-icon kg-file-card-caption kg-align-center kg-align-left kg-callout-card-grey kg-callout-card-white kg-callout-card-blue kg-callout-card-green kg-callout-card-yellow kg-callout-card-red kg-callout-card-pink kg-callout-card-purple kg-callout-card-accent kg-html-card kg-md-card placeholder
;; ---------------------------------------------------------------------------
;; Image card
;; ---------------------------------------------------------------------------
(defcomp ~kg_cards/kg-image (&key (src :as string) (alt :as string?) (caption :as string?) (width :as string?) (href :as string?))
(figure :class (str "kg-card kg-image-card"
(if (= width "wide") " kg-width-wide"
(if (= width "full") " kg-width-full" "")))
(if href
(a :href href (img :src src :alt (or alt "") :loading "lazy"))
(img :src src :alt (or alt "") :loading "lazy"))
(when caption (figcaption caption))))
;; ---------------------------------------------------------------------------
;; Gallery card
;; ---------------------------------------------------------------------------
(defcomp ~kg_cards/kg-gallery (&key (images :as list) (caption :as string?))
(figure :class "kg-card kg-gallery-card kg-width-wide"
(div :class "kg-gallery-container"
(map (lambda (row)
(div :class "kg-gallery-row"
(map (lambda (img-data)
(figure :class "kg-gallery-image"
(img :src (get img-data "src") :alt (or (get img-data "alt") "") :loading "lazy")
(when (get img-data "caption") (figcaption (get img-data "caption")))))
row)))
images))
(when caption (figcaption caption))))
;; ---------------------------------------------------------------------------
;; HTML card — wraps user-pasted HTML so the editor can identify the block.
;; Content is native sx children (no longer an opaque HTML string).
;; ---------------------------------------------------------------------------
(defcomp ~kg_cards/kg-html (&rest children)
(div :class "kg-card kg-html-card" children))
;; ---------------------------------------------------------------------------
;; Markdown card — rendered markdown content, editor can identify the block.
;; ---------------------------------------------------------------------------
(defcomp ~kg_cards/kg-md (&rest children)
(div :class "kg-card kg-md-card" children))
;; ---------------------------------------------------------------------------
;; Embed card
;; ---------------------------------------------------------------------------
(defcomp ~kg_cards/kg-embed (&key (html :as string) (caption :as string?))
(figure :class "kg-card kg-embed-card"
(~rich-text :html html)
(when caption (figcaption caption))))
;; ---------------------------------------------------------------------------
;; Bookmark card
;; ---------------------------------------------------------------------------
(defcomp ~kg_cards/kg-bookmark (&key (url :as string) (title :as string?) (description :as string?) (icon :as string?) (author :as string?) (publisher :as string?) (thumbnail :as string?) (caption :as string?))
(figure :class "kg-card kg-bookmark-card"
(a :class "kg-bookmark-container" :href url
(div :class "kg-bookmark-content"
(div :class "kg-bookmark-title" (or title ""))
(div :class "kg-bookmark-description" (or description ""))
(when (or icon author publisher)
(span :class "kg-bookmark-metadata"
(when icon (img :class "kg-bookmark-icon" :src icon :alt ""))
(when author (span :class "kg-bookmark-author" author))
(when publisher (span :class "kg-bookmark-publisher" publisher)))))
(when thumbnail
(div :class "kg-bookmark-thumbnail"
(img :src thumbnail :alt ""))))
(when caption (figcaption caption))))
;; ---------------------------------------------------------------------------
;; Callout card
;; ---------------------------------------------------------------------------
(defcomp ~kg_cards/kg-callout (&key (color :as string?) (emoji :as string?) (content :as string?))
(div :class (str "kg-card kg-callout-card kg-callout-card-" (or color "grey"))
(when emoji (div :class "kg-callout-emoji" emoji))
(div :class "kg-callout-text" (or content ""))))
;; ---------------------------------------------------------------------------
;; Button card
;; ---------------------------------------------------------------------------
(defcomp ~kg_cards/kg-button (&key (url :as string) (text :as string?) (alignment :as string?))
(div :class (str "kg-card kg-button-card kg-align-" (or alignment "center"))
(a :href url :class "kg-btn kg-btn-accent" (or text ""))))
;; ---------------------------------------------------------------------------
;; Toggle card (accordion)
;; ---------------------------------------------------------------------------
(defcomp ~kg_cards/kg-toggle (&key (heading :as string?) (content :as string?))
(div :class "kg-card kg-toggle-card" :data-kg-toggle-state "close"
(div :class "kg-toggle-heading"
(h4 :class "kg-toggle-heading-text" (or heading ""))
(button :class "kg-toggle-card-icon"
(~rich-text :html "<svg viewBox=\"0 0 14 14\"><path d=\"M7 0a.5.5 0 0 1 .5.5v6h6a.5.5 0 1 1 0 1h-6v6a.5.5 0 1 1-1 0v-6h-6a.5.5 0 0 1 0-1h6v-6A.5.5 0 0 1 7 0Z\" fill=\"currentColor\"/></svg>")))
(div :class "kg-toggle-content" (or content ""))))
;; ---------------------------------------------------------------------------
;; Audio card
;; ---------------------------------------------------------------------------
(defcomp ~kg_cards/kg-audio (&key (src :as string) (title :as string?) (duration :as string?) (thumbnail :as string?))
(div :class "kg-card kg-audio-card"
(if thumbnail
(img :src thumbnail :alt "audio-thumbnail" :class "kg-audio-thumbnail")
(div :class "kg-audio-thumbnail placeholder"
(~rich-text :html "<svg viewBox=\"0 0 24 24\"><path d=\"M2 12C2 6.48 6.48 2 12 2s10 4.48 10 10-4.48 10-10 10S2 17.52 2 12zm7.5 5.25L16 12 9.5 6.75v10.5z\" fill=\"currentColor\"/></svg>")))
(div :class "kg-audio-player-container"
(div :class "kg-audio-title" (or title ""))
(div :class "kg-audio-player"
(button :class "kg-audio-play-icon"
(~rich-text :html "<svg viewBox=\"0 0 24 24\"><path d=\"M8 5v14l11-7z\" fill=\"currentColor\"/></svg>"))
(div :class "kg-audio-current-time" "0:00")
(div :class "kg-audio-time" (str "/ " (or duration "0:00")))
(input :type "range" :class "kg-audio-seek-slider" :max "100" :value "0")
(button :class "kg-audio-playback-rate" "1×")
(button :class "kg-audio-unmute-icon"
(~rich-text :html "<svg viewBox=\"0 0 24 24\"><path d=\"M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z\" fill=\"currentColor\"/></svg>"))
(input :type "range" :class "kg-audio-volume-slider" :max "100" :value "100")))
(audio :src src :preload "metadata")))
;; ---------------------------------------------------------------------------
;; Video card
;; ---------------------------------------------------------------------------
(defcomp ~kg_cards/kg-video (&key (src :as string) (caption :as string?) (width :as string?) (thumbnail :as string?) (loop :as boolean?))
(figure :class (str "kg-card kg-video-card"
(if (= width "wide") " kg-width-wide"
(if (= width "full") " kg-width-full" "")))
(div :class "kg-video-container"
(video :src src :controls true :preload "metadata"
:poster (or thumbnail nil) :loop (or loop nil)))
(when caption (figcaption caption))))
;; ---------------------------------------------------------------------------
;; File card
;; ---------------------------------------------------------------------------
(defcomp ~kg_cards/kg-file (&key (src :as string) (filename :as string?) (title :as string?) (filesize :as string?) (caption :as string?))
(div :class "kg-card kg-file-card"
(a :class "kg-file-card-container" :href src :download (or filename "")
(div :class "kg-file-card-contents"
(div :class "kg-file-card-title" (or title filename ""))
(when filesize (div :class "kg-file-card-filesize" filesize)))
(div :class "kg-file-card-icon"
(~rich-text :html "<svg viewBox=\"0 0 24 24\"><path d=\"M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z\" fill=\"currentColor\"/></svg>")))
(when caption (div :class "kg-file-card-caption" caption))))
;; ---------------------------------------------------------------------------
;; Paywall marker
;; ---------------------------------------------------------------------------
(defcomp ~kg_cards/kg-paywall ()
(~rich-text :html "<!--members-only-->"))