Skip to content

Commit

Permalink
Build static SCI playground page in GitHub pages (#31)
Browse files Browse the repository at this point in the history
Build a GitHub Pages site with an editor with pre-loaded all libs
this sci.configs support, so that people may play with them.
  • Loading branch information
holyjak authored Oct 2, 2023
1 parent bf9769c commit 503b83e
Show file tree
Hide file tree
Showing 8 changed files with 326 additions and 0 deletions.
48 changes: 48 additions & 0 deletions .github/workflows/deloy-site.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Build and Deploy
on:
push:
branches:
- main
permissions:
contents: read
pages: write
id-token: write
jobs:
build-and-deploy:
concurrency: ci-${{ github.ref }} # Recommended if you intend to make multiple deployments in quick succession.
runs-on: ubuntu-latest
steps:

- name: Checkout 🛎️
uses: actions/checkout@v3

- name: Prepare java
uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: '11'

- name: Install clojure tools
uses: DeLaGuardo/[email protected]
with:
# Install just one or all simultaneously
# The value must indicate a particular version of the tool, or use 'latest'
# to always provision the latest version
cli: latest
bb: latest

- name: Install and Build 🔧
run: |
cd playground && bb build
- name: Setup Pages
uses: actions/configure-pages@v3

- name: Upload artifact
uses: actions/upload-pages-artifact@v2
with:
path: 'playground/www'

- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2
4 changes: 4 additions & 0 deletions playground/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
www/js/
node_modules/
package-lock.json
yarn.lock
6 changes: 6 additions & 0 deletions playground/bb.edn
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{:tasks
{build {:doc "Build the static 'SCI Playground' site with sci and an editor"
:task (do (shell "yarn install")
(clojure "-M:dev:shadow-cli release playground")
(println "Built www"))}
watch (clojure "-M:dev:shadow-cli watch playground")}}
18 changes: 18 additions & 0 deletions playground/deps.edn
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{:deps {; SCI
org.babashka/sci.configs {:local/root ".."}
org.babashka/sci {:git/url "https://github.com/babashka/sci"
:git/sha "987910fb38fdd166865458c3fd4b468a22fb9992"}
;; Editor
io.github.nextjournal/clojure-mode {:git/sha "7b911bf6feab0f67b60236036d124997627cbe5e"}
;; Included libs
org.clojure/clojurescript {:mvn/version "1.11.51"}
applied-science/js-interop {:mvn/version "0.3.3"}
cljs-bean/cljs-bean {:mvn/version "1.9.0"}
com.fulcrologic/fulcro {:mvn/version "3.6.10"}
datascript/datascript {:mvn/version "1.5.3"}
funcool/promesa {:git/url "https://github.com/funcool/promesa"
:git/sha "e503874b154224ce85b223144e80b697df91d18e"}
reagent/reagent {:mvn/version "1.1.0"}
re-frame/re-frame {:mvn/version "1.3.0"}}
:aliases {:dev {:extra-deps {thheller/shadow-cljs {:mvn/version "2.25.7"}}}
:shadow-cli {:main-opts ["-m" "shadow.cljs.devtools.cli"]}}}
25 changes: 25 additions & 0 deletions playground/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "sci_playground",
"version": "1.0.0",
"devDependencies": {
"@codemirror/autocomplete": "^6.0.2",
"@codemirror/commands": "^6.0.0",
"@codemirror/lang-markdown": "6.0.0",
"@codemirror/language": "^6.1.0",
"@codemirror/lint": "^6.0.0",
"@codemirror/search": "^6.0.0",
"@codemirror/state": "^6.0.1",
"@codemirror/view": "^6.0.2",
"@lezer/common": "^1.0.0",
"@lezer/generator": "^1.0.0",
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "^1.0.0",
"@nextjournal/lezer-clojure": "1.0.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"shadow-cljs": "^2.25.7"
},
"dependencies": {
"w3c-keyname": "^2.2.4"
}
}
11 changes: 11 additions & 0 deletions playground/shadow-cljs.edn
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{:deps {:aliases [:dev]}
:nrepl {:port 9000}
:dev-http {8081 "www"}
:builds {:playground {:compiler-options {:output-feature-set :es8
:optimizations :advanced
:source-map true
:output-wrapper false}
:target :browser
:output-dir "www/js"
:modules {:playground {:init-fn playground/init}}
:devtools {:after-load playground/reload}}}}
171 changes: 171 additions & 0 deletions playground/src/playground.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
(ns playground
"Build CodeMirror editor with SCI evaluation for the SCI Playground."
(:require
[sci.core :as sci]
[clojure.string :as str]

;; All the configs
sci.configs.applied-science.js-interop
sci.configs.cljs.pprint
sci.configs.cljs.test
;sci.configs.clojure.test
sci.configs.fulcro.fulcro
sci.configs.funcool.promesa
sci.configs.mfikes.cljs-bean
sci.configs.re-frame.re-frame
sci.configs.reagent.reagent
sci.configs.reagent.reagent-dom-server
sci.configs.tonsky.datascript
; sci.configs.clojure-1-11

;; Code editor
;; Inspiration: https://github.com/nextjournal/clojure-mode/blob/main/demo/src/nextjournal/clojure_mode/demo.cljs
["@codemirror/commands" :refer [history historyKeymap]]
["@codemirror/language" :refer [#_foldGutter
syntaxHighlighting
defaultHighlightStyle]]
["@codemirror/state" :refer [EditorState]]
["@codemirror/view" :as view :refer [EditorView lineNumbers showPanel]]


;; JS deps for re-export to sci
["react" :as react]
["react-dom" :as react-dom]

[nextjournal.clojure-mode :as cm-clj]
))

;; Necessary to avoid the error 'Attempting to call unbound fn: #'clojure.core/*print-fn*'
;; when calling `println` inside the evaluated code
(enable-console-print!)
(sci/alter-var-root sci/print-fn (constantly *print-fn*))
(sci/alter-var-root sci/print-err-fn (constantly *print-err-fn*))

;; ------------------------------------------------------------ SCI eval

(def all-configs ; vars so that we can extract ns info
[#'sci.configs.applied-science.js-interop/config
#'sci.configs.cljs.pprint/config
#'sci.configs.cljs.test/config
;#'sci.configs.clojure.test/config
#'sci.configs.fulcro.fulcro/config
#'sci.configs.funcool.promesa/config
#'sci.configs.mfikes.cljs-bean/config
#'sci.configs.re-frame.re-frame/config
#'sci.configs.reagent.reagent/config
#'sci.configs.reagent.reagent-dom-server/config
#'sci.configs.tonsky.datascript/config])

(def sci-ctx
(->> all-configs
(map deref)
(reduce
sci/merge-opts
(sci/init {:classes {'js js/globalThis :allow :all}
:js-libs {"react" react
"react-dom" react-dom}}))))

(defn eval-code [code]
(try (sci/eval-string* sci-ctx code)
(catch :default e
(try (js/console.log "Evaluation failed:" (ex-message e)
(some-> e ex-data clj->js))
(catch :default _))
{::error (str (.-message e)) :data (ex-data e)})))

(defn eval-all [on-result x]
(on-result (some->> (.-doc (.-state x)) str eval-code))
true)

(defn sci-extension [on-result]
(.of view/keymap
#js [#js {:key "Mod-Enter" ; Cmd or Ctrl
:run (partial eval-all on-result)}]))

;; ------------------------------------------------------------ Code editor

(defn mac? []
(some? (re-find #"(Mac)|(iPhone)|(iPad)|(iPod)" js/navigator.platform)))

(defn output-panel-extension
"Display a panel below the editor with the output of the
last evaluation (read from the passed-in `result-atom`)"
[result-atom]
(let [dom (js/document.createElement "div")]
(add-watch result-atom :output-panel
(fn [_ _ _ new]
(if (::error new)
(do
(.add (.-classList dom) "error")
(set! (.-textContent dom) (str "ERROR: " (::error new)
(some->> new :data pr-str (str " ")))))
(do
(.remove (.-classList dom) "error")
(set! (.-textContent dom) (str ";; => " (pr-str new)))))))
(set! (.-className dom) "cm-output-panel")
(set! (.-textContent dom)
(str "Press "
(if (mac?) "Cmd" "Ctrl")
"-Enter in the editor to evaluate it. Return value will show up here."))
(.of showPanel
(fn [_view] #js {:dom dom}))))

(def theme
(.theme
EditorView
#js {".cm-output-panel.error" #js {:color "red"}}))

(defonce extensions
#js[theme
(history)
(syntaxHighlighting defaultHighlightStyle)
(view/drawSelection)
(lineNumbers)
(.. EditorState -allowMultipleSelections (of true))
cm-clj/default-extensions
(.of view/keymap cm-clj/complete-keymap)
(.of view/keymap historyKeymap)])

(defn bind-editor! [el code]
{:pre [el code]}
(let [target-el (js/document.createElement "div")
last-result (atom nil)
exts (.concat extensions
#js [(sci-extension (partial reset! last-result))
(output-panel-extension last-result)])]
(.replaceWith el target-el)
(new EditorView
#js {:parent target-el
:state (.create EditorState #js {:doc code
:extensions exts})})))

(defn list-libraries [all-config-vars]
(->> all-config-vars
(map (comp name :ns meta))
(map #(clojure.string/replace % #"^sci\.configs\.[\w-]+\." ""))
(remove #{"pprint" "test"})
sort
(str/join ", ")))

(defn ^:export init []
(let [code-el (js/document.getElementById "code")
code (.-textContent code-el)
libs-el (js/document.getElementById "libs")]
(set! (.-textContent libs-el) (list-libraries all-configs))
(bind-editor! code-el code))
(println "Init run"))

(defn ^:export reload []
(println "Reload run (noop)"))

(comment

(def X #'sci.configs.cljs.pprint/config)


(-> X meta :ns name
(clojure.string/replace-first "sci.configs." ""))



)
43 changes: 43 additions & 0 deletions playground/www/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8" />
<title>SCI Playground</title>
<link rel="stylesheet" href="/css/blog.css">
</head>

<body>
<h1>SCI Playground</h1>
<p>Write and evaluate SCI Clojure using any of the libraries in sci.configs
in the editor below. Use Cmd / Ctr - Enter to evaluate.</p>
<p>Supported libraries: <span id="libs">todo</span>.</p>

<pre><code id="code">(ns test1
(:require
[applied-science.js-interop :as j]
[promesa.core :as p]
[cljs-bean.core :refer [bean ->clj ->js]]
[re-frame.core :as rf]
[re-frame.db :as r.db]
[reagent.core :as r]
[reagent.dom.server :as rds]
[reagent.debug]
[reagent.ratom :as ratom]
[datascript.core :as d]
[datascript.db :as d.db]
[com.fulcrologic.fulcro.application :as app]
[com.fulcrologic.fulcro.components :as comp :refer [defsc]]
[com.fulcrologic.fulcro.dom :as dom]))
(defsc Root [this props]
(dom/div (dom/h3 "Hello from SCI!")
(dom/p "Here you can play with Fulcro and Reagent apps and much more!")))
(app/mount! (app/fulcro-app) Root "app")
</code></pre>
<div id="app" style="border: 1px dashed; padding: 0.4rem">
<em>You can use this <code>&lt;div id="app"&gt;</code> to render DOM.</em>
</div>
</body>
<script src="js/playground.js"></script>

</html>

0 comments on commit 503b83e

Please sign in to comment.