HesterG dab0be78b2 docusaurus blog (#266)
# Notes

- date must not have double quotes, e.g., should be like `date: 2022-10-30T18:25:00+09:15`

- Updated header.

  ![Screen Shot 2023-07-11 at 17.27.49](/attachments/d14b0c62-3630-47a9-bcda-25c6459d4e8b)

- Used customized blog plugin to get all tags with reference to [plugins/blog/index.js]( Filter by tag section, tag list page.

  ![Screen Shot 2023-07-11 at 18.10.28](/attachments/59561940-a2f7-4ab5-b99a-1d5b84fdbcad)

  ![Screen Shot 2023-07-11 at 17.31.20](/attachments/d6d82976-12c0-4c0f-acfe-218e30c7c627)

- Added coverImage to frontmatter to assgin cover image, e.g., `coverImage: "/img/blog-covers/test.jpeg"`
  And on blog page, cover image will appear on top if `coverImage` added to frontmatter, e.g.,

   ![Screen Shot 2023-07-11 at 17.33.09](/attachments/c27e7971-11db-4241-bc53-9e43df235fbc)

  ![Screen Shot 2023-07-11 at 17.33.20](/attachments/552dd9cf-fc55-4547-8014-864aeb0e5050)

- authors are managed by the `authors.yml` file. [reference](
- [ejected]( `@docusaurus/theme-classic BlogListPage` and `@docusaurus/theme-classic BlogPostPage`, which are marked as `unsafe` by docusaurus, so need to maintain these components. ([original BlogListPage](, [original BlogPostPage](
- [referenced repo](

# How to test

npm i
npm run start

# Build and serve

npm run build
npm run serve

# Help Needed

Deploy and preview steps

# More Screenshots

![Screen Shot 2023-07-11 at 17.34.54](/attachments/ee9d46ac-72ac-49de-90df-38e2afc6db02)


![Screen Shot 2023-07-11 at 18.16.54](/attachments/8f1471a3-27cc-459a-a2ce-c0e5bdf604d3)

![Screen Shot 2023-07-10 at 17.53.18](/attachments/992d9f24-e130-41a9-8b55-86744539524c)

![Screen Shot 2023-07-06 at 11.02.21](/attachments/af1632b8-6a61-47f7-b15d-4a6080bebadb)

![Screen Shot 2023-07-11 at 18.17.43](/attachments/af0df617-27a4-46f9-a8a3-037be268e1cd)

![Screen Shot 2023-07-03 at 15.32.45](/attachments/ad2c1217-e82d-434c-81c8-5d4058e18591)


- [x] Add Banner to blog page

Reviewed-on: gitea/blog#266
Co-authored-by: HesterG <>
Co-committed-by: HesterG <>
2023-07-12 03:25:19 +00:00

5.4 KiB

date authors title tags draft
2022-04-28T20:00:00+00:00 HarvsG How to render Jupyter Notebooks on Gitea

This guide will show you how to configure an external renderer to display Jupyter Notebooks. However this guide will also work for other types of files and even binary files! The sky is the limit.

How Gitea displays .ipynb files natively

Lets create a new repository on our Gitea instance and push an example notebook to it:

20200721 before ext renderer

So as we can see Gitea just renders the raw content of the file - efficient but hard to read.

How to generate HTML to display

In order to display something more attractive to the user we need some HTML, luckily Jupyter has a module called nbconvert:

Install our converter software of choice on the Gitea machine:

sudo apt install python3-pip
pip3 install nbconvert

If we wanted we could test this by running a test command:

jupyter nbconvert --to html --template full path/to/some/test/notebook.ipynb

If we open the resulting .html file in a browser we get something that looks like: image

This looks promising....

Configuring Gitea to use the converter

Like most options, we can configure the Gitea instance using only app.ini.

add this it custom/conf/app.ini:

; Gitea looks for markup.xxxxx and will apply both "markup" and "xxxxx" as a class to the parent <div>
ENABLED = true
; all the file extensions we want to convert, comma separated.
; Lets use out nbconvert command from earlier - making to sure to convert to HTML and to output to stdout
RENDER_COMMAND = "jupyter nbconvert --stdout --to html --template full "
; nbconvert accepts a path to a file and not stdin

; the name after sanitizer doesn't really matter
; Jupyter chiefly uses divs
; we will need access to html classes later
ALLOW_ATTR = class
; we don't mind which classes we keep, so let's keep all of them

Now lets see what we get when we restart Gitea.

20200721 after install of nb convert

As you can see this is better, but not the same as when we opened our HTML file earlier. This is becasue Gitea removes the inline style sheets for security needs. What styling there is is inherited from the markup class in the <div> that wraps the code.

Getting back our styling

If we look at the contents of the HTML file we created earlier we can see several inline stylesheets

<style type="text/css">
* Twitter Bootstrap
 * Bootstrap v3.3.7 (
 * Copyright 2011-2016 Twitter, Inc.
 * Licensed under MIT (
/*! normalize.css v3.0.3 | MIT License | */
html {
  font-family: sans-serif;
  -ms-text-size-adjust: 100%;
  -webkit-text-size-adjust: 100%;
body {
  margin: 0;

So lets strip out all of style sheets and combine them into one jupyter.less making sure to delete all the HTML tags like <style></style>. We now have a stylesheet that contains all our beautiful styling. However it has lots of generic selectors like:

body {
  margin: 0;

This is certainly going to conflict with the default styling of Gitea. So we need to make sure that we limit the scope of the styling. Luckily, Gitea and less can help us with that.

In less

.someclass {
  body {
    margin: 0;
  summary {
    display: block;

Is equivalent to the css:

.someclass body {
	margin: 0;

.someclass summary {
	display: block;

And Gitea has given the classes markup and jupyter from app.ini to the <div> that surrounds the code. image

So lets wrap the whole contents of jupyter.less in .markup.jupyter {} to generate something that looks like this.

Now lets put that file in our 'custom' directory , in my case that is /root/custom/public/css/jupyter.less.

I then added to /root/custom/templates/header.tmpl

<!-- lets import the less stylesheet {{AppSubUrl}} is a variable that gitea will autofill -->
<link rel="stylesheet/less" type="text/css" href="{{AppSubUrl}}/css/jupyter.less" />
<!-- we need the javascript to compile the less into css -->
<script src="//" ></script>

And now lets restart gitea and see what we get.

Final appearance with the .markup.jupyer custom styling 20200721 after css

Looks good but we are getting some problems with text over-flowing the edge of the border. This is caused by some pesky CSS at-rules, so lets delete the below from the jupyter.less

@media (min-width: 768px) {
  .container {
    width: 768px;
@media (min-width: 992px) {
  .container {
    width: 940px;
@media (min-width: 1200px) {
  .container {
    width: 1140px;


Ta-da! Perfectly styled Jupyter Notebooks.