feat: add TodoMVC example with editorial design

Full TodoMVC implementation using Pocketbook's synced atom:
- Add, toggle, edit (double-click), destroy todos
- Filter: All / Active / Completed
- Toggle all, clear completed
- Live sync status indicator (online/offline/pending)
- Data persists in IndexedDB, syncs to server via Transit

Design: Ink & Paper aesthetic — cream paper background with grain
texture, Instrument Serif italic header, Newsreader body text,
DM Mono UI chrome, terracotta accent with green completion marks,
deckled left border on card.

Also:
- Server now serves static files from resources/public (/ → todomvc.html)
- Fix CLJS compilation: resolve close!/put! naming conflicts with
  core.async, use qualified async/close!, add clojure.string require
- Fix unbalanced parens in do-pull!
- Remove old placeholder example
This commit is contained in:
Florian Schroedl
2026-04-04 16:45:14 +02:00
parent 55cddf751b
commit ab68a21dd6
10 changed files with 770 additions and 156 deletions

View File

@@ -2,7 +2,7 @@
"IndexedDB wrapper with Transit serialization.
Stores documents as Transit-encoded strings preserving all Clojure types."
(:require [cognitect.transit :as t]
[cljs.core.async :refer [chan put! close!]]))
[cljs.core.async :as async :refer [chan put!]]))
;; ---------------------------------------------------------------------------
;; Transit
@@ -40,11 +40,11 @@
(set! (.-onsuccess req)
(fn [e]
(put! ch (.-result (.-target e)))
(close! ch)))
(async/close! ch)))
(set! (.-onerror req)
(fn [e]
(js/console.error "IDB open error:" e)
(close! ch)))
(async/close! ch)))
ch))
(defn- tx
@@ -68,8 +68,8 @@
:deleted (boolean (:deleted doc false))
:synced (boolean (:synced doc false))}
req (.put store obj)]
(set! (.-onsuccess req) (fn [_] (put! ch true) (close! ch)))
(set! (.-onerror req) (fn [e] (js/console.error "IDB put error:" e) (close! ch)))
(set! (.-onsuccess req) (fn [_] (put! ch true) (async/close! ch)))
(set! (.-onerror req) (fn [e] (js/console.error "IDB put error:" e) (async/close! ch)))
ch))
(defn put-docs!
@@ -86,8 +86,8 @@
:deleted (boolean (:deleted doc false))
:synced (boolean (:synced doc false))}]
(.put store obj)))
(set! (.-oncomplete txn) (fn [_] (put! ch true) (close! ch)))
(set! (.-onerror txn) (fn [e] (js/console.error "IDB batch put error:" e) (close! ch)))
(set! (.-oncomplete txn) (fn [_] (put! ch true) (async/close! ch)))
(set! (.-onerror txn) (fn [e] (js/console.error "IDB batch put error:" e) (async/close! ch)))
ch))
(defn get-doc
@@ -107,9 +107,9 @@
:updated (.-updated result)
:deleted (.-deleted result)
:synced (.-synced result)}))
(close! ch))))
(async/close! ch))))
(set! (.-onerror req)
(fn [e] (js/console.error "IDB get error:" e) (close! ch)))
(fn [e] (js/console.error "IDB get error:" e) (async/close! ch)))
ch))
(defn get-all-by-prefix
@@ -137,9 +137,9 @@
(.continue cursor))
(do
(put! ch @docs)
(close! ch))))))
(async/close! ch))))))
(set! (.-onerror req)
(fn [e] (js/console.error "IDB cursor error:" e) (close! ch)))
(fn [e] (js/console.error "IDB cursor error:" e) (async/close! ch)))
ch))
(defn get-unsynced
@@ -166,9 +166,9 @@
(.continue cursor))
(do
(put! ch @docs)
(close! ch))))))
(async/close! ch))))))
(set! (.-onerror req)
(fn [e] (js/console.error "IDB unsynced error:" e) (close! ch)))
(fn [e] (js/console.error "IDB unsynced error:" e) (async/close! ch)))
ch))
(defn delete-doc!
@@ -178,8 +178,8 @@
txn (tx db "docs" :readwrite)
store (.objectStore txn "docs")
req (.delete store id)]
(set! (.-onsuccess req) (fn [_] (put! ch true) (close! ch)))
(set! (.-onerror req) (fn [e] (js/console.error "IDB delete error:" e) (close! ch)))
(set! (.-onsuccess req) (fn [_] (put! ch true) (async/close! ch)))
(set! (.-onerror req) (fn [e] (js/console.error "IDB delete error:" e) (async/close! ch)))
ch))
;; ---------------------------------------------------------------------------
@@ -197,8 +197,8 @@
(fn [e]
(let [result (.-result (.-target e))]
(put! ch (when result (.-value result)))
(close! ch))))
(set! (.-onerror req) (fn [_] (close! ch)))
(async/close! ch))))
(set! (.-onerror req) (fn [_] (async/close! ch)))
ch))
(defn set-meta!
@@ -208,15 +208,15 @@
txn (tx db "meta" :readwrite)
store (.objectStore txn "meta")
req (.put store #js {:key key :value value})]
(set! (.-onsuccess req) (fn [_] (put! ch true) (close! ch)))
(set! (.-onerror req) (fn [_] (close! ch)))
(set! (.-onsuccess req) (fn [_] (put! ch true) (async/close! ch)))
(set! (.-onerror req) (fn [_] (async/close! ch)))
ch))
;; ---------------------------------------------------------------------------
;; Close
;; ---------------------------------------------------------------------------
(defn close!
(defn close-db!
"Close the IDB connection."
[db]
(when db (.close db)))