with one click
clay
// REPL-friendly data visualization and literate programming for Clojure with Kindly convention
// REPL-friendly data visualization and literate programming for Clojure with Kindly convention
Clojure library for spawning sub-processes and shell operations
Pure Clojure/Script logging library with flexible configuration and powerful features
Django-inspired HTML templating system for Clojure with filters, tags, and template inheritance
Structured logging and telemetry for Clojure/Script with tracing and performance monitoring
Emacs Lisp package development standards and conventions
A guide to using magit-section for building collapsible, hierarchical buffer UIs in Emacs.
| name | clay |
| description | REPL-friendly data visualization and literate programming for Clojure with Kindly convention |
REPL-friendly Clojure tool for data visualization and literate programming. Renders Clojure namespaces and forms as HTML pages, notebooks, and Quarto documents using the Kindly convention.
Clay transforms Clojure code into visual documents by interpreting the Kindly convention for data visualization. It integrates with your REPL workflow and editor, rendering forms and namespaces as HTML with support for charts, tables, markdown, and interactive components.
Key Features:
Artifact: org.scicloj/clay
Latest Version: 2.0.3
License: EPL-1.0
Repository: https://github.com/scicloj/clay
Add to deps.edn:
{:deps {org.scicloj/clay {:mvn/version "2.0.3"}}}
Or Leiningen project.clj:
[org.scicloj/clay "2.0.3"]
Import in namespace:
(ns my-notebook
(:require [scicloj.clay.v2.api :as clay]
[scicloj.kindly.v4.kind :as kind]))
Kindly standardizes how values request visualization. Attach kind metadata to values:
;; Using kind function
(kind/md "# Hello World")
;; Using metadata
^:kind/md ["# Hello World"]
The primary entry point. Renders forms, namespaces, or files:
;; Render single form
(clay/make! {:single-form '(+ 1 2 3)})
;; Render current namespace
(clay/make! {:source-path "notebooks/my_notebook.clj"})
;; Render with options
(clay/make! {:source-path "src/analysis.clj"
:format [:html]
:show true})
Clay produces:
http://localhost:1971/Render content with configuration options.
(clay/make! {:source-path "notebooks/analysis.clj"})
(clay/make! {:single-form '(kind/md "# Title")
:show true})
(clay/make! {:source-path "src/report.clj"
:format [:quarto :html]
:base-target-path "docs"})
Options:
:source-path - Path to .clj file to render:single-form - Single form to evaluate and render:format - Output format(s): :html, :quarto, :revealjs:show - Open browser after rendering (default: true):browse - Enable browser opening:title - Document title:favicon - Path to favicon:base-target-path - Output directory (default: "docs"):quarto - Quarto-specific configuration mapReopen the Clay view in browser.
(clay/browse!)
Configuration merges from multiple sources (lowest to highest priority):
clay-default.edn)~/.config/scicloj-clay/config.edn)clay.edn):clay key)Project clay.edn:
{:source-path "notebooks"
:base-target-path "docs"
:show true
:format [:html]
:quarto {:format {:html {:theme :cosmo}}}}
Namespace metadata:
(ns my-notebook
{:clay {:title "My Analysis"
:quarto {:format {:html {:toc true}}}}})
(kind/md "# Heading
Paragraph with **bold** and *italic*.
- List item 1
- List item 2")
;; With LaTeX
(kind/md "Let $x=9$. Then $x^2=81$.")
(kind/hiccup
[:div {:style {:color "blue"}}
[:h1 "Title"]
[:p "Paragraph"]
[:ul
[:li "Item 1"]
[:li "Item 2"]]])
(kind/html "<div class='alert'>Warning!</div>")
(kind/code "(defn hello [name]
(str \"Hello, \" name \"!\"))")
(kind/tex "\\int_0^\\infty e^{-x^2} dx = \\frac{\\sqrt{\\pi}}{2}")
(kind/vega-lite
{:data {:values [{:x 1 :y 2}
{:x 2 :y 4}
{:x 3 :y 3}]}
:mark :line
:encoding {:x {:field :x :type :quantitative}
:y {:field :y :type :quantitative}}})
;; Bar chart
(kind/vega-lite
{:data {:values [{:category "A" :value 28}
{:category "B" :value 55}
{:category "C" :value 43}]}
:mark :bar
:encoding {:x {:field :category :type :nominal}
:y {:field :value :type :quantitative}}})
(kind/plotly
{:data [{:x [1 2 3 4]
:y [10 15 13 17]
:type :scatter
:mode :lines+markers}]
:layout {:title "Line Chart"}})
;; 3D scatter
(kind/plotly
{:data [{:x [1 2 3]
:y [1 2 3]
:z [1 2 3]
:type :scatter3d
:mode :markers}]})
(kind/echarts
{:xAxis {:type :category
:data ["Mon" "Tue" "Wed" "Thu" "Fri"]}
:yAxis {:type :value}
:series [{:data [150 230 224 218 135]
:type :bar}]})
(kind/highcharts
{:chart {:type :line}
:title {:text "Monthly Sales"}
:xAxis {:categories ["Jan" "Feb" "Mar" "Apr"]}
:series [{:name "Sales"
:data [29.9 71.5 106.4 129.2]}]})
;; Row vectors
(kind/table
{:column-names [:name :age :city]
:row-vectors [["Alice" 30 "NYC"]
["Bob" 25 "LA"]
["Carol" 35 "Chicago"]]})
;; Row maps
(kind/table
{:row-maps [{:name "Alice" :age 30}
{:name "Bob" :age 25}]})
;; With DataTables options
(kind/table
{:row-maps data
:use-datatables true})
(require '[tablecloth.api :as tc])
(-> (tc/dataset {:x [1 2 3]
:y [4 5 6]})
kind/dataset)
;; With print options
(-> dataset
(kind/dataset {:dataset/print-range 20}))
;; From URL
(kind/image {:src "https://example.com/image.png"})
;; BufferedImage object
(kind/image buffered-image)
;; With caption
(kind/image {:src "chart.png"
:alt "Sales Chart"})
;; URL
(kind/video {:src "video.mp4"})
;; YouTube
(kind/video {:youtube-id "dQw4w9WgXcQ"})
(kind/reagent
['(fn []
(let [count (reagent.core/atom 0)]
(fn []
[:div
[:p "Count: " @count]
[:button {:on-click #(swap! count inc)}
"Increment"]])))])
;; With dependencies
(kind/reagent
['(fn [] [:div "Component"])]
{:deps [:reagent]})
(kind/fragment
[(kind/md "# Section 1")
(kind/vega-lite chart-spec)
(kind/md "## Analysis")
(kind/table data)])
(kind/hidden (expensive-computation))
(kind/mermaid
"graph TD
A[Start] --> B{Decision}
B -->|Yes| C[Do Something]
B -->|No| D[Do Other]")
(kind/cytoscape
{:elements {:nodes [{:data {:id "a"}}
{:data {:id "b"}}]
:edges [{:data {:source "a" :target "b"}}]}
:style [{:selector "node"
:style {:label "data(id)"}}]
:layout {:name "preset"}})
(ns my-notebook
{:clay {:title "Data Analysis"}}
(:require [scicloj.kindly.v4.kind :as kind]
[tablecloth.api :as tc]))
;; # Introduction
;; This notebook analyzes...
^:kindly/hide-code
(def data (tc/dataset {:x (range 10)
:y (map #(* % %) (range 10))}))
;; ## Data Overview
(kind/table {:row-maps (tc/rows data :as-maps)})
;; ## Visualization
(kind/vega-lite
{:data {:values (tc/rows data :as-maps)}
:mark :point
:encoding {:x {:field :x :type :quantitative}
:y {:field :y :type :quantitative}}})
;; Hide code for specific form
^:kindly/hide-code
(kind/md "Only output visible")
;; Global hide for certain kinds
{:kindly/options {:kinds-that-hide-code #{:kind/md :kind/hiccup}}}
;; CSS via kindly options
(-> (kind/hiccup [:div "Styled"])
(kind/hiccup {:element/style {:color "red"
:font-size "20px"}}))
;; CSS classes
(kind/hiccup
[:div {:class "alert alert-warning"}
"Warning message"])
(ns my-doc
{:clay {:quarto {:format {:html {:toc true
:theme :cosmo}}
:title "My Document"
:author "Name"}}})
;; Generate Quarto output
(clay/make! {:source-path "notebooks/doc.clj"
:format [:quarto]})
;; Render multiple files
(clay/make! {:source-path ["src/intro.clj"
"src/analysis.clj"
"src/conclusion.clj"]
:base-target-path "docs"})
;; Watch for changes
(clay/make! {:source-path "notebooks"
:watch true})
Use Calva Power Tools or add to .clojure/calva.exports/config.edn:
{:customREPLCommandSnippets
[{:name "Clay: Make HTML"
:snippet "(do (require '[scicloj.clay.v2.api :as clay])
(clay/make! {:source-path \"$current-file-name\"
:format [:html]}))"}]}
Install clay.el package or add manually:
(defun clay-make ()
(interactive)
(cider-interactive-eval
(format "(do (require '[scicloj.clay.v2.api :as clay])
(clay/make! {:source-path \"%s\"}))"
(buffer-file-name))))
Add to .idea/repl-commands.xml:
<command command="(do (require '[scicloj.clay.v2.api :as clay])
(clay/make! {:source-path "$FilePath$"}))"
name="Clay: Make HTML"/>
Install clay.nvim plugin.
;; Computation runs only when visualized
(delay
(Thread/sleep 5000)
(kind/md "Slow result"))
;; Limit displayed rows
(-> large-dataset
(kind/dataset {:dataset/print-range 100}))
;; Only sync specified directories (excludes src)
{:subdirs-to-sync ["notebooks" "data"]}
Ensure visualization libraries are loaded:
;; For datasets
{:deps {scicloj/tablecloth {:mvn/version "..."}}}
;; For specific chart types
{:deps {applied-science/darkstar {:mvn/version "..."}}}
Verify kind is attached correctly:
;; Check metadata
(meta (kind/md "test"))
;;=> {:kindly/kind :kind/md}
JVM Only: Clay requires the JVM and is not compatible with Babashka or ClojureScript compilation. It renders ClojureScript components (Reagent) but runs on the JVM.
Browser Required: Visualization output is served via local HTTP server and displayed in browser.
Copyright © Scicloj Distributed under the EPL-1.0 (same as Clojure)