The idea of this document is to imagine an implementation of a browser plugin for elm-ui, similar to Vue devtools or React Developer Tools.
These developer tools add new panels to the browser's devtools whenever Vue or React components are detected.
It is not fully specified what exactly an elm-ui "devtool panel" could be able to do. The bare minimum requirement is to visualize the "element tree" similar to the way the Inspector panel lets you visualize the DOM tree.
We'd need to know which elm-ui API calls were made in order to build an "element tree". The current implementation of elm-ui does not preserve that information. I toyed with the idea of creating a copy of elm-ui that behaves the same and also preserves information about API calls, but it looks hard and error-prone.
The idea I came up to make this possible in a cheap and easy way is to publish a separate package, let's call it elm-ui-devtools, that exposes almost the exact same API as elm-ui.
You'd then need to change your elm.json file to use the development version:
// before:
"mdgriffith/elm-ui": "1.18"
// after:
"xxxxxxxx/elm-ui-devtools": "0.X.X"(An alternative would be to have both packages in elm.json and only change imports, for example change import Element to import Devtools.Element).
As a safety measure, the Element.layout and Element.layoutWith functions would take an extra argument: a Todo.
-- before:
Element.layout
[ ... ]
(...)
-- after:
Element.layout
Debug.todo
[ ... ]
(...)This fake layout would render an empty custom element. This element would receive all the API calls as an encoded JSON object.
This means all the API calls in elm-ui-devtools would just generate a representation of themselves:
type Element msg
= None
| Text String
| El (List (Attribute msg)) (Element msg)
| Row (List (Attribute msg)) (List (Element msg))
| Column (List (Attribute msg)) (List (Element msg))
| ...
-- Include also input constructors, i.e.: Input.button, Input.checkbox, etc.
type Attribute msg
= Width Length
| Height Length
| CenterX
| CenterY
| ...
-- This call:
Element.el [ Element.centerX ] (Element.text "Hello, world")
-- Would return:
El [ CenterX ] (Text "Hello, world")
{- Which can be encoded to something like:
{
"tag": "el",
"attributes": [ "centerX" ],
"child": { "tag": "text", "value": "Hello, world" }
-}We can send the "encoded layout" (the encoded API calls) to the custom element.
The custom element will do several things with this information:
On mount
- Instantiate a new elm app that receives the encoded layout as a flag and renders it using
mdgriffith/elm-ui. - Init the devtool panel and send the encoded layout.
When the encoded layout changes
- Send the new encoded layout to the "internal" elm app through a port.
- Send the new encoded layout to the devtool panel.
The "internal" elm app can render the exact same API calls that were given originally. But it can also render different things in reaction to events in the devtool panel.
For example, whenever we hover or click an element in the "tree view" in the devtools panel, we can add (or change) the background and border of that element to show where is it. (This is similar to the behavior of the Inspector panel).
We'd need:
- An elm package called
elm-ui-devtoolsor something similar. - A content script that declares the custom element.
- An elm app (instantiated inside the custom element), that receives the "encoded layout" and renders it using
mdgriffith/elm-ui. - A background script that initializes and renders the devtool panel.
- The communication ports between the content script and the background script.
I'm linking to MDN docs but apparently the WebExtensions API is standardized and also works in Google Chrome.
This has been an interesting thought experiment. But is it really possible/worth it to implement this?
Having to change elm.json is uncomfortable. (Having to change all your imports is even worse.) Besides, we already have Element.explain.
It would certainly be cool to have a developer tools extension, but is it worth it? (Personally, I would have appreciated having it when I was first learning elm-ui, but I don't think I'd use it much now).
Also, what other things could be done?