diff --git a/.gitignore b/.gitignore index 08581c3..649accf 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ vsl/* /resources/midi/soundfonts/ /src/noon/doc.clj /cljs-test-runner-out/ +/docs/ +/notebooks/ diff --git a/client/noon/client/core.cljs b/client/noon/client/core.cljs index f36bfe2..fff1192 100644 --- a/client/noon/client/core.cljs +++ b/client/noon/client/core.cljs @@ -17,5 +17,4 @@ (defn ^:export init [] (stylefy/init {:dom (gdom/init)}) - #_ (println guide/guide) (render)) diff --git a/client/noon/client/ui/code_editor.cljs b/client/noon/client/ui/code_editor.cljs index 71648b9..8d4632f 100644 --- a/client/noon/client/ui/code_editor.cljs +++ b/client/noon/client/ui/code_editor.cljs @@ -14,7 +14,6 @@ ["react-spinners/BeatLoader" :default spinner] ["@uiw/codemirror-themes-all" :as cm-themes] [noon.client.ui.misc :as ui.misc] - [noon.client.state :refer [<<]] ["react-highlight" :default Highlight])) (def EDITOR_EXTENSIONS @@ -97,10 +96,10 @@ :hover (when (not= state :visible) {:color "#64748b"})} :on-click on-click} (c :span {:style {:display :inline-block - :width "7px" :height "7px" - :border-radius "50%" - :background color - :opacity (case state :visible 1, :dimmed 0.4, 0.15)}}) + :width "7px" :height "7px" + :border-radius "50%" + :background color + :opacity (case state :visible 1, :dimmed 0.4, 0.15)}}) (str "ch " ch))) ;; ── Piano roll view ────────────────────────────────────────────── @@ -159,7 +158,6 @@ (seq dimmed-chs) (assoc :dimmed-channels (set dimmed-chs)) channel-order (assoc :channel-order channel-order))))}}))))) - (defui code-editor [{:keys [source options]}] (let [input-editor-ref (uix/use-ref) [source set-source] (uix/use-state source) @@ -168,9 +166,8 @@ [editing set-editing] (uix/use-state false) [evaluating set-evaluating] (uix/use-state false) [playing set-playing] (uix/use-state false) - [local-piano-roll set-local-piano-roll] (uix/use-state nil) ;; nil = follow global, true/false = override - global-piano-rolls (<< [:piano-rolls.get]) - show-piano-roll? (if (some? local-piano-roll) local-piano-roll global-piano-rolls) + [local-piano-roll set-local-piano-roll] (uix/use-state nil) ;; nil = follow option, true/false = override + show-piano-roll? (if (some? local-piano-roll) local-piano-roll (:show-piano-roll? options)) color :light-skyblue error? (:error return)] @@ -346,7 +343,6 @@ :on-click (fn [_] (set-local-piano-roll true))} (c TbPiano {:size 18}))) - (if editing (c CodeMirror diff --git a/client/noon/client/ui/doc.cljs b/client/noon/client/ui/doc.cljs index ea2130f..875009d 100644 --- a/client/noon/client/ui/doc.cljs +++ b/client/noon/client/ui/doc.cljs @@ -11,6 +11,10 @@ [noon.client.ui.breadcrumbs :as ui.breadcrumbs] [noon.client.constants :as constants])) +(defui connected-code-editor [props] + (let [show-piano-roll? (<< [:piano-rolls.get])] + (uix/$ ui.code-editor/code-editor (assoc-in props [:options :show-piano-roll?] show-piano-roll?)))) + (defn render-doc-node [node] (case (:type node) :section (uix/$ ui.section/section @@ -21,7 +25,7 @@ (concat (:content node) (sort-by :idx (vals (:children node)))))) :raw (uix/$ ui.misc/raw node) - :code (uix/$ ui.code-editor/code-editor node))) + :code (uix/$ connected-code-editor node))) (def doc-content (render-doc-node doc/doc-data)) diff --git a/client/noon/widget.cljs b/client/noon/widget.cljs new file mode 100644 index 0000000..ccefa5d --- /dev/null +++ b/client/noon/widget.cljs @@ -0,0 +1,45 @@ +(ns noon.widget + "Mounting layer for embedding noon code-editor widgets in external pages. + Scans the DOM for [data-noon-widget] elements and mounts the + noon.client.ui.code-editor component on them. + + Called explicitly via noon.widget.init() from a + diff --git a/shadow-cljs.edn b/shadow-cljs.edn index 639eab3..3464c32 100644 --- a/shadow-cljs.edn +++ b/shadow-cljs.edn @@ -4,7 +4,7 @@ :builds {:client {:target :browser - :modules {:main {:init-fn noon.client.core/init + :modules {:main {:entries [noon.client.core noon.widget] :reload-fn noon.client.core/reload}} :devtools {:http-root "public" :http-port 8999 diff --git a/src/noon/viz/clay.clj b/src/noon/viz/clay.clj new file mode 100644 index 0000000..dea40bc --- /dev/null +++ b/src/noon/viz/clay.clj @@ -0,0 +1,34 @@ +(ns noon.viz.clay + "Clay/Kindly integration for embedding interactive noon editor widgets. + Produces `kind/hiccup` output that mounts the noon code-editor component. + + Usage in a Clay notebook: + (require '[noon.viz.clay :as nclay]) + (nclay/editor \"(play (tup s0 s1 s2))\") + + The widget JS is part of the main noon client build (shadow-cljs).") + +(def ^:dynamic *widget-js-path* + "Path to the noon client JS bundle. + Default points to the local shadow-cljs dev server. + Override with `binding` for production or custom setups." + "http://localhost:8999/js/main.js") + +(defn editor + "Return a `kind/hiccup` form that renders an interactive noon editor widget. + + The widget includes: + - A CodeMirror editor with the given source code + - An Eval & Play button (runs via SCI in the browser) + - A piano roll visualization + - Audio playback via Web Audio API" + [source] + ^{:kindly/kind :kind/hiccup + :kindly/hide-code true} + [:div + [:div {:data-noon-widget ""} + [:pre {:class "noon-source" + :style {:display "none"}} + source]] + [:script {:src *widget-js-path*}] + [:script "noon.widget.init();"]])