diff --git a/bb.edn b/bb.edn
new file mode 100644
index 0000000..135a51d
--- /dev/null
+++ b/bb.edn
@@ -0,0 +1,77 @@
+{:deps {io.github.babashka/sci.nrepl
+ #_{:local/root "../sci.nrepl"}
+ {:git/sha "2f8a9ed2d39a1b09d2b4d34d95494b56468f4a23"}
+ io.github.babashka/http-server
+ {:git/tag "v0.1.14"
+ :git/sha "4af3c76"}
+ io.github.scittle/build
+ {:local/root "build"}}
+
+ :tasks
+ {:requires ([scittle.build :as build]
+ [babashka.fs :as fs]
+ [cheshire.core :as json]
+ [babashka.process :as p :refer [process]])
+
+ clean {:doc "Start from clean slate."
+ :task (do (fs/delete-tree (fs/file "resources" "public" "js"))
+ (fs/delete-tree ".cpcache")
+ (fs/delete-tree ".shadow-cljs"))}
+
+ shadow:watch {:doc "Development build. Starts webserver and watches for changes."
+ :task (build/build {:action "watch"
+ :args *command-line-args*})}
+
+ http-server {:doc "Starts http server for serving static files"
+ :requires ([babashka.http-server :as http])
+ :task (do (http/serve {:port 1341 :dir "resources/public"})
+ (println "Serving static assets at http://localhost:1341"))}
+
+ browser-nrepl {:doc "Start browser nREPL"
+ :requires ([sci.nrepl.browser-server :as bp])
+ :task (bp/start! {})}
+
+ -dev {:depends [shadow:watch browser-nrepl http-server]}
+
+ dev {:doc "Development build. Starts webserver and watches for changes."
+ :task (do (run '-dev {:parallel true})
+ (deref (promise)))}
+
+ prod {:doc "Builds production artifacts."
+ :task (build/build {})
+ :depends [clean]}
+
+ dist {:doc "Prepare dist folder for npm package"
+ :depends [prod]
+ :task (do
+ (fs/delete-tree "dist")
+ (fs/create-dirs "dist/dev")
+ (run! (fn [f] (fs/copy f "dist" {:replace-existing true}))
+ (fs/glob "resources/public/js" "*.{js,js.map}"))
+ (run! (fn [f] (fs/copy f "dist/dev" {:replace-existing true}))
+ (fs/glob "resources/public/js/dev" "*.{js,js.map}")))}
+
+ bump-version {:doc "Bumps package.json and pushes new git tag"
+ :task (do (shell "npm version patch")
+ (shell "git push --atomic origin main"
+ (str "v" (:version (json/parse-string (slurp "package.json") true)))))}
+
+ npm-publish {:doc "Updates NPM ibrary"
+ :task (do (run 'dist)
+ (run 'bump-version)
+ (shell "npm publish"))}
+
+ replace-version {:doc "Ported from bash one-liners. Expects two versions.
+ TODO: port to Clojure.
+ TODO: skip changelog.md
+ "
+ :task
+ (let [[prev next] *command-line-args*]
+ (-> (process ["bash" "-c"
+ (format "rg %s --files-with-matches -g '!/CHANGELOG.md' | xargs sed -i '' 's/%s/%s/g'"
+ prev prev next)]
+ {:inherit true})
+ p/check))}
+
+ gh-pages {:doc "Updates Github pages with new release build."
+ :task (shell "script/release.clj")}}}
diff --git a/build/deps.edn b/build/deps.edn
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/build/deps.edn
@@ -0,0 +1 @@
+{}
diff --git a/build/src/scittle/build.clj b/build/src/scittle/build.clj
new file mode 100644
index 0000000..02382e5
--- /dev/null
+++ b/build/src/scittle/build.clj
@@ -0,0 +1,83 @@
+(ns scittle.build
+ "Provides bb tasks for building and releasing scittle"
+ (:require
+ [babashka.classpath :as classpath]
+ [babashka.fs :as fs]
+ [babashka.tasks :refer [clojure]]
+ [clojure.edn :as edn]
+ [clojure.string :as str]))
+
+(defn- feature-files
+ []
+ (filter fs/exists?
+ (map (fn [d]
+ (fs/file d "scittle_plugin.edn"))
+ (classpath/split-classpath (classpath/get-classpath)))))
+
+(defn- read-configs
+ [files]
+ (->> files
+ (mapcat (comp edn/read-string slurp str))))
+
+(defn- build-cmd [cmd scittle-dir]
+ (let [files (feature-files)
+ feature-configs (read-configs files)
+ ;; Each ./src/scittle_plugin.edn has a ./deps.edn
+ feature-dirs (map (comp fs/parent fs/parent) files)
+ cmd' (if (seq files)
+ (format "-Sdeps '%s' %s"
+ {:deps
+ (merge (into {}
+ (map (fn [dir]
+ [(symbol (str (fs/file-name dir) "/deps"))
+ {:local/root (str dir)}])
+ feature-dirs))
+ {'scittle/deps {:local/root scittle-dir}})}
+ cmd)
+ cmd)]
+ (when (seq feature-configs)
+ (println "Building features:" (str/join ", " (map :name feature-configs)) "..."))
+ (if (seq feature-configs)
+ (apply str cmd'
+ (map (fn [m] (format " --config-merge '%s'" (pr-str (:shadow-config m))))
+ feature-configs))
+ cmd')))
+
+(defn- build*
+ [cmd]
+ (let [building-outside-scittle? (not (fs/exists? "shadow-cljs.edn"))
+ scittle-dir (when building-outside-scittle?
+ (->> (classpath/get-classpath)
+ classpath/split-classpath
+ ;; Pull out scittle from local/root or git/url
+ (some #(when (re-find #"(scittle/[0-9a-f]+|scittle)/src" %) %))
+ fs/parent))]
+ (when building-outside-scittle?
+ (fs/copy (fs/file scittle-dir "shadow-cljs.edn") "shadow-cljs.edn"))
+ (let [cmd (build-cmd cmd (str scittle-dir))]
+ (println "> clojure" cmd)
+ (clojure {:extra-env {"SCI_ELIDE_VARS" "true"}} cmd))
+ (when building-outside-scittle?
+ (fs/delete "shadow-cljs.edn"))))
+
+(defn build
+ "Build scittle shadow builds using clojure cmd and commandline args. Features on
+ classpath are automatically added.
+
+ Options:
+
+ * :action - compile action, defaults to release, but may also be compile or watch"
+ [{:keys [action
+ args] :or {action "release"}}]
+ (build* (format "-M -m shadow.cljs.devtools.cli --force-spawn %s main %s" action (str/join " " args)))
+ (when (= "release" action)
+ (println "Also building dev release build")
+ (build* (format "-M -m shadow.cljs.devtools.cli --force-spawn %s main %s %s"
+ action
+ "--config-merge '{:compiler-options {:optimizations :simple
+ :pretty-print true
+ :pseudo-names true}
+ :output-dir \"resources/public/js/dev\"
+ :modules {:scittle.cljs-devtools {:entries [scittle.cljs-devtools]
+ :depends-on #{:scittle}}}}'"
+ (str/join " " args)))))
diff --git a/deps.edn b/deps.edn
new file mode 100644
index 0000000..8733a60
--- /dev/null
+++ b/deps.edn
@@ -0,0 +1,30 @@
+{:paths ["src" "resources"]
+ :deps
+ {org.clojure/clojure {:mvn/version "1.11.1"}
+ thheller/shadow-cljs {:mvn/version "3.1.8"}
+ org.babashka/sci {:git/url "https://github.com/babashka/sci"
+ :git/sha "756376056b32198d96dd5b272cee8fc483db60df"}
+ #_{:local/root "../babashka/sci"}
+ reagent/reagent {:mvn/version "1.1.1"}
+ no.cjohansen/replicant {:mvn/version "2025.03.27"}
+ re-frame/re-frame {:mvn/version "1.3.0"}
+ cljsjs/react {:mvn/version "18.2.0-1"}
+ cljsjs/react-dom {:mvn/version "18.2.0-1"}
+ cljsjs/react-dom-server {:mvn/version "18.2.0-1"}
+ cljs-ajax/cljs-ajax {:mvn/version "0.8.4"}
+ applied-science/js-interop {:mvn/version "0.4.2"}
+ funcool/promesa {:mvn/version "11.0.678"}
+ io.github.babashka/sci.nrepl
+ #_{:local/root "../sci.nrepl"}
+ {:git/url "https://github.com/babashka/sci.nrepl"
+ :git/sha "75f379c685bbd58c3e23f531339eb144e104937d"}
+ io.github.babashka/sci.configs
+ #_{:local/root "/Users/borkdude/dev/sci.configs"}
+ {:git/url "https://github.com/babashka/sci.configs"
+ :git/sha "aa84a1b4f1fe45735e5b748769309fc842f737c1"
+ :exclusions [org.babashka/sci]}
+ binaryage/devtools {:mvn/version "1.0.7"}}
+ :aliases
+ {:dev
+ {:extra-paths ["dev"]
+ :extra-deps {}}}}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..71d9dbd
--- /dev/null
+++ b/package.json
@@ -0,0 +1,11 @@
+{
+ "name": "scittle",
+ "version": "0.7.27",
+ "files": [
+ "dist"
+ ],
+ "devDependencies": {
+ "react": "17.0.1",
+ "react-dom": "17.0.1"
+ }
+}
diff --git a/plugins/datascript/deps.edn b/plugins/datascript/deps.edn
new file mode 100644
index 0000000..467f4cd
--- /dev/null
+++ b/plugins/datascript/deps.edn
@@ -0,0 +1,4 @@
+{:deps
+ {datascript/datascript {:mvn/version "1.3.12"}
+ io.github.babashka/sci.configs {:git/sha "aa84a1b4f1fe45735e5b748769309fc842f737c1"
+ :exclusions [org.babashka/sci]}}}
diff --git a/plugins/datascript/src/scittle/datascript.cljs b/plugins/datascript/src/scittle/datascript.cljs
new file mode 100644
index 0000000..f3dac97
--- /dev/null
+++ b/plugins/datascript/src/scittle/datascript.cljs
@@ -0,0 +1,9 @@
+(ns scittle.datascript
+ {:no-doc true}
+ (:require [sci.configs.tonsky.datascript :refer [config]]
+ [scittle.core :as scittle]))
+
+(defn init []
+ (scittle/register-plugin!
+ ::datascript
+ config))
diff --git a/plugins/datascript/src/scittle_plugin.edn b/plugins/datascript/src/scittle_plugin.edn
new file mode 100644
index 0000000..09eae58
--- /dev/null
+++ b/plugins/datascript/src/scittle_plugin.edn
@@ -0,0 +1,12 @@
+[{:name scittle/datascript
+ :namespaces [datascript.core datascript.db]
+ :js "./scittle.datascript.js"
+ :shadow-config
+ {:compiler-options {:externs ["datascript/externs.js"]}
+ :modules
+ {:scittle.datascript
+ {:init-fn scittle.datascript/init
+ ;; From https://github.com/tonsky/datascript/issues/298#issuecomment-813790783
+ :prepend "globalThis.datascript = {};"
+ :depends-on #{:scittle}
+ :entries [datascript.core]}}}}]
diff --git a/plugins/dataspex/deps.edn b/plugins/dataspex/deps.edn
new file mode 100644
index 0000000..badf11e
--- /dev/null
+++ b/plugins/dataspex/deps.edn
@@ -0,0 +1,8 @@
+{:deps
+ {no.cjohansen/dataspex {:git/url "https://github.com/cjohansen/dataspex"
+ :git/sha "02112200651c2bd932907bb69fba1ff50b881741"
+ :exclusions [ring/ring-core
+ ring/ring-jetty-adapter
+ com.cognitect/transit-clj]}
+ io.github.babashka/sci.configs {:git/sha "aa84a1b4f1fe45735e5b748769309fc842f737c1"
+ :exclusions [org.babashka/sci]}}}
diff --git a/plugins/dataspex/src/scittle/dataspex.cljs b/plugins/dataspex/src/scittle/dataspex.cljs
new file mode 100644
index 0000000..66b3908
--- /dev/null
+++ b/plugins/dataspex/src/scittle/dataspex.cljs
@@ -0,0 +1,9 @@
+(ns scittle.dataspex
+ {:no-doc true}
+ (:require [sci.configs.cjohansen.dataspex :refer [config]]
+ [scittle.core :as scittle]))
+
+(defn init []
+ (scittle/register-plugin!
+ ::dataspex
+ config))
diff --git a/plugins/dataspex/src/scittle_plugin.edn b/plugins/dataspex/src/scittle_plugin.edn
new file mode 100644
index 0000000..af629e2
--- /dev/null
+++ b/plugins/dataspex/src/scittle_plugin.edn
@@ -0,0 +1,8 @@
+[{:name scittle/dataspex
+ :namespaces [dataspex.core]
+ :js "./scittle.dataspex.js"
+ :shadow-config
+ {:modules
+ {:scittle.dataspex {:init-fn scittle.dataspex/init
+ :depends-on #{:scittle :scittle.datascript}
+ :entries [dataspex.core]}}}}]
diff --git a/plugins/demo/README.md b/plugins/demo/README.md
new file mode 100644
index 0000000..7bd15f8
--- /dev/null
+++ b/plugins/demo/README.md
@@ -0,0 +1,29 @@
+# Demo
+
+A demo project of a custom scittle build.
+
+This demo project uses the `scittle.javelin` and `scittle.hoplon` plugins which aren't part of the normal scittle distribution.
+
+To produce release `.js` files, run: `bb release`.
+
+See:
+
+- `bb.edn` with
+ - `:deps` which includes:
+ - a dependency on the `scittle.build` project to build scittle + custom features
+ - zero or more plugin dependencies
+ - helpers like static file server
+ - development `:tasks`. Run `bb dev` for development and `bb release` to produce release artifacts.
+- `deps.edn`: this only contains a dependency on scittle itself
+
+Available plugins are in the `plugins` directory inside the top level directory of this repo.
+
+Writing a plugin involves writing
+
+- SCI configuration (this can be shared via the [sci.configs](https://github.com/babashka/sci.configs) project too)
+- Adding a `scittle_plugin.edn` file on the plugin's classpath (e.g. in the `src` directory). This EDN file contains:
+ - `:name`, name of the plugin
+ - `:namespaces`: the namespaces exposed to SCI
+ - `:js`: the name of the produced `.js` module file
+ - `:shadow-config`: the shadow-cljs configuration specific to this plugin
+- A `.cljs` file with an `init` function which calls `scittle/register-plugin!`.
diff --git a/plugins/demo/bb.edn b/plugins/demo/bb.edn
new file mode 100644
index 0000000..e7486c2
--- /dev/null
+++ b/plugins/demo/bb.edn
@@ -0,0 +1,22 @@
+{:deps {io.github.babashka/scittle.build {:local/root "../../build"}
+ ;; datascript plugin
+ ; io.github.babashka/scittle.datascript {:local/root "../../plugins/datascript"}
+ io.github.babashka/scittle.dataspex {:local/root "../../plugins/dataspex"}
+ io.github.babashka/scittle.javelin {:local/root "../../plugins/javelin"}
+ io.github.babashka/scittle.hoplon {:local/root "../../plugins/hoplon"}
+ io.github.babashka/http-server
+ {:git/sha "b38c1f16ad2c618adae2c3b102a5520c261a7dd3"}}
+ :tasks
+ {:requires ([scittle.build :as build])
+ watch {:doc "Watch build"
+ :task (build/build {:action "watch"})}
+ serve {:doc "Starts http server for serving static files"
+ :requires ([babashka.http-server :as http])
+ :task (do (http/serve {:port 1341 :dir "resources/public"})
+ (println "Serving static assets at http://localhost:1341"))}
+ -dev {:depends [watch serve]}
+ dev {:doc "Run compilation in watch mode and start http server"
+ :task (do (run '-dev {:parallel true})
+ (deref (promise)))}
+ release {:doc "Release build (advanced compiled JS)"
+ :task (build/build {})}}}
diff --git a/plugins/demo/deps.edn b/plugins/demo/deps.edn
new file mode 100644
index 0000000..62915a5
--- /dev/null
+++ b/plugins/demo/deps.edn
@@ -0,0 +1 @@
+{:deps {io.github.babashka/scittle {:local/root "../.."}}}
diff --git a/plugins/demo/resources/public/index.html b/plugins/demo/resources/public/index.html
new file mode 100644
index 0000000..7f328cc
--- /dev/null
+++ b/plugins/demo/resources/public/index.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+ Hello Hoplon!
+
+
diff --git a/plugins/demo/shadow-cljs.edn b/plugins/demo/shadow-cljs.edn
new file mode 100644
index 0000000..47e5deb
--- /dev/null
+++ b/plugins/demo/shadow-cljs.edn
@@ -0,0 +1,32 @@
+{:deps
+ {:aliases [:dev]}
+
+ :dev-http
+ {8000 "classpath:public"}
+
+ :builds
+ {:main
+ {:target :browser
+ :js-options
+ {:resolve {"react" {:target :global
+ :global "React"}
+ "react-dom" {:target :global
+ :global "ReactDOM"}}}
+ :modules
+ {:scittle {:entries [scittle.core]}
+ :scittle.nrepl {:entries [scittle.nrepl]
+ :depends-on #{:scittle}}
+ :scittle.promesa {:entries [scittle.promesa]
+ :depends-on #{:scittle}}
+ :scittle.pprint {:entries [scittle.pprint]
+ :depends-on #{:scittle}}
+ :scittle.reagent {:entries [scittle.reagent]
+ :depends-on #{:scittle}}
+ :scittle.re-frame {:entries [scittle.re-frame]
+ :depends-on #{:scittle.reagent
+ :scittle}}
+ :scittle.cljs-ajax {:entries [scittle.cljs-ajax]
+ :depends-on #{:scittle}}}
+ :build-hooks [(shadow.cljs.build-report/hook)]
+ :output-dir "resources/public/js"
+ :devtools {:repl-pprint true}}}}
diff --git a/plugins/hoplon/deps.edn b/plugins/hoplon/deps.edn
new file mode 100644
index 0000000..0eab577
--- /dev/null
+++ b/plugins/hoplon/deps.edn
@@ -0,0 +1,4 @@
+{:deps
+ {hoplon/hoplon {:mvn/version "7.5.0"}
+ io.github.babashka/sci.configs {:git/sha "aa84a1b4f1fe45735e5b748769309fc842f737c1"
+ :exclusions [org.babashka/sci]}}}
diff --git a/plugins/hoplon/src/scittle/hoplon.cljs b/plugins/hoplon/src/scittle/hoplon.cljs
new file mode 100644
index 0000000..46e5a3b
--- /dev/null
+++ b/plugins/hoplon/src/scittle/hoplon.cljs
@@ -0,0 +1,9 @@
+(ns scittle.hoplon
+ {:no-doc true}
+ (:require [sci.configs.hoplon.hoplon :refer [config]]
+ [scittle.core :as scittle]))
+
+(defn init []
+ (scittle/register-plugin!
+ ::hoplon
+ config))
diff --git a/plugins/hoplon/src/scittle/javelin.cljs b/plugins/hoplon/src/scittle/javelin.cljs
new file mode 100644
index 0000000..7334a2d
--- /dev/null
+++ b/plugins/hoplon/src/scittle/javelin.cljs
@@ -0,0 +1,9 @@
+(ns scittle.javelin
+ {:no-doc true}
+ (:require [sci.configs.hoplon.javelin :refer [config]]
+ [scittle.core :as scittle]))
+
+(defn init []
+ (scittle/register-plugin!
+ ::javelin
+ config))
diff --git a/plugins/hoplon/src/scittle_plugin.edn b/plugins/hoplon/src/scittle_plugin.edn
new file mode 100644
index 0000000..5a9f8f6
--- /dev/null
+++ b/plugins/hoplon/src/scittle_plugin.edn
@@ -0,0 +1,13 @@
+[{:name scittle/hoplon
+ :namespaces [javelin.core
+ hoplon.core
+ hoplon.dom]
+ :js "./scittle.hoplon.js"
+ :shadow-config
+ {:modules
+ {:scittle.hoplon {:init-fn scittle.hoplon/init
+ :depends-on #{:scittle :scittle.javelin}
+ :entries [hoplon.core hoplon.dom]}
+ :scittle.javelin {:init-fn scittle.javelin/init
+ :depends-on #{:scittle}
+ :entries [javelin.core]}}}}]
diff --git a/plugins/javelin/deps.edn b/plugins/javelin/deps.edn
new file mode 100644
index 0000000..f4b4533
--- /dev/null
+++ b/plugins/javelin/deps.edn
@@ -0,0 +1,4 @@
+{:deps
+ {hoplon/javelin {:mvn/version "3.9.3"}
+ io.github.babashka/sci.configs {:git/sha "aa84a1b4f1fe45735e5b748769309fc842f737c1"
+ :exclusions [org.babashka/sci]}}}
diff --git a/plugins/javelin/src/scittle/javelin.cljs b/plugins/javelin/src/scittle/javelin.cljs
new file mode 100644
index 0000000..7334a2d
--- /dev/null
+++ b/plugins/javelin/src/scittle/javelin.cljs
@@ -0,0 +1,9 @@
+(ns scittle.javelin
+ {:no-doc true}
+ (:require [sci.configs.hoplon.javelin :refer [config]]
+ [scittle.core :as scittle]))
+
+(defn init []
+ (scittle/register-plugin!
+ ::javelin
+ config))
diff --git a/plugins/javelin/src/scittle_plugin.edn b/plugins/javelin/src/scittle_plugin.edn
new file mode 100644
index 0000000..c34873f
--- /dev/null
+++ b/plugins/javelin/src/scittle_plugin.edn
@@ -0,0 +1,8 @@
+[{:name scittle/javelin
+ :namespaces [javelin.core]
+ :js "./scittle.javelin.js"
+ :shadow-config
+ {:modules
+ {:scittle.javelin {:init-fn scittle.javelin/init
+ :depends-on #{:scittle}
+ :entries [javelin.core]}}}}]
diff --git a/japji/resources/audio/japji_bindranwale.mp3 b/resources/public/audio/japji_bindranwale.mp3
similarity index 100%
rename from japji/resources/audio/japji_bindranwale.mp3
rename to resources/public/audio/japji_bindranwale.mp3
diff --git a/resources/public/cljs/bookmarklet.cljs b/resources/public/cljs/bookmarklet.cljs
new file mode 100644
index 0000000..69d2169
--- /dev/null
+++ b/resources/public/cljs/bookmarklet.cljs
@@ -0,0 +1,140 @@
+(ns bookmarklet
+ (:require [reagent.core :as r]
+ [reagent.dom :as rdom]))
+
+(defn append-tag [tag {:keys [body onload onerror] :as attributes}]
+ (str "var s=document.createElement('" (name tag) "');"
+ (clojure.string/join ";" (map (fn [[k v]] (str "s.setAttribute('" (name k) "','" (name v) "')")) (dissoc attributes :body :onload :onerror)))
+ (when body
+ (str ";s.innerText=" body))
+ (when onload
+ (str ";s.onload=" onload))
+ (when onerror
+ (str ";s.onerror=" onerror))
+ ";document.body.appendChild(s);"))
+
+(defn pr-code [code-str]
+ (pr-str (str "#_CODE_" code-str "#_CODE_")))
+
+(defn read-code [code-str]
+ (when-let [raw-code (second (re-find #"#_CODE_(.+)#_CODE_" code-str))]
+ ;; Use read-string to undo escaping of characters by pr-str (e.g. newlines)
+ (read-string (str "\"" raw-code "\""))))
+
+(defn load-gist [gist callback]
+ (let [set-content (fn [progress-event]
+ (callback (.. progress-event -srcElement -responseText)))
+ oreq (js/XMLHttpRequest.)]
+ (.addEventListener oreq "load" set-content)
+ (.open oreq "GET" (str "https://gist.githubusercontent.com/" gist "/raw"))
+ (.send oreq)))
+
+
+(defn bookmarklet-href [code-str]
+ (str "javascript:(function(){"
+ "var runCode = function() {
+ try {
+ scittle.core.eval_string(" (pr-code code-str) ")
+ } catch (error) {
+ console.log('Error in code', error);
+ alert('Error running code, see console')
+ }
+ };"
+ "if(typeof scittle === 'undefined'){"
+ (append-tag :script {:src "https://babashka.github.io/scittle/js/scittle.js"
+ :onerror "function(){alert('Error loading ' + this.src)}"
+ :onload "runCode"})
+ "} else {
+ runCode() }"
+ "})();"))
+
+(defn query-params []
+ (let [query-str (.substring js/window.location.search 1)]
+ (into {}
+ (map (fn [pair]
+ (let [[k v] (.split pair "=" 2)]
+ [(keyword (js/decodeURIComponent k))
+ (js/decodeURIComponent v)])))
+ (.split query-str "&"))))
+
+
+(def *initial-name (r/atom nil))
+(def *initial-code (r/atom nil))
+
+;; Initialize code
+(let [{:keys [gist code name]} (query-params)]
+ (cond gist
+ (do
+ (reset! *initial-name "---")
+ (reset! *initial-code ";; loading from gist")
+ (load-gist gist (fn [content]
+ (let [[code meta-str] (reverse (clojure.string/split content #";;---+\n"))
+ {bookmark-name :name} (when meta-str
+ (read-string meta-str))]
+ (when bookmark-name
+ (reset! *initial-name bookmark-name))
+ (reset! *initial-code code)))))
+ code
+ (do
+ (reset! *initial-name (or name "My first bookmarklet"))
+ (reset! *initial-code code))
+ :else
+ (do
+ (reset! *initial-name "My first bookmarklet")
+ (reset! *initial-code (str "; This is the code of your bookmarklet\n"
+ (pr-str '(js/alert "Hello")))))))
+
+(defn bookmark-name-field [initial-name *bookmark-name]
+ (let [*name (r/atom initial-name)]
+ [(fn []
+ [:input {:type "text"
+ :placeholder "The name of the Bookmarklet"
+ :value @*name
+ :on-change (fn [e]
+ (let [v (.. e -target -value)]
+ (reset! *name v)
+ (reset! *bookmark-name
+ (if (clojure.string/blank? v)
+ (str "Bookmarklet " (rand-int 1000))
+ v))))}])]))
+
+(defn editor [*code]
+ [:textarea
+ {:rows 10 :cols 80
+ :value @*code
+ :on-drop (fn [e]
+ (let [bookmarklet (js/decodeURIComponent (.. e -dataTransfer (getData "text")))
+ cljs-snippet (read-code bookmarklet)
+ new-code (if cljs-snippet
+ (str "; Extracted snippet\n" cljs-snippet)
+ (str "; Failed to extract snippet\n" bookmarklet))]
+ (js/console.log "Dropped" bookmarklet)
+ (set! (.. e -target -value) new-code)
+ (reset! *code new-code)
+ (.preventDefault e)))
+ :on-change (fn [e] (reset! *code (.. e -target -value)))}])
+
+
+
+(defn workspace []
+ (let [value @*initial-code
+ *code (r/atom value)
+ bookmark-name @*initial-name
+ *bookmark-name (r/atom bookmark-name)]
+ [:div
+ [bookmark-name-field bookmark-name *bookmark-name]
+ [:br]
+ [editor *code]
+ [:br]
+ [:br]
+ "Click the following link or drag it to the bookmarks bar: "
+ [(fn []
+ [(fn [] [:a {:href (bookmarklet-href @*code)} @*bookmark-name])])
+ *code]
+ [:br]
+ [(fn []
+ [:a {:href (str "?name=" (js/encodeURIComponent @*bookmark-name)
+ "&code=" (js/encodeURIComponent @*code)
+ "%20")} "Copy this link to share ⤴️"])]]))
+
+(rdom/render [workspace] (.getElementById js/document "app"))
diff --git a/resources/public/cljs/codemirror.cljs b/resources/public/cljs/codemirror.cljs
new file mode 100644
index 0000000..db28746
--- /dev/null
+++ b/resources/public/cljs/codemirror.cljs
@@ -0,0 +1,56 @@
+(require '[clojure.string :as str])
+(declare cm)
+
+(defn eval-me []
+ (js/scittle.core.eval_string (-> cm .-state .-doc .toString)))
+
+(def extension
+ (.of js/cv.keymap
+ (clj->js [{:key "Mod-Enter"
+ :run (fn []
+ (eval-me))}
+ #_{:key (str modifier "-Enter")
+ :shift (partial eval-top-level on-result)
+ :run (partial eval-at-cursor on-result)}])))
+(def cm
+ (let [doc (str/trim "
+(require '[reagent.core :as r]
+ '[reagent.dom :as rdom]
+ '[re-frame.core :as rf])
+
+(rf/reg-event-fx ::click (fn [{:keys [db]} _] {:db (update db :clicks (fnil inc 0))}))
+(rf/reg-sub ::clicks (fn [db] (:clicks db)))
+
+(defn my-component []
+ (let [clicks (rf/subscribe [::clicks])]
+ [:div
+ [:p \"Clicks: \" @clicks]
+ [:p [:button {:on-click #(rf/dispatch [::click])}
+ \"Click me!\"]]]))
+
+(rdom/render [my-component] (.getElementById js/document \"reagent\"))
+")]
+ (js/cm.EditorView. #js {:doc doc
+ :extensions #js [js/cm.basicSetup, (js/lc.clojure), (.highest js/cs.Prec extension)]
+ :parent (js/document.querySelector "#app")
+ #_#_:dispatch (fn [tr] (-> cm (.update #js [tr])) (eval-me))
+ })))
+(set! (.-eval_me js/globalThis) eval-me)
+(set! (.-cm_instance js/globalThis) cm)
+
+(defn linux? []
+ (some? (re-find #"(Linux)|(X11)" js/navigator.userAgent)))
+
+(defn mac? []
+ (and (not (linux?))
+ (some? (re-find #"(Mac)|(iPhone)|(iPad)|(iPod)" js/navigator.platform))))
+
+(let [elt (js/document.getElementById "evalMe")
+ txt (.-innerText elt)
+ mod-symbol (if (mac?)
+ "⌘"
+ "⌃")
+ txt (str txt " " mod-symbol"-⏎")]
+ (set! (.-innerHTML elt) txt))
+
+(eval-me)
diff --git a/resources/public/cljs/nrepl_playground.cljs b/resources/public/cljs/nrepl_playground.cljs
new file mode 100644
index 0000000..2c71307
--- /dev/null
+++ b/resources/public/cljs/nrepl_playground.cljs
@@ -0,0 +1,15 @@
+(ns nrepl-playground)
+
+(+ 1 2 3)
+
+(->
+ (js/document.getElementsByTagName "body")
+ first
+ (.append
+ (doto (js/document.createElement "p")
+ (.append
+ (js/document.createTextNode "there")))))
+
+(defn foo [])
+
+(js/alert "Isn't this cool? :)")
diff --git a/resources/public/cljs/replicant_tictactoe/core.cljs b/resources/public/cljs/replicant_tictactoe/core.cljs
new file mode 100644
index 0000000..b5ad81f
--- /dev/null
+++ b/resources/public/cljs/replicant_tictactoe/core.cljs
@@ -0,0 +1,33 @@
+;; COPIED FROM https://github.com/cjohansen/replicant-tic-tac-toe/blob/7a33fb12f0cd6658b2f555ff673dee031d4aa921/src/tic_tac_toe/core.cljs
+
+(ns replicant-tictactoe.core
+ (:require [replicant.dom :as r]
+ [replicant-tictactoe.game :as game]
+ [replicant-tictactoe.ui :as ui]))
+
+(defn start-new-game [store]
+ (reset! store (game/create-game {:size 3})))
+
+(defn main []
+ ;; Set up the atom
+ (let [store (atom nil)
+ el (js/document.getElementById "app")]
+
+ ;; Globally handle DOM events
+ (r/set-dispatch!
+ (fn [_ [action & args]]
+ (case action
+ :tic (apply swap! store game/tic args)
+ :reset (start-new-game store))))
+
+ ;; Render on every change
+ (add-watch store ::render
+ (fn [_ _ _ game]
+ (->> (ui/game->ui-data game)
+ ui/render-game
+ (r/render el))))
+
+ ;; Trigger the first render by initializing the game.
+ (start-new-game store)))
+
+(main)
diff --git a/resources/public/cljs/replicant_tictactoe/game.cljs b/resources/public/cljs/replicant_tictactoe/game.cljs
new file mode 100644
index 0000000..3f62d7f
--- /dev/null
+++ b/resources/public/cljs/replicant_tictactoe/game.cljs
@@ -0,0 +1,41 @@
+;; COPIED FROM https://github.com/cjohansen/replicant-tic-tac-toe/blob/7a33fb12f0cd6658b2f555ff673dee031d4aa921/src/tic_tac_toe/game.cljs
+
+(ns replicant-tictactoe.game)
+
+(defn create-game [{:keys [size]}]
+ {:next-player :x
+ :size size})
+
+(def next-player {:x :o, :o :x})
+
+(defn winner? [tics path]
+ (when (= 1 (count (set (map tics path))))
+ path))
+
+(defn get-winning-path [{:keys [size tics]} y x]
+ (or (winner? tics (mapv #(vector y %) (range 0 size)))
+ (winner? tics (mapv #(vector % x) (range 0 size)))
+ (when (= y x)
+ (winner? tics (mapv #(vector % %) (range 0 size))))))
+
+(defn maybe-conclude [game y x]
+ (if-let [path (get-winning-path game y x)]
+ (-> (dissoc game :next-player)
+ (assoc :over? true
+ :victory {:player (get-in game [:tics [y x]])
+ :path path}))
+ (let [tie? (= (count (:tics game)) (* (:size game) (:size game)))]
+ (cond-> game
+ tie? (dissoc :next-player)
+ tie? (assoc :over? true)))))
+
+(defn tic [game y x]
+ (let [player (:next-player game)]
+ (if (or (get-in game [:tics [y x]])
+ (<= (:size game) x)
+ (<= (:size game) y))
+ game
+ (-> game
+ (assoc-in [:tics [y x]] player)
+ (assoc :next-player (next-player player))
+ (maybe-conclude y x)))))
\ No newline at end of file
diff --git a/resources/public/cljs/replicant_tictactoe/style.css b/resources/public/cljs/replicant_tictactoe/style.css
new file mode 100644
index 0000000..7ec052a
--- /dev/null
+++ b/resources/public/cljs/replicant_tictactoe/style.css
@@ -0,0 +1,50 @@
+/* COPIED from https://github.com/cjohansen/replicant-tic-tac-toe/blob/7a33fb12f0cd6658b2f555ff673dee031d4aa921/resources/public/styles.css */
+.cell {
+ aspect-ratio: 1 / 1;
+ background: rgba(255, 255, 255, 0.8);
+ border-radius: 6%;
+ border: none;
+ display: block;
+ flex: 1 1 0%;
+ outline: none;
+ position: relative;
+ width: 100%;
+}
+
+.cell-content {
+ opacity: 1;
+ transition: opacity 0.25s;
+}
+
+.transparent {
+ opacity: 0;
+}
+
+.cell-dim {
+ background: rgba(249, 249, 240, 0.3);
+}
+
+.cell-highlight {
+ background: #fcfcf3;
+}
+
+.clickable {
+ cursor: pointer;
+}
+
+.board {
+ --gap: 0.75rem;
+ background: #833ab4;
+ background: linear-gradient(90deg, #833ab4 0%, #fd1d1d 50%, #fcb045 100%);
+ display: flex;
+ flex-direction: column;
+ gap: var(--gap);
+ padding: var(--gap);
+ max-width: 80vh;
+}
+
+.row {
+ display: flex;
+ flex-direction: row;
+ gap: var(--gap);
+}
\ No newline at end of file
diff --git a/resources/public/cljs/replicant_tictactoe/ui.cljs b/resources/public/cljs/replicant_tictactoe/ui.cljs
new file mode 100644
index 0000000..c53dc65
--- /dev/null
+++ b/resources/public/cljs/replicant_tictactoe/ui.cljs
@@ -0,0 +1,75 @@
+;; COPIED FROM https://github.com/cjohansen/replicant-tic-tac-toe/blob/7a33fb12f0cd6658b2f555ff673dee031d4aa921/src/tic_tac_toe/ui.cljs
+
+(ns replicant-tictactoe.ui)
+
+(def mark-x
+ [:svg {:xmlns "http://www.w3.org/2000/svg"
+ :viewBox "0 -10 108 100"}
+ [:path
+ {:fill "currentColor"
+ :d "m1.753 69.19.36-1.08q.35-1.09 1.92-2.97 1.58-1.87 3.85-3.84 2.29-1.97 4.6-3.54 2.31-1.57 4.93-3.24 2.62-1.66 4.65-2.9 2.04-1.23 3.91-2.27 1.87-1.05 3.98-2.31 2.11-1.27 4.12-2.5 2.01-1.24 4.33-2.51l4.6-2.52q2.27-1.25 4.84-2.86 2.56-1.62 5.03-3.09 2.47-1.47 4.5-2.88 2.03-1.4 3.82-2.82t3.81-3.47q2.01-2.06 3.7-3.51 1.69-1.46 3.47-3.03 1.77-1.57 4.01-3.69 2.24-2.11 4.13-3.7 1.89-1.58 3.93-2.97 2.04-1.39 4.05-2.49 2.01-1.11 5.26-2.54 3.24-1.44 4.48-1.46 1.24-.01 2.42.37 1.18.37 2.18 1.11 1 .74 1.71 1.75.71 1.02 1.06 2.21.34 1.19.3 2.43-.05 1.24-.5 2.39-.44 1.16-1.23 2.12-.79.95-1.84 1.61-1.05.65-2.26.94-1.21.28-2.44.16-1.23-.11-2.37-.62-1.13-.5-2.04-1.34-.91-.84-1.51-1.93-.6-1.08-.81-2.3-.22-1.22-.04-2.45.18-1.23.75-2.33.56-1.1 1.45-1.97.89-.86 2.01-1.4 1.11-.54 2.35-.69 1.23-.15 2.44.1t2.29.87q1.07.63 1.88 1.56.82.93 1.29 2.08.48 1.14.56 2.38.09 1.24-.23 2.44-.31 1.19-.99 2.23-.68 1.04-1.66 1.8-.98.76-2.15 1.18l-1.16.41-2.28 1.17q-2.28 1.18-4.38 2.7-2.1 1.51-4.2 3.44-2.1 1.92-4.18 3.7-2.08 1.77-3.9 3.44-1.81 1.68-3.41 3.13-1.6 1.46-3.38 3.09-1.79 1.62-3.44 2.97-1.66 1.34-3.53 2.4-1.88 1.06-4.17 2.65-2.3 1.6-4.79 2.74-2.48 1.14-4.98 2.71-2.5 1.57-4.51 2.47-2.01.9-3.99 1.87-1.98.97-3.88 2.02-1.91 1.05-4.38 2.34-2.46 1.28-4.94 2.53-2.47 1.25-4.48 2.38-2 1.12-3.96 2.14-1.95 1.01-3.83 1.99-1.89.98-4.37 2.05-2.48 1.06-2.96 2.01-.48.96-.78 1.49-.3.53-.71.97-.41.44-.92.77-.51.34-1.09.54-.57.2-1.17.25-.6.06-1.2-.03t-1.16-.32q-.56-.23-1.05-.59-.49-.35-.89-.82-.39-.46-.65-1.01-.27-.54-.4-1.14-.13-.59-.12-1.19.02-.6.18-1.19l.16-.59Z"}]
+ [:path
+ {:fill "currentColor"
+ :d "m28.099 4.991 2.69 1.97q2.69 1.96 4.5 3.22 1.8 1.28 4.54 3.46 2.74 2.18 4.57 3.89t3.38 3.72q1.54 2.02 2.88 4.3 1.34 2.28 2.83 4.46 1.48 2.18 2.63 4.14 1.15 1.96 2.74 4.07 1.59 2.1 3.59 4.19 1.99 2.08 4.23 4.48 2.24 2.4 3.7 4.04 1.47 1.64 2.91 3.23 1.44 1.59 3.08 3.58 1.64 1.99 3.51 4.08 1.87 2.09 3.55 3.77 1.69 1.68 4.1 3.51 2.42 1.83 3.9 2.58 1.48.74 2.14 1.34.66.6 1.15 1.33.5.74.8 1.57.31.84.4 1.72.1.88-.02 1.76-.12.88-.44 1.71-.33.82-.84 1.55-.51.72-1.19 1.3-.67.58-1.46.98-.79.41-1.65.61-.87.2-1.76.19-.88-.01-1.74-.24-.86-.22-1.64-.64-.78-.42-2.27-2.72-1.48-2.3-1.52-3.49-.03-1.19.31-2.33.35-1.14 1.04-2.11.69-.97 1.66-1.67.96-.7 2.1-1.05 1.14-.35 2.33-.32 1.19.02 2.31.43t2.05 1.15q.93.75 1.58 1.75.64 1 .93 2.15.29 1.16.2 2.35-.09 1.18-.56 2.28-.47 1.1-1.26 1.99-.79.88-1.83 1.47t-2.2.82q-1.17.23-2.35.07-1.19-.16-2.25-.68-1.07-.53-1.92-1.37-.84-.84-1.37-1.9-.54-1.07-.7-2.25-.17-1.18.06-2.35.22-1.17.8-2.21.58-1.04 1.47-1.84.88-.79 1.98-1.27 1.09-.47 2.28-.57 1.18-.1 2.34.18 1.16.29 2.16.93 1.01.63 1.76 1.56.74.93-.33-.26-1.07-1.18-.41-.58.66.59 1.15 1.33.5.74.8 1.57.31.83.4 1.72.1.88-.02 1.76-.12.88-.44 1.7-.33.83-.84 1.55-.51.73-1.19 1.31-.67.58-1.46.98-.79.41-1.65.61-.87.2-1.75.19-.89-.01-1.75-.24-.86-.22-1.64-.64-.78-.42-2.73-1.57-1.95-1.14-4.26-2.95-2.31-1.8-3.87-3.43-1.57-1.62-3.17-3.29-1.6-1.66-3.55-4.05-1.95-2.39-3.33-4.15-1.39-1.76-2.77-3.4-1.38-1.64-3.07-3.56-1.7-1.91-3.91-4.13-2.2-2.22-3.74-4.1-1.54-1.88-2.79-3.75-1.24-1.87-2.4-4.33t-2.39-4.46q-1.23-2.01-2.4-4.59-1.17-2.59-2.53-5.01-1.36-2.43-3.35-4.44-1.99-2.02-4.52-4.27-2.54-2.25-5.33-4.04-2.81-1.79-3.28-2.21-.47-.41-.83-.92-.35-.51-.58-1.1-.22-.58-.3-1.2-.08-.62-.01-1.23.08-.62.29-1.21.22-.58.58-1.1.35-.51.81-.93.47-.42 1.02-.71t1.16-.45q.61-.15 1.23-.15t1.22.14q.61.15 1.17.44l.55.28Z"}]])
+
+(def mark-o
+ [:svg {:xmlns "http://www.w3.org/2000/svg"
+ :viewBox "0 0 114 114"}
+ [:path
+ {:fill "none"
+ :stroke "currentColor"
+ :stroke-linecap "round"
+ :stroke-width "6"
+ :d "M74.616 8.935c7.73 2.38 15.96 9.34 21.58 16.04 5.63 6.69 10.57 15.46 12.18 24.11 1.6 8.65.74 19.67-2.53 27.77-3.27 8.11-10.12 15.37-17.09 20.88-6.98 5.51-16.07 10.81-24.76 12.17-8.7 1.35-19.32-.76-27.42-4.06-8.1-3.29-15.73-8.93-21.21-15.73-5.48-6.81-10.32-16.5-11.67-25.09-1.35-8.6.19-18.39 3.57-26.51 3.38-8.11 9.99-16.6 16.71-22.19 6.72-5.59 13.95-10.52 23.63-11.36 9.68-.84 28.04 4.34 34.45 6.32 6.42 1.97 4.37 4.6 4.04 5.55m-48.33-9.69c7.65-3.32 19.78-3.63 28.63-2.01 8.86 1.63 17.85 5.89 24.49 11.76 6.64 5.87 12.7 15.08 15.37 23.48 2.67 8.41 2.5 18.4.65 26.95-1.85 8.54-5.98 17.59-11.77 24.34-5.78 6.74-14.56 13.05-22.93 16.11-8.37 3.06-18.75 4.19-27.29 2.25-8.54-1.93-17.37-7.89-23.96-13.87-6.59-5.97-12.89-13.58-15.57-21.96-2.69-8.39-2.31-19.94-.56-28.34 1.75-8.4 5.21-15.74 11.06-22.09 5.85-6.35 19.92-13.32 24.04-16.01 4.12-2.7.37-1.1.67-.16"}]])
+
+(defn render-cell [{:keys [content on-click dim? highlight? clickable?]}]
+ [:button.cell
+ {:on {:click on-click}
+ :class (cond-> []
+ dim? (conj "cell-dim")
+ highlight? (conj "cell-highlight")
+ clickable? (conj "clickable"))}
+ (when content
+ [:div.cell-content
+ {:replicant/mounting {:class "transparent"}
+ :replicant/unmounting {:class "transparent"}}
+ content])])
+
+(defn render-board [{:keys [rows]}]
+ [:div.board
+ (for [row rows]
+ [:div.row
+ (for [cell row]
+ (render-cell cell))])])
+
+(defn render-game [{:keys [board button]}]
+ [:div
+ (render-board board)
+ (when button
+ [:button {:on {:click (:on-click button)}
+ :style {:margin-top 20
+ :font-size 20}}
+ (:text button)])])
+
+(def player->mark
+ {:x mark-x
+ :o mark-o})
+
+(defn game->ui-data [{:keys [size tics victory over?]}]
+ (let [highlight? (set (:path victory))]
+ {:button (when over?
+ {:text "Start over"
+ :on-click [:reset]})
+ :board
+ {:rows
+ (for [y (range size)]
+ (for [x (range size)]
+ (if-let [player (get tics [y x])]
+ (let [victorious? (highlight? [y x])]
+ (cond-> {:content (player->mark player)}
+ victorious? (assoc :highlight? true)
+ (and over? (not victorious?)) (assoc :dim? true)))
+ (if over?
+ {:dim? true}
+ {:clickable? true
+ :on-click [:tic y x]}))))}}))
\ No newline at end of file
diff --git a/resources/public/cljs/script.cljs b/resources/public/cljs/script.cljs
new file mode 100644
index 0000000..27b8b45
--- /dev/null
+++ b/resources/public/cljs/script.cljs
@@ -0,0 +1,2 @@
+(defn my-alert2 []
+ (js/alert "My alert 2!"))
diff --git a/resources/public/cljs/tictactoe.cljs b/resources/public/cljs/tictactoe.cljs
new file mode 100644
index 0000000..d348658
--- /dev/null
+++ b/resources/public/cljs/tictactoe.cljs
@@ -0,0 +1,85 @@
+(ns tictactoe
+ "Ported from https://github.com/borkdude/tictactoe-cljs"
+ (:require [reagent.core :as r]
+ [reagent.dom :as rdom]))
+
+(def empty-board [[\- \- \-]
+ [\- \- \-]
+ [\- \- \-]])
+
+(def state (r/atom {:board empty-board :player \X}))
+
+(defn get-board-cell
+ ([board row col]
+ (get-in board [row col])))
+
+(defn get-player [app-state]
+ (-> app-state :game-state :player))
+
+(defn other-player [player]
+ (if (= player \X) \O \X))
+
+(defn winner-in-rows? [board player]
+ (boolean (some (fn [row] (every? (fn [c] (= c player)) row)) board)))
+
+(defn transposed-board [board]
+ (vec (apply map vector board)))
+
+(defn winner-in-cols? [board player]
+ (winner-in-rows? (transposed-board board) player))
+
+(defn winner-in-diagonals? [board player]
+ (let [diag-coords [[[0 0] [1 1] [2 2]]
+ [[0 2] [1 1] [2 0]]]]
+ (boolean (some (fn [coords]
+ (every? (fn [coord]
+ (= player (apply get-board-cell board coord)))
+ coords))
+ diag-coords))))
+
+(defn winner?
+ "checks if there is a winner. when called with no args, checks for player X and player O.
+returns the character for the winning player, nil if there is no winner"
+ ([board]
+ (boolean (or (winner? board \X)
+ (winner? board \O))))
+ ([board player]
+ (when (or (winner-in-rows? board player)
+ (winner-in-cols? board player)
+ (winner-in-diagonals? board player))
+ player)))
+
+(defn full-board?
+ [board]
+ (let [all-cells (apply concat board)]
+ (not-any? #(= % \-) all-cells)))
+
+(defn new-state [old-state row col]
+ (if (and (= (get-board-cell (:board old-state) row col) \-)
+ (not (winner? (:board old-state))))
+ {:board (assoc-in (:board old-state) [row col] (:player old-state))
+ :player (other-player (:player old-state))}
+ old-state))
+
+(defn tictactoe []
+ [:div
+ (if (winner? (:board @state))
+ (str "The winner is " (other-player (:player @state)))
+ (if (full-board? (:board @state))
+ "It's a draw"
+ (str "Your turn, player " (:player @state))))
+ (let [board (-> @state :board)]
+ [:table
+ [:tbody
+ (map-indexed
+ (fn [i row]
+ ^{:key i}
+ [:tr
+ (map-indexed (fn [j elt]
+ ^{:key j}
+ [:td {:on-click (fn []
+ (swap! state new-state i j))}elt])
+ row)])
+ board)]])])
+
+(rdom/render [tictactoe] (.getElementById js/document "app"))
diff --git a/japji/resources/css/style.css b/resources/public/css/style.css
similarity index 100%
rename from japji/resources/css/style.css
rename to resources/public/css/style.css
diff --git a/japji/resources/data/japji-nihung-timings.edn b/resources/public/data/japji-nihung-timings.edn
similarity index 100%
rename from japji/resources/data/japji-nihung-timings.edn
rename to resources/public/data/japji-nihung-timings.edn
diff --git a/japji/resources/data/japji-nihung-timings.json b/resources/public/data/japji-nihung-timings.json
similarity index 100%
rename from japji/resources/data/japji-nihung-timings.json
rename to resources/public/data/japji-nihung-timings.json
diff --git a/japji/resources/fonts/NotoSansGurmukhi-Medium.ttf b/resources/public/fonts/NotoSansGurmukhi-Medium.ttf
similarity index 100%
rename from japji/resources/fonts/NotoSansGurmukhi-Medium.ttf
rename to resources/public/fonts/NotoSansGurmukhi-Medium.ttf
diff --git a/japji/resources/fonts/NotoSansGurmukhi-VariableFont_wdth,wght.ttf b/resources/public/fonts/NotoSansGurmukhi-VariableFont_wdth,wght.ttf
similarity index 100%
rename from japji/resources/fonts/NotoSansGurmukhi-VariableFont_wdth,wght.ttf
rename to resources/public/fonts/NotoSansGurmukhi-VariableFont_wdth,wght.ttf
diff --git a/japji/resources/fonts/README.md b/resources/public/fonts/README.md
similarity index 100%
rename from japji/resources/fonts/README.md
rename to resources/public/fonts/README.md
diff --git a/japji/resources/fonts/bulara_5.ttf b/resources/public/fonts/bulara_5.ttf
similarity index 100%
rename from japji/resources/fonts/bulara_5.ttf
rename to resources/public/fonts/bulara_5.ttf
diff --git a/japji/resources/fonts/bulara_6.ttf b/resources/public/fonts/bulara_6.ttf
similarity index 100%
rename from japji/resources/fonts/bulara_6.ttf
rename to resources/public/fonts/bulara_6.ttf
diff --git a/japji/resources/fonts/bulara_8.ttf b/resources/public/fonts/bulara_8.ttf
similarity index 100%
rename from japji/resources/fonts/bulara_8.ttf
rename to resources/public/fonts/bulara_8.ttf
diff --git a/japji/resources/fonts/bulara_9.ttf b/resources/public/fonts/bulara_9.ttf
similarity index 100%
rename from japji/resources/fonts/bulara_9.ttf
rename to resources/public/fonts/bulara_9.ttf
diff --git a/japji/resources/fonts/bularab5.ttf b/resources/public/fonts/bularab5.ttf
similarity index 100%
rename from japji/resources/fonts/bularab5.ttf
rename to resources/public/fonts/bularab5.ttf
diff --git a/japji/resources/fonts/bularah5.ttf b/resources/public/fonts/bularah5.ttf
similarity index 100%
rename from japji/resources/fonts/bularah5.ttf
rename to resources/public/fonts/bularah5.ttf
diff --git a/japji/resources/fonts/bularah7.ttf b/resources/public/fonts/bularah7.ttf
similarity index 100%
rename from japji/resources/fonts/bularah7.ttf
rename to resources/public/fonts/bularah7.ttf
diff --git a/japji/resources/fonts/bularao5.ttf b/resources/public/fonts/bularao5.ttf
similarity index 100%
rename from japji/resources/fonts/bularao5.ttf
rename to resources/public/fonts/bularao5.ttf
diff --git a/japji/resources/fonts/bularap5.ttf b/resources/public/fonts/bularap5.ttf
similarity index 100%
rename from japji/resources/fonts/bularap5.ttf
rename to resources/public/fonts/bularap5.ttf
diff --git a/resources/public/html/cljs-ajax.html b/resources/public/html/cljs-ajax.html
new file mode 100644
index 0000000..e83446c
--- /dev/null
+++ b/resources/public/html/cljs-ajax.html
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/public/html/export.html b/resources/public/html/export.html
new file mode 100644
index 0000000..a66f73b
--- /dev/null
+++ b/resources/public/html/export.html
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
diff --git a/resources/public/html/local.html b/resources/public/html/local.html
new file mode 100644
index 0000000..d9c36bf
--- /dev/null
+++ b/resources/public/html/local.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/public/html/reagent.html b/resources/public/html/reagent.html
new file mode 100644
index 0000000..f0f1468
--- /dev/null
+++ b/resources/public/html/reagent.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/japji/index.html b/resources/public/index.html
similarity index 99%
rename from japji/index.html
rename to resources/public/index.html
index 3c1250a..6fae6bf 100644
--- a/japji/index.html
+++ b/resources/public/index.html
@@ -7,13 +7,13 @@
-
-
+
-
+
@@ -1723,7 +1723,7 @@
(def student-recordings (atom (apply vector (repeat (count data) nil))))
- (defn enable-play-button [phrase-no])
+ (defn enable-play-button! [phrase-no])
(defn record-student-sound!
[phrase-no]
@@ -1732,10 +1732,10 @@
(try
(.then (.getUserMedia (.mediaDevices js/navigator) {:audio true})
(fn [arg]
- (let [media-recorder (MediaRecorder. arg)
+ (let [media-recorder (js/MediaRecorder. arg)
audio-chunks (atom [])]
(.start media-recorder)
- (set! media-recorder onerror
+ (set! (.-onerror media-recorder)
(fn [s]
(.log js/console (str "Error while recording sound: " s))))
(.addEventListener media-recorder "dataavailable"
@@ -1747,7 +1747,7 @@
(js/console.log "data available after MediaRecorder.stop() called.")
(if (> (count @audio-chunks) 0)
(do
- ;; Store the blob in the student-sounds data structure
+ ;; Store the blob in the student-recordings data structure
(swap! student-recordings assoc phrase-no
(js/Blob. (clj->js @audio-chunks)))
(enable-play-button! phrase-no))))))))
@@ -1801,7 +1801,7 @@
(defn play-student!
[phrase-no]
- (.play (js/Audio. (.createObjectURL js/URL (@student-sounds phrase-no)))))
+ (.play (js/Audio. (.createObjectURL js/URL (@student-recordings phrase-no)))))
(defn animate-progress-bar!
[id duration]
@@ -1900,7 +1900,7 @@
Select words to hear them. Select the bullet • to hear the whole line. Select three dots ... to record your own
voice.
-
+
ਕ੍ਰਿਪਾ ਕਰਕੇ ਉਡੀਕ ਕਰੋ...