Implement KatexTag (#6237) [deploy]

* Install Katex gem

* Add Katex stylesheet and spec

* Implement basic error handling

* Remove comment

* Create more spec

* Update approval file

Changes is due to turning off RedCarpet's superscript feature

* Add instruction to _editor_guide_text

* Serve our own KaTeX css

* Apply Rhyme's suggestion

* Limit Katex's css use

* Update spec

* Add spec

Co-authored-by: Ben Halpern <bendhalpern@gmail.com>
This commit is contained in:
Mac Siri 2020-03-09 17:58:54 -04:00 committed by GitHub
parent d461d83540
commit 8e4905522d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 100 additions and 2 deletions

View file

@ -58,6 +58,7 @@ gem "inline_svg", "~> 1.7" # Embed SVG documents in your Rails views and style t
gem "jbuilder", "~> 2.10" # Create JSON structures via a Builder-style DSL
gem "jquery-rails", "~> 4.3" # A gem to automate using jQuery with Rails
gem "kaminari", "~> 1.2" # A Scope & Engine based, clean, powerful, customizable and sophisticated paginator
gem "katex", "~> 0.6.0" # This rubygem enables you to render TeX math to HTML using KaTeX. It uses ExecJS under the hood
gem "libhoney", "~> 1.14" # Ruby gem for sending data to Honeycomb
gem "liquid", "~> 4.0" # A secure, non-evaling end user template engine with aesthetic markup
gem "nokogiri", "~> 1.10" # HTML, XML, SAX, and Reader parser

View file

@ -453,6 +453,8 @@ GEM
activerecord
kaminari-core (= 1.2.0)
kaminari-core (1.2.0)
katex (0.6.0)
execjs (~> 2.7)
launchy (2.5.0)
addressable (~> 2.7)
libhoney (1.14.4)
@ -925,6 +927,7 @@ DEPENDENCIES
jbuilder (~> 2.10)
jquery-rails (~> 4.3)
kaminari (~> 1.2)
katex (~> 0.6.0)
launchy (~> 2.5)
libhoney (~> 1.14)
liquid (~> 4.0)

View file

@ -6,3 +6,4 @@
//= link s3_direct_upload.js
// = link administrate/application.css
// = link administrate/application.js
// = link katex.css

View file

@ -0,0 +1 @@
@import 'katex';

View file

@ -0,0 +1,31 @@
class KatexTag < Liquid::Block
PARTIAL = "liquids/katex".freeze
KATEX_EXISTED = "katex_existed".freeze
def initialize(tag_name, markup, tokens)
super
end
def render(context)
block = Nokogiri::HTML(super).at("body").text
parsed_content = begin
Katex.render(block, display_mode: true)
rescue ExecJS::ProgramError => e
e.message
end
should_render_css = !context[KATEX_EXISTED]
unless context[KATEX_EXISTED]
context[KATEX_EXISTED] = true
end
ActionController::Base.new.render_to_string(
partial: PARTIAL,
locals: { parsed_content: parsed_content, should_render_css: should_render_css },
)
end
end
Liquid::Template.register_tag("katex", KatexTag)

View file

@ -0,0 +1,7 @@
<% if should_render_css %>
<%= stylesheet_link_tag "katex" %>
<% end %>
<div class="katex-element">
<%= parsed_content %>
</div>

View file

@ -201,6 +201,11 @@
<h3><strong>RunKit Embed</strong></h3>
<p>Put executable code within a runkit liquid block, as follows:</p>
<pre>{% runkit<br>// hidden setup JavaScript code goes in this preamble area<br>const hiddenVar = 42<br>%}<br>// visible, reader-editable JavaScript code goes here<br>console.log(hiddenVar)<br>{% endrunkit %}<br></pre>
<h3><strong>KaTeX Embed</strong></h3>
<p>Place your mathematical expression within a KaTeX liquid block, as follows:</p>
<pre>{% katex %}<br> c = \pm\sqrt{a^2 + b^2}<br>{% endkatex %}<br></pre>
<h3><strong>Stackblitz Embed</strong></h3>
<p>All you need is the ID of the Stackblitz:</p>
<code>{% stackblitz ball-demo %}</code>

View file

@ -5,7 +5,7 @@
lax_html_blocks: true,
lax_spacing: true,
strikethrough: true,
superscript: true,
superscript: false,
tables: true,
footnotes: true
}.freeze

View file

@ -0,0 +1,12 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" media="screen" href="/assets/katex-19c8f10fdd19a6277c7841240b25c66d035c60cd6f4118c7f70a74d2689535c8.css" />
</head>
<body>
<div class="katex-element">
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi><mo>=</mo><mo>±</mo><msqrt><mrow><msup><mi>a</mi><mn>2</mn></msup><mo>+</mo><msup><mi>b</mi><mn>2</mn></msup></mrow></msqrt></mrow><annotation encoding="application/x-tex">c = \pm\sqrt{a^2 + b^2}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.43056em;vertical-align:0em;"></span><span class="mord mathdefault">c</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1.2399999999999998em;vertical-align:-0.177736em;"></span><span class="mord">±</span><span class="mord sqrt"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.0622639999999999em;"><span class="svg-align" style="top:-3.2em;"><span class="pstrut" style="height:3.2em;"></span><span class="mord" style="padding-left:1em;"><span class="mord"><span class="mord mathdefault">a</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.740108em;"><span style="top:-2.9890000000000003em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mord"><span class="mord mathdefault">b</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.740108em;"><span style="top:-2.9890000000000003em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span><span style="top:-3.0222640000000003em;"><span class="pstrut" style="height:3.2em;"></span><span class="hide-tail" style="min-width:1.02em;height:1.28em;"><svg width="400em" height="1.28em" viewbox="0 0 400000 1296" preserveaspectratio="xMinYMin slice"><path d="M263,681c0.7,0,18,39.7,52,119&#10;c34,79.3,68.167,158.7,102.5,238c34.3,79.3,51.8,119.3,52.5,120&#10;c340,-704.7,510.7,-1060.3,512,-1067&#10;l0 -0&#10;c4.7,-7.3,11,-11,19,-11&#10;H40000v40H1012.3&#10;s-271.3,567,-271.3,567c-38.7,80.7,-84,175,-136,283c-52,108,-89.167,185.3,-111.5,232&#10;c-22.3,46.7,-33.8,70.3,-34.5,71c-4.7,4.7,-12.3,7,-23,7s-12,-1,-12,-1&#10;s-109,-253,-109,-253c-72.7,-168,-109.3,-252,-110,-252c-10.7,8,-22,16.7,-34,26&#10;c-22,17.3,-33.3,26,-34,26s-26,-26,-26,-26s76,-59,76,-59s76,-60,76,-60z&#10;M1001 80h400000v40h-400000z"></path></svg></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:0.177736em;"><span></span></span></span></span></span></span></span></span></span>
</div>
</body>
</html>

View file

@ -0,0 +1,36 @@
require "rails_helper"
RSpec.describe KatexTag, type: :liquid_tag do
describe "#render" do
def generate_katex_liquid(content)
Liquid::Template.register_tag("katex", described_class)
Liquid::Template.parse("{% katex %}#{content}{% endkatex %}")
end
it "generates Katex output" do
content = "c = \\pm\\sqrt{a^2 + b^2}"
rendered = generate_katex_liquid(content).render
verify(format: :html) { rendered }
end
it "includes the css style tag only once when rendering multiple" do
content = <<~CONTENT
{% katex %}
hello
{% endkatex %}
{% katex %}
Hello
{% endkatex %}
CONTENT
Liquid::Template.register_tag("katex", described_class)
render = Liquid::Template.parse(content).render
expect(Nokogiri::HTML(render).css("link").count).to eq(1)
end
it "generates Katex errors" do
content = "\\c = \\pm\\sqrt{a^2 + b^2}"
rendered = generate_katex_liquid(content).render
expect(rendered).to include("ParseError: KaTeX parse error: ")
end
end
end

View file

@ -45,7 +45,8 @@
<h1>
<a name="multiword-heading-with-weird-punctuampation" href="#multiword-heading-with-weird-punctuampation" class="anchor" id="multiword-heading-with-weird-punctuampation">
</a>
multiword heading with ( weird ? punctu&amp;<sup>ation</sup></h1>
multiword heading with ( weird ? punctu&amp;^ation
</h1>
<h1>
<a name="emoji-heading" href="#emoji-heading" class="anchor" id="emoji-heading">