Skip to content

Instantly share code, notes, and snippets.

@mauroc8
Last active October 20, 2020 01:09
Show Gist options
  • Select an option

  • Save mauroc8/eaa26cfb93e67deb4181724331d173ee to your computer and use it in GitHub Desktop.

Select an option

Save mauroc8/eaa26cfb93e67deb4181724331d173ee to your computer and use it in GitHub Desktop.

elm-ui developer tools

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.

The idea

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

  1. Instantiate a new elm app that receives the encoded layout as a flag and renders it using mdgriffith/elm-ui.
  2. Init the devtool panel and send the encoded layout.

When the encoded layout changes

  1. Send the new encoded layout to the "internal" elm app through a port.
  2. 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).

The implementation

We'd need:

  1. An elm package called elm-ui-devtools or something similar.
  2. A content script that declares the custom element.
  3. An elm app (instantiated inside the custom element), that receives the "encoded layout" and renders it using mdgriffith/elm-ui.
  4. A background script that initializes and renders the devtool panel.
  5. 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.

Discussion

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment