https://squint-cljs.github.io/squint/?src=KHJlcXVpcmUgJ1siaHR0cHM6Ly9lc20uc2gvaWRpb21vcnBoIiA6cmVmZXIgW0lkaW9tb3JwaF0gOnJlbmFtZSB7SWRpb21vcnBoIEl9XSkKCihkZWYgc2l6ZSAyMCkgOzsgZ3JpZCBzaXplICgyMHgyMCkKKGRlZiBjZWxsIDEwKSA7OyBlYWNoIGNlbGwgPSAxMHB4CgooZGVmb25jZSBzdGF0ZQogIChhdG9tIHs6c25ha2UgW1s1IDEwXSBbNCAxMF0gWzMgMTBdXSA7IGluaXRpYWwgc25ha2UgYm9keQogICAgICAgICA6ZGlyIFsxIDBdIDsgbW92aW5nIHJpZ2h0CiAgICAgICAgIDpmb29kIFsxNSAxMF0gOyBpbml0aWFsIGZvb2QKICAgICAgICAgOmFsaXZlPyB0cnVlfSkpCgooZGVmbiByYW5kb20tZm9vZCBbXQogIFsocmFuZC1pbnQgc2l6ZSkgKHJhbmQtaW50IHNpemUpXSkKCihkZWZuIG1vdmUtc25ha2UgW10KICAoc3dhcCEgc3RhdGUKICAgIChmbiBbezprZXlzIFtzbmFrZSBkaXIgZm9vZCBhbGl2ZT9dIDphcyBzdH1dCiAgICAgIChpZiAobm90IGFsaXZlPykKICAgICAgICAgICAgIHN0CiAgICAgICAgICAgICAobGV0IFtoZWFkICh2ZWMgWygrIChudGggKGZpcnN0IHNuYWtlKSAwKSAobnRoIGRpciAwKSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKCsgKG50aCAoZmlyc3Qgc25ha2UpIDEpIChudGggZGlyIDEpKV0pCiAgICAgICAgICAgICAgICAgICBhdGU%2FICg9IGhlYWQgZm9vZCkKICAgICAgICAgICAgICAgICAgIG5ldy1zbmFrZSAodmVjIChjb25zIGhlYWQgKGlmIGF0ZT8gc25ha2UgKGJ1dGxhc3Qgc25ha2UpKSkpCiAgICAgICAgICAgICAgICAgICB4IChudGggaGVhZCAwKQogICAgICAgICAgICAgICAgICAgeSAobnRoIGhlYWQgMSkKICAgICAgICAgICAgICAgICAgIGhpdC13YWxsPyAob3IgKDwgeCAwKSAoPCB5IDApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoPj0geCBzaXplKSAoPj0geSBzaXplKSkKICAgICAgICAgICAgICAgICAgIGhpdC1zZWxmPyAoc29tZSAjKD0gaGVhZCAlKSAocmVzdCBuZXctc25ha2UpKV0KICAgICAgICAgICAgICAgKGNvbmQKICAgICAgICAgICAgICAgICBoaXQtd2FsbD8gKGFzc29jIHN0IDphbGl2ZT8gZmFsc2UpCiAgICAgICAgICAgICAgICAgaGl0LXNlbGY%2FIChhc3NvYyBzdCA6YWxpdmU%2FIGZhbHNlKQogICAgICAgICAgICAgICAgIGF0ZT8gKGFzc29jIHN0IDpzbmFrZSBuZXctc25ha2UgOmZvb2QgKHJhbmRvbS1mb29kKSkKICAgICAgICAgICAgICAgICA6ZWxzZSAoYXNzb2Mgc3QgOnNuYWtlIG5ldy1zbmFrZSkpKSkpKSkKCjs7IFRpbWVyIGxvb3AKKGpzL3NldEludGVydmFsIG1vdmUtc25ha2UgMjAwKQoKOzsgS2V5Ym9hcmQgY29udHJvbHMKKC5hZGRFdmVudExpc3RlbmVyIGpzL3dpbmRvdyAia2V5ZG93biIKICAoZm4gW2VdCiAgICAobGV0IFtrZXkgKC4ta2V5IGUpXQogICAgICAgKHN3YXAhIHN0YXRlIHVwZGF0ZSA6ZGlyCiAgICAgICAgIChmbiBbZGlyXQogICAgICAgICAgIChjYXNlIGtleQogICAgICAgICAgICAgICAgICAiQXJyb3dVcCIgKGlmICg9IGRpciBbMCAxXSkgZGlyIFswIC0xXSkKICAgICAgICAgICAgICAgICAgIkFycm93RG93biIgKGlmICg9IGRpciBbMCAtMV0pIGRpciBbMCAxXSkKICAgICAgICAgICAgICAgICAgIkFycm93TGVmdCIgKGlmICg9IGRpciBbMSAwXSkgZGlyIFstMSAwXSkKICAgICAgICAgICAgICAgICAgIkFycm93UmlnaHQiIChpZiAoPSBkaXIgWy0xIDBdKSBkaXIgWzEgMF0pCiAgICAgICAgICAgICAgICAgIGRpcikpKSkpKQoKKGRlZm4gY2VsbC1yZWN0IFtbeCB5XSBjb2xvcl0KICAjaHRtbAogIFs6cmVjdCB7OnggKCogeCBjZWxsKSA6eSAoKiB5IGNlbGwpCiAgICAgICAgICA6d2lkdGggY2VsbCA6aGVpZ2h0IGNlbGwKICAgICAgICAgIDpmaWxsIGNvbG9yfV0pCgooZGVmbiBnYW1lLWJvYXJkIFtdCiAgKGxldCBbezprZXlzIFtzbmFrZSBmb29kIGFsaXZlP119IEBzdGF0ZV0KICAgICNodG1sCiAgICBbOnN2ZyB7OndpZHRoICgqIHNpemUgY2VsbCkgOmhlaWdodCAoKiBzaXplIGNlbGwpCiAgICAgICAgICAgOnN0eWxlIHs6Ym9yZGVyICIxcHggc29saWQgYmxhY2siCiAgICAgICAgICAgICAgICAgICA6YmFja2dyb3VuZCAiI2VlZiJ9fQogICAgIDs7IHNuYWtlCiAgICAgKGZvciBbcGFydCBzbmFrZV0KICAgICAgIChjZWxsLXJlY3QgcGFydCAiZ3JlZW4iKSkKICAgICA7OyBmb29kCiAgICAgKGNlbGwtcmVjdCBmb29kICJyZWQiKQogICAgIDs7IGdhbWUgb3ZlciBvdmVybGF5CiAgICAgKHdoZW4gKG5vdCBhbGl2ZT8pCiAgICAgICAjaHRtbAogICAgICAgWzp0ZXh0IHs6eCA1MCA6eSAxMDAgOmZvbnQtc2l6ZSAyMCA6ZmlsbCAiYmxhY2sifSAiR2FtZSBPdmVyISJdKV0pKQoKKGRlZiByb290IChvcgogICAgICAgICAgICAoanMvZG9jdW1lbnQucXVlcnlTZWxlY3RvciAiI2NvbnRhaW5lciIpCiAgICAgICAgICAgIChkb3RvIChqcy9kb2N1bWVudC5jcmVhdGVFbGVtZW50ICJkaXYiKQogICAgICAgICAgICAgIChzZXQhIC1pZCAiY29udGFpbmVyIikKICAgICAgICAgICAgICAoanMvZG9jdW1lbnQuYm9keS5wcmVwZW5kKSkpKQoKKGRlZm4gbW9ycGggW10KICAoSS9tb3JwaCByb290IChzdHIgKGdhbWUtYm9hcmQpKQogICAgezptb3JwaFN0eWxlIDppbm5lckhUTUx9KSkKCihhZGQtd2F0Y2ggc3RhdGUgOnN0YXRlIChmbiBbXyBfIF8gX10KICAgICAgICAgICAgICAgICAgICAgICAgICAobW9ycGgpKSkKCihtb3JwaCk%3D Only a few changes needed :) Now hosted here: https://www.michielborkent.nl/squint-snake/ According to browser: 7.2kb gzip
(add-watch state :state (fn [_ _ _ _]
(morph)))
This is cool πHmm, it seems for this game I don't even need idiomorph, just this works equally well:
(defn morph []
(set! (js/document.querySelector "#app") -innerHTML (str (game))))
:-snow 5.13 kB
even with a grid size of 200x200 I don't see any difference
I think Mithril might work like this - just blatting the new dom without any diffing.
it seems mithril does have a vdom
maybe we've been misled and we don't need vdoms ;)
ha ha yes maybe
so input state etc won't work well with innerHTML
for something where you repaint the whole scene every 200ms it's fine to use innerHTML (like the snake game)
but for crud apps idiomorph is better
when you type in the input field, then you see it's constantly refocussing
Yep - focus tracking
what is still annoying with the idiomorph + squint aproach is that on-click etc don't work so you have to define those out of band
When the dom diffing is done correctly then the input doesn't get unmounted at all so the focus remains.
This does work with idiomorph:
:onClick "globalThis.user.fuck()"
but I don't know how to express this in squint...writing :onClick (fuck) won't work since that will just evaluate fuck and then insert the result of that
:onClick #(fuck) doesn't work?
no, that's now how browser :onClick attributes work I think?
it expects a string of JS I think
Depends what idiomorph is doing - you can set .onclick to a function on an element, not a string.
oh really!
ah but here I'm fucked by the html tag creating a string
so maybe I need to introduce #dom ;)
Ah yep I see so it's stringifying the cljs
If you introduce dom (which would be like a recursive createElement?)I could re-use it in Eucalpyt.
If it could be used runtime that is.
Oh it would have to have a runtime counterpart wouldn't it because you can't have dom elements at compile time.
I think for dom it doesn't make sense to have a compile time thing, since you can't have compile time dom nodes
how do I create a dom node with onclick where it's a function. setAttribute assumes text
(aset el "onclick" (fn [ev] ...)) should work?
Looks like idiomorph doesn't attach it correctly. If I right click the button, inspect, do "Use in Console" and then this:
temp0.onclick = () => alert("hi"); it then alerts if i click the button.
oooh ok
So that method definitely does attach the handler
document.querySelector("#btn").onclickis null
You mean the idiomorph rendered version?
yeah
i don't know anything about it but maybe they have some other way of mounting functions
but when Iog it in my building of the dom it's there:
(js/console.log :set??? (.-onclick el))are you sure idiomorph supports onclick, where did you read this
huh? did I say that?
i mean the aset method of attaching onclick
I don't know anything about idiomorph
lol
maybe I'm confusing you with chatGPT :P
sorry
yeah I'll try attaching an onclick, to see if that works
i am basically a human chatgpt, always having hallucinations
lol
I'm trying (.addEventListener el "click" v)
but no lock
luck
unlike chatgpt i have to sleep unfortunately. good luck with it! π
fair enough! sleep well
anyway, this wasn't in vain. I've discovered that idiomorph doesn't preserve event handlers, so eucalypt has a +1 on this one now too
snabbdom might be nice to try next
it does allow you to preserve event listeners and seems to be just as small as idiomorph
probably eucalypt already does something like snabbdom
(6kb zipped single page with vite)
Very nice!
That's so clean.
Yeah I knew
Probably we can also build replicant without snabbdom now that we have eucalypts example
I'm 74% of the way through porting the Preact test suite to Eucalypt. Have fixed a ton of bugs due to this (`preact-tests` branch).
nice!
Dumdom was a stop-gap solution to get us off React. It was built to be 1:1 API compliant with Quiescent, the react wrapper we used. I chose snabbdom for the vdom implementation, because I thought doing that part would be too difficult. The dumdom code was not very good, and certainly not very performant. Still it served us well for a few years. The main priority was to get us off React, but ultimately the goal was to get out of the JS ecosystem entirely. After a few years I worked up the courage to try my hand at the DOM diffing, and built Replicant as a pure Clojure alternative that supported the ideas we developed with Dumdom as first class concepts: event handlers as data, no component local state, hiccup/data all the way. There wasn't anything particularly wrong with snabbdom. We just wanted a pure CLJ solution.
Just for reference, might be interesting for @chris358 too Here's kinda lifecycle management using snabbdom https://squint-cljs.github.io/squint/?src=KHJlcXVpcmUgJ1siaHR0cHM6Ly9lc20uc2gvc25hYmJkb20iIDpyZWZlciBbaAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbml0CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsYXNzTW9kdWxlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb3BzTW9kdWxlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0eWxlTW9kdWxlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV2ZW50TGlzdGVuZXJzTW9kdWxlXV0pCgooZGVmIHBhdGNoIChpbml0IFtjbGFzc01vZHVsZSBwcm9wc01vZHVsZSBzdHlsZU1vZHVsZSBldmVudExpc3RlbmVyc01vZHVsZV0pKQoKKGRlZm4gY3JlYXRlLW5vZGUgW2hpY2N1cF0KICAoY29uZAogICAgKG9yCiAgICAgIChudW1iZXI%2FIGhpY2N1cCkKICAgICAgKHN0cmluZz8gaGljY3VwKQogICAgICAoYm9vbGVhbj8gaGljY3VwKQogICAgICAobmlsPyBoaWNjdXApKSAoc3RyIGhpY2N1cCkKICAgICh2ZWN0b3I%2FIGhpY2N1cCkKICAgIChsZXQgW1t0YWcgJiBbYXR0cnMgJiBjaGlsZHJlbiA6YXMgYWxsLWNoaWxkcmVuXV0gaGljY3VwCiAgICAgICAgICBbYXR0cnMgY2hpbGRyZW5dIChpZiAobWFwPyBhdHRycykKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBbYXR0cnMgY2hpbGRyZW5dCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgW25pbCBhbGwtY2hpbGRyZW5dKQogICAgICAgICAgZWwgKGlmIChmbj8gdGFnKQogICAgICAgICAgICAgICAoY3JlYXRlLW5vZGUgKGFwcGx5IHRhZyBhbGwtY2hpbGRyZW4pKQogICAgICAgICAgICAgICAoZG8KICAgICAgICAgICAgICAgICAoaCB0YWcgYXR0cnMKICAgICAgICAgICAgICAgICAgIChtYXB2IGNyZWF0ZS1ub2RlIGNoaWxkcmVuKSkpKV0KICAgICAgZWwpCiAgICA6ZWxzZQogICAgKGRvCiAgICAgICh0aHJvdyAoanMvRXJyb3IuIChzdHIgIkludmFsaWQgaGljY3VwOiAiIGhpY2N1cCkpKSkpKQoKKGRlZm4gc2xpZGVyIFt0aGUtYXRvbSBjYWxjLWZuIHBhcmFtIHZhbHVlIG1pbiBtYXggc3RlcCBpbnZhbGlkYXRlc10KICBbOmlucHV0IHs6cHJvcHMgezp0eXBlICJyYW5nZSIgOnZhbHVlIHZhbHVlIDptaW4gbWluIDptYXggbWF4IDpzdGVwIHN0ZXB9CiAgICAgICAgICAgOnN0eWxlIHs6d2lkdGggIjUwJSJ9CiAgICAgICAgICAgOm9uIHs6aW5wdXQgKGZuIFtlXQogICAgICAgICAgICAgICAgICAgICAgICAgKGxldCBbbmV3LXZhbHVlIChqcy9wYXJzZUZsb2F0CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoYWdldCAoYWdldCBlICJ0YXJnZXQiKSAidmFsdWUiKSldCiAgICAgICAgICAgICAgICAgICAgICAgICAgIChzd2FwISB0aGUtYXRvbQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIChmbiBbZGF0YV0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICgtPiBkYXRhCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChhc3NvYyBwYXJhbSBuZXctdmFsdWUpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChkaXNzb2MgaW52YWxpZGF0ZXMpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhbGMtZm4pKSkpKX19XSkKCihkZWZuIGNhbGMtb2htcyBbezprZXlzIFt2b2x0YWdlIGN1cnJlbnQgcmVzaXN0YW5jZV0gOmFzIGRhdGF9XQogIChpZiAobmlsPyB2b2x0YWdlKQogICAgKGFzc29jIGRhdGEgOnZvbHRhZ2UgKCogY3VycmVudCByZXNpc3RhbmNlKSkKICAgIChhc3NvYyBkYXRhIDpjdXJyZW50ICgvIHZvbHRhZ2UgcmVzaXN0YW5jZSkpKSkKCihkZWYgb2htcy1kYXRhIChhdG9tIHs6dm9sdGFnZSAxMiA6Y3VycmVudCAwLjUgOnJlc2lzdGFuY2UgMjQKICAgICAgICAgICAgICAgICAgICAgIDpzaG93PyB0cnVlfSkpCgooZGVmbiBvaG1zLWxhdy1wYWdlIFtdCiAgKGxldCBbezprZXlzIFt2b2x0YWdlIGN1cnJlbnQgcmVzaXN0YW5jZV19IEBvaG1zLWRhdGFdCiAgICBbOmRpdgogICAgIFs6YnV0dG9uIHs6b24gezpjbGljayAjKHN3YXAhIG9obXMtZGF0YSB1cGRhdGUgOnNob3c%2FIG5vdCl9fQogICAgICAiU2hvdz8gIiAoOnNob3c%2FIEBvaG1zLWRhdGEpXQogICAgICh3aGVuICg6c2hvdz8gQG9obXMtZGF0YSkKICAgICBbOmRpdgogICAgICB7Omhvb2sgezpjcmVhdGUgKGZuIFt2bm9kZV0gKGpzL2NvbnNvbGUubG9nICJtb3VudGVkIikpCiAgICAgICAgICAgICA6dXBkYXRlIChmbiBbb2xkIG5ld10gKGpzL2NvbnNvbGUubG9nICJ1cGRhdGVkIikpCiAgICAgICAgICAgICA6ZGVzdHJveSAoZm4gW3Zub2RlXSAoanMvY29uc29sZS5sb2cgInVubW91bnRlZCIpKX19CiAgICAgIFs6aDMgIk9obSdzIExhdyBDYWxjdWxhdG9yIl0KICAgICAgWzpkaXYKICAgICAgIlZvbHRhZ2U6ICIgKC50b0ZpeGVkIHZvbHRhZ2UgMikgIlYiCiAgICAgIFtzbGlkZXIgb2htcy1kYXRhIGNhbGMtb2htcyA6dm9sdGFnZSB2b2x0YWdlIDAgMzAgMC4xIDpjdXJyZW50XV0KICAgICAgWzpkaXYKICAgICAgIkN1cnJlbnQ6ICIgKC50b0ZpeGVkIGN1cnJlbnQgMikgIkEiCiAgICAgIFtzbGlkZXIgb2htcy1kYXRhIGNhbGMtb2htcyA6Y3VycmVudCBjdXJyZW50IDAgMyAwLjAxIDp2b2x0YWdlXV0KICAgICAgWzpkaXYKICAgICAgIlJlc2lzdGFuY2U6ICIgKC50b0ZpeGVkIHJlc2lzdGFuY2UgMikgIiIKICAgICAgIFtzbGlkZXIgb2htcy1kYXRhIGNhbGMtb2htcyA6cmVzaXN0YW5jZSByZXNpc3RhbmNlIDAgMTAwIDEgOnZvbHRhZ2VdXV0pXSkpCgooZGVmIHJvb3QgKG9yCiAgICAgICAgICAgIChqcy9kb2N1bWVudC5xdWVyeVNlbGVjdG9yICIjY29udGFpbmVyIikKICAgICAgICAgICAgKGRvdG8gKGpzL2RvY3VtZW50LmNyZWF0ZUVsZW1lbnQgImRpdiIpCiAgICAgICAgICAgICAgKHNldCEgLWlkICJjb250YWluZXIiKQogICAgICAgICAgICAgIChqcy9kb2N1bWVudC5ib2R5LnByZXBlbmQpKSkpCgooZGVmIHZkb20gKGF0b20gKHBhdGNoIHJvb3QgKGNyZWF0ZS1ub2RlIFtvaG1zLWxhdy1wYWdlXSkpKSkKCihkZWZuIG1vcnBoIFtdCiAgKGxldCBbbmV3IChjcmVhdGUtbm9kZSBbb2htcy1sYXctcGFnZV0pXQogICAgKHBhdGNoIEB2ZG9tIG5ldykKICAgIChyZXNldCEgdmRvbSBuZXcpKSkKCihhZGQtd2F0Y2ggb2htcy1kYXRhIDpzdGF0ZSAoZm4gW18gXyBfIF9dCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChtb3JwaCkpKQ%3D%3D (click on Show to unmount and re-mount component)
btw @pez I think implementing something that looks a lot like replicant on top of snabbdom will work in squint (see demo above)
Ooh very nice. > Snabbdom consists of an extremely simple, performant, and extensible core that is only β 200 SLOC. π€―
If Eucalypt could be a Reagent-API wrapper around something like this it would mean less code to test/maintain which would be good.
worth a branch :)
wonder what it will do to the size
my hacking sessions survived your bed time... now I need to catch some zzz myself I think, before you confuse me with an LLM
ha ha sleep well
I'll finish porting Preact tests over as I think having a good test suite will make it easier to try these things out.
thanks. looking forward to the new version with the bugfix!
oh i should npm publish taht, thanks for the reminder
Replicantβs predecessor, Dumdom, used snabbdom, as it happens.