如何在 Gitea 上渲染 Jupyter Notebook
本指南将展示如何配置外部渲染器来显示 Jupyter Notebooks。不过,本指南也适用于其他类型的文件,甚至二进制文件!可能性是无限的。
Gitea 如何原生显示 .ipynb 文件
我们在 Gitea 实例上创建一个新仓库,并将一个示例笔记本推送进去:

如我们所见,Gitea 只是渲染了文件的原始内容——效率很高,但可读性很差。
如何生成用于显示的 HTML
为了向用户展示更吸引人的内容,我们需要一些 HTML。幸运的是,Jupyter 有一个名为 nbconvert 的模块:
在 Gitea 所在的机器上安装我们选择的转换软件:
sudo apt install python3-pip
pip3 install nbconvert
我们可以运行一条测试命令来验证:
jupyter nbconvert --to html --template full path/to/some/test/notebook.ipynb
如果在浏览器中打开生成的 .html 文件,我们会看到类似这样的效果:

看起来很有希望……
配置 Gitea 使用该转换器
与大多数选项一样,我们只需通过 app.ini 来配置 Gitea 实例。
将以下内容添加到 custom/conf/app.ini:
; Gitea looks for markup.xxxxx and will apply both "markup" and "xxxxx" as a class to the parent <div>
[markup.jupyter]
ENABLED = true
; all the file extensions we want to convert, comma separated.
FILE_EXTENSIONS = .ipynb
; 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
IS_INPUT_FILE = true
; the name after sanitizer doesn't really matter
[markup.sanitizer.jupyter0]
; Jupyter chiefly uses divs
ELEMENT = div
; 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
REGEXP =
现在重启 Gitea,看看效果。

可以看到效果有所改善,但与之前直接打开 HTML 文件时并不完全一样。这是因为出于安全考虑,Gitea 会移除内联样式表。现有的样式继承自包裹代码的 <div> 中的 markup 类。
找回我们的样式
如果查看之前生成的 HTML 文件内容,可以看到多个内联样式表:
<style type="text/css">
/*!
*
* Twitter Bootstrap
*
*/
/*!
* Bootstrap v3.3.7 (http://getbootstrap.com)
* Copyright 2011-2016 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */
html {
font-family: sans-serif;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
body {
margin: 0;
}
[...]
</stlye>
现在,我们把所有样式表剥离出来,合并成一个 jupyter.less,并确保删除所有类似 <style></style> 的 HTML 标签。这样我们就得到了一个包含所有精美样式的样式表。然而,其中包含大量通用选择器,例如:
body {
margin: 0;
}
这肯定会与 Gitea 的默认样式发生冲突。因此,我们需要确保限制样式的生效范围。幸运的是,Gitea 和 Less 可以帮我们实现这一点。
在 Less 中:
.someclass {
body {
margin: 0;
}
summary {
display: block;
}
}
等价于以下 CSS:
.someclass body {
margin: 0;
}
.someclass summary {
display: block;
}
而 Gitea 已经将 app.ini 中的 markup 和 jupyter 类应用到了包裹代码的 <div> 上。

因此,我们将 jupyter.less 的全部内容用 .markup.jupyter {} 包裹起来,生成类似这样的效果。
现在,将那个文件放到我们的 custom 目录下,在我的示例中,该路径为 /root/custom/public/css/jupyter.less。
接着,在 /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="//cdn.jsdelivr.net/npm/less" ></script>
然后重启 Gitea,看看最终效果。
使用 .markup.jupyter 自定义样式后的最终外观

看起来不错,但我们遇到了文本溢出边框的问题。这源于一些讨厌的 CSS at-rule,于是我们从 jupyter.less 中删除了以下内容:
@media (min-width: 768px) {
.container {
width: 768px;
}
}
@media (min-width: 992px) {
.container {
width: 940px;
}
}
@media (min-width: 1200px) {
.container {
width: 1140px;
}
}

嗒哒!完美渲染的 Jupyter Notebooks。
