Modifying wikitext for headers to be collapsible?

Agreed, while remaining “tiddler agnostic” in the sense that the location of the field (i.e. where we store the status) isn’t determined until render-time. At least, that’s how it was initially written…

The other thing… its ability to collapse/expand doesn’t happen in wikitext, per se, but in CSS.

(Of course, I shouldn’t ignore $eventcatcher… without which we wouldn’t be talking about this :wink: )

The “issue” I have left is how best to abstract the CSS (the <style> element) from the demo tiddler into a stylesheet tiddler – getting currentTiddler to work in the CSS such that {{!!selected-header}} resolves correctly. That’s easy in the scope of a regular tiddler but it will lose that scope in a stylesheet tiddler.

Maybe I’ll end up with a macro that transcludes the required css… or leave it like it is :confused:

Thoughts?

I didn’t follow this thread before. But once I’m reading it, it doesn’t seem to be about “overriding default wikitext’” per se. It’s about a particular desired GUI — being able to collapse stuff under headers. Is that right?

Have you checked out the Section Editor plugin by @Mohammad?

https://kookma.github.io/TW-Section/

I know you said you didn’t want plugins with lots of additional bells and whistles, but really, getting headers to behave like collapsible <details> sections is pretty much the central feature of that plugin… At any rate, you could look at that, and clone a version that boils it down to what you most care about.

(For my own part, I was disappointed that it can’t really handle nested hierarchical layers intuitively. And apparently there were good technical reasons for that single-level-deep limitation. So I actually generate old-fashioned <details> and <summary> tag-sets quite a bit, since they can be made to nest pretty well as needed, and will nicely export to html as needed.)

Yes. And you’re right, it did wander off into one specific use-case.

Jeremy’s and Mario’s answer talking about his custom markup parse are certainly on topic. My stuff is about trying to solve OP’s use-case (which he was clearly happy to pursue).

I did wonder though, @Justin_H, did you want to change the topic title?

Yea, I should probably reword the title to be a bit more precise. Let me take care of that right quick!

But yes I was directed to it, and I was impressed by it, but I believe the discussion moved to a different approach afterwords.

2 Likes

Just a short update regarding the topic, since we’re talking about it, I’ve been recommended to a few plugins for reference, but there was talk of a custom parser idea that I think was the main aim.

The things I’m really looking for were:

  1. Doesnt change the syntax of the header wikitext (ie !!! HEADER 3 would be used and show as a dropdown, say as if you were to make an <h3> element)

  2. The default setting should be all headers being open, and close independently of one another. (Though there was a cool accordian effect achieved by storing the open state in the tiddlers field that looked cool)

  3. Didnt include extra functions or unrelated features if possible.

I’m still thinking about if theres a way to clone the javascript tiddler but instead have it place the header elements inside ul li elements, or even just in a div that can have an eventcatcher or onclick function, that way it doesnt break accessibility.

But thats all just braindumping :sweat_smile:

  • It would operate until next Hn tag, and needs to know how to handle Hn+n
  • I think that is a setting
  • Its being a long time, it would be a block mode definition, but I would consider using a different wikitext character and not use headings even if they look like headings. eg the aforementioned +

You may feel this desirable, but you are proposing changing what is effectively and normally a simple implementation of html headers. I think this unwise and may have unwanted side effects. If you can limit this behaviour to the text field of a nominated tiddlers perhaps. But seriously why not replace ! !! and !! with + ++ +++ or similar wikitext character and allowing an optional close such as = == and ===?

  • Keep in mind such an alternate symbol can be applied with a search and replace.
  • Similarly you could apply a class to each header eg !.hopen !!.hclosed using existing methods if you can leverage the hn html elements.
    • I am confident there are ways to hack CSS now that may allow you to leverage something like nth to scope to the next Hn but it would need a real CSS wizard. Animatingor display:none may be the answer.

Once you have a mechanism to do what you want, with out changing existing wikitext, it is possible to introduce tools to easily convert from headings to this alternative in selected text/tiddlers or view template, ie restraining its scope.

Well, I guess if that would be the only really feasible to achieve this, then I could adjust, but that does still leave the challenge of how to create collapsible sections with headers.

The reason I want to avoid it is so that you can still export the text and send it to someone who maybe doesnt have this configuration, but still wants to view it with the same header formatting applied.

Instead they would see

+++ Hello There

How are you

+++

And not

!!! Hello There
"""
How are you
"""

Which the 2nd translates correctly to

<h3>Hello There</h3>
<p>
How are you
</p>

Unless you exported it after it was parsed, atleast that is my understanding.

There are a range of ways to effectively do this separate from the html h tags.

  • I will experiment.

Of course we could quite easily make an export, a template or batch do this conversion.

So! I’ve done a bit of brainstorming and playing around with VSCode (and repeatedly asked ChatGPT to fix my errors :sweat_smile:) but I’ve created a non-tiddlywiki html demo of what I think would work if we replaced the vanilla JavaScript with built in TiddlyWiki functionality.

! WARNING !
This demo code does have JavaScript in it, and you should NOT just haphazardly trust code on the internet! but I think most of us are pretty used to that…

But, here’s my experiment, you can just throw this into a .txt file, rename it to .htm or .html (whatever is your cup of tea) and open it in browser to see it in action, or you can copy it into vscode.dev to look at the code itself. Nvm… I forgot TTW just… shows code. My brains a little fried haha…

It uses the html tags to apply the functionality to the paragraphs following the headers to emulate the similar functionality to the details disclosure element.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Collapsible Headers</title>
    <style>
        h2 {
            border-bottom: solid 1px rgba(128, 128, 128, 0.5); /* <<color muted-foreground>> is what I would use instead imo. This is just for stylistic testing, to see if i could get the border to disappear when collapsed. */
        }
        h2.closed {
            border: none;
        }
        .collapsible-content {
            display: block; /* Default state is open */
            padding-left: 20px; /* Indent the content for clarity */
            margin: 0;
        }
        .collapsible-header {
            cursor: pointer;
            padding: 10px;
            margin: 0;
        }
        .collapsible-header.closed:after {
            content:" ...";
            color: rgba(128, 128, 128, 0.5); /* <<color muted-foreground>> is what I would use instead imo. */
        }
        .collapsible-content.collapsed {
            display: none;
        }
    </style>
</head>
<body>
    <h1 class="collapsible-header h1-main-header">Main Header</h1>
    <div class="collapsible-content p-main-header">
        <h2 class="collapsible-header h2-sub-header">Sub Header</h2>
        <div class="collapsible-content p-sub-header">
            <p>Normal Text Here.</p>
        </div>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', () => {
            const headers = document.querySelectorAll('.collapsible-header');

            headers.forEach(header => {
                header.addEventListener('click', () => {
                    const isOpen = !header.classList.contains('closed');
                    toggleContent(header, !isOpen);
                    header.classList.toggle('closed', isOpen);
                });
            });

            function toggleContent(header, shouldOpen) {
                const content = header.nextElementSibling;
                if (content) {
                    content.classList.toggle('collapsed', !shouldOpen);
                    // Handle nested collapsible content
                    let nestedContent = content.querySelector('.collapsible-content');
                    while (nestedContent) {
                        nestedContent.classList.toggle('collapsed', !shouldOpen);
                        nestedContent = nestedContent.querySelector('.collapsible-content');
                    }
                }
            }
        });
    </script>
</body>
</html>

I think instead of using the tag type as the tag prefix to accompany collapsible-header/content, just having a sluggified header text with a wikified suffix (sub-header-019357135 … kinda… stuff.)

How about a screenshot…

I’ve been reading, but been too busy to contribute.

I was picturing an output much like what you used here:

    <h1 class="collapsible-header h1-main-header">Main Header</h1>
    <div class="collapsible-content p-main-header">
        <h2 class="collapsible-header h2-sub-header">Sub Header</h2>
        <div class="collapsible-content p-sub-header">
            <p>Normal Text Here.</p>
        </div>
    </div>

But the big concern is this is awkward to achieve. Transforming the headers is simple, and determining the nesting isn’t too complex. But finding the content between the headers might be much more awkward. Expanding a bit, this still works as expected:

 <h1 class="collapsible-header h1-main-header">Main Header</h1>  
  <h1 class="collapsible-header h1-main-header">Main Header</h1>
  <div class="collapsible-content p-main-header">
    <h2 class="collapsible-header h2-sub-header">Sub Header</h2>
    <div class="collapsible-content p-sub-header">
      <p>Normal Text Here.</p>
    </div>
    <h2 class="collapsible-header h2-sub-header">Another Sub Header</h2>
    <div class="collapsible-content p-sub-header">
      <p>More Normal Text Here.</p>
    </div>
  </div>
  <h1 class="collapsible-header h1-main-header">Another Main Header</h1>
  <div class="collapsible-content p-main-header">
    <h2 class="collapsible-header h2-sub-header">A third Sub Header</h2>
    <div class="collapsible-content p-sub-header">
      <p>Non-normal Text Here.</p>
    </div>
  </div>

But finding which content applies to which header, and then nesting that in a DIV you can expand and collapse is trickier. It’s certainly doable, but that will make the job much more challenging. I suppose with Jeremy’s suggestion of a totally new parser, this wouldn’t be as hard – just maintaining a stack of open headers would probably do it. But I’m picturing this running as an optional step after the syntax tree has been generated, which I think would be more flexible, and easy to enable/disable as desired.

Either way, I think this is far from trivial.

BTW, I posted my extension of your code to http://scott.sauyet.com/Tiddlywiki/Demo/Misc/ttw10654/demo1.html.

thank you very much!:blush:

And, yes I agree. the h1-main-header area atleast in my mind, was supposed to be a placeholder for whatever is used as the text of the header. However I’m still unsure if it communicates well enough to avoid them being too ambiguous. And yea… a bit awkward for applying css.

So for instance if I had a header of “Tomatos make good soup” it would take that, slugify it, and use it for the tags used in linking the header and div elements, and then in the event you have two headers with the same name or nested and with the same name, wikifying it would atleast keep them unique.

kinda like,

\define header(name) <h1 class="collapsible-header h1-$name$-((wikify))">$name$</h1>

which would change your code to: (also what is in double parenthesis is palceholder for stuff I currently don’t remember how to do.)

<h1 class="collapsible-header h1-main-header-01">Main Header</h1>  
  <h1 class="collapsible-header h1-main-header-02">Main Header</h1>
  <div class="collapsible-content div-main-header-01">
    <h2 class="collapsible-header h2-sub-header-01">Sub Header</h2>
    <div class="collapsible-content div-sub-header-01">
      <p>Normal Text Here.</p>
    </div>
    <h2 class="collapsible-header h2-another-sub-header-01">Another Sub Header</h2>
    <div class="collapsible-content div-another-sub-header-01">
      <p>More Normal Text Here.</p>
    </div>
  </div>
  <h1 class="collapsible-header h1-another-main-header-01">Another Main Header</h1>
  <div class="collapsible-content div-another-main-header-01">
    <h2 class="collapsible-header h2-a-third-sub-header-01">A third Sub Header</h2>
    <div class="collapsible-content div-a-third-sub-header-01">
      <p>Non-normal Text Here.</p>
    </div>
  </div>

I wanted to skip the divs entirely and use the content (collapsible-content) tags on the p elements instead, but being that they are inheretly inline elements, they did not function as that :sweat_smile:

Sidenote, something I am just now noticing but, it should be div-sub-header instead of p, thats deprecated. whoopsie!

Do you have any suggestions that would be less awkward in implementation? this was the only approach I could come up with unfortunately.

No, I think it’s an intrinsically difficult problem. You have a series of nodes in the document, and you want to wrap contiguous collections of them into new containers.

It’s not hard to structure a document directly with those collections. The <section> tag is built on this idea, and <section>s nest well. But TiddlyWiki is not designed to simply give you that nesting.

Outlining has a sad history in HTML, I’m afraid.

I do have the ability, I’m quite sure, to write a JS algorithm that does this conversion for HTML. But I don’t have time for a few days to even try, and I still have no idea how to port that into TW.

1 Like

If I am not wrong in your example you do delimit the collapsible part of the content using a div.

If you are prepared to do this back in standard html or tiddlywiki this should be trivial. It can be implemented in a details tag, custom html tag, using a widget and a number of other methods. So this makes me wonder if you are going down an unnecessary path?

  • The methods I am thinking of require the content author to decide the extent of the collapsible content, which is not automatic but is more powerful.

Speculation.

Perhaps with an editor toolbar button to assist, we make use of a HTML <hn> with an attribute that names the heading, then allow another HTML element that indicates the end of the collapsible area to also have the matching name?

  • This pattern if made to work could be used to support other features.

Post script;

If we created a widget we could add a range of sources to use within headings

This was offered as my original idea in the beginning actually! but it was deemed to be bad practice, and also from an accessibility point of view, it would be problematic for screen readers.

this is one of the reasons I don’t want to change the syntax used for normal headers as well, so it can work with other plugins and features, or without any of these modifications or work with backwards compatibility possible.

As far as making it a manual process, that would mean typing each tiddler using the normal header tags, correct? in which case, yes, that will always remain backwards compatible, but it takes away ease of use from users :pensive:

Would it be reasonable that it if the support for an extension is not available it resulted in un-collapsible headings?

hm… like, an external source, like a web browser extension, or viewing TW’s code on a different platform like VSCode?

I was thinking any html or in this case tiddltywiki without the matching css or plugin support etc…
Although I am still searching for a solution, there exists similar features already in HTML/CSS for example addressing the nth element. eg collapse the nth h1 element.

The idea would be to make the heading or an other element clickable and this would toggle the nominated “section” from display or not, ideally with a little animation.

  • In the absence of the extra css just display the section

Would you be able to provide some dummy code as an example of what your thinking of?

because right now I’m thinking that maybe your proposing the use of a button thats generated along with each header element and then a reveal widget generated along with it, or something to that effect.