Jan Černohorský
2 years ago
8 changed files with 160 additions and 2 deletions
@ -0,0 +1 @@ |
|||||
|
node_modules |
@ -0,0 +1 @@ |
|||||
|
This was made by Standa Lukeš @exyi |
@ -0,0 +1 @@ |
|||||
|
console.log(require('katex').renderToString('\\frac{2a}{b}')) |
@ -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<void>} |
||||
|
* */ |
||||
|
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') |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
@ -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" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -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" |
||||
|
} |
||||
|
} |
@ -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]) |
Loading…
Reference in new issue