HTML term rendering

SWISH renders terms using term//2 from library(http/term_html). This mimics write_term/2, emitting the term with the same text as write_term/2 with a lot of HTML <span> elements that allow for highlighting as well as attaching JavaScript for some interaction (currently only allows collapsing compounds and lists in SWISH).

I’m looking for a more advanced version of this. Think a bit about the JSON and JavaScript object rendering in the development environment of browsers. For one, I’d like to be able to render compounds vertically (with each argument on a line) and I want more interaction (changing display mode for elements, folding/unfolding, etc.)

I wonder whether that has been done and, if not, whether people are interested in some brainstorming and collaboration to make this happen.

5 Likes

Some feedback so that you understand what others (at least me) are thinking. :slightly_smiling_face:

The first problem with this IMHO is not the existing way(s) or even new ways, it is getting others to use and understand what can currently be done. Obviously you know this but to list my current knowledge for reference as there are still many parts of SWI-Prolog I don’t know or have no experience.

  • when I started outputing data using Prolog it was with write/1 and nl/0 (which as you noted should never have been added to Prolog)

  • learned and incorporated format/2,3,

  • then portray/1

  • along the way learned and incorporated DCGs with difference list (not closed list with []) to create the terms and depending upon the output format (console, file, HTML, Cytoscape.js) will enhance as necessary. Odd thing here is that write/1 with with_output_to/2 using codes(Codes,Tail) with difference list (ref) to convert a string or atom to a difference list is quite useful but not what many would do. I can’t count how many times I search for atom_codes/3 or string_codes/3 only to remember the odd way to do it.

  • currently considering using SWISH rendering for output node/edge graphs with coloring for showing graph isomorphisms and the variations.

  • Lets not forget print_message/2 which can sit in parallel with this for printing warnings and errors

  • other hooks.

  • for the reverse of getting the custom format back into SWI-Prolog there are quasiquotations.

Granted my use of SWISH is next to nothing but the sandboxing in SWISH often kills most of what I try there, so I just don’t use it much. :slightly_frowning_face:

In working with JSON a while ago I found converting JSON into dicts (ref) for working with SWI-Prolog quite useful and for viewing JSON created by SWI-Prolog would just use tell/1,told/0 to output it to file and then use one of the many online JSON viewers (Google search) to display the JSON (usually as a copy/paste as the data was about a page or less).

Another thing I am thinking is that JSON is typically a format for transferring data and is only looked at for development or bugs. The more useful format is YAML as JSON doesn’t have comments and YAML comes in handy when wanting JSON as a configuration file with human readable comments.

For me I can solve my problems with what I currently know.

The only thing I can think of that would help you is the (Google search) of JSON viewers for ideas.

My scope is far more limited. When, in SWISH, you write a term, e.g. ?- write(hello(world))., you’ll see it does a bit of highlighting (using colors and fonts) to make the term more readable. You can hover over the hello and click it to collapse the term into … (and click the … to re-expand the term).

I’m only referring to JSON viewers because I want that kind of functionality. While dicts are similar to JSON, we have quite a bit more Prolog terms (variables, compounds, compounds defined as operators, etc.). The challenge is to make it easy to understand complex terms using layout as well as interaction to switch (sub) terms to different layouts (collapse, vertical, horizontal, etc.)

If you want it anyway, run it locally. Then you can disable the sandbox or even run it in IDE mode where it runs on top of your Prolog process such that you can also use the toplevel.

Running of the examples noted in Jan W. post.

image

image


OCaml has a format module which has hbox and vbox and I have used but really don’t like. I mention it because a Google search for hbox and vbox bring up similar things in other languages and might lead to something better. At the very least it is an idea that is in more than a few programming languages.

https://ocaml.org/learn/tutorials/format.html

HTH


EDIT

I am curious to hear from @jamesnvc on the original post and if CSS can help.

I am just gonna through this out there without trying to sell it too hard. Have you considered SVG instead of traditional HTML tags for rendering? The only thing that SVG sucks at is actual text rendering :slight_smile: but as long as the texts stay short this isn’t a pressing issue.

I think it’s a great idea.
that’s how compound terms are rendered in a DAP debugger session, see:

I find it really convenient to be able to “fold” large compound terms like that with a graphical interface

3 Likes

I take it that when designing that part you had inspiration from something and possibly looked at existing code. Care to share that line of thinking. :grinning:

I also like the use of the single changing arrow character before the group; (⏵ ⏷) I have been using such for years.
Just the other day I saw it done differently with two different buttons on the screen at the same time (not changing on click), drove me nuts.

image

Unfortunately, I can’t claim any credit for the UI, it is implemented it this Emacs package DAP Mode. I’ve only implemented the server that adapts the SWI-Prolog debugger to benefit from this interface

Another nice feature to think about is the copy as icon/button, E.g. as text, as html, …

Discourse has a text only version of this

Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint
occaecat cupidatat non proident, sunt in culpa qui officia 
deserunt mollit anim id est laborum.

In the text above if you mouse over the upper right it will show a copy button.

image


Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

For wider text Discourse has an expand button.

image

The folding bits can be done with vanilla HTML, using the <details> element.

Certainly we could apply CSS to make it look like pretty much whatever we want, but I guess the question is where that CSS comes from – if term//2 from term_html is just outputting the plain HTML, would there be a separate DCG to emit the corresponding CSS, to be put in the head of the embedding page? On one hand, having the CSS separate would make it easier to make it look different in different contexts, not to mention making it easier to emit responsive styles. On the other hand though, that’s another, possible annoying step to do, and it makes term//2 less “stand-alone”. Adding the styles inline in the generated HTML is a possible solution to that, but that would both make it more annoying to override and wouldn’t allow things like styling pseudo-selectors and states (e.g. on hover, making the detail disclosure element look different).

The suggestion to use SVG is also promising; I’ve had success with dynamically generated SVGs in the past; it could even be a hybrid – one can use CSS with SVGs too.

3 Likes

I didn’t even get past the first line before I really liked your reply. I did not know about <details> .

Thank you very much.

1 Like

Thanks for the hint about <details>. That could make things a bit easier. I’m making some progress, mostly by adding more tags to the emitted HTML. The nice thing is that without any styling it is just a Prolog term as write_term/2 would produce. I think I’d like to keep that.

The pure folding tree mode is also attractive and I’m now balancing between the two, allowing the user to display any node as “vertical”, “horizontal” or “ellipsis”. There is an “auto” mode that selects a mode based on the sizes. I think it needs more input and tweaks, but it does make progress :slight_smile:

I think the CSS must be separate, right now this is part of SWISH. Eventually it should be bundled with Pengines, still allowing the user the chose dedicated CSS. More will follow …

2 Likes

:grinning: Details is an amazingly flexible element, very useful for all sorts of things. Some extra fun details-hacks I’ve employed:

You can style the little triangle with the ::marker pseudo-element; for example, to make it be a little “menu” icon (actually an i-chang trigram, but it looks close enough) instead of the triangle, you could do

details>summary::marker { 
  content: "\u2630";
}

Another fun trick, to make the “summary” show different text when it’s open vs closed:

HTML:

<details>
  <summary>
    <span class="closed">Click to show</span>
    <span class="opened">Click to hide</span>
  </summary>
<!-- the content -->
</details>

CSS:

details[open] > summary .closed {
   display: hidden;
}
details > summary .opened {
  display: hidden;
}
details[open] > summary .opened {
  display: initial;
}
3 Likes

Great! Maybe someone has a solution for this:

image

This shows the current output after auto layout. A hook tells the system that dataset/2 terms must be in ellipsis mode (unlike the terminal term depth limit, you can click them to see the details). In does two things:

  • It recognizes the select/3 terms is too big and be better shown in vertical mode.
  • It recognizes sequences of operators (a,b,c,…) and renders these as a vertical list if the elements are large. In this case a conjunction.

However, the conjunctions requires parenthesis. As is, these are far too small. Is there a reasonable way to make these fit the height of the expression? All I can see is to put them in their own <span> with some class and have JavaScript setting the appropriate font scaling :frowning:

Note this whole thing is interactive :slight_smile:

2 Likes

Scaling the fonts with javascripts seems like it would be somewhat annoying…it might be worth using SVGs for this? A little goofy-looking, but:

<!DOCTYPE html>
<html>
  <head>
    <style>
      .rdf {
        display: flex;
        flex-direction: row;
        flex-wrap: nowrap;
      }
      .rdf .code {
        max-width: 10rem;
        word-break: break-all;
      }
      .rdf .paren svg {
        height: 100%;
        max-width: 2rem;
      }
    </style>
  </head>
  <body>
    <code>
      <div class="content">
        select(
        <div>[o,p,s],</div>
        <div>[O,P,S],</div>
        <div class="rdf">
          <div class="paren">
            <svg viewBox="0 0 10 10">
              <text textLength="100%" x="0" y="10">(</text>
            </svg>
          </div>
          <div class="code">rdf_triple(dataset(...), t(S,P,O)), \+ \+ rdf_triple(dataset(...), t(S,P,'http://www.example.org/o'))</div>
          <div class="paren">
            <svg viewBox="0 0 10 10">
              <text textLength="100%" x="0" y="10">)</text>
            </svg>
          </div>
        </div>
      </div>
    </code>
  </body>
</html>

Might even go to the extent of actually drawing the parens with svg paths for full control…

2 Likes

I don’t think you will use this but I will note it.

When I look at text I think of it as a thought converted it into a linear form for conveyance of an idea with the text made up of glyphs. Those glyphs can be characters of an alphabet, emojis, symbols etc. or a mix of them. Understanding The Rebus Principle helps to explain how we arrived at present day writing.

Hyperlinks makes it possible to jump from a linear form to a graphical representation that is not limited to being linear or even being static, the representation can be interactive and/or multidimensional.

So instead of trying to expand and collapse data to/from a linear form, instead a link to a different representation might be better. This is one of the reasons I like using Cytoscape.js among many other means of representation.

When clicking on such links, as we know, it is better to keep the origin of the link on the screen at the same time as the destination and thus the need for more than one panel. One of my favorite demonstrations of this is the Reactome browser.

I’ve made a first version available on SWISH -- SWI-Prolog for SHaring. Some notes:

  • The initial rendering of bindings is controlled by Edit/Smart binding layout, which is by default enabled. Disabling causes answers to be written as write/1, but they are still adjustable.
  • Hovering over the term is sensitive for functors, dict tags, list-open ([) and the variable name in a binding. Clicking there opens a menu that allows controlling the layout of the subterm. There is also a Copy option to copy the text as valid Prolog input for read/1.

Note that the output of write/1 and friends are also subject to this behavior, but the initial layout is always simple horizontal (wrapped) text. All styling is done using <span> elements.

If the new behavior does not show up, try a shift-reload (should be automatic some day).

Feedback and improvements are welcome. The term → HTML rendering is in packages/pengines/http/term_html.pl of the main source. The SWISH interaction is in web/js/term.js and web/css/term.css in the SWISH repository. Eventually this should probably move to the Pengine source as well.

Enjoy — Jan

3 Likes

Here are some screen shots and links to the modified code as noted in the topic by Jan W.


image



When converting the format via the ellipsis (…) and then doing a copy and paste, the copied version shows an ellipsis (…).

image

image

image

On Windows using Ctrl-v to paste in notepad, this is the result.

term(...)a)

Should the ... in the output be expected or not?


EDIT

Reported as GitHub issue


EDIT

Overall I really like this. :+1:

Great! Very useful. Thank you :slight_smile: