one.el: the static site generator for Emacs Lisp Programmers

Tony Aldon - tony@tonyaldon.com

Format: 23-min talk; Q&A: BigBlueButton conference room
Status: Q&A to be extracted from the room recordings

Talk

00:00.000 Introduction 00:24.000 Documentation 02:02.200 Starting a new project 02:27.400 Building 03:19.760 Side by side 04:32.160 Writing a render function 05:44.680 New page 06:41.720 Linking between pages 08:40.000 CSS 10:23.160 How to write a render function 19:03.200 Rendering content 20:37.160 Rendering CSS

Duration: 22:18 minutes

Q&A

Listen to just the audio:
Duration: 27:39 minutes

Description

Have you ever wanted to write a blog:

  • contained in a unique org file,
  • rendered with only one Emacs command,
  • that can be modified by writing Emacs Lisp code (and CSS too),
  • with "html templates" that are plain Emacs Lisp data,
  • with no config file,
  • and no dependencies on external static site generators?

If so, you might be interested in one.el package.

In this talk, we'll look at how to get started with one.el and write a a simple blog as an example.

What kind of static sites can be produced with one.el? I don't know but you can get an idea by checking those 3 websites built with one.el:

Below you can see the basics of a one.el website.

In one.el, the following org file/buffer defines a website with 2 pages that we build by calling one-build command while we are visiting it:

*** My website
:PROPERTIES:
:ONE: one-default-home
:CUSTOM_ID: /
:END:

Welcome to my website!

*** Blog post 1
:PROPERTIES:
:ONE: one-default
:CUSTOM_ID: /blog/page-1/
:END:

My first blog post!

The path ~/~ in the first ~CUSTOM_ID~ org property tells ~one.el~ that the
page "My website" is the home page. That page is rendered using
~one-default-home~ render function, value of ~ONE~ org property of the
same headline.

The path ~/blog/page-1/~ in the second ~CUSTOM_ID~ org property tells
~one.el~ that we want to render "Blog post 1" page in such a way
that when we serve our website locally at ~http://localhost:3000~ for
instance, that page is served at ~http://localhost:3000/blog/page-1/~.
How that page is rendered is determined by the value of ~ONE~ org
property of the same headline which is ~one-default~, a render
function.

As you might have noticed, a ~one.el~ website is an org file where the
pages are the headlines of level 1 with the org properties ~ONE~ and
~CUSTOM_ID~ set. Nothing more!

~ONE~ is the only org property added by ~one.el~. Its value (an Elisp
function which returns an HTML string) for a given page determines how
~one.el~ renders that page.

The paths of pages are set using ~CUSTOM_ID~ org property.

That's it!

note: I wanted to have a blog written in org-mode that I can modify
only by evaluating some Emacs Lisp code. This is how one.el got
started. Down that path I found that the Org parser and exporter do an
amazing job, in fact 95 percent of the heavy work in one.el. I just
had to find a way to pass org data to render functions and write an
Emacs Lisp html generator package to be used by those render
functions. I'm having a good user experience so far and I hope its
design will fit your workflow.

Discussion

Notes

  • Also provides a demo of Jack package (https://github.com/tonyaldon/jack/)
  • Custom function for rendering by interogating the page-tree
    • Of use: grabbing metadata from the document/node
  • Easy menu and tab generation would also be great.

  • very cool indeed 👏

  • thx for the nice presentation
  • Yeah, definitely a fun project that solves a problem to keep more Emacs and less external services. Static web sites are the best. :-D
  • I agree. I currently use Hugo, but I think this looks great!
  • I really like that jack-html is a separate project, as that looks nifty.
  • jack-html feels like a hiccup for elisp, nice
    • A very programatically solution!
  • I didn't watch the talk, but I use esxml to template pages that I generate from Org files: https://codeberg.org/SystemCrafters/systemcrafters-site/src/commit/b9b33910e68c6a9321ee7dcd92015b8a29b260bd/publish.el#L176 - Lisp backquotes are the best templating language :)
  • This looks like a nice setup. My blog is still using org-page, which was abandoned years ago.
  • org-page still works though
    • I know, I'm still using it. But it's finicky in a bunch of ways and I'd like a replacement static site generator. I don't really care if the code is elisp or not, as long as I can write in Org Mode and don't have to run Wordpress.

Questions and answers

  • Q: does the "one" part of one.el refers to one source file?
  • Does one.el support #+include: to add from other (org) files?
  • Q: What's the main motivation for this new package? I used to use ox-hugo and use github action to build the blog.  (Curious as well, as I use ox-hugo and have almost 1000 pages)
    • A: Mapping from org-mode to Hugo added another system to understand; wanted Emacs centric approach.  (https://one.tonyaldon.com/ has some rational)
    • understand. For me, it's just org-mode, ox-hugo take care of the rest. And I find it is easy for me. Maybe, I am not used it so much. Full control definiitely requires your package.
  • Q: Is it possible to use #+include to add content from other files?
    • A: Not included; the idea was to only have one file. It is possible to code what you want in elisp.
    • Perhaps org-transclusion would play with this?
  • Q: Can this generate a single site from different sources like blog.org (for example.org/blog/), videos.org (for example.org/videos/), contact.org (for example.org/contact/), etc?
    • A: Refer to the previous question's answer
  • Q:Do you have pre-made templates already along with the one.el package?
    • A:Yes and no. There are quite a few constructs/templates in the one.el code, you could perhaps use them to customize to get it to do what you want.
  • Q: What additional features are there that you would like to add to one.el in the future?
    • A: A full text search
    • (Comment not from presenter:) I've used Lunrjs which is a JS package that keeps all things local; but your site generator does need to kick out a JSON representation of the content (e.g. path, text, tags/keywords).  I've been considering http://elasticlunr.com/
  • Q:Can you create navbars on a website and fancy things like carousels (pictures rolling/sliding from one to another) using one.el?
    • A:Sidebars, navbars are already part of the package. one.el also generates responsive. pages. pages argument used recursively can manage/mimic the carousel effect.
  • Q: Would there be an automated way to convert an existing HTML document into jack-html form?
    • A:
    • One challenge is that HTML documents do not need to be "precise" (you don't need to close tags).  So finding a tree-parser for HTML (perhaps treesitter?) to build the conceptual tree.
  • Q: Does this or you use any other Emacs Packages for your package/website ex org-publish.

Transcript

[00:00:00.000] Introduction

Hi, everybody. Welcome to the EmacsConf 2023. I hope you're doing well and you're having fun. I'm Tony Aldon, and in this talk, we are going to see how to build a static website with the package one.el that I wrote. But before we start, I'd like to thank all the people who organized that conference, so thank you all for the great work.

[00:00:24.000] Documentation

Now let's jump into the documentation of one.el, which is built with one.el. In the install page, we can see that we have a sidebar with all of the pages in the documentation, some buttons to switch between pages, and we also have a table of contents for some of the pages if we need it. Now let's jump into one.el repository and see why I like how it is implemented, because the website that we've seen, the documentation, is just one file. So this is that file, with the headline of level 1 being the web pages. There needs to be a web page to have the property, the Org property :ONE:, set to a render function. We are going to see how they work after. And the :CUSTOM_ID:, the value of the :CUSTOM_ID:, is the path of the page. So really, the website that we have on the left is this file. So to me, this is something simple like that that I wanted. And another thing is that when we want to change something with one.el, we don't change configuration or write JavaScript or anything else. We just write Emacs Lisp code or a bit of CSS. So this is what we have with a minibuffer website that is built with one.el, and the only thing that I had to do is to write Emacs Lisp code. So those are two things: the content in one file, and if we want to change the layout, CSS and Emacs Lisp. This is one.el.

[00:02:02.200] Starting a new project

Now let's go to our node, and we are going to start a new project. How do we do that? In a new empty directory, so new project directory, we call the function one-default-new-project. We have that project, which is one file with the five default type of pages that we have, and one CSS file.

[00:02:27.400] Building

How to build that website? Okay, so we call the function one-build. This builds the website. We jump into a terminal, and now if we run tree, we can see that the website has been built in the public directory with the information in the Org properties and the content of one.org files. Okay, cool. Now we are going to render that in the browser to serve that, and to do that we can use browser-sync utility, which is cool with that, in that each time we are going to... So we go into public... Each time we are going to change and rebuild the website, this will be reloaded in the browser. So one, this is that website, is now this one.

[00:03:19.760] Side by side

So let's put them side by side. We go there, and we may do something like that. So one.el, the home page, so our custom ID with the value just a /, is rendered with that function one-default-home, which is a render function, and the first argument of that function is the headline, this current headline. So, parsed with the Org parser, and then we do the thing that we want to do, and the render function returns an HTML string that is used to build the pages at the custom ID. Now we can go to another web page, the second web page, and we see that there is a different value for the :ONE: property, so another render function, and the custom ID at the path of that page. So we can see that in the browser. So this is /blog/default-home-list-pages. So this is that. Now there are three other pages, but we can list that like that. We do a grep in that files, and we see the different default render function.

[00:04:32.160] Writing a render function

In the second part of that talk, we are going to write a render function. So we are going to see that after. Now maybe we can go to the default page, and let's modify that default page. We see that this uses one-default render function, and now let's write "foo bar baz". We want to modify the content. We save. We call again one-build distribute, and we see here we have it: foo bar baz in the default page. Now we can use... When we use one-build, this also copies the files in the asset directory into the public directory. This is not always what we want to do. Sometimes we just change the content, and for that we can use one-render-page-at-point. If we use that one, this just renders the current page. So we see that we have again "foo bar baz" in the page.

[00:05:44.680] New page

Now let's add a new page. To add a new page, we just have to copy one of them, maybe the default page. We are going to call it maybe emacsconf-2023. We still use one default render function to render it, but we want to change the path. So the custom ID, we are going to give it /blog and emacsconf-2023 with a slash at the end, and the content... We no longer want this one, but maybe "We're having a lot of fun". So we save that, we rebuild with one-build this, and now we can look at the top and pass it the path `/blog/emacsconf-2023/. So we have that new page.

[00:06:41.720] Linking between pages

Now, how to link between pages? So we are going to write a link that links to the last page, so a page with the table of contents. To do that, we just have to use the value of the custom ID, and to link to a custom ID inside Org mode, we use the hashtag. We pass it here, then we pass it in the description, so TOC and sidebar, and now if we press RET inside Emacs, we jump to that page. So this is cool. Now we build again, and we see that we are going to have the link to the page in the browser. So this link to the default page with a table of contents, fine, but maybe what we want to do is to link to the "Headline foo" in that page. How do we do that? We do that by adding a custom id. We keep the first part, which is the page where we are, and we added hash with foo, so that Headline foo will have the ID "foo" in its H2 tag, HTML tag, and now we can link it here with still custom ID, so "foo", and now it's headline... headline with what? Headline foo in TOC page. So we have that. If we press RET, we jump to that headline in Emacs. So this is super cool. And now, if we call one-build, we see in the browser that we have a new link, and this link linked to that specific headline. So this is cool. So we have the link between pages that works inside Emacs and that works well also in the browser.

[00:08:40.000] CSS

Now let's say that we want to change the CSS. So we've added a page with specific content, and we've done some links. Now we want to modify the CSS file which is in the asset directory, the one.css. Each time we change it, we want to have live reload that copy that file into the public directory, so the same. We go back here, and there is a utility called entr, e-n-t-r. Yes, this one, and using that, so a new terminal, we are at the beginning. This will watch the changing in what.css, and entr will copy it into the public directory each time this changes. Let's go back to Org mode, because I chose some color that is cool, and now we go back to the CSS files. We put them side by side, and maybe we go to the new page that we were changing, and we are going to change the body, the background color, and maybe we can change with the color that we've just taken. So we save, and we see the changing happening. We can do it again with the color that we have at the beginning, and this is the user experience that we have with one.el and the default function.

[00:10:23.160] How to write a render function

Now that we've seen that, we've done all of that part, and now we are going to see how to write a render function. So let's go. The render function, so one.org, we remember these are the functions that are in the :ONE: Org property. So we are going to remove that part. We no longer want that one. We don't want this. We just keep that. one-default, we want this to be the home of our website. We have that. We rebuild, and now we just have one page, and we have that page. We are going to add another page that we call "foo", and here we pass it the render function foo that doesn't exist yet, and we are going to write it. So maybe with some content, and we copy, copy. We have that. We call it "bar" to have something to show. So here we are. If we build that, so we build it, and we see in the echo area at the bottom that we have an error "void", which is because the function foo doesn't exist. So now we are going to write that function, and we write it in the onerc.el where we put any Elisp code that we want to be run each time we build the website or render the pages. So we want a render function called foo. So that takes three arguments: page-tree, pages, and global. We are going to look at the page-tree in our case, and the render function return an HTML string. This is the thing that we want from them. So maybe foo, bar, and baz. Now this is something well-defined, and with one.org here, the file, we rebuild this, and we can see now in the browser, if we go to the page foo, that we have "foo bar baz". So this is exactly what we have rendered by the render function that is set, we see at the bottom in the one.org file, in the :ONE: property. Now this is HTML, so we can pass it, for instance, h1, the tag h1. We save that file. We go in the one.org file, we build again, and now we see that we have an h1. Okay, this is interesting, but if we would have to build this function with a string like that, this is boring and not the best way. So we can use the library Jack, which offers function jack-html that takes some data structure, for instance, an h1, a nested list that represents the HTML that we want to render, and transform it into an HTML string. So we have that, we saved, we rebuild in the one.org file with one-build, and we see now that this has been built using jack-html. Now what do we want to do? Okay, see, the thing that we want to do is to understand page-tree. So what is page-tree? page-tree is when we go to one.org, this is really for foo, this is the parsed data of that headline, that page. So this is done with, no, not this one, we use, so in the mini-buffer, we use one-parse-buffer, and we see this is the data that we have with that function, first headline, and the second headline, this is the parse tree that we have there. This is that data that is passed to the foo render function. One thing that is cool, so I see here, is that as we are dealing with data, we have all the data of the website, we can show them in the web page. Now, why not? It's great to write the website and also to debug if we need to debug at some point. So let's render page-tree directly in the page, one.org, and we rebuild, we reload, and we see this is what we have, this is the data that we have, okay? And we have, for instance, the :raw-value with this "foo", which is the headline, the content of the headline in a raw format, and we also have custom, so here we have the :CUSTOM_ID: foo and :ONE: foo, which are the properties, and when we are inside those render functions, we have access to this. So let's, what can we do now, is to, let's get the row value. So we no longer need that. Maybe we can do something like that. We create now HTML. In HTML, we want the body, we want an h1 tag, and we are going to pass it a title, and in the title, this is something that we let-bind here, so the value of the title, we get it with org-element-property, and the :raw-value, so this is the property that we want, so raw-value, and from which data we want that, to page-tree. So now, let's have one.org at the bottom, we build again, and now we reload, and we see that we get a foo. This is that title, the value of that variable in that data structure. Now, let's get those two properties. How do we get those two properties? The same way, :ONE: that we call one, so raw-value, we change that for :ONE, the other raw-value for :CUSTOM_ID, we change the title for custom-id, and what we want now is for instance, yes, h1 again, and org properties. We add the org properties, and let's do a list, another list, with li element, one, we want that value, and that value will be the value of the variable one. We can do that with also custom-id, and now, in the one that we have to save, and in one of our files, not like that, we rebuild that, and we see that we can get those properties. This is super cool. As we are dealing with data, and we have the information of the whole website, we can do whatever we want, because we have access to that data. Let's, for instance, add a date, the date of 2023, so I think this is 02, when there is the conference, see, and we can get access to that one again, here, so date, and we go, we change the :CUSTOM_ID with the :DATE, and in the list here, so we want in the list, this to be the date, and we build again that, and we have access to the date. Really, we can do whatever we want.

[00:19:03.200] Rendering content

Now, we want the content. So far, we get the property, but what about the content, so h1, and now we put "Org content", and this is going to be something in the variable content, and we have to add that variable, so in the let binding, we write our content, we are going to have that content from the page-tree. To do that, we use org-export, so we need to export something into HTML, we export the data with the backend. So the data that we want is page-tree, but we don't want the first headline, so we use org-element-contents, and we pass it page-tree, so this is that. But for the exported, we need to pass it, and also, how do we call that, we call that an Org backend. So one-ox is our backend provided by one.el, and the last argument is nil. We are almost done. Now with one.org, we build the website, and we see that we have an error, it's because this is not a content, so there.. No, okay, there was this org-element-contents, I think, and now we build it, and we must see it here.

[00:20:37.160] Rendering CSS

So we have the content, we have the Org values, and last thing that we can do maybe is to put some CSS. Let's have a look to one-default function. We can see in one.el file that we have a lot of default functions that we can use to take inspiration. The last thing that we need is to link to the one.css file, so we are going to do that onerc file. This is here, so html we don't need, we have that one, we want the head to be here, and we pass it a class, which is a title, a div with the class content. We have that. Now with one.org, we build it again, and we should see the website render with the CSS, the property, and all the content, and we've done that just with that Emacs Lisp file, so this is all I wanted to show you today with one.el, I hope you enjoyed the talk, and have a nice day, and a nice conference.

Captioner: sachac

Q&A transcript (unedited)

the other screen, I don't see the chat, so maybe someone can tell me. and we are live. So hi again everyone. Hi Tony, how are you doing? as fantastically as I can be doing, having to put out fire in the background Let me just try to set up everything so that I can show the questions and all this. Do you mind if I read you the question? It might be a little more interactive and this way you can focus on either presenting stuff on your end. to do and I will do that. I'll invite people to go to the pad and ask questions because it was a very interesting talk and I'm sure you have plenty of questions but I only see 1 right now. Do we have people on BigBlueButton? Yes we do have people joining right now. So reading the first question then. So what's the main motivation for this new package? I used to use org.yugo and use GitHub Actions to build a blog. So can you go in a little bit of details on this? I didn't want to have, to, I will push that here. So my goal was to not have to rely on another static site generator to produce my website. So if you use a Yugo, that means that you take, so this is the website that we've seen in the talk, this 1. And I didn't want to have to use a piece of software in Emacs that translate to some other files to be feed to another statistic generator because this way I have 2 things to understand. I have to understand how that software translates my files into the other files and then I have to understand how Hugo works. So if I want to change something I need to understand Hugo. So at some point I need to work with Hugo. So if I need to work with Hugo, maybe I can work with it directly. And I wanted also something that was purely Emacs-centric and working on it, I found out about that solution. And I wanted also something that we have only 1 file that have all the entries. And when I thought about that, finally I found a way that maybe we can just use 1 or 3 to pass it the information of the website. And if you look, If you just try to work with Gatsby, Ugo or all those websites, when you start, you download 10, 20, 30, thousand for hundreds of dependencies to do. Just to me, I'm a small guy and I just want to have some documentation on the website like this 1. It just, it shouldn't need that much of a dependency. And if you look at the website, if you want to hack on something, you need a lot of to understand how the config files work. So you need to, how does it work this config file? But I want, it's always happened that you want to add 1 thing or to add that things. What do you have to do? You have to, you can't because it's not offered by the configuration file. With that solution that I built for me first, I don't care if I need something else. I just have to go in that file. It doesn't need to be that file because as I am in Emacs if the render functions are already evaluated they exist and I can use it but I just have to change that file so if I want something more I just I go there let's say so does it answer the question or I continue to show something? question. I think you veered off a little bit from just why not you go but then you kind of redid part of your presentation to justify used to want to show more things than what there is in the question. Just for people who do not know, we tend to restrict speakers when they submit a presentation. We tell them, oh, you can do a flash talk in 10 minutes or a bit of a longer talk in 20 minutes or 40 minutes. And usually, because we have a lot of speakers, we have to kind of coerce people into going to shorter formats and sometimes it's a lot about killing your darlings. But just to reassure you, we're just about to go on a launch break in about 10 minutes, so you've got the full 10 minutes to use however you want, but I'll just tell you, you have a lot of questions so you might want to perhaps move on to the next 1 as soon as you can. want to stay more I can also stay more. Right. I understand if people need to go to lunch, they can, but people that want to stay, if it's possible, I'm here to answer any question. question. Is it possible to include the include org tag to add content from other files. Do you see what I'm talking about? So the idea was really to have only 1 file and have no options. So if you look at the, let's go into, so the answer is no, but if you want, you can write the code that do it. But let's just go into one.n, so that files. So this is the files where you have everything, and there is only 2 dependencies. Maybe we can see that at the top so which are htmlis on the Jack and the other are Augment. So for me, they're not dependencies because they come with Emacs. But the question is, can I add other things? If you look at that, you don't see the orange color which are viable, it's because I didn't want any configuration nor option. So there is no, if you think about, you are used to use org export normally and to use all the options that are possible on all the things they are not included. You can add them because when you are in a render function. So this is the render function that I showed in the theme. You have a page tree so you have the information but in the global I think, yes in global, you can pass anything you want and if you want you can pass the parse tree of the whole file. So if you pass the parse tree of the whole file, what you can do is that you can get it there. So I don't have it right now, but you might have your include stuff and you get it with a node property that target something in the global variable. So if we look just to be short but those 3 parts, the first 1 is page tree. So it's this page that you are on the right, pages are a list of all the pages and global is something that you can set and reset once and you have the whole part street. So anything that you add in your op-files could go in global if you want, but it's not included. question. Can this generate a single file from different sources like blog.org, videos.org? I think you've just answered So moving on to the other question. Do you have pre-made templates already along with the 1.el package? 1.n, so this file, so the first are blah, blah, blah. How it works, so, okay, so you have the 1-hocs, which is what can translate the org parse tree into HTML. So this is for the content of each page. So this is very useful. Then we have a bunch of functions that help to render the function, each page. And you have a bunch of... Everything that starts with dash default is a render function. So there's no template, but each page that if you want, so that 1, the home, you can use 1 default home. So, if you want to list the page, you have that 1. For a page with no table of content, you use that thing. And if you go back to be short, if we go there, I put this like that. So this that we see here is the first inline of 1.org. By the way, it doesn't have to be called 1.org. It's just as you want, but maybe we can call it. So default, what was the other 1? Default with sidebar. Or is it default with sidebar or default? Yes, with sidebar. Sidebar, if it's worked correctly. Okay, so, okay, so I don't know why the CSS is not working correctly. problems occurring at some point. Okay. Or we stuck. So we are going to use this 1, we've talked this 1, but maybe better in this 1 that add something. So we build it again and now, oh, come on. We have it and we have the, sorry, if we have just default, we rebuild and now this is the default layer that if we do with table of content, you have it, you have the default content. So how to change, and they are not template. They are render functions that takes your page as a tree and render HTML string. So you can build any function that you want. So yes, I think that answers the question. There is no template like in other systems. We have 2 more questions and then we'll need to go on a lunch break. I don't see anyone join the room. Remember, Tony has said that he would be willing to answer more questions during the lunch break, perhaps because it's not lunch break for you. Are you in Europe right now? So that's why for us, also for me it's very dark, but it's not lunch break for us, it's going to be dinner break soon actually. I'm okay. questions. What additional features are there that you would like to add to 1.EL in the future? search done in a simple way. So I don't meet what simple way means, but when I see something complicated, it doesn't enter in 1 to me. So, but really, if you see that, I would like to have some way. So, this is the documentation and I would like to have some way to just have another function because we are not talking about those websites on the 1.L. It's not made for a big company or of your things, it's just for a random guy that have a blog or a few blogs and If you are a great blogger, maybe you are going to write 100 or 200 or 300 pages in many years. So this enter in that category. So it's small. So I think it can, we could find a way to make a full text search. And that is simple. I don't need to, to go with, with solution like Algolia that is, that works super fine. But this is something that I don't control and I have to give them the data and I'm not against that but it's just that I think with a bit of work something can be done with full textile. But this is the only thing that I would like to add. Can you create navbars on a website and fancy things like carousels using 1.EL? Now carousels is just, I think, a fancy way to display pictures and please correct me whoever asked this question. Otherwise I see you taking notes for the answers, thank you very much. But if you could specify maybe carousels so that Tony and I may get a better idea. But still, first part of the question, can you create navbars on a website? you see there, to me, it's not a, it's a navbar. So you already have it. I didn't show that in the talk, but the CSS for the default function that works is responsive. So, out of the box, if you are using something, you will have an app bar done for you with all the pages that you have. So, if we go to install, we have that. And if we no longer have that, we have that sidebar there. And how it's done. So, the same way. I like simple fields that are flexible and I didn't want configuration because if you want to write the code to change something you just have to write code. So any function, render function, is yours. So you can do whatever you want and you enter the html that you want to render. So let's see how do we get that navigation bar that we have when we do that this is a CSS stuff. But when we click, this is a JS stuff that, so let's go to one.l And maybe this is a sidebar. Why that function because, okay. So when that function, so 1 default sidebar is 1 that is used to do some of the things at some point, what we return is a JackHTML that take a data structure and return a string. So this is your HTML. So you can see at the top you have the end, then you have the body, and if we go at the end we can add a script thing. So what we've seen with the sidebar it's just that much line of JavaScript. So this is the only JavaScript that there is to get what we have here when we do that. So you can add whatever you want. It's code and you're the master of that code. stuff that we mentioned before, it's pictures rolling or sliding from 1 to the other. It's kind of like having a gallery, imagine a fancy dynamic gallery where you can scroll pictures. Do you see what I'm talking about? some javascript added somewhere and I can show you another website. So for instance if we go because there are not all the data of the website are not all public, but the website they are. So for instance, a mini-buffer, it's not a carousel, but at the home page, we can do whatever we want. Still those pages, still, this is only 1 file for each page. So if we click, we can get those things. It's just that when we, for the home page for instance, when we go back on that home page, we have the list at that point. So let's go back to that function that we're, so not that 1, maybe the 1, 1 different, it's better because that 1 is simpler. So almost nothing happened. We have the list of the pages. So I can do whatever I want with that list. I can loop over and we can see that 1, that default home list of pages, so that list of the pages, and we see where is the list. Okay, so this is a, here we have a function that just, we want the pages, but I think we, but the home page, and we have that list, and then here we do that. And we get something listed, But then as you control everything that you do, you can pass any CSS class that you want to do those things. So, for instance, that div, add the class either. Yes, you can do. I don't remember the question, but I think I was answering the right 1. It was about carousels and about having fancy display for image galleries. And I think you've answered. Basically, you just put your JavaScript, you embed it inside the code. automated way to convert an existing HTML document into a JackHTML form? It's another topic, but maybe there are some kind of session because some people that know, that are used to Lisp, common Lisp or Clojure or other, Jack-html, that function, is something classic, but I didn't find, So I wrote it because I didn't find it already done the way I want for Emacs. And this is something for E-cup closure. So really I take, it's not that I take my impression, just that when you have something that exists and you look at how it's done. So you have a eCup for Crusher, does the same thing that HTML. It's more that I do a Jack HTML do what eCup does, but maybe they do it a better way. So I think maybe in that community, it might already exist something that go from HTML to Jack. So you can see, is it big enough? I will make it big enough. you see those things. There are things that I couldn't do, for instance, for the ID, I couldn't use the hash in the name of, of how do we name that, of the keywords, because it's used for something else in a Emacs Lisp. So, I use... Anyway, so you see that you have that things but in Emacs we don't have the map with that syntax. We have a hash map but they are not with that syntax and I wanted that syntax so we use only list and Here we have an array with a hash map. So let me just say, so the question was, does it exist something? I think not, but it could be built or maybe exist for E-Cups, you are interested. question perfectly. And our final question, does this or you use any other Emacs packages for your packages slash website, example, or publish? Like, rephrasing the question, do you use it for your own personal usage or do you interact with other packages? Can you please repeat the question? will leave you interpret it however you want. Thank you. Does this or you use any other Emacs packages for your package slash website like org-publish? I just accept dependency of 1.n. So, we are in 1.n and we go at the top and we see that those are the dependencies. I use nothing. So what I do is that I publish, I just generate the public directory. So if we go to public, this 1, no, I don't want this 1. I want to go to the website of the video. If we see here, everything is rendered in the public. Any services, if you use your own server and you save those files, you have your website. So I don't use anything else. I just git push and I'm using Netlify as a service to run to save my files, but you can use anything you want. Because your website is really what is into a public. So, this is another, It's not the concern of 1.L to answer. I'm not using org.publish. I think the question was also about other things, but I think If the person wants a more clear answer to their question, feel free to clarify the question and Tony might be able to answer it later on. Alright Tony, I think that's all the questions we had. Thank you so much for taking the time not only to present Adimax Kant, but also for answering all the questions people had. organizing and thank you for all those questions and you can send me any emails if you have a question and open the issues if it's not working the way it should work for you. Please send me those things. Thank you, everybody. so right now we're gonna go on a lunch break. We'll be back in about 40 minutes for the talk called Emacs Turbocharges My Writing. And I will not tell you more. You can look at the talk page to see a little bit of a synopsis but otherwise keep the surprise. So have a good lunch or have a good dinner if you are in dinner-friendly times and I will see you afterwards. Thank you again, Tony. All right, got it. OK, so thank you so much, Tony. I just had to clear everything up on the stream. I'm going to need to... Sorry. I'm going to stop.

Questions or comments? Please e-mail tony@tonyaldon.com