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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.