commit 2b7bb080340e3798eab71eb853c7124f7c151521 Author: gered Date: Sat Dec 3 14:13:54 2016 -0500 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8b7c83b --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +.DS_Store +/target +/classes +/checkouts +pom.xml +pom.xml.asc +*.jar +*.class +/.lein-* +/.nrepl-port diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0781dfc --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Gered King + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..016cb43 --- /dev/null +++ b/README.md @@ -0,0 +1,182 @@ +# electroncljs + +A Leiningen template for [Electron](http://electron.atom.io/) +applications written using ClojureScript, Figwheel and Reagent. + +This template is based on [descjop](https://github.com/karad/lein_template_descjop). + +## Usage + +``` +$ lein new electroncljs +``` + +Then, + +``` +$ cd / +$ lein npm install +``` + +This will download all the required NPM dependencies necessary. + +Once complete you can get to work on your app. + +## Getting Started With Electron App Development + +I **strongly** recommend first reading the [Quick Start Guide](http://electron.atom.io/docs/tutorial/quick-start/) +on the Electron website before continuing further! At least up until it +starts talking about running/packaging your app, as that part is +different here. + +## Project Layout + +#### `/app` + +The main Electron app directory. Everything in here is what will be +available to your app when it is running and will be included with your +app when packaged for distribution. + +You should place your app resources here such as images, CSS, other JS +scripts, etc. + +`index.html` is what will be loaded first when your application starts. + +`package.json` is used by Electron to specify details about your +application and most importantly what the startup script is. You +probably won't need to change this other then updating the application +name and version as needed. + +#### `/src` + +Common source directory that will be available to both the Electron +main process code and the renderer process code. You can also think of +this as where you'd put your `.cljc` files if this were a typical +Clojure/ClojureScript web app. + +#### `/src_front` + +Source root for the Electron renderer process code. This is likely +where the vast majority of your application code will go. + +#### `/src_main` + +Source root for the Electron main process code. + +#### `/env` + +Additional source roots for `/src`, `/src_front`, and `/src_main` which +are pulled in for either development or production builds. + +There is also an application config map located under +`/env/[profile]/src/[your-project-name]/config.cljs` + +## Building + +To build both the code for the main and renderer process: + +``` +$ lein build +``` + +Or to build each separately: + +``` +$ lein build-main +$ lein build-front +``` + +JS artifacts will be output to `/app/js`. + +To clean up all build artifacts: + +``` +$ lein clean +``` + +## Running + +After building, simply do: + +``` +$ lein run +``` + +And Electron should start up with your app running. + +## Figwheel + +IMPORTANT: You should start up Figwheel **before** Electron is running! + +``` +$ lein figwheel +``` + +And then in another terminal: + +``` +$ lein run +``` + +By default `lein figwheel` will run for the Electron renderer process +code which is probably what you will want the majority of the time. + +You can connect to the Figwheel REPL on port 7888. Once connected if +you run: + +``` +(cljs-repl) +``` + +You are now set up to work with your renderer process application code. + +Note that you can also use Figwheel for the Electron main process, but +do keep in mind that each time you make a code change Electron will +restart to make sure _all_ potential main process code changes are +reloaded. + +``` +$ lein figwheel main +``` + +You can change this reload behaviour if you need to by editing the +function `on-figwheel-reload!` located under +`/env/dev/src_main/[your-project-name]/main/init.cljs`. If you do change +this, just remember that certain code changes won't take effect until +Electron relaunches. + +## Packaging for Release + +Out of the box, you can do: + +``` +$ lein package +``` + +Which will prepare an Electron release for your app under `/releases`. +Note that with the default `project.clj` configuration, this will only +build a release for the current platform/architecture. + +You can add additional platform/architectures and change other +packaging properties by editing the `package` alias in `project.clj`. + +For example, to build for all platforms (Win/Linux/Mac): + +``` +["with-profile" "prod" "shell" "./node_modules/.bin/electron-packager" + "app" :project/name + ~(str "--version=" electron-version) + "--asar" + "--out=releases" + "--overwrite" + "--platform=all"] +``` + +Building Windows releases under Mac/Linux is supported but you will +need to install Wine. + +Packaging is performed via [electron-packager](https://www.npmjs.com/package/electron-packager). + +## License + +Distributed under the the MIT License. See LICENSE for more details. diff --git a/project.clj b/project.clj new file mode 100644 index 0000000..b2d6848 --- /dev/null +++ b/project.clj @@ -0,0 +1,6 @@ +(defproject electroncljs/lein-template "0.1" + :description "Leiningen template for creating Electron apps with ClojureScript" + :url "https://github.com/gered/electroncljs" + :license {:name "MIT License" + :url "http://opensource.org/licenses/MIT"} + :eval-in-leiningen true) diff --git a/resources/leiningen/new/electroncljs/.gitignore b/resources/leiningen/new/electroncljs/.gitignore new file mode 100644 index 0000000..45eea1c --- /dev/null +++ b/resources/leiningen/new/electroncljs/.gitignore @@ -0,0 +1,13 @@ +.DS_Store +/target +/classes +/checkouts +pom.xml +pom.xml.asc +*.jar +*.class +/.lein-* +/.nrepl-port +/node_modules +/releases +/*.log diff --git a/resources/leiningen/new/electroncljs/LICENSE b/resources/leiningen/new/electroncljs/LICENSE new file mode 100644 index 0000000..87f3095 --- /dev/null +++ b/resources/leiningen/new/electroncljs/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) {{year}} YOUR-NAME-HERE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/resources/leiningen/new/electroncljs/README.md b/resources/leiningen/new/electroncljs/README.md new file mode 100644 index 0000000..113e66a --- /dev/null +++ b/resources/leiningen/new/electroncljs/README.md @@ -0,0 +1,12 @@ +# {{name}} + +A ClojureScript Electron application designed to ... well, that part is +up to you. + +## Usage + +FIXME + +## License + +Distributed under the the MIT License. See LICENSE for more details. \ No newline at end of file diff --git a/resources/leiningen/new/electroncljs/app/css/todos.css b/resources/leiningen/new/electroncljs/app/css/todos.css new file mode 100644 index 0000000..95be5c4 --- /dev/null +++ b/resources/leiningen/new/electroncljs/app/css/todos.css @@ -0,0 +1,558 @@ +@charset "utf-8"; +html, +body { + margin: 0; + padding: 0; +} + +button { + margin: 0; + padding: 0; + border: 0; + background: none; + font-size: 100%; + vertical-align: baseline; + font-family: inherit; + color: inherit; + -webkit-appearance: none; + -ms-appearance: none; + -o-appearance: none; + appearance: none; +} + +body { + font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; + line-height: 1.4em; + background: #eaeaea; + /* background: #eaeaea url('bg.png'); */ + color: #4d4d4d; + width: 550px; + margin: 0 auto; + -webkit-font-smoothing: antialiased; + -moz-font-smoothing: antialiased; + -ms-font-smoothing: antialiased; + -o-font-smoothing: antialiased; + font-smoothing: antialiased; +} + +button, +input[type="checkbox"] { + outline: none; +} + +#todoapp { + background: #fff; + background: rgba(255, 255, 255, 0.9); + margin: 130px 0 40px 0; + border: 1px solid #ccc; + position: relative; + border-top-left-radius: 2px; + border-top-right-radius: 2px; + box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.2), + 0 25px 50px 0 rgba(0, 0, 0, 0.15); +} + +#todoapp:before { + content: ''; + border-left: 1px solid #f5d6d6; + border-right: 1px solid #f5d6d6; + width: 2px; + position: absolute; + top: 0; + left: 40px; + height: 100%; +} + +#todoapp input::-webkit-input-placeholder { + font-style: italic; +} + +#todoapp input::-moz-placeholder { + font-style: italic; + color: #a9a9a9; +} + +#todoapp h1 { + position: absolute; + top: -120px; + width: 100%; + font-size: 70px; + font-weight: bold; + text-align: center; + color: #b3b3b3; + color: rgba(255, 255, 255, 0.3); + text-shadow: -1px -1px rgba(0, 0, 0, 0.2); + -webkit-text-rendering: optimizeLegibility; + -moz-text-rendering: optimizeLegibility; + -ms-text-rendering: optimizeLegibility; + -o-text-rendering: optimizeLegibility; + text-rendering: optimizeLegibility; +} + +#header { + padding-top: 15px; + border-radius: inherit; +} + +#header:before { + content: ''; + position: absolute; + top: 0; + right: 0; + left: 0; + height: 15px; + z-index: 2; + border-bottom: 1px solid #6c615c; + background: #8d7d77; + background: -webkit-gradient(linear, left top, left bottom, from(rgba(132, 110, 100, 0.8)),to(rgba(101, 84, 76, 0.8))); + background: -webkit-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); + background: linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); + /* filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670'); */ + border-top-left-radius: 1px; + border-top-right-radius: 1px; +} + +#new-todo, +.edit { + position: relative; + margin: 0; + width: 100%; + font-size: 24px; + font-family: inherit; + line-height: 1.4em; + border: 0; + outline: none; + color: inherit; + padding: 6px; + border: 1px solid #999; + box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + -o-box-sizing: border-box; + box-sizing: border-box; + -webkit-font-smoothing: antialiased; + -moz-font-smoothing: antialiased; + -ms-font-smoothing: antialiased; + -o-font-smoothing: antialiased; + font-smoothing: antialiased; +} + +#new-todo { + padding: 16px 16px 16px 60px; + border: none; + background: rgba(0, 0, 0, 0.02); + z-index: 2; + box-shadow: none; +} + +#main { + position: relative; + z-index: 2; + border-top: 1px dotted #adadad; +} + +label[for='toggle-all'] { + display: none; +} + +#toggle-all { + position: absolute; + top: -42px; + left: -4px; + width: 40px; + text-align: center; + /* Mobile Safari */ + border: none; +} + +#toggle-all:before { + content: '»'; + font-size: 28px; + color: #d9d9d9; + padding: 0 25px 7px; +} + +#toggle-all:checked:before { + color: #737373; +} + +#todo-list { + margin: 0; + padding: 0; + list-style: none; +} + +#todo-list li { + position: relative; + font-size: 24px; + border-bottom: 1px dotted #ccc; +} + +#todo-list li:last-child { + border-bottom: none; +} + +#todo-list li.editing { + border-bottom: none; + padding: 0; +} + +#todo-list li.editing .edit { + display: block; + width: 506px; + padding: 13px 17px 12px 17px; + margin: 0 0 0 43px; +} + +#todo-list li.editing .view { + display: none; +} + +#todo-list li .toggle { + text-align: center; + width: 40px; + /* auto, since non-WebKit browsers doesn't support input styling */ + height: auto; + position: absolute; + top: 0; + bottom: 0; + margin: auto 0; + /* Mobile Safari */ + border: none; + -webkit-appearance: none; + -ms-appearance: none; + -o-appearance: none; + appearance: none; +} + +#todo-list li .toggle:after { + content: '✔'; + /* 40 + a couple of pixels visual adjustment */ + line-height: 43px; + font-size: 20px; + color: #d9d9d9; + text-shadow: 0 -1px 0 #bfbfbf; +} + +#todo-list li .toggle:checked:after { + color: #85ada7; + text-shadow: 0 1px 0 #669991; + bottom: 1px; + position: relative; +} + +#todo-list li label { + white-space: pre; + word-break: break-word; + padding: 15px 60px 15px 15px; + margin-left: 45px; + display: block; + line-height: 1.2; + -webkit-transition: color 0.4s; + transition: color 0.4s; +} + +#todo-list li.completed label { + color: #a9a9a9; + text-decoration: line-through; +} + +#todo-list li .destroy { + display: none; + position: absolute; + top: 0; + right: 10px; + bottom: 0; + width: 40px; + height: 40px; + margin: auto 0; + font-size: 22px; + color: #a88a8a; + -webkit-transition: all 0.2s; + transition: all 0.2s; +} + +#todo-list li .destroy:hover { + text-shadow: 0 0 1px #000, + 0 0 10px rgba(199, 107, 107, 0.8); + -webkit-transform: scale(1.3); + -ms-transform: scale(1.3); + transform: scale(1.3); +} + +#todo-list li .destroy:after { + content: '✖'; +} + +#todo-list li:hover .destroy { + display: block; +} + +#todo-list li .edit { + display: none; +} + +#todo-list li.editing:last-child { + margin-bottom: -1px; +} + +#footer { + color: #777; + padding: 0 15px; + position: absolute; + right: 0; + bottom: -31px; + left: 0; + height: 20px; + z-index: 1; + text-align: center; +} + +#footer:before { + content: ''; + position: absolute; + right: 0; + bottom: 31px; + left: 0; + height: 50px; + z-index: -1; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3), + 0 6px 0 -3px rgba(255, 255, 255, 0.8), + 0 7px 1px -3px rgba(0, 0, 0, 0.3), + 0 43px 0 -6px rgba(255, 255, 255, 0.8), + 0 44px 2px -6px rgba(0, 0, 0, 0.2); +} + +#todo-count { + float: left; + text-align: left; +} + +#filters { + margin: 0; + padding: 0; + list-style: none; + position: absolute; + right: 0; + left: 0; +} + +#filters li { + display: inline; +} + +#filters li a { + color: #83756f; + margin: 2px; + text-decoration: none; +} + +#filters li a.selected { + font-weight: bold; +} + +#clear-completed { + float: right; + position: relative; + line-height: 20px; + text-decoration: none; + background: rgba(0, 0, 0, 0.1); + font-size: 11px; + padding: 0 10px; + border-radius: 3px; + box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.2); +} + +#clear-completed:hover { + background: rgba(0, 0, 0, 0.15); + box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.3); +} + +#info { + margin: 65px auto 0; + color: #a6a6a6; + font-size: 12px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7); + text-align: center; +} + +#info a { + color: inherit; +} + +/* + Hack to remove background from Mobile Safari. + Can't use it globally since it destroys checkboxes in Firefox and Opera +*/ + +@media screen and (-webkit-min-device-pixel-ratio:0) { + #toggle-all, + #todo-list li .toggle { + background: none; + } + + #todo-list li .toggle { + height: 40px; + } + + #toggle-all { + top: -56px; + left: -15px; + width: 65px; + height: 41px; + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); + -webkit-appearance: none; + appearance: none; + } +} + +.hidden { + display: none; +} + +hr { + margin: 20px 0; + border: 0; + border-top: 1px dashed #C5C5C5; + border-bottom: 1px dashed #F7F7F7; +} + +.learn a { + font-weight: normal; + text-decoration: none; + color: #b83f45; +} + +.learn a:hover { + text-decoration: underline; + color: #787e7e; +} + +.learn h3, +.learn h4, +.learn h5 { + margin: 10px 0; + font-weight: 500; + line-height: 1.2; + color: #000; +} + +.learn h3 { + font-size: 24px; +} + +.learn h4 { + font-size: 18px; +} + +.learn h5 { + margin-bottom: 0; + font-size: 14px; +} + +.learn ul { + padding: 0; + margin: 0 0 30px 25px; +} + +.learn li { + line-height: 20px; +} + +.learn p { + font-size: 15px; + font-weight: 300; + line-height: 1.3; + margin-top: 0; + margin-bottom: 0; +} + +.quote { + border: none; + margin: 20px 0 60px 0; +} + +.quote p { + font-style: italic; +} + +.quote p:before { + content: '“'; + font-size: 50px; + opacity: .15; + position: absolute; + top: -20px; + left: 3px; +} + +.quote p:after { + content: '”'; + font-size: 50px; + opacity: .15; + position: absolute; + bottom: -42px; + right: 3px; +} + +.quote footer { + position: absolute; + bottom: -40px; + right: 0; +} + +.quote footer img { + border-radius: 3px; +} + +.quote footer a { + margin-left: 5px; + vertical-align: middle; +} + +.speech-bubble { + position: relative; + padding: 10px; + background: rgba(0, 0, 0, .04); + border-radius: 5px; +} + +.speech-bubble:after { + content: ''; + position: absolute; + top: 100%; + right: 30px; + border: 13px solid transparent; + border-top-color: rgba(0, 0, 0, .04); +} + +.learn-bar > .learn { + position: absolute; + width: 272px; + top: 8px; + left: -300px; + padding: 10px; + border-radius: 5px; + background-color: rgba(255, 255, 255, .6); + -webkit-transition-property: left; + transition-property: left; + -webkit-transition-duration: 500ms; + transition-duration: 500ms; +} + +@media (min-width: 899px) { + .learn-bar { + width: auto; + margin: 0 0 0 300px; + } + + .learn-bar > .learn { + left: 8px; + } + + .learn-bar #todoapp { + width: 550px; + margin: 130px auto 40px auto; + } +} \ No newline at end of file diff --git a/resources/leiningen/new/electroncljs/app/index.html b/resources/leiningen/new/electroncljs/app/index.html new file mode 100644 index 0000000..1a1c377 --- /dev/null +++ b/resources/leiningen/new/electroncljs/app/index.html @@ -0,0 +1,15 @@ +{{=<% %>=}} + + + + + <%name%> + + + +
+

Reagent has not loaded yet.

+
+ + + \ No newline at end of file diff --git a/resources/leiningen/new/electroncljs/app/package.json b/resources/leiningen/new/electroncljs/app/package.json new file mode 100644 index 0000000..2d83b32 --- /dev/null +++ b/resources/leiningen/new/electroncljs/app/package.json @@ -0,0 +1,6 @@ +{{=<% %>=}} +{ + "name": "<%name%>", + "version": "0.1.0", + "main": "js/main.js" +} diff --git a/resources/leiningen/new/electroncljs/env/dev/src/root_ns/config.cljs b/resources/leiningen/new/electroncljs/env/dev/src/root_ns/config.cljs new file mode 100644 index 0000000..0dfdc5d --- /dev/null +++ b/resources/leiningen/new/electroncljs/env/dev/src/root_ns/config.cljs @@ -0,0 +1,8 @@ +(ns {{root-ns}}.config) + +(def config + {:env :dev + :dev-tools {:auto-open? false + :position "bottom"} + :browser-window {:width 1024 + :height 768}}) \ No newline at end of file diff --git a/resources/leiningen/new/electroncljs/env/dev/src/user.clj b/resources/leiningen/new/electroncljs/env/dev/src/user.clj new file mode 100644 index 0000000..a006283 --- /dev/null +++ b/resources/leiningen/new/electroncljs/env/dev/src/user.clj @@ -0,0 +1,2 @@ +(ns user + (:use figwheel-sidecar.repl-api)) diff --git a/resources/leiningen/new/electroncljs/env/dev/src_front/root_ns/init.cljs b/resources/leiningen/new/electroncljs/env/dev/src_front/root_ns/init.cljs new file mode 100644 index 0000000..281afd4 --- /dev/null +++ b/resources/leiningen/new/electroncljs/env/dev/src_front/root_ns/init.cljs @@ -0,0 +1,11 @@ +(ns {{root-ns}}.init + (:require + [{{root-ns}}.core :as core])) + +(enable-console-print!) + +(defn start-front! + [] + (core/init!)) + +(start-front!) diff --git a/resources/leiningen/new/electroncljs/env/dev/src_main/root_ns/main/init.cljs b/resources/leiningen/new/electroncljs/env/dev/src_main/root_ns/main/init.cljs new file mode 100644 index 0000000..8054b96 --- /dev/null +++ b/resources/leiningen/new/electroncljs/env/dev/src_main/root_ns/main/init.cljs @@ -0,0 +1,19 @@ +(ns {{root-ns}}.main.init + (:require + [cljs.nodejs :as nodejs] + [{{root-ns}}.main.core :as core])) + +(nodejs/enable-util-print!) + +(def on-figwheel-reload! + (fn [] + (println "Figwheel initiated application reload.") + (.relaunch core/app) + (.exit core/app 0))) + +(def start-electron! + (fn [] + (reset! core/window nil) + (core/init!))) + +(set! *main-cli-fn* start-electron!) diff --git a/resources/leiningen/new/electroncljs/env/prod/src/root_ns/config.cljs b/resources/leiningen/new/electroncljs/env/prod/src/root_ns/config.cljs new file mode 100644 index 0000000..169c6d6 --- /dev/null +++ b/resources/leiningen/new/electroncljs/env/prod/src/root_ns/config.cljs @@ -0,0 +1,6 @@ +(ns {{root-ns}}.config) + +(def config + {:env :prod + :browser-window {:width 1024 + :height 768}}) \ No newline at end of file diff --git a/resources/leiningen/new/electroncljs/env/prod/src_front/root_ns/init.cljs b/resources/leiningen/new/electroncljs/env/prod/src_front/root_ns/init.cljs new file mode 100644 index 0000000..281afd4 --- /dev/null +++ b/resources/leiningen/new/electroncljs/env/prod/src_front/root_ns/init.cljs @@ -0,0 +1,11 @@ +(ns {{root-ns}}.init + (:require + [{{root-ns}}.core :as core])) + +(enable-console-print!) + +(defn start-front! + [] + (core/init!)) + +(start-front!) diff --git a/resources/leiningen/new/electroncljs/env/prod/src_main/root_ns/main/init.cljs b/resources/leiningen/new/electroncljs/env/prod/src_main/root_ns/main/init.cljs new file mode 100644 index 0000000..abfbe6e --- /dev/null +++ b/resources/leiningen/new/electroncljs/env/prod/src_main/root_ns/main/init.cljs @@ -0,0 +1,13 @@ +(ns {{root-ns}}.main.init + (:require + [cljs.nodejs :as nodejs] + [{{root-ns}}.main.core :as core])) + +(nodejs/enable-util-print!) + +(def start-electron! + (fn [] + (reset! core/window nil) + (core/init!))) + +(set! *main-cli-fn* start-electron!) diff --git a/resources/leiningen/new/electroncljs/project.clj b/resources/leiningen/new/electroncljs/project.clj new file mode 100644 index 0000000..ec10cbe --- /dev/null +++ b/resources/leiningen/new/electroncljs/project.clj @@ -0,0 +1,107 @@ +(def electron-version "1.4.10") +(def figwheel-version "0.5.8") + +(defproject {{name}} "0.1.0-SNAPSHOT" + :description "FIXME: write description" + :url "http://example.com/FIXME" + :license {:name "MIT License" + :url "http://opensource.org/licenses/MIT"} + + :dependencies [[cljsjs/bootstrap "3.3.6-1"] + [figwheel ~figwheel-version] + [org.clojure/clojure "1.8.0"] + [org.clojure/clojurescript "1.9.293"] + [org.webjars/bootstrap "3.3.6"] + [reagent "0.6.0"] + [ring/ring-core "1.5.0"]] + + :plugins [[lein-cljsbuild "1.1.4"] + [lein-externs "0.1.6"] + [lein-figwheel ~figwheel-version] + [lein-npm "0.6.2"] + [lein-shell "0.5.0"]] + + :npm {:devDependencies [[electron-prebuilt ~electron-version] + [electron-packager "^8.3.0"] + [closurecompiler-externs "^1.0.4"] + [ws "^1.1.1"]]} + + :source-paths ["src"] + + :clean-targets ^{:protect false} [:target-path + "releases" + [:cljsbuild :builds :main :compiler :output-to] + [:cljsbuild :builds :main :compiler :output-dir] + [:cljsbuild :builds :front :compiler :output-to] + [:cljsbuild :builds :front :compiler :output-dir] + "app/js/externs_main.js" + "app/js/externs_front.js"] + + :figwheel {:nrepl-port 7888 + :nrepl-middleware [cemerick.piggieback/wrap-cljs-repl] + :builds-to-start [:front]} + + :cljsbuild {:builds + {:main + {:source-paths ["src" "src_main"] + :figwheel {:on-jsload {{root-ns}}.main.init/on-figwheel-reload!} + :compiler {:main {{root-ns}}.main.init + :output-to "app/js/main.js" + :output-dir "app/js/out_main" + :asset-path "app/js/out_main" + :externs ["app/js/externs_main.js"] + :target :nodejs + :optimizations :none}} + :front + {:source-paths ["src" "src_front"] + :figwheel {:on-jsload {{root-ns}}.init/start-front!} + :compiler {:main {{root-ns}}.init + :output-to "app/js/front.js" + :output-dir "app/js/out_front" + :asset-path "js/out_front" + :externs ["app/js/externs_front.js"] + :optimizations :none}}}} + + :profiles {:dev {:source-paths ["env/dev/src"] + :dependencies [[com.cemerick/piggieback "0.2.1"] + [figwheel-sidecar ~figwheel-version]] + :cljsbuild {:builds + {:main {:source-paths ["env/dev/src" "env/dev/src_main"]} + :front {:source-paths ["env/dev/src" "env/dev/src_front"]}}}} + + :prod {:source-paths ["env/prod/src"] + :cljsbuild {:builds + {:main {:source-paths ["env/prod/src" "env/prod/src_main"] + :compiler ^:replace + {:output-to "app/js/main.js" + :externs ["app/js/externs_main.js"] + :optimizations :advanced + :target :nodejs + :pretty-print false}} + :front {:source-paths ["env/prod/src" "env/prod/src_front"] + :compiler ^:replace + {:output-to "app/js/front.js" + :externs ["app/js/externs_front.js"] + :optimizations :advanced + :pretty-print false}}}}}} + + + :aliases {"build-main" ["do" + ["externs" "main" "app/js/externs_main.js"] + ["cljsbuild" "once" "main"]] + "build-front" ["do" + ["externs" "front" "app/js/externs_front.js"] + ["cljsbuild" "once" "front"]] + "build" ["do" "build-main" "build-front"] + "run" ["do" + ["shell" "./node_modules/.bin/electron" "app"]] + "package" ["do" + "clean" + ["with-profile" "prod" "build"] + ["with-profile" "prod" "shell" "./node_modules/.bin/electron-packager" + "app" :project/name + ~(str "--version=" electron-version) + "--asar" + "--out=releases" + "--overwrite"]]} + ) diff --git a/resources/leiningen/new/electroncljs/src_front/root_ns/core.cljs b/resources/leiningen/new/electroncljs/src_front/root_ns/core.cljs new file mode 100644 index 0000000..6ffe41e --- /dev/null +++ b/resources/leiningen/new/electroncljs/src_front/root_ns/core.cljs @@ -0,0 +1,117 @@ +(ns {{root-ns}}.core + (:require + [reagent.core :as r])) + +(defonce todos (r/atom (sorted-map))) + +(defonce counter (r/atom 0)) + +(defn add-todo + [text] + (let [id (swap! counter inc)] + (swap! todos assoc id {:id id :title text :done false}))) + +(defn toggle [id] (swap! todos update-in [id :done] not)) +(defn save [id title] (swap! todos assoc-in [id :title] title)) +(defn delete [id] (swap! todos dissoc id)) + +(defn mmap [m f a] (->> m (f a) (into (empty m)))) +(defn complete-all [v] (swap! todos mmap map #(assoc-in % [1 :done] v))) +(defn clear-done [] (swap! todos mmap remove #(get-in % [1 :done]))) + +(defonce init (do + (add-todo "Rename Cloact to Reagent") + (add-todo "Add undo demo") + (add-todo "Make all rendering async") + (add-todo "Allow any arguments to component functions") + (complete-all true))) + +(defn todo-input + [{:keys [title on-save on-stop]}] + (let [val (r/atom title) + stop #(do (reset! val "") + (if on-stop (on-stop))) + save #(let [v (-> @val str clojure.string/trim)] + (if-not (empty? v) (on-save v)) + (stop))] + (fn [{:keys [id class placeholder]}] + [:input {:type "text" :value @val + :id id :class class :placeholder placeholder + :on-blur save + :on-change #(reset! val (-> % .-target .-value)) + :on-key-down #(case (.-which %) + 13 (save) + 27 (stop) + nil)}]))) + +(def todo-edit (with-meta todo-input + {:component-did-mount #(.focus (r/dom-node %))})) + +(defn todo-stats + [{:keys [filt active done]}] + (let [props-for (fn [name] + {:class (if (= name @filt) "selected") + :on-click #(reset! filt name)})] + [:div + [:span#todo-count + [:strong active] " " (case active 1 "item" "items") " left"] + [:ul#filters + [:li [:a (props-for :all) "All"]] + [:li [:a (props-for :active) "Active"]] + [:li [:a (props-for :done) "Completed"]]] + (when (pos? done) + [:button#clear-completed {:on-click clear-done} + "Clear completed " done])])) + +(defn todo-item + [] + (let [editing (r/atom false)] + (fn [{:keys [id done title]}] + [:li {:class (str (if done "completed ") + (if @editing "editing"))} + [:div.view + [:input.toggle {:type "checkbox" :checked done + :on-change #(toggle id)}] + [:label {:on-double-click #(reset! editing true)} title] + [:button.destroy {:on-click #(delete id)}]] + (when @editing + [todo-edit {:class "edit" :title title + :on-save #(save id %) + :on-stop #(reset! editing false)}])]))) + +(defn todo-app + [props] + (let [filt (r/atom :all)] + (fn [] + (let [items (vals @todos) + done (->> items (filter :done) count) + active (- (count items) done)] + [:div + [:section#todoapp + [:header#header + [:h1 "todos"] + [todo-input {:id "new-todo" + :placeholder "What needs to be done?" + :on-save add-todo}]] + (when (-> items count pos?) + [:div + [:section#main + [:input#toggle-all {:type "checkbox" :checked (zero? active) + :on-change #(complete-all (pos? active))}] + [:label {:for "toggle-all"} "Mark all as complete"] + [:ul#todo-list + (for [todo (filter (case @filt + :active (complement :done) + :done :done + :all identity) items)] + ^{:key (:id todo)} [todo-item todo])]] + [:footer#footer + [todo-stats {:active active :done done :filt filt}]]])] + [:footer#info + [:p "Double-click to edit a todo"]]])))) + +(defn init! + [] + (r/render + [todo-app] + (.getElementById js/document "app"))) diff --git a/resources/leiningen/new/electroncljs/src_main/root_ns/main/core.cljs b/resources/leiningen/new/electroncljs/src_main/root_ns/main/core.cljs new file mode 100644 index 0000000..b00339a --- /dev/null +++ b/resources/leiningen/new/electroncljs/src_main/root_ns/main/core.cljs @@ -0,0 +1,54 @@ +(ns {{root-ns}}.main.core + (:require + [cljs.nodejs :as nodejs] + [{{root-ns}}.config :refer [config]])) + +(def path (nodejs/require "path")) +(def electron (nodejs/require "electron")) +(def app (.-app electron)) + +(def BrowserWindow (.-BrowserWindow electron)) + +(defonce window (atom nil)) + +(defn on-window-closed + [] + (reset! window nil)) + +(defn on-window-ready-to-show + [] + (.show @window)) + +(defn on-process-error + [error] + (println "ERROR:" error)) + +(defn on-app-window-all-closed + [] + (.quit app)) + +(defn on-app-quit + [e exit-code] + (println "Main process quitting. Exit code:" exit-code)) + +(defn on-app-ready + [launch-info] + (let [window-settings (merge + {:show false} + (:browser-window config))] + (println "App ready.") + (reset! window (BrowserWindow. (clj->js window-settings))) + (.loadURL @window (str "file://" (.getAppPath app) "/index.html")) + (.on @window "ready-to-show" on-window-ready-to-show) + (.on @window "closed" on-window-closed) + (if (get-in config [:dev-tools :auto-open?]) + (.openDevTools (.-webContents @window) + (clj->js {:mode (get-in config [:dev-tools :position] "right")}))))) + +(defn init! + [] + (println "Main process started, app is starting up.") + (.on nodejs/process "error" on-process-error) + (.on app "window-all-closed" on-app-window-all-closed) + (.on app "quit" on-app-quit) + (.on app "ready" on-app-ready)) diff --git a/src/leiningen/new/electroncljs.clj b/src/leiningen/new/electroncljs.clj new file mode 100644 index 0000000..f390be0 --- /dev/null +++ b/src/leiningen/new/electroncljs.clj @@ -0,0 +1,66 @@ +(ns leiningen.new.electroncljs + (:import + [java.util Calendar GregorianCalendar]) + (:require + [leiningen.core.main :as main] + [leiningen.new.templates :as t])) + +(def render (t/renderer "electroncljs")) + +(defn electroncljs + [name] + (let [data {:name name + :sanitized (t/sanitize name) + :root-ns (t/sanitize-ns name) + :root-ns-path (t/name-to-path name) + :year (.get (GregorianCalendar.) (Calendar/YEAR))}] + (main/info "Creating a new ClojureScript Electron project \"" name "\" ...") + (t/->files + data + "app/js" + "app/img" + ["app/css/todos.css" (render "app/css/todos.css")] + ["app/index.html" (render "app/index.html" data)] + ["app/package.json" (render "app/package.json" data)] + ["env/dev/src/{{root-ns-path}}/config.cljs" (render "env/dev/src/root_ns/config.cljs" data)] + ["env/dev/src/user.clj" (render "env/dev/src/user.clj" data)] + ["env/dev/src_front/{{root-ns-path}}/init.cljs" (render "env/dev/src_front/root_ns/init.cljs" data)] + ["env/dev/src_main/{{root-ns-path}}/main/init.cljs" (render "env/dev/src_main/root_ns/main/init.cljs" data)] + + ["env/prod/src/{{root-ns-path}}/config.cljs" (render "env/prod/src/root_ns/config.cljs" data)] + ["env/prod/src_front/{{root-ns-path}}/init.cljs" (render "env/prod/src_front/root_ns/init.cljs" data)] + ["env/prod/src_main/{{root-ns-path}}/main/init.cljs" (render "env/prod/src_main/root_ns/main/init.cljs" data)] + "src/{{root-ns-path}}" + ["src_front/{{root-ns-path}}/core.cljs" (render "src_front/root_ns/core.cljs" data)] + ["src_main/{{root-ns-path}}/main/core.cljs" (render "src_main/root_ns/main/core.cljs" data)] + [".gitignore" (render ".gitignore")] + ["LICENSE" (render "LICENSE" data)] + ["README.md" (render "README.md" data)] + ["project.clj" (render "project.clj" data)]) + (main/info "------------------------------------------------------------------------") + (main/info "Your new Electron project is ready! To get started:") + (main/info "") + (main/info " $ cd" name) + (main/info " $ lein npm install") + (main/info "") + (main/info "You app's code is located in a few places:\n") + (main/info " /src - code common to both main and renderer (front)") + (main/info " /src_front - code for renderer process (UI)") + (main/info " /src_main - code for main process (backend)") + (main/info "") + (main/info "Build profile-specific sources are located under /env") + (main/info "Application code config map at:") + (main/info (str " /env/dev/src/" (:root-ns-path data) "/config.cljs")) + (main/info (str " /env/prod/src/" (:root-ns-path data) "/config.cljs")) + (main/info "") + (main/info "Electron app resources (HTML, CSS, JS, etc) are located under /app") + (main/info "") + (main/info "Build: $ lein build") + (main/info "Run: $ lein run") + (main/info "Rebuild: $ lein clean && lein build") + (main/info "Package: $ lein package") + (main/info "Figwheel: (do in order!)") + (main/info " (terminal #1) $ lein figwheel") + (main/info " (terminal #2) $ lein run") + (main/info "(Figwheel REPL running on port 7888, run (cljs-repl) after connecting)") + (main/info "------------------------------------------------------------------------")))