### Build and Install Electric Artifacts Source: https://github.com/hyperfiddle/electric/blob/master/src-build/build.md Use these commands to build and install the Electric artifact locally. Ensure the version is set in `deps.edn`. ```shell clojure -T:build build clojure -T:build install ``` -------------------------------- ### Test Local Electric Build in Electric Starter App Source: https://github.com/hyperfiddle/electric/blob/master/src-build/build.md Test your locally installed Electric artifact within the `electric-starter-app`. Replace `` with the actual version number. ```shell clj -A:dev -X dev/-main -Sdeps '{:deps {com.hyperfiddle/electric {:mvn/version ""}}}' ``` -------------------------------- ### Boot Electric Client Application Source: https://context7.com/hyperfiddle/electric/llms.txt Boots the Electric client application, establishing connection to the server and starting the reactive program. Supports custom WebSocket URLs and transit handlers for custom types. ```clojure (ns my-app.client (:require [hyperfiddle.electric3 :as e] [hyperfiddle.electric-dom3 :as dom])) ;; In your ClojureScript entry point (defonce reactor (e/boot-client {} my-app.main/Main)) ;; With custom WebSocket URL (defonce reactor-custom (binding [hyperfiddle.electric-client3/*ws-server-url* "wss://api.example.com/electric"] (e/boot-client {} my-app.main/Main))) ;; With transit handlers for custom types (defonce reactor-transit (e/boot-client {:cognitect.transit/read-handlers {"my-type" (transit/read-handler my-type-reader)} :cognitect.transit/write-handlers {MyType (transit/write-handler "my-type" my-type-writer)}} my-app.main/Main)) ``` -------------------------------- ### Deploy Electric Artifact to Clojars Source: https://github.com/hyperfiddle/electric/blob/master/src-build/build.md Deploy the Electric artifact to Clojars after a successful local install. This requires setting `CLOJARS_USERNAME` and `CLOJARS_PASSWORD` (a generated token) as environment variables. ```shell env $(cat .env | xargs) clojure -T:build deploy ``` -------------------------------- ### Triple Store AVE Index Example Source: https://github.com/hyperfiddle/electric/blob/master/src/hyperfiddle/electric/impl/lang_3_walkthrough.md Demonstrates the AVE (attribute-value-entity) index structure. This index facilitates graph traversal and is used to maintain node ordering via sorted sets. ```clojure ave - the key-value index which allows traversing the graph in arbitrary ways. For `:foo` from the map before it looks like `{:foo {:bar (sorted-set 1)}}` ``` -------------------------------- ### Integrate Electric with Ring Middleware Source: https://context7.com/hyperfiddle/electric/llms.txt Provides Ring middleware for integrating Electric server programs into a Ring-based web server. This includes starting the server on WebSocket connections and handling client requests. ```clojure (ns my-app.server (:require [ring.adapter.jetty :as jetty] [hyperfiddle.electric-ring-adapter3 :as electric-ring] [hyperfiddle.electric3 :as e])) ;; Define your Electric entrypoint (e/defn Main [ring-request] (e/server (binding [e/http-request ring-request] (e/client ;; Your Electric UI here (my-app.ui/App))))) ;; Create the Ring handler (defn electric-handler [ring-request] (e/boot-server {} Main ring-request)) ;; Wrap with Electric middleware (def app (-> (fn [req] {:status 404 :body "Not Found"}) (electric-ring/wrap-electric-websocket electric-handler) (electric-ring/wrap-reject-stale-client {:hyperfiddle/electric-user-version "1.0.0"}) (ring.middleware.params/wrap-params) (ring.middleware.cookies/wrap-cookies))) ;; Start the server (defn start-server [] (jetty/run-jetty app {:port 8080 :join? false})) ``` -------------------------------- ### Live Reload Browser Tests with Shadow-CLJS and Karma Source: https://github.com/hyperfiddle/electric/blob/master/ci/running_dom_tests.md Start a live-reloading test environment for browser tests. This requires two shells: one for shadow-cljs and one for Karma. Karma will manage browser instances. ```bash clojure -M:test:shadow-cljs watch :browser-test ``` ```bash ./node_modules/.bin/karma start ``` -------------------------------- ### Triple Store EAV Index Example Source: https://github.com/hyperfiddle/electric/blob/master/src/hyperfiddle/electric/impl/lang_3_walkthrough.md Illustrates the EAV (entity-attribute-value) index structure of the triple store. This index allows retrieval of inserted maps through a single map lookup. ```clojure eav - the main index, for `{:db/id 1, :foo :bar}` it looks like `{1 {:db/id 1, :foo :bar}}` ``` -------------------------------- ### Collapsing AP with Pure Functions Example Source: https://github.com/hyperfiddle/electric/blob/master/src/hyperfiddle/electric/impl/lang_3_walkthrough.md Demonstrates the `collapse-ap-with-only-pures` pass, which optimizes `r/ap` calls containing only pure functions. It can either wrap the combined pure functions or inline them based on whether the primary function is impure. ```clojure collapse-ap-with-only-pures - `(r/ap (r/pure x) (r/pure y) (r/pure z))` can optimize to 2 cases: - `(r/ap (r/pure (fn* [] (x y z))))` if `x` is an impure fn - `(r/pure (x y z))` if `x` is a pure fn This pass handles both cases. We list pure fns in a hash-map. ``` -------------------------------- ### Rerouting Local Aliases Example Source: https://github.com/hyperfiddle/electric/blob/master/src/hyperfiddle/electric/impl/lang_3_walkthrough.md Illustrates the `reroute-local-aliases` pass, which optimizes code by replacing references to local aliases with references to the original local. This is analogous to Clojure's local variable optimization. ```clojure reroute-local-aliases - if a local just aliases another one, reroutes the references to the origin. E.g. a similar clojure pass would rewrite `(let [x 1, y x] [y y])` to `(let [x 1] [x x])`. ``` -------------------------------- ### Run Browser Tests Once Source: https://github.com/hyperfiddle/electric/blob/master/ci/running_dom_tests.md Execute this command to run browser tests a single time. Ensure you are in the project root directory. ```bash ./ci/run_tests_browser.sh ``` -------------------------------- ### Continuous Flow Integration with e/input Source: https://context7.com/hyperfiddle/electric/llms.txt Integrates Missionary continuous flows into Electric, returning the current state of the flow. Use for external data sources and time-varying values. Requires Missionary core. ```clojure (ns my-app.flows (:require [hyperfiddle.electric3 :as e :refer [$]] [hyperfiddle.electric-dom3 :as dom] [missionary.core :as m])) ;; Integrate a Missionary flow (e/defn Clock [] (e/client (let [time (e/input (m/watch (atom (js/Date.))))] (dom/div (dom/text "Current time: " (.toLocaleTimeString time)))))) ``` ```clojure ;; Custom continuous flow (e/defn MousePosition [] (e/client (let [pos (e/input (m/observe (fn [!] (let [handler #(! {:x (.-clientX %) :y (.-clientY %)})] (.addEventListener js/document "mousemove" handler) #(.removeEventListener js/document "mousemove" handler)))))] (dom/div (dom/text "Mouse: " (:x pos) ", " (:y pos)))))) ``` ```clojure ;; System time (built-in) (e/defn Timer [] (e/client (let [ms (e/System-time-ms)] (dom/div (dom/text "Milliseconds: " ms))))) ``` -------------------------------- ### Site Directives: e/client and e/server Source: https://context7.com/hyperfiddle/electric/llms.txt Explicitly marks code to run on the client or server, enabling the compiler to determine the network boundary. Values flow transparently across this boundary. ```clojure (ns my-app.data (:require [hyperfiddle.electric3 :as e :refer [$]] [hyperfiddle.electric-dom3 :as dom])) (e/defn UserProfile [user-id] ;; Server-side database query (let [user (e/server (db/get-user user-id))] ;; Client-side rendering (e/client (dom/div (dom/props {:class "profile"}) (dom/h1 (dom/text (:name user))) (dom/p (dom/text (:email user))) ;; Server-side computation, result sent to client (dom/p (dom/text "Account age: " (e/server (calculate-account-age user))))))) ;; Nested site transitions (e/defn DataFlow [] (e/client (let [input-value (dom/On "input" #(-> % .-target .-value) "")]) (e/server ;; Process on server (let [processed (process-data input-value)]) (e/client ;; Display result on client (dom/text processed)))))) ``` -------------------------------- ### Creating Reactive HTML Elements Source: https://context7.com/hyperfiddle/electric/llms.txt Use DOM macros like `dom/div`, `dom/span`, and `dom/button` to create reactive HTML elements. These macros execute their body within the element's context, allowing for nested elements and event handling. ```clojure (ns my-app.ui (:require [hyperfiddle.electric3 :as e :refer [$]] [hyperfiddle.electric-dom3 :as dom])) (e/defn Card [title content] (e/client (dom/div (dom/props {:class "card"}) (dom/header (dom/h2 (dom/text title))) (dom/main (dom/p (dom/text content))) (dom/footer (dom/button (dom/text "Learn More")))))) ;; Comprehensive element example (e/defn FormExample [] (e/client (dom/form (dom/fieldset (dom/legend (dom/text "User Information")) (dom/label (dom/text "Username: ") (dom/input (dom/props {:type "text" :name "username" :required true}))) (dom/label (dom/text "Password: ") (dom/input (dom/props {:type "password" :name "password"}))) (dom/label (dom/text "Role: ") (dom/select (dom/option (dom/props {:value "user"}) (dom/text "User")) (dom/option (dom/props {:value "admin"}) (dom/text "Admin")))) (dom/button (dom/props {:type "submit"}) (dom/text "Submit")))))) ``` -------------------------------- ### Render Text Content with dom/text Source: https://context7.com/hyperfiddle/electric/llms.txt Mounts a DOM TextNode with the given content. Multiple arguments create multiple text nodes. Supports simple text, concatenation of values, and reactive text updates. ```clojure (ns my-app.text (:require [hyperfiddle.electric3 :as e :refer [$]] [hyperfiddle.electric-dom3 :as dom])) (e/defn TextExamples [] (e/client (dom/div ;; Simple text (dom/p (dom/text "Hello, World!")) ;; Multiple values concatenated (dom/p (dom/text "Count: " 42 " items")) ;; Reactive text (let [!name (atom "Alice")] (dom/p (dom/text "Hello, " (e/watch !name) "!"))) ;; Formatted text (let [price 29.99] (dom/p (dom/text "Price: $" (.toFixed price 2))))))) ``` -------------------------------- ### Create Scoped CSS Rules with Electric CSS Source: https://context7.com/hyperfiddle/electric/llms.txt Use `css/rule` to define CSS styles that are automatically managed (added on mount, removed on unmount). Supports nested rules and pseudo-classes like `:hover` and `:active`. ```clojure (ns my-app.styled (:require [hyperfiddle.electric3 :as e :refer [$]] [hyperfiddle.electric-dom3 :as dom] [hyperfiddle.electric-css3 :as css])) (e/defn StyledButton [variant] (e/client (dom/button (css/rule {:background-color (case variant :primary "blue" :secondary "gray" :danger "red") :color "white" :padding "0.5rem 1rem" :border "none" :border-radius "4px" :cursor "pointer"}) (css/rule "&:hover" {:opacity "0.8"}) (css/rule "&:active" {:transform "scale(0.98)"}) (dom/text "Click me")))) ;; CSS animations (e/defn AnimatedBox [] (e/client (dom/div (css/keyframes "pulse" (css/keyframe "0%" {:transform "scale(1)"}) (css/keyframe "50%" {:transform "scale(1.1)"}) (css/keyframe "100%" {:transform "scale(1)"})) (css/rule {:width "100px" :height "100px" :background "coral" :animation "pulse 2s infinite"})))) ``` -------------------------------- ### Background Thread Execution with e/Offload Source: https://context7.com/hyperfiddle/electric/llms.txt Runs a thunk on a background thread, returning the result when complete. Useful for blocking I/O operations that shouldn't block the reactive loop. Can specify a custom executor. ```clojure (ns my-app.async (:require [hyperfiddle.electric3 :as e :refer [$]] [hyperfiddle.electric-dom3 :as dom])) ;; Offload blocking operations to background thread (e/defn HeavyComputation [data] (e/server (e/Offload (fn [] ;; This runs on a separate thread (Thread/sleep 1000) (expensive-computation data))))) ``` ```clojure ;; Database query with offload (e/defn UserSearch [query] (let [results (e/server (e/Offload #(when (seq query) (db/search-users query))))] (e/client (dom/ul (e/for [user results] (dom/li (dom/text (:name user)))))))) ``` ```clojure ;; With custom executor (e/defn CustomExecutor [task] (e/server (e/Offload task my-thread-pool))) ``` -------------------------------- ### Handle DOM Events with dom/On Source: https://context7.com/hyperfiddle/electric/llms.txt Listens to DOM events and returns transformed values reactively. The callback extracts the value from the event, and an initial value is provided for before any events fire. Use for basic click handling, input value tracking, and more. ```clojure (ns my-app.events (:require [hyperfiddle.electric3 :as e :refer [$]] [hyperfiddle.electric-dom3 :as dom])) ;; Basic click handling (e/defn ClickCounter [] (e/client (let [clicks (atom 0)] (dom/button (swap! clicks inc (dom/On "click" (fn [_] 1) 0)) (dom/text "Clicks: " @clicks))))) ``` ```clojure (e/defn SearchInput [] (e/client (dom/input (dom/props {:type "text" :placeholder "Search..."}) (let [query (dom/On "input" (fn [e] (-> e .-target .-value)) "")] (dom/div (dom/text "Searching for: " query)))))) ``` ```clojure (e/defn InteractiveBox [] (e/client (dom/div (dom/props {:style {:width "100px" :height "100px" :background "lightblue"}}) (let [hover? (dom/Mouse-over?) focused? (dom/Focused?) mouse-down? (dom/Mouse-down?)] (dom/text (cond mouse-down? "Pressed!" hover? "Hovering" focused? "Focused" :else "Interact with me")))))) ``` ```clojure (e/defn ScrollHandler [] (e/client (dom/div (dom/props {:style {:height "200px" :overflow "auto"}}) (let [scroll-top (dom/On "scroll" (fn [e] (-> e .-target .-scrollTop)) 0 {:passive true})] (dom/text "Scroll position: " scroll-top))))) ``` -------------------------------- ### Efficient List Rendering with `diff-by` Source: https://context7.com/hyperfiddle/electric/llms.txt Utilize `e/diff-by` with a key function to stabilize collection identities for efficient incremental updates in lists. Each item maintains its identity across updates, ensuring smooth rendering. ```clojure (ns my-app.incseq (:require [hyperfiddle.electric3 :as e :refer [$]] [hyperfiddle.electric-dom3 :as dom] [hyperfiddle.incseq :as i])) ;; Efficient list rendering with stable identity (e/defn EfficientList [items] (e/client (dom/ul (e/for [item (e/diff-by :id items)] ;; Each item maintains identity across updates (dom/li (dom/props {:key (:id item)}) (dom/text (:name item))))))) ;; Manual diff operations (e/defn DiffOperations [] (let [;; Create an empty diff empty (i/empty-diff 0) ;; Compose permutations p1 {0 1, 1 0} ; swap first two p2 {1 2, 2 1} ; swap second two combined (i/compose p1 p2)] (prn "Combined permutation:" combined))) ``` -------------------------------- ### Reactive Atom Observation with e/watch Source: https://context7.com/hyperfiddle/electric/llms.txt Observes Clojure atoms reactively, updating whenever the atom changes. This is the primary method for introducing reactivity from mutable state. ```clojure (ns my-app.state (:require [hyperfiddle.electric3 :as e :refer [$]] [hyperfiddle.electric-dom3 :as dom])) (def !todos (atom [{:id 1 :text "Learn Electric" :done false} {:id 2 :text "Build an app" :done false}])) (e/defn TodoApp [] (let [todos (e/watch !todos)] (e/client (dom/div (dom/h1 (dom/text "Todo List")) (e/for [todo todos] (dom/div (dom/props {:class (if (:done todo) "done" "pending")}) (dom/text (:text todo)) (dom/button (dom/props {:onclick #(swap! !todos (fn [ts] (mapv (fn [t] (if (= (:id t) (:id todo)) (update t :done not) t)) ts)))}) (dom/text (if (:done todo) "Undo" "Done"))))))))) ;; Server-side atom watching (e/defn ServerState [] (let [server-data (e/server (e/watch !server-atom))] (e/client (dom/pre (dom/text (pr-str server-data)))))) ``` -------------------------------- ### Setting Element Properties with `dom/props` Source: https://context7.com/hyperfiddle/electric/llms.txt Use `dom/props` to set HTML attributes and properties on DOM elements. It supports special handling for `class`, `style`, and custom CSS properties, as well as dynamic property binding. ```clojure (ns my-app.styling (:require [hyperfiddle.electric3 :as e :refer [$]] [hyperfiddle.electric-dom3 :as dom])) (e/defn StyledComponent [active? disabled?] (e/client (dom/div (dom/props {:id "my-component" :class ["base-class" (when active? "active") (when disabled? "disabled")] :style {:background-color (if active? "green" "gray") :padding "1rem" :border-radius "4px" :--custom-var "blue"} ; CSS custom properties :data-testid "styled-component" :aria-label "A styled component"}) (dom/text "Styled content")))) ;; Dynamic props (e/defn DynamicProps [props-map] (e/client (dom/div (dom/props props-map) (dom/text "Dynamic properties")))) ;; Input with controlled value (e/defn ControlledInput [value on-change] (e/client (dom/input (dom/props {:value value :type "text"}) (on-change (dom/On "input" #(-> % .-target .-value) value))))) ``` -------------------------------- ### Token-based Form Submission Source: https://context7.com/hyperfiddle/electric/llms.txt Use `e/Token` to manage controlled side effects like form submissions, ensuring they execute exactly once and providing error handling. The token is consumed on success or passed an error on failure. ```clojure (ns my-app.forms (:require [hyperfiddle.electric3 :as e :refer [$]] [hyperfiddle.electric-dom3 :as dom])) ;; Token-based form submission (e/defn SubmitButton [on-submit] (e/client (let [[token err] (e/Token (dom/On "click" (fn [_] true) false))]) (dom/button (dom/props {:disabled (some? token)}) (dom/text (if token "Submitting..." "Submit"))) (when token (e/server (try (on-submit) (token) ; success - consume token (catch Exception e (token e)))))))) ; failure - report error ;; Form with validation and submission (e/defn ContactForm [] (let [!name (atom "") !email (atom "")] (e/client (dom/form (dom/input (dom/props {:placeholder "Name"}) (reset! !name (dom/On "input" #(-> % .-target .-value) ""))) (dom/input (dom/props {:placeholder "Email" :type "email"}) (reset! !email (dom/On "input" #(-> % .-target .-value) ""))) ($ SubmitButton (fn [] (save-contact! {:name @!name :email @!email}))))) ``` -------------------------------- ### Ambient Values with e/amb Source: https://context7.com/hyperfiddle/electric/llms.txt Creates a table of multiple simultaneous values from expressions, useful for representing concurrent reactive values. Can be used for conditional rendering; an empty amb renders nothing. ```clojure (ns my-app.amb (:require [hyperfiddle.electric3 :as e :refer [$]] [hyperfiddle.electric-dom3 :as dom])) ;; amb creates multiple simultaneous values (e/defn MultipleValues [] (e/client (dom/div ;; Each amb branch renders independently (e/amb (dom/p (dom/text "First paragraph")) (dom/p (dom/text "Second paragraph")) (dom/p (dom/text "Third paragraph")))))) ``` ```clojure ;; Conditional rendering with amb (e/defn ConditionalContent [show-extra?] (e/client (dom/div (dom/text "Always visible") (if show-extra? (e/amb (dom/p (dom/text "Extra content 1")) (dom/p (dom/text "Extra content 2"))) (e/amb))))) ; empty table = nothing rendered ``` ```clojure ;; Filter pattern with amb (e/defn FilteredList [items predicate] (e/for [item items] (if (predicate item) item (e/amb)))) ; exclude non-matching items ``` -------------------------------- ### Define Electric Functions with e/defn Source: https://context7.com/hyperfiddle/electric/llms.txt Defines a reactive Electric function that can span client and server. Supports multiple arities, recursion, and closures. Use '$' to call Electric functions. ```clojure (ns my-app.core (:require [hyperfiddle.electric3 :as e :refer [$]])) ;; Define a simple Electric function (e/defn Greeting [name] (e/server (println "Server received:" name)) (e/client (str "Hello, " name "!"))) ;; Multi-arity Electric function (e/defn Counter ([] (Counter 0)) ([initial] (let [!count (atom initial) count (e/watch !count)] (e/client (dom/div (dom/text "Count: " count) (dom/button (dom/props {:onclick #(swap! !count inc)}) (dom/text "Increment"))))))) ;; Call Electric functions with $ (e/defn App [] ($ Greeting "World") ($ Counter 10)) ``` -------------------------------- ### Reactive Collection Iteration with e/for Source: https://context7.com/hyperfiddle/electric/llms.txt Iterates over collections reactively, efficiently updating only changed items. Use for rendering lists and grids. Nested iteration creates a cartesian product. ```clojure (ns my-app.lists (:require [hyperfiddle.electric3 :as e :refer [$]] [hyperfiddle.electric-dom3 :as dom])) (e/defn ItemList [items] (e/client (dom/ul (e/for [item items] (dom/li (dom/text (:name item)) (dom/span (dom/props {:class "price"}) (dom/text " $ " (:price item)))))))) ``` ```clojure ;; Nested iteration (cartesian product) (e/defn Grid [rows cols] (e/client (dom/table (e/for [row rows] (dom/tr (e/for [col cols] (dom/td (dom/text (str row "-" col))))))))) ``` ```clojure ;; With key function for stable identity (e/defn StableList [items] (e/for-by :id [item items] (dom/div (dom/props {:key (:id item)}) (dom/text (:name item))))) ``` -------------------------------- ### Electric Analyzer Node Types Source: https://github.com/hyperfiddle/electric/blob/master/src/hyperfiddle/electric/impl/lang_3_walkthrough.md Details specific node types used in the analyzer, such as `::mklocal`, `::bindlocal`, `::localref`, and `::lookup`, which represent local variable creation, binding, referencing, and dynamic lookups respectively. ```clojure ::mklocal and ::bindlocal - `let` expands to these, but also `e/letfn` uses these. `::mklocal` introduces a local and `::bindlocal` binds it. ``` ```clojure ::localref - a reference to an electric local. E.g. the returning `x` in `(let [x 1] x)`. ``` ```clojure ::lookup - in electric all vars are dynamic and can be rebound. This node type is a lookup into the dynamic binding of the vars. ``` -------------------------------- ### Merge Request Map in Clojure Source: https://github.com/hyperfiddle/electric/blob/master/docs/electric-protocol.md This function merges request maps by summing values for common keys and removing keys with zero values. Use this to aggregate local event effects for remote expression dependencies. ```clojure (def merge-request (partial reduce-kv (fn [r k n] (let [n (+ n (r k 0))] (if (zero? n) (dissoc r k) (assoc r k n)))))) ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.