Fork me on GitHub
#figwheel-main
<
2018-09-14
>
grease08:09:23

Trying to install figwheel.main as per the instructions at https://figwheel.org/tutorial . However, I get the following error:

➜  figwheel_test clj -m figwheel.main
[Figwheel] Compiling build figwheel-default-repl-build to "/var/folders/fd/w1x6p8k14n7c4snc157xbqsh0000gn/T/figwheel15853421684972697414repl/public/cljs-out/main.js"
[Figwheel] Successfully compiled build figwheel-default-repl-build to "/var/folders/fd/w1x6p8k14n7c4snc157xbqsh0000gn/T/figwheel15853421684972697414repl/public/cljs-out/main.js" in 5.787 seconds.
[Figwheel] Starting Server at 
[Figwheel] Starting REPL
Prompt will show when REPL connects to evaluation environment (i.e. a REPL hosting webpage)
Figwheel Main Controls:
          (figwheel.main/stop-builds id ...)  ;; stops Figwheel autobuilder for ids
          (figwheel.main/start-builds id ...) ;; starts autobuilder focused on ids
          (figwheel.main/reset)               ;; stops, cleans, reloads config, and starts autobuilder
          (figwheel.main/build-once id ...)   ;; builds source one time
          (figwheel.main/clean id ...)        ;; deletes compiled cljs target files
          (figwheel.main/status)              ;; displays current state of system
Figwheel REPL Controls:
          (figwheel.repl/conns)               ;; displays the current connections
          (figwheel.repl/focus session-name)  ;; choose which session name to focus on
In the cljs.user ns, controls can be called without ns ie. (conns) instead of (figwheel.repl/conns)
    Docs: (doc function-name-here)
    Exit: :cljs/quit
 Results: Stored in vars *1, *2, *3, *e holds last exception object
Exception in thread "main" clojure.lang.ExceptionInfo: Figwheel: Unable to dynamicly load figwheel.server.jetty-websocket/run-server {:not-loaded figwheel.server.jetty-websocket/run-server}
	at clojure.core$ex_info.invokeStatic(core.clj:4739)
	at clojure.core$ex_info.invoke(core.clj:4739)
	at figwheel.repl$dynload.invokeStatic(repl.cljc:1076)
	at figwheel.repl$dynload.invoke(repl.cljc:1072)
	at figwheel.repl$run_default_server_STAR_.invokeStatic(repl.cljc:1095)
	at figwheel.repl$run_default_server_STAR_.invoke(repl.cljc:1092)
	at figwheel.repl$run_default_server.invokeStatic(repl.cljc:1129)
	at figwheel.repl$run_default_server.invoke(repl.cljc:1128)
	at figwheel.repl$setup.invokeStatic(repl.cljc:1198)
	at figwheel.repl$setup.invoke(repl.cljc:1193)
	at figwheel.repl.FigwheelReplEnv._setup(repl.cljc:1299)
	at cljs.repl$repl_STAR_$fn__6607.invoke(repl.cljc:942)
	at cljs.compiler$with_core_cljs.invokeStatic(compiler.cljc:1289)
	at cljs.compiler$with_core_cljs.invoke(compiler.cljc:1278)
--more--

grease08:09:42

I've tried googling for it, and also ensuring if another server/repl might be running. Also tried the same after a machine restart.

grease08:09:56

Any ideas about what could be wrong here?

ingesol11:09:17

Is your os by any chance windows? Check this out https://github.com/bhauman/figwheel-main/issues/74

bhauman12:09:33

@U9DNZGVN2 are you following the tutorial to the letter?

bhauman12:09:54

I’m assuming you are not on windows because you are using clj

bhauman12:09:29

@U9DNZGVN2 when your available let me know I’d like to get to the bottom of this

grease13:09:39

@bhauman Hey, just came online. I'm using mac os x (10.13.6), Java 1.8.0_181, clojure 1.9

grease13:09:07

I followed the tutorial to the letter afaik (this is only the second step, immediately after writing out deps.edn). I wanted to set up figwheel from first principles (without lein magic).

bhauman13:09:14

@U9DNZGVN2 yeah this is tough

bhauman13:09:30

have you used clojure before?

grease13:09:54

Nope. I'm kinda newbie to clojure

grease13:09:05

Just starting to learn about it

bhauman13:09:17

hmm it sounds like something went wrong when you installed clojure

bhauman13:09:48

I’d try installing it again

bhauman13:09:18

and delete the .cpcache in your hello-cljs directory

grease13:09:28

Ok. Will do that and report here. Thanks for looking into it

bhauman13:09:01

@U9DNZGVN2 do you have a CLASSPATH environment variable set or anything strange related to Java

bhauman13:09:25

OK good to know

grease14:09:32

Reinstalled clojure. Still get the same error 😞 Also tried changing default browser from Chrome to Safari (I was wondering if there was some browser issue because jetty wasn't able to open a websocket with it). Still same error. Anyway, I thought I was doing some noob stupidity. It doesn't seem like it. Let me grope around a bit more. Will report here if I figure how to fix it. Thanks again.

bhauman14:09:18

@U9DNZGVN2 I’m going to ask you to try a newer version of figwheel in a minute

bhauman14:09:43

it has better error reporting for this situation

bhauman14:09:59

which is the main problem here

bhauman14:09:39

@U9DNZGVN2 I’m thinking you delete your ~/.m2 directory and try again

bhauman14:09:31

and if that doesn’t work try com.bhauman/figwheel-main 0.2.0-SNAPSHOT

bhauman14:09:01

{:deps {com.bhauman/figwheel-main {:mvn/version "0.2.0-SNAPSHOT"}}}

grease15:09:01

removing ~/.m2 didn't help. Changing deps.edn to {:deps {com.bhauman/figwheel-main {:mvn/version "0.2.0-SNAPSHOT"}}} gave the following:

Downloading: clj-time/clj-time/0.14.3/clj-time-0.14.3.jar from 
Downloading: org/eclipse/jetty/websocket/websocket-servlet/9.2.24.v20180105/websocket-servlet-9.2.24.v20180105.jar from 
Downloading: org/eclipse/jetty/jetty-security/9.2.24.v20180105/jetty-security-9.2.24.v20180105.jar from 
Downloading: ring/ring-codec/1.1.1/ring-codec-1.1.1.jar from 
Downloading: ring/ring-anti-forgery/1.3.0/ring-anti-forgery-1.3.0.jar from 
Downloading: com/bhauman/figwheel-core/0.2.0-SNAPSHOT/figwheel-core-0.2.0-20180914.141640-1.jar from 
Downloading: org/eclipse/jetty/websocket/websocket-server/9.2.24.v20180105/websocket-server-9.2.24.v20180105.jar from 
Downloading: org/eclipse/jetty/websocket/websocket-common/9.2.24.v20180105/websocket-common-9.2.24.v20180105.jar from 
Downloading: ring/ring-servlet/1.7.0/ring-servlet-1.7.0.jar from 
Downloading: ring/ring/1.7.0/ring-1.7.0.jar from 
Downloading: org/eclipse/jetty/jetty-server/9.2.24.v20180105/jetty-server-9.2.24.v20180105.jar from 
Downloading: ring/ring-core/1.7.0/ring-core-1.7.0.jar from 
Exception in thread "main" java.lang.NoClassDefFoundError: org/eclipse/jetty/http/HttpParser$ProxyHandler, compiling:(ring/adapter/jetty.clj:28:9)
	at clojure.lang.Compiler.analyzeSeq(Compiler.java:7010)
	at clojure.lang.Compiler.analyze(Compiler.java:6773)
	at clojure.lang.Compiler.analyzeSeq(Compiler.java:6991)
	at clojure.lang.Compiler.analyze(Compiler.java:6773)
	at clojure.lang.Compiler.analyze(Compiler.java:6729)
	at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:6100)
	at clojure.lang.Compiler$LetExpr$Parser.parse(Compiler.java:6420)
	at clojure.lang.Compiler.analyzeSeq(Compiler.java:7003)
	at clojure.lang.Compiler.analyze(Compiler.java:6773)
	at clojure.lang.Compiler.analyzeSeq(Compiler.java:6991)
	at clojure.lang.Compiler.analyze(Compiler.java:6773)
	at clojure.lang.Compiler.analyze(Compiler.java:6729)

bhauman17:09:10

@U9DNZGVN2 that is so strange, but this is the error that’s causing your problems I’m going to investigate further

bhauman18:09:52

@U9DNZGVN2 yeah I think there is something off with your system, what version of Java are you running?

bhauman18:09:19

java -version

bhauman18:09:31

you need 1.8 at least

grease01:09:30

java version "1.8.0_181"
Java(TM) SE Runtime Environment (build 1.8.0_181-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)

bhauman16:09:47

@U9DNZGVN2 one more thing to try rm -rf .cpcache in the directory

bhauman16:09:53

and then re-run it

bhauman16:09:05

although it’s a long shot

grease09:09:53

Just tried that. Removed both .m2 and .cpcache. Also reinstalled java to 10.x ... Still same error.

grease05:09:35

Hey @bhauman, I finally figured the problem. My ~/.clojure/deps.edn had com.datomic/client-pro {:mvn/version "0.8.17"} in it. Removing it solved the problem. This seems similar to the problem stated here: https://clojurians-log.clojureverse.org/datomic/2017-06-05 . There is a conflict in jetty versions in different dependencies. I'm not sure how/if you can solve this problem in figwheel itself. Hope this is helpful in case someone else reports a similar issue. Thanks again for all your help.

kwladyka12:09:12

Hmm I had situation when figwheel-main didn’t load form-validator-demo.events. I was restarting it (stop REPL and again run clj -A:fig -A:build) and trying everything. After all I added (println "foo") to form-validator-demo.events and then it started to work. I removed it again and it works. It looks like something didn’t load, unless I touched form-validator-demo.events file.

kwladyka12:09:49

I have no idea it is issue about figwheel-main or cli

bhauman12:09:40

@kwladyka its important to clean between builds

bhauman12:09:15

rm -rf target/public

bhauman12:09:36

especially if you experience a problem

kwladyka12:09:00

is it temporary issue or should I add this to run always with clj -A:fig -A:build?

bhauman12:09:01

it depends

bhauman12:09:18

if you do a production build then you really would want to clean

bhauman12:09:22

afterwards

bhauman12:09:47

or if you may have done some other compile that affected your output-to file

kwladyka12:09:56

no production, standard developing day 🙂

bhauman12:09:20

or if you had a bug that stayed in the code

bhauman12:09:36

this can happen when you are developing live

bhauman12:09:49

you had a typo,

bhauman12:09:55

but it didn’t load

bhauman12:09:22

but then you start it up later and the poorly compiled code is still in the output directory

bhauman12:09:58

its hard to say, without being that

bhauman12:09:02

being there

bhauman12:09:19

but the output directory is stateful and we are hammering on it a bit

bhauman12:09:14

so if you have a problem, you can clean things up and roll from there

bhauman12:09:36

I always advise cleaning before your production build or before a final test run

kwladyka12:09:40

ok thx, so conclusion is I will never know when it is time to clean 😉 Not all bugs are so obviously 🙂

kwladyka12:09:14

so just clean from time to time 🙂

bhauman12:09:28

you can clean everytime 🙂

bhauman12:09:43

but then your start up time is slower

bhauman12:09:16

this isn’t a big deal if you only start it once and a while

kwladyka12:09:37

I really like the future :npm {:bundles {"dist/index_bundle.js" "src/js/index.js"}} 🍻

bhauman12:09:05

I need to write a document about working with node modules

kwladyka13:09:00

Does it do the magic, that I don’t have to write (:require [react])? Because I see it works without it and with :exclusions [cljsjs/react cljsjs/react-dom]

kwladyka13:09:48

So to import all this dependency I have to add them 1by1 to index.js?

(ns material-ui.core
  (:require [reagent.core :as r]
            ["@material-ui/core/Styles" :refer [MuiThemeProvider createMuiTheme]]

            ;["@material-ui/core/Grid" :default Grid]
            ;["@material-ui/core/Paper" :default Paper]

            ;["@material-ui/core/AppBar" :default AppBar]
            ;["@material-ui/core/Toolbar" :default Toolbar]
            ;["@material-ui/core/Typography" :default Typography]
            ;["@material-ui/core/IconButton" :default IconButton]
            ;["@material-ui/core/Button" :default Button]
            ;["@material-ui/core/Drawer" :default Drawer]

            ;["@material-ui/core/TextField" :default TextField]
            ))
^shadow code

bhauman13:09:30

@kwladyka it depends you could just add one object to your window in your index.js

bhauman13:09:41

it depends on the library

kwladyka13:09:36

but then I will have super big js library instead of load only files which I need? I mean it will work like cljsjs, which will generate 2 MB js?

bhauman13:09:15

@kwladyka oh you should definitely only load what you need

kwladyka13:09:43

so I have to load each file separately in index.js?

bhauman13:09:12

yes all your imports will be in the index

kwladyka13:09:46

Should it look like import MaterialUIStyles from '@material-ui/core/Styles';? Normally it will be probably something like this import {MuiThemeProvider createMuiTheme} from '@material-ui/core/Styles';. Not sure how to use it and require in ns

kwladyka13:09:03

I was using shadow before and this wepack solution is still something new for me.

bhauman13:09:28

@kwladyka its all about how you assign it to window

kwladyka13:09:26

ERROR in ./src/js/index.js 3:25
Module parse failed: Unexpected token (3:25)
You may need an appropriate loader to handle this file type.
| import React from 'react';
| import ReactDOM from 'react-dom';
> import {MuiThemeProvider createMuiTheme} from '@material-ui/core/Styles';
So I can’t use {...}

bhauman13:09:44

you can you just have bad syntax

bhauman13:09:15

that error was from webpack right?

kwladyka13:09:20

Can you give me an example? You will save me hours of my life 🙂

bhauman13:09:08

in this case I think you can just use Styles

kwladyka13:09:40

Do you mean something like this import MaterialUIStyles from '@material-ui/core/Styles'; ?

kwladyka13:09:53

with window.MaterialUIStyles = MaterialUIStyles;

bhauman13:09:59

you need commas when you do {MuiThemeProvider, createMuiTheme}

kwladyka13:09:19

But then for some reason I have this error

kwladyka13:09:40

Even without require this ns, just with this index.js file

kwladyka13:09:53

oh not, not true, give me a sec

bhauman13:09:38

yeah this is defnitely a case where you want to clean

bhauman13:09:50

if you change a bundle file

kwladyka13:09:37

yes, it is what I did 🙂

kwladyka13:09:44

hmm now I have this

[Figwheel] Compile Warning   target/public/cljs-out/dev/clojure/test/check/generators.cljc   line:999  column:15

  cljs.core/<=, all arguments must be numbers, got [#{nil js/Number} number] instead

   994                   ubexp (max -1023 (get-exponent upper-bound))]
   995          (cond (<= 0.0 lower-bound)
   996                (tuple (gen-exp lbexp ubexp)
   997                       (return 1.0))
   998
   999                (<= upper-bound 0.0)
                      ^---
  1000                (tuple (gen-exp ubexp lbexp)
  1001                       (return -1.0))
  1002
  1003                :else
  1004                (fmap (fn [[exp sign :as pair]]
But it looks like different issue

bhauman13:09:14

wow that’s super cool what version of clojruescript are you using?

kwladyka13:09:05

{:deps {org.clojure/clojure {:mvn/version "1.9.0"}
        org.clojure/clojurescript {:mvn/version "1.10.339"}
        reagent {:mvn/version "0.8.1" :exclusions [cljsjs/react cljsjs/react-dom]}
        re-frame {:mvn/version "0.10.5"}
        bidi {:mvn/version "2.1.3"}
        venantius/accountant {:mvn/version "0.2.4"}
        stylefy {:mvn/version "1.5.1"}
        com.taoensso/timbre {:mvn/version "4.10.0"}
        org.clojure/test.check {:mvn/version "0.9.0"}}
 :paths ["src" "resources"]
 :aliases {:fig {:extra-deps
                 {com.bhauman/rebel-readline-cljs {:mvn/version "0.1.4"}
                  com.bhauman/figwheel-main {:mvn/version "0.1.9"}}
                 :extra-paths ["target" "test"]}
           :build {:main-opts ["-m" "figwheel.main" "-b" "dev" "-r"]}
           :min {:main-opts ["-m" "figwheel.main" "-O" "advanced" "-bo" "dev"]}
           :test {:main-opts ["-m" "figwheel.main" "-co" "test.cljs.edn" "-m" form-validator-demo.test-runner]}}}

kwladyka13:09:24

I wouldn’t call it cool 😉

bhauman13:09:36

but yes that’s a very different issue

kwladyka13:09:38

hmm how to require this "@material-ui/core/Styles" in cljs later?

bhauman13:09:17

OK the way it works is that you can require what’s being provided

bhauman13:09:01

its what you put on window

bhauman13:09:17

but in the kebab case form

bhauman13:09:41

“ReactDOM” > “react-dom”

bhauman13:09:41

“material-uistyles”

bhauman13:09:46

in this case

bhauman13:09:51

unfortunately

bhauman13:09:15

window.MaterialUIStyles

bhauman13:09:30

means that you can require material-uistyles

kwladyka13:09:29

[material-styles :refer [MuiThemeProvider createMuiTheme]

bhauman13:09:42

I’m not sure about refer

kwladyka13:09:44

ok it seems to work (I changed names in index.js)

kwladyka13:09:54

but then I don’t know how to use refer 🙂

kwladyka13:09:14

In shadow I could use it like (def create-mui-theme createMuiTheme)

kwladyka13:09:36

But refers don’t work

kwladyka13:09:38

core.cljs:10 Uncaught TypeError: Cannot read property 'createMuiTheme' of undefined

bhauman13:09:38

@kwladyka material-uistyles/createMuiTheme

kwladyka13:09:18

doesn’t work

bhauman13:09:19

@kwladyka it really depends on the library

kwladyka13:09:34

Can I read something which will explain me how to deal with it?

kwladyka13:09:31

In js it will be import {MuiThemeProvider createMuiTheme} from '@material-ui/core/Styles'

bhauman13:09:34

canyou show me your index.js

bhauman13:09:49

that needs a comma

bhauman13:09:00

you can now change it to that if you want

kwladyka13:09:58

import React from 'react';
import ReactDOM from 'react-dom';
import MaterialStyles from '@material-ui/core/Styles';
window.React = React;
window.ReactDOM = ReactDOM;
window.MaterialStyles = MaterialStyles;

bhauman13:09:40

then it would be (:require [material-styles])

kwladyka13:09:31

Sure but how to use it?

(ns material-ui.core
  (:require [reagent.core :as r]
            [material-styles]))
(def create-mui-theme material-styles/createMuiTheme)
(def mui-theme-provider (r/adapt-react-class material-styles/MuiThemeProvider))

bhauman13:09:33

but ‘@material-ui/core/Styles’ might not have a default export

bhauman13:09:17

so yes you would have to go back to {MuiThemeProvider, createMuiTheme}

bhauman13:09:35

it needs a comma!!

bhauman13:09:17

I said that a few times so I thought you understood that

kwladyka13:09:07

yes, I was thinking what this comma is about

bhauman13:09:33

and you can do

bhauman13:09:38

window.MaterialStyles = {createMuiTheme: createMuiTheme, MuiThemeProvider: MuiThemeProvider}

kwladyka13:09:50

and then use :refer?

kwladyka13:09:26

[material-styles :refer [create-mui-theme mui-theme-provider]]?

bhauman13:09:05

I’m not sure about refer

bhauman13:09:19

first try material-styles/createMuiTheme

bhauman13:09:49

then try :refer [createMuiTheme]

kwladyka13:09:01

oh yeah finally it works, thank you 🍻

kwladyka13:09:07

I wish to read tutorial about it

kwladyka13:09:17

I feel a little blind about that part

bhauman13:09:30

there is the webpack tutorial

kwladyka13:09:58

https://clojurescript.org/guides/webpack this one? it is too simple to show how to solve issues

kwladyka14:09:06

I know this tutorial, but I don’t feel it really helps me to start be productive. To many unknown

bhauman14:09:13

@kwladyka did the :refer work?

kwladyka14:09:19

But it is nice to get an idea it is possible

bhauman14:09:45

remember you can also use the clj -m figwheel.main -pc -b dev -r

kwladyka14:09:45

when? why? 🙂

bhauman14:09:08

to see the :foreign-libs config

bhauman14:09:12

its helpful

bhauman14:09:20

to see the provides

kwladyka14:09:53

yes, [material-styles :refer [createMuiTheme MuiThemeProvider]] and material-styles/createMuiTheme. Both work.

bhauman14:09:44

if you use that print command you will see the :foreign-libs configuration

bhauman14:09:13

and thus see the provides

bhauman14:09:16

but yes I will write a tutorial

👍 4
kwladyka14:09:35

cool, thanks for that trick. To be consistent it is clj -A:fig -m figwheel.main -pc -b dev -r

kwladyka14:09:05

at least this is suggested configuration after use template to create project

kwladyka14:09:30

thank you for help! You saved me whole day! 🙂

kwladyka15:09:08

import Grid from "@material-ui/core/Grid"; (:require [grid]) (def grid (r/adapt-react-class grid)) <- this line throw an error Uncaught Error: Assert failed: Component must not be nil <- I can’t figure out it. Any hints?

kwladyka15:09:14

it works with shadow and require ["@material-ui/core/Grid" :default Grid] (which is translated to import Grid from "@material-ui/core/Grid";)

kwladyka16:09:53

hmm as window.Grid = {foo: Grid}; it works

kwladyka16:09:59

ah I have to do [grid :as grid2], heh

shaun-mahood16:09:14

@bhauman: I've been working on getting figwheel-main hooked up with Integrant, it's working great with the api. Based on how Integrant works, I was wondering if it would make sense to have figwheel-start return a map with the started configuration info? It`s pretty easy to work around but it seems like something that may be useful for other things as well.

bhauman17:09:52

@shaun-mahood The only worry there is the REPL experience

bhauman17:09:09

of dumping a huge datastructure

bhauman17:09:29

and since you can’t do anything with that datastructure

shaun-mahood17:09:16

Oh yeah, that would not be ideal. Any issues with pulling from the build-registry directly?

bhauman18:09:53

@shaun-mahood do you have to store the data?

bhauman18:09:17

you can put the build-registry in there

bhauman18:09:55

but figwheel-main is stateful

bhauman18:09:47

You should just store a {:figwheel.main/running true}

bhauman18:09:59

The build registry could perhaps be a dynamic binding, and then you could make this all integranty, but that has some ramifications that I’m not sure about

shaun-mahood18:09:37

All I really need to do with it is to be able to stop and restart the currently running builds - so likely I could read it as part of the stop function. What I’m doing now is just passing the initial build id into a map, which works fine for me. I’ll experiment with reading the state on stop and see how that goes.

dominicm21:09:40

Is there a strategy for serving cljsjs assets with figwheel main?

dominicm21:09:45

In particular I have some css

bhauman21:09:24

so there is a cljsjs jar with assets in it

dominicm21:09:57

@bhauman yup. They're on the classpath at cljsjs/react-vis/common/react-vis.inc.css

bhauman21:09:29

@dominicm yeah that sounds like you have to add some middleware to your ring handler

bhauman21:09:46

or just copy it

bhauman21:09:12

the problem is that it isn’t in a public directory

bhauman21:09:20

which is really to bad

bhauman21:09:36

and give it a cljsjs root-path

bhauman21:09:57

or maybe we should add that as a default to the figwheel-main server

bhauman21:09:30

it kinda makes sense

bhauman21:09:00

I don’t know about adding it as a default

bhauman21:09:11

seems pretty easy to integrate

dominicm21:09:41

I'm being particularly lazy here, and hoping I can config it without too much work?

bhauman21:09:49

in user.clj

bhauman21:09:25

(defn handler (ring.middleware.cljsjs/wrap-cljs (fn [req] {:status 404})))

bhauman21:09:38

in dev.cljs.edn

bhauman21:09:56

:ring-handler user/handler

bhauman21:09:35

or wrap your handler if you already have one

bhauman21:09:50

that’s pretty minimal

bhauman21:09:59

but really it seems like (wrap-resource handler "cljsjs") should work just fine

dominicm21:09:41

ring-cljsjs does some stuff to work around the fact that you want to serve from production/dev with different files.

dominicm21:09:00

@bhauman what's the default handler?

bhauman21:09:33

what do you mean what’s the default handler?

bhauman21:09:46

its a 404 handler

bhauman21:09:59

if the request gets to that point its not handled

dominicm21:09:51

Much more beautiful 😛

bhauman21:09:55

you know if you were just in a rush you could have just copied the file right?

dominicm21:09:33

I'm just fiddling now 🙂 This is an excuse to play with figwheel options.

bhauman21:09:45

oh gotcha 🙂

dominicm21:09:56

I usually end up wanting to do unusual stuff, so fiddling is a useful exercise.