From f71eea3c069fc77a9784756f02c295d1576bcfa8 Mon Sep 17 00:00:00 2001 From: Greenscreener Date: Thu, 2 Feb 2023 18:48:48 +0100 Subject: [PATCH] Started working on KaTeX integration. --- formatitko.py | 7 ++- katex-server/.gitignore | 1 + katex-server/README.md | 1 + katex-server/index.js | 1 + katex-server/index.mjs | 105 +++++++++++++++++++++++++++++++++ katex-server/package-lock.json | 21 +++++++ katex-server/package.json | 14 +++++ katex.py | 12 ++++ 8 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 katex-server/.gitignore create mode 100644 katex-server/README.md create mode 100644 katex-server/index.js create mode 100644 katex-server/index.mjs create mode 100644 katex-server/package-lock.json create mode 100644 katex-server/package.json create mode 100644 katex.py diff --git a/formatitko.py b/formatitko.py index 62c4f33..4293929 100755 --- a/formatitko.py +++ b/formatitko.py @@ -10,6 +10,7 @@ from transform import transform from util import * from context import Context from html import html +from katex import KatexClient from mj_show import show @@ -22,5 +23,7 @@ doc = doc.walk(transform, context) print("---------------------") #print(show(doc)) #print(convert_text(doc, input_format="panflute", output_format="markdown")) -open("output.html", "w").write(" " + html(doc)) - +#open("output.html", "w").write(" " + html(doc)) +k = KatexClient() +input() +print(k) diff --git a/katex-server/.gitignore b/katex-server/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/katex-server/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/katex-server/README.md b/katex-server/README.md new file mode 100644 index 0000000..082cd55 --- /dev/null +++ b/katex-server/README.md @@ -0,0 +1 @@ +This was made by Standa Lukeš @exyi diff --git a/katex-server/index.js b/katex-server/index.js new file mode 100644 index 0000000..d6754c8 --- /dev/null +++ b/katex-server/index.js @@ -0,0 +1 @@ +console.log(require('katex').renderToString('\\frac{2a}{b}')) diff --git a/katex-server/index.mjs b/katex-server/index.mjs new file mode 100644 index 0000000..9987351 --- /dev/null +++ b/katex-server/index.mjs @@ -0,0 +1,105 @@ +// KaTeX rendering server +// Listens on unix socket, path is provided as first argument +// Expects JSON lines, each line is a query with the following schema: +// { +// formulas: [ +// { +// tex: string, +// options?: object +// } +// ], +// options?: object +// } + +// see https://katex.org/docs/options.html for list of available options +// If options formulas[].options field is used, the global options field is ignored. + +// For each line, returns one JSON line with the following schema: +// { +// results: [ +// { html?: string } | { error?: string } +// ] +// } | { error?: string } + + +// If one formula is invalid, the error in results is used +// If the entire query is invalid (couldn't parse JSON, for example), the outer error field is used + + +import katex from 'katex' +import net from 'net' +import * as readline from 'readline' + +const myArgs = process.argv.slice(2) + +const unixSocketPath = myArgs[0] +if (!unixSocketPath) { + console.error('you must specify socket path') + process.exit(1) +} + +// This server listens on a Unix socket at /var/run/mysocket +var unixServer = net.createServer(handleClient); +unixServer.listen(unixSocketPath); + +function handleExit(signal) { + // unixServer.emit('close') + unixServer.close(function () { + + }); + process.exit(0); // put this into the callback to avoid closing open connections +} +process.on('SIGINT', handleExit); +process.on('SIGQUIT', handleExit); +process.on('SIGTERM', handleExit); +process.on('exit', handleExit); + +const defaultOptions = {} + +/** + * @param {net.Socket} socket + * @returns {Promise} + * */ +function socketWrite(socket, data) { + return new Promise((resolve, reject) => { + socket.write(data, (err) => { + if (err) { + reject(err) + } else { + resolve() + } + }) + }) +} + +/** + * @param {net.Socket} client + * */ +async function handleClient(client) { + const rl = readline.createInterface({ input: client }) + + for await (const line of rl) { + try { + const query = JSON.parse(line) + + const results = [] + for (const input of query.formulas) { + const options = input.options ?? query.options ?? defaultOptions + try { + const html = katex.renderToString(input.tex, options) + + results.push({ html }) + } catch (e) { + results.push({ error: String(e) }) + } + } + await socketWrite(client, JSON.stringify({ results }, null, query.debug ? ' ' : undefined)) + await socketWrite(client, '\n') + } catch (e) { + console.error(e) + await socketWrite(client, JSON.stringify({ error: String(e) })) + await socketWrite(client, '\n') + } + } +} + diff --git a/katex-server/package-lock.json b/katex-server/package-lock.json new file mode 100644 index 0000000..9307bf4 --- /dev/null +++ b/katex-server/package-lock.json @@ -0,0 +1,21 @@ +{ + "name": "ksp-katex-server", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==" + }, + "katex": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.3.tgz", + "integrity": "sha512-3EykQddareoRmbtNiNEDgl3IGjryyrp2eg/25fHDEnlHymIDi33bptkMv6K4EOC2LZCybLW/ZkEo6Le+EM9pmA==", + "requires": { + "commander": "^8.0.0" + } + } + } +} diff --git a/katex-server/package.json b/katex-server/package.json new file mode 100644 index 0000000..3d68121 --- /dev/null +++ b/katex-server/package.json @@ -0,0 +1,14 @@ +{ + "name": "ksp-katex-server", + "version": "1.0.0", + "description": "", + "main": "index.mjs", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "katex": "^0.16.3" + } +} diff --git a/katex.py b/katex.py new file mode 100644 index 0000000..dc54da9 --- /dev/null +++ b/katex.py @@ -0,0 +1,12 @@ +import socket +import subprocess +import tempfile + + +class KatexClient: + def __init__(self): + self._client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self._temp_dir = tempfile.TemporaryDirectory(prefix='formatitko') + socket_file = self._temp_dir.name + "/katex-socket" + self._client.bind(socket_file) + self._server = subprocess.Popen(["node", "./katex-server/index.mjs", socket_file])