I call it S. T. A. P

Project URL: https://glitch.com/~stapui

Suppose you had some code to generate a clickable button.

button('b', () => {
  alert('b');
})

image (don’t click this, it’s just a screenshot)

Add a wrapper called place() to remember where you call this b() function.

function b() {
  return place(button('b', () => {
    alert('b');
  }));
}

Pass those to a function called layout(). Then place() and layout() will lay them out for you.

layout(
  b(), b(),
)

image

What if you want them above and below? Well in the CSS box model, buttons are inline, and for above and below what you would really want is– I’m kidding. You just do this.

layout(
  b(),
  b(),
)

Wait but that’s still the same function call, the whitespace doesn’t–

image

Or what if left and right was the way to go, you just needed some more space in between? So at that point the inline layout is kind of… actually there’s this thing called CSS grid, which– I’m kidding again, you just do this.

layout(
  b(),   b(),
)

image

You can put them however you want.

layout(
      b(),
  b(),    b(),
      b(),
)

image

Hello police, I’m on the Glitch forum and there’s this guy that’s–

Wrap it in a function to make reusable sublayouts.

function buttons() {
  function b() {
    return place(button('b', () => {
      alert('b');
    }));
  }
  return (0,
    layout(
          b(),
      b(),    b(),
          b(),
    )
  );
}
function megaButtons() {
  function b() {
    return place(buttons());
  }
  return (0,
    layout(
                  b(        ),


      b(        ),            b(        ),


                  b(        ),
    )
  );
}
megaButtons()

image

No no no no no no no no

Now you can lay out your UIs intuitively.

layout(
  t('Filter prefix:'), p('        '),
  l('                             '), t('Name:'),    n('        '),
                                      t('Surname:'), s('        '),


  c('Create'), u('Update'), d('Delete'),
)

image

8 Likes

Very cool :sunglasses:

1 Like

very clever and very cursed. I love it.

2 Likes

Clearly something other than the program’s abstract syntax tree is at play here. The place() function uses the source code positions of where you call b() et al. And that information is available through error stack traces.

place@https://stapui.glitch.me/layout.js:2:17
b@https://stapui.glitch.me/:13:14
buttons@https://stapui.glitch.me/:19:13
main/a/<@https://stapui.glitch.me/:181:24

It needs a little more information to work things out though:

  1. We have a whole stack trace, but which stack frame is the one that’s actually laid out in the source code?
  2. The line:column positions are from a big file with many layouts and non-layout code, so where is everything relative to?

The answers to these come from having this outer layout() function which provides a reference stack trace.

layout@https://stapui.glitch.me/layout.js:11:23
buttons@https://stapui.glitch.me/:18:7
main/a/<@https://stapui.glitch.me/:181:24

From this, we can find out which frame calls the function named “layout,” and from that we can find out how deep in the call stack we need to look for the positions that matter. Because the callsite looks like this,

layout(
      b(),
  b(),    b(),
      b(),
)

it should be the frame just outside the layout call—so it’s the buttons@... line in this case. We can then look at the frames at the same depth from the traces collected in the place() calls.

Untitled

Graphviz source
digraph {
node [shape=none]
labeljust=l

subgraph cluster_layout {
    label="detected \"layout\" call"
    "layout@...:11:23"
}

subgraph cluster_relevant {
    label="relevant frames"
    subgraph cluster_reference {
        label="reference frame"
        "buttons@...:18:7"
    }
    "buttons@...:19:13"
}

"layout@...:11:23"

"layout@...:11:23" ->
"buttons@...:18:7" ->
"main/a/<@...:181:24"

"place@...:2:17" ->
"b@...:13:14" ->
"buttons@...:19:13" ->
"main/a/<@...:181:24"
}

We also use the position of the layout() to figure out where the upper left corner of the layout should be—on the next line and indented one level.

image

  1. position where layout() is called
  2. origin for this layout
  3. position of something to be placed

And then we can generate the CSS.

layout() position 18:7
origin position 19:9 (next line, two space indent)
place() position 19:13
relative position down 0, right 4
scale 1.5rem/line, 0.375/column
CSS top: 0rem; left: 1.5rem;

We set them to position: absolute; and we’re done.

Stack traces + absolute position. S. T. A. P.

4 Likes