tlienart / Franklin.jl

(yet another) static site generator. Simple, customisable, fast, maths with KaTeX, code evaluation, optional pre-rendering, in Julia.
https://franklinjl.org
MIT License
932 stars 108 forks source link

[Feature request] Code block annotations #1048

Closed adrhill closed 9 months ago

adrhill commented 9 months ago

The Modern Julia Workflows blog switches between shell, code and REPL code blocks quite frequently. It would be nice to be able to annotate code blocks to avoid confusing readers.

Example of this feature in a blog post: https://fasterthanli.me/series/advent-of-code-2022/part-1

Mock-up illustrating the problem:

Screenshot 2023-08-16 at 00 20 26 Screenshot 2023-08-16 at 00 20 41

CC @gdalle

tlienart commented 9 months ago

I've not looked into it yet but you should be able to do precisely the same kind of stuff as in the blog post; shell repl and code blocks have separate classes and so a different style can be applied to those.

Have you tried copying whatever it is that they're doing for the styling of their code blocks on the rust blog post and see if it works for you?

adrhill commented 9 months ago

I'm not well versed in web technologies, but I got something that works halfway using .hljs::before:

.hljs::before {
  content: attr(class);
  position: absolute;
  top: 0;
  right: 0;
  padding: 0.2rem 0.5rem;
  background-color: #1d2735;
  border-bottom-left-radius:10px;
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
}
image

Since the class attribute doesn't exactly correspond to the language name, LANGUAGE-JULIA HLJS is printed.

Reading the highlightjs docs, it looks like access to the data-language attribute is easy to obtain: https://highlightjs.readthedocs.io/en/latest/plugin-recipes.html#data-language

How can I make use of this in Franklin?

adrhill commented 9 months ago

I've also noticed a bug where using Franklin's live REPL evaluation, docstring headers are identified as Ruby code:

image
tlienart commented 9 months ago

ok so I was under the impression that there was one additional class, not just the language-julia-repl for REPL, Shell and Help mode; will change that.

tlienart commented 9 months ago

would you mind testing #1050 which should hopefully help you for your use case?

adrhill commented 9 months ago

In the following example, #1050 turns the class from language-julia-repl hljs to language-julia-repl julia-repl-help hljs.

image

The code block inside the doc string is still wrongly shown as hljs language-ruby.

How can I include https://highlightjs.readthedocs.io/en/latest/plugin-recipes.html#data-language in Franklin? This should allow me to turn e.g. language-bash hljs to Bash and language-julia hljs to Julia.

tlienart commented 9 months ago

(sorry for not answering your points properly in the first shot)

so the custom classes should allow you to add specific styling only to the dedicated class, let me play with it a bit so I can see whether I can modify what you did to get something that might be more satisfactory

for the ruby stuff what happens is that the help path in Franklin takes the help result from Julia and just "places" it there, and that stuff has code blocks which are not labelled (i.e. naked triple backquotes as opposed to ones with julia). This is then seen by highlight.js which, when it doesn't get an indication of what the language is, tries to infer what it is. And here it gets to ruby.

next steps are:

(1) let me play with the CSS stuff for the annotation thing, I'll get back to you here if I can make something nicer work (2) see if we can make code blocks in the help mode be annotated so that highlight knows it's julia, I'll try to extend #1050 to that effect. There may be corner cases where examples in docstrings are actually not Julia code but I imagine it's going to be seldom so for now it's probably fine.

tlienart commented 9 months ago

ok (2) is done, I'll try to look into (1) soon but basically what you should try is to use a more specific selector instead of

.hljs::before { 
content: attr(class);
...
}

something like

.hljs::before {
... /* everything but content */
}
.julia-repl .hljs::before {
content: "Julia repl";
}
.julia-repl-shell .hljs::before {
content: "Julia repl / shell mode";
}
.julia-repl-help .hljs::before {
content: "Julia repl / help mode";
}
.julia-repl-pkg .hljs::before {
content: "Julia repl / pkg mode";
}
adrhill commented 9 months ago

Thanks a lot!

adrhill commented 9 months ago

The lastest commit of #1050 works really well already!

However, sing something like

.julia-repl::before {
  content: "Julia REPL";
}

seems to currently remove the whitespace after the help?> and julia> prompts in code. I'm not sure where this is coming from.

image
tlienart commented 9 months ago

hmm this is odd, Franklin definitely adds a space. Could you:

  1. in the browser dev console, highlight one of those code blocks and copy paste the entire corresponding HTML here so we can check that there's a space (there should be) and what classes have been added (by franklin and by highlightjs)
  2. paste here the entirety of the CSS that has pre code hljs or .julia-repl associated with it so I can try to reproduce the issue and play with it
  3. tell me how exactly you're loading highlight.js whether it's a bundled script or whether it's your own version tweaked to find these julia>, shell> and pkg> and what code you entered to do that

alternatively, if you could post here a standalone HTML with a single code cell, the styling, and the javascript, so I can reproduce what you see locally and try to see what CSS rules are clashing.

Edit: wait am playing with ah/... branch now will see how far I get

tlienart commented 9 months ago

ok, there's some stuff I don't understand (why that space got swallowed), I suspect it's due to the julia> being selected into a single span without the space after it, then the code being inserted afterwards and the code getting lost in it. I think it's the javascript that removes this but I did not dig into it.

In any case the following will fix the problem:

.hljs .prompt_::after {
content: " ";
}

the prompt_ class is used for the julia> ,shell>, help?> etc elements, so here we're just forcing a space to be added after it.

Note: I didn't check but let's say you only had the problem with julia-repl then you'd just do .julia-repl .prompt_::after{ ...} etc.

hope this works! I'll merge the modifications in #1050 which seem to do what you were hoping for.

edit the pr closed the issue but feel free to re open if you feel we missed something

adrhill commented 9 months ago

Thanks a lot! :)

gdalle commented 8 months ago

@adrhill can you transfer this to our new Xranklin version?

adrhill commented 8 months ago

Sure, let me try. 👍

adrhill commented 7 months ago

For reference, this is used in https://github.com/modernjuliaworkflows/modernjuliaworkflows.github.io/pull/67.


Some insights since then:

The weird julia>Julia prompt in https://github.com/tlienart/Franklin.jl/issues/1048#issuecomment-1684142414 stems from the fact that language-julia-repl contains other language-julia blocks. These trigger ::before from the language tags, printing "Julia" again:

.language-julia::before {content: "Julia";}
image

I fixed this adding

/* Julia code after REPL prompts doesn't need a language tag */
.language-julia-repl .language-julia::before {content: "";}
.julia-repl          .language-julia::before {content: "";}
.julia-repl-pkg      .language-julia::before {content: "";}
.julia-repl-help     .language-julia::before {content: "";}
.julia-repl-shell    .language-julia::before {content: "";}

All in all, the approach with ::before works, but I'm not sure its the best solution. The language tags end up inside the HLJS <code> element, making them scroll with the code. It would also be nice to combine them with a "copy code" button on hover.

Some discussion on this topic can be found in https://github.com/highlightjs/highlight.js/issues/1108#issuecomment-608415953, but I'm not familiar enough with JS and its DOM to really understand it.

I also noticed that Xranklin doesn't have the distinction between julia-repl and other REPL modes like julia-repl-pkg, julia-repl-help and julia-repl-shell that was introduced to Franklin in #1050.