Found a solution: index.html needs to be changed to the following rearrangement of the above code:
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.15.1/dist/katex.min.css" integrity="sha384-R4558gYOUz8mP9YWpZJjofhk+zx0AS11p36HnD2ZKj/6JR5z27gSSULCNHIRReVs" crossorigin="anonymous">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.15.1/dist/katex.min.js" integrity="sha384-z1fJDqw8ZApjGO3/unPWUPsIymfsJmyrDVWC8Tv/a1HeOtGmkwNd/7xUS0Xcnvsx" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.15.1/dist/contrib/auto-render.min.js" integrity="sha384-+XBljXPPiv+OzfbB3cVmLHf4hdUFHlWNZN5spNQ7rmHTXpd7WvJum6fIACpNNfIR" crossorigin="anonymous"></script>
<link rel="stylesheet" type="text/css" href="3rdparty/markdown.css">
<script src="3rdparty/marked.js"></script>
<script src="qrc:/qtwebchannel/qwebchannel.js"></script>
</head>
<body>
<div id="placeholder"></div>
<script>
'use strict';
document.addEventListener("DOMContentLoaded", function() {
var placeholder = document.getElementById('placeholder');
var renderMath = function() {
renderMathInElement(document.body, {
// customised options
// • auto-render specific keys, e.g.:
delimiters: [
{left: '$$', right: '$$', display: true},
{left: '$', right: '$', display: false},
{left: '\\(', right: '\\)', display: false},
{left: '\\[', right: '\\]', display: true}
],
// • rendering keys, e.g.:
throwOnError : false
});
}
var updateText = function(text) {
placeholder.innerHTML = marked(text);
renderMath();
}
new QWebChannel(qt.webChannelTransport,
function(channel) {
var content = channel.objects.content;
updateText(content.text);
content.textChanged.connect(updateText);
}
);
});
</script>
</body>
</html>
That is we need to call KaTeX after marked() and do so in the same update function. I had to put everything into a document-on-load since for some reason that was needed, but now I'm thinking maybe not. It depends on how KaTeX behaves.