Looking for feedback on plugin wikitext design

Hey all,

I mentioned before that I’m building a replacement for TiddlyMap. It’s going to be awesome. More extensible and hackable, more lightweight, and it won’t be tied to any one graphing engine, but to as many as we want (currently memgraph/orb and vis-network).

But I’m at a design crossroads, and I’d like to take the choice the gels best with Tiddlywiki best practices. Currently, you can attach actions to nodes, and to the canvas as a whole, like this:

<$graph>
  <$action-to-occur-on-canvas-dbclick />
  <$list filter="[tag[myNodes]]">
    <$node>
      <$action-to-occur-on-node-dbclick />
</$node></$list></$graph>

It already works like a charm. You can even use this to add and remove nodes from the graph, BUT there are more things you can do to nodes, edges, and the graph, such as right clicks, hovers, blurs, drags, etc… So I need to figure out a way to differentiate actions based on event type. If I were to follow the lead of the <$checkbox>, <$range>, and <$link> widgets, I’d do this:

<$graph actions="""<$action-to-occur-on-canvas-dbclick />""" >
  <$list filter="[tag[myNodes]]">
    <$node
      actions="""<$action-to-occur-on-node-dbclick />"""
      actionsDrag="""<$action-record-new-node-location />"""
      actionsHover="""<$action-delay>
                        <$action-show-tooltip>{{TooltipContent}}</$action-show-tooltip>
                      </$action-delay>"""
      actionsBlur="""<$action-hide-tooltip />""" >
</$node></$list></$graph>

This would work. It’s simple, intuitive, and in line with many existing widgets. But it’s also limiting, and given the advent of the \widget, and widgets like <$list-join>, <$list-empty> to replace attributes, and from my talks with @jeremyruston, It seems like TiddlyWiki is now discouraging putting wikitext in attributes. So I was considering introducing a new widget like “<$actions>” to allow syntax like this:

<$graph>
  <$actions for="doubleclick"><$action-to-occur-on-canvas-dbclick /></$actions>
  <$list filter="[tag[myNodes]]">
    <$node>
      <$actions for="doubleclick"><$action-to-occur-on-node-dbclick /></$actions>
      <$actions for="drag"><$action-record-new-node-location /></$actions>
      <$actions for="hover">
        <$action-delay>
          <$action-show-tooltip>{{TooltipContent}}</$action-show-tooltip>
        </$action-delay>
      </$actions>
      <$actions for="blur"><$action-hide-tooltip /></$actions>
</$node></$list></$graph>

So this syntax is more verbose, but it keeps all wikitext in the main parseTreeNode, plus it allows for packaging of features more easily. For instance, all the infrastructure needed just to have tooltips could be wrapped like this:

\widget $node.tooltip()
<$actions for="hover">
  <$action-delay>
    <$action-show-tooltip>
      <$slot $name="ts-raw">
    </$action-show-tooltip>
  </$action-delay>
</$actions>
<$actions for="blur">
  <$action-hide-tooltip />
</$actions>
\end

Tooltips are actually even more complicated than that. (What if they unhover before tooltip appears, etc…), which is why it’s important to be able to wrap functionality like that. You can’t do something like this if actions are done in attributes because this “action package” spans multiple types of events. This is one example, but there are others where actions in parseTrees are more useful for compound behavior (such as a create/edit/delete tiddlers action package). But it’s just so verbose.

Maybe I’m trying to be too extensible, and tooltips should be baked in behavior, and doubleclicks the only actions graph objects care about?

Is this idea too un-TiddlyWiki, or is it in line with the direction TiddlyWiki is going? I really want this plugin to adhere to TiddlyWiki’s vision, so I’d love to hear your thoughts. @pmario, @saqimtiaz, you guys seem in particular to have a good sense of TW’s direction.

-Flibbles

7 Likes

Now that I’m thinking about it, another approach might be the “<$keyboard>” widget way.

<$graph>
  <$actions for="doubleclick"><$action-to-occur-on-canvas-dbclick /></$actions>
  <$graphevent type="doubleclick" actions="""<$action-to-occur-on-node-dbclick />""">
  <$graphevent type="drag" actions="""<$action-record-new-node-location />""" >
  <$graphevent type="hover" actions="""<$action-delay><$action-show-tooltip/></$action-delay>""" >
  <$graphevent type="blur" actions="""<$action-hide-tooltip />""" >
    <$list filter="[tag[myNodes]]">
      <$node />
</$list></$graphevent></$graphevent></$graphevent></$graphevent>
  <$reveal><$transclude $tiddler={{$:/state/tooltipOrSomething}} /></$reveal>
</$graph>

The downside to this is that an “action package” for this would have to use slots. And slots are not great in their current form.

\widget $node.tooltip()
<$graphevent type="doubleclick" actions="""<$action-to-occur-on-node-dbclick />""">
<$graphevent type="drag" actions="""<$action-record-new-node-location />""" >
<$graphevent type="hover" actions="""<$action-delay><$action-show-tooltip/></$action-delay>""" >
<$graphevent type="blur" actions="""<$action-hide-tooltip />""" >
  <$slot $name="graph-nodes" />
</$graphevent></$graphevent></$graphevent></$graphevent>
<$reveal>
  <$slot $name="tooltip-contents" />
</$reveal>
\end

But how do you use that?

<$node.tooltip>
  <$fill $name="graph-nodes">
    <$list filter="[tag[myNodes]]">
      <$node />
    </$list>
  </$fill>
  <$fill $name="tooltip-contents">
    <$transclude $tiddler={{$:/state/tooltipTarget}} />
  </$fill>
</$node.tooltip>

Not exactly saving the users keystrokes there.

1 Like

Hi @Flibbles the Geospatial plugin uses a similar pattern of nested widgets, and likewise I’ve found it to be a convenient pattern that fits together with wikitext features quite nicely. The Geospatial plugin doesn’t have extensive support for actions, though, and so I haven’t tackled the issues you’re encountering now.

You are correct that I would now see actions in an attribute as an anti-pattern, but we’re still using it in the absence of a general purpose alternative. I do like the idea of a generic <$actions> or <$event> widget that is embedded within the widget to which it applies. It would be nice to retrospectively add support for it to existing widgets that trigger actions.

2 Likes

Thank you. I’ve decided to go with the $graphevent solution for now, which unfortunately does use attributes, but provides a lot of ease in other ways.

I’m also doing this for if/when tiddlywiki supports something like <$event> or <$actions> natively, this plugin will adopt that native means as well. I’d love to be part of that discussion.

1 Like

Usually this code from you example

\widget $node.tooltip()
<$graphevent type="doubleclick" actions="""<$action-to-occur-on-node-dbclick />""">
<$graphevent type="drag" actions="""<$action-record-new-node-location />""" >
<$graphevent type="hover" actions="""<$action-delay><$action-show-tooltip/></$action-delay>""" >
<$graphevent type="blur" actions="""<$action-hide-tooltip />""" >
  <$slot $name="graph-nodes" />
</$graphevent></$graphevent></$graphevent></$graphevent>
<$reveal>
  <$slot $name="tooltip-contents" />
</$reveal>
\end

would look as follows. Which imo makes it much more flexible and does avoid plain wikitext in widget arguments, which is hard to debug.

\widget $node.tooltip()
<$graphevent type="doubleclick" actions=<<doubleclick-actions>> >
  <$graphevent type="drag" actions=<<drag-actions>> >
    <$graphevent type="hover" actions=<<hover-actions>> >
      <$graphevent type="blur" actions=<<blure-actions>> >
        <$slot $name="graph-nodes" />
      </$graphevent>
    </$graphevent>
  </$graphevent>
</$graphevent>
<$reveal>
  <$slot $name="tooltip-contents" />
</$reveal>
\end

I would go with $graph-event, because it is much more readable.

In the example you use <$reveal which imo should be replaced by <%if %>, because reveal always creates a DOM element. If should only be used if a DOM element has to be created.

1 Like

Readability, and the ability to have “action-packages” wrap node declarations using \widget are indeed the reasons I’m going with <$graph-event> for now. Though I would argue that wikitext is still in attributes. It’s just hidden away in procedures. It still requires spinning up a separate wikitext parser for each attribute.

I normally agree with you about the widget, but in this case, <$reveal> has a lot of special handling for popups, such as placing their location, so my current implementation uses them. The reveal immediately includes a transclude which references a $:/state tiddler, so the dom elements shouldn’t be a burden until the hover events set the state.

This looks really interesting, looking forward to a demo. :slight_smile: