derpycoder

derpycoder

Made a Source Code Inspector, useful in big projects or large teams

I didn’t think I could do it, but after getting the hang of the Hooks, I have created a tool that I really needed in front-end web development space.

I can barely remember what I worked on today, let alone the file I worked on. So when I get thrown into a project or get asked to work on a web component that was built by my colleagues, I get lost in the labyrinthian code.

Here’s my best attempt at solving that issue:

Source Code Viewer


The tooltip has 2 icons, 1 that leads to the source code, and the other will lead to the Storybook components page. Which I haven’t implemented yet.

Also, this is currently restricted to dev, but I wish to enable it for prod as well. We can enable the inspector like we enable latency sim, and clicking on the show source would open up GitHub/Storybook.

And it works everywhere, see:


P.S. Just stuck figuring out a way to add multiple JS Hook. (I found out a post on Elixir Forum, by someone who figured it out and even did lazy loaded Hook!)

P.P.S. It’s not a library, just changes made across several places. I can paste the whole thing if people want. Took me a while to get the popup to work, but it was worth it.


Inspired by: https://bit.dev (Toggle the inspect button in the top right to hover over each element and see their names and link!)

I suggested this to many people, I really want this to be part of LiveView, or Storybook, or both. (See: Add dev config for injecting HTML comments around function components)


P.S. Here’s the full source code:

Most Liked

chrismccord

chrismccord

Creator of Phoenix

Nice work! The goal in the linked issue is to add this to the HTML engine and have it inject whatever is needed to do this kind of thing. If you’d like to get involved with that issue, please let me know!

derpycoder

derpycoder

Here’s the code, so others can play around with it, till the Phoenix team adds it to Phoenix 2.0!

defmodule DerpyToolsWeb.Nav do
import Phoenix.LiveView
use Phoenix.Component

def on_mount(:default, _params, _session, socket) do
  {:cont,
   socket
   |> attach_hook(:inspect_source, :handle_event, &handle_event/3)}
end

defp handle_event("inspect-source", %{"file" => file, "line" => line}, socket) do
  System.cmd("code", ["--goto", "#{file}:#{line}"])

  {:halt, socket}
end

defp handle_event(_, _, socket), do: {:cont, socket}
scope "/", DerpyToolsWeb do
  pipe_through :browser

  live_session :no_log_in_required,
    on_mount: [DerpyToolsWeb.Nav] do
    live "/", HomePageLive
    ...
  end
end

Usage

<h2
  id="test-div"
  data-file={__ENV__.file}
  data-line={__ENV__.line}
  phx-hook={Mix.env() == :dev && "SourceInspector"}
  class="..."
>
    Hover over this!
</h2>

JavaScript Side

source_inspector.js

import { computePosition, flip, offset, arrow } from "../vendor/floating-ui";

const SourceInspector = {
  mounted() {
    if (!this.el.dataset) {
      console.log("Please pass in file & line data attributes!");
      return;
    }

    const globalTooltip = document.querySelector("#inspector-tooltip");

    let tooltip = globalTooltip.cloneNode(true);
    tooltip.setAttribute("id", `inspect-${this.el.id}`);
    const inspectSourceBtn = tooltip.querySelector("#source-btn");
    const arrowElement = tooltip.querySelector("#arrow");

    this.el.addEventListener("mouseenter", () => {
      const { file, line } = this.el.dataset;

      this.el.appendChild(tooltip);

      tooltip.classList.remove("hidden");
      tooltip.classList.add("flex");

      placeTooltip(this.el, tooltip, arrowElement);

      this.el.classList.add(
        "rounded-lg",
        "outline",
        "outline-offset-4",
        "outline-pink-500"
      );

      inspectSourceBtn.setAttribute("phx-value-file", file);
      inspectSourceBtn.setAttribute("phx-value-line", line);
    });
    this.el.addEventListener("mouseleave", (e) => {
      handleMouseLeave(this.el, tooltip);
    });
  },
};

function handleMouseLeave(target, tooltip) {
  target.classList.remove(
    "rounded-lg",
    "outline",
    "outline-offset-4",
    "outline-pink-500"
  );

  tooltip.classList.add("hidden");
  tooltip.classList.remove("flex");
}

function placeTooltip(target, tooltip, arrowElement) {
  computePosition(target, tooltip, {
    placement: "top",
    middleware: [
      flip(),
      offset(8),
      arrow({
        element: arrowElement,
      }),
    ],
  }).then(({ x, y, placement, middlewareData }) => {
    Object.assign(tooltip.style, {
      left: `${x}px`,
      top: `${y}px`,
    });

    const { x: arrowX, y: arrowY } = middlewareData.arrow;

    const staticSide = {
      top: "bottom",
      right: "left",
      bottom: "top",
      left: "right",
    }[placement.split("-")[0]];

    Object.assign(arrowElement.style, {
      left: arrowX != null ? `${arrowX}px` : "",
      top: arrowY != null ? `${arrowY}px` : "",
      right: "",
      bottom: "",
      [staticSide]: "-10px",
    });
  });
}

export default SourceInspector;

app.js

import SourceInspector from "./source_inspector";

let liveSocket = new LiveSocket("/live", Socket, {
  params: {
    _csrf_token: csrfToken,
  },
  hooks: {
    SourceInspector,
  },
});

N.B. Don’t forget to add this in the environment variable:
export ELIXIR_EDITOR="code --goto __FILE__:__LINE__"

This way, the Beam instance will know to open the VS Code editor!


P.S. I used float-ui, which is the next iteration of Popper.js, for the tooltip.

Just download the ESM files from JS Delivr, i.e.

derpycoder

derpycoder

That, plus I have a major qualm with the current state of Web Development:

  1. There’s a lack of a tool, as universal and unchanging as a Guitar. (Why does Node.js exist? It’s just a time-sink to debug. :bug:)
  2. The wrapper-around-wrapper approach that’s nuking performance back to the 1980s. (Microsoft Teams and it’s slow as heck next version is being shoved down our throat as fast! :turtle:)
  3. Code rot, in projects built with nearsighted frameworks. (My angular project, which I built 5 years back, doesn’t run anymore. :cross_mark:)
  4. Complexity that has crept into the tooling. (Webpack at work, compiles our project which is written in an Interpreted language, at a snail’s pace. :snail:)

I wanted to quit this field. But decided to stick around because of Elixir, Phoenix & Live View.

Hope it pans out.

Where Next?

Popular in Discussions Top

sashaafm
I’m trying to evaluate the best combo/stack for a BEAM Web app. Right now I’m exploring Yaws a bit, after having dealt with Phoenix for a...
New
cvkmohan
The upcoming Phoenix 1.6 release looks very interesting. Became a habit to watch the commits - and - what they are bringing in. phx.gen...
New
jeramyRR
This is an interesting article to read. Elixir’s performance, like usual, is excellent. However, it seems like the high CPU usage is co...
New
Nvim
Elixir appears to be a superior language to Python. I don’t see any advantage of Python over Elixir. Are there any?
New
Fl4m3Ph03n1x
Background This question comes mainly from my ignorance. Today is Black Friday, one of my favorite days of the year to buy books. One boo...
New
arpan
Hello everyone :wave: Today I am very excited to announce a project that I have been working on for almost 3 months now. The project is...
New
chuck
Let me start by stating an assumption: Phoenix is a great approach to building REST APIs. There are many reasons for this, but I will ass...
New
IVR
Hi all, I’ve seen a number of related threads in the past, but I’d still be very curious to hear an up-to-date opinion on this topic. I...
New
boundedvariable
I am going through the kafka architecture. All the features what the kafka is providing are already in Erlang. I would like hear your opi...
New
100phlecs
Are there any downsides, like perf issues, to putting all functional components in CoreComponents as long as you prefix it with the conte...
New

Other popular topics Top

malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 36128 110
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
AstonJ
Seen any cool LiveView demos, sample apps or examples? Please post them here! :003:
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

We're in Beta

About us Mission Statement