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:
parent
d461d83540
commit
8e4905522d
11 changed files with 100 additions and 2 deletions
1
Gemfile
1
Gemfile
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -6,3 +6,4 @@
|
|||
//= link s3_direct_upload.js
|
||||
// = link administrate/application.css
|
||||
// = link administrate/application.js
|
||||
// = link katex.css
|
||||
|
|
|
|||
1
app/assets/stylesheets/ltags/KatexTag.scss
Normal file
1
app/assets/stylesheets/ltags/KatexTag.scss
Normal file
|
|
@ -0,0 +1 @@
|
|||
@import 'katex';
|
||||
31
app/liquid_tags/katex_tag.rb
Normal file
31
app/liquid_tags/katex_tag.rb
Normal 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)
|
||||
7
app/views/liquids/_katex.html.erb
Normal file
7
app/views/liquids/_katex.html.erb
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<% if should_render_css %>
|
||||
<%= stylesheet_link_tag "katex" %>
|
||||
<% end %>
|
||||
|
||||
<div class="katex-element">
|
||||
<%= parsed_content %>
|
||||
</div>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
lax_html_blocks: true,
|
||||
lax_spacing: true,
|
||||
strikethrough: true,
|
||||
superscript: true,
|
||||
superscript: false,
|
||||
tables: true,
|
||||
footnotes: true
|
||||
}.freeze
|
||||
|
|
|
|||
12
spec/fixtures/approvals/katextag/render/generates_katex_output.approved.html
vendored
Normal file
12
spec/fixtures/approvals/katextag/render/generates_katex_output.approved.html
vendored
Normal 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 c34,79.3,68.167,158.7,102.5,238c34.3,79.3,51.8,119.3,52.5,120 c340,-704.7,510.7,-1060.3,512,-1067 l0 -0 c4.7,-7.3,11,-11,19,-11 H40000v40H1012.3 s-271.3,567,-271.3,567c-38.7,80.7,-84,175,-136,283c-52,108,-89.167,185.3,-111.5,232 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 s-109,-253,-109,-253c-72.7,-168,-109.3,-252,-110,-252c-10.7,8,-22,16.7,-34,26 c-22,17.3,-33.3,26,-34,26s-26,-26,-26,-26s76,-59,76,-59s76,-60,76,-60z 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>
|
||||
36
spec/liquid_tags/katex_tag_spec.rb
Normal file
36
spec/liquid_tags/katex_tag_spec.rb
Normal 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
|
||||
|
|
@ -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&<sup>ation</sup></h1>
|
||||
multiword heading with ( weird ? punctu&^ation
|
||||
</h1>
|
||||
|
||||
<h1>
|
||||
<a name="emoji-heading" href="#emoji-heading" class="anchor" id="emoji-heading">
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue