feat(markdown): wrap titled images in figures with caption
This commit is contained in:
+40
-1
@@ -1,6 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "kramdown"
|
require "kramdown"
|
||||||
|
require "rexml/document"
|
||||||
|
|
||||||
module Volumen
|
module Volumen
|
||||||
# Renders Markdown to HTML using kramdown's GFM parser (tables, fenced code,
|
# Renders Markdown to HTML using kramdown's GFM parser (tables, fenced code,
|
||||||
@@ -8,6 +9,10 @@ module Volumen
|
|||||||
#
|
#
|
||||||
# Posts are authored by the single trusted admin, so kramdown's default
|
# Posts are authored by the single trusted admin, so kramdown's default
|
||||||
# behaviour of passing raw HTML through is intentional.
|
# behaviour of passing raw HTML through is intentional.
|
||||||
|
#
|
||||||
|
# Images with a title (``) are wrapped in a `<figure>`
|
||||||
|
# element with a `<figcaption>` so the frontend can style captions and
|
||||||
|
# overlay an info icon.
|
||||||
module Markdown
|
module Markdown
|
||||||
OPTIONS = {
|
OPTIONS = {
|
||||||
input: "GFM",
|
input: "GFM",
|
||||||
@@ -17,7 +22,41 @@ module Volumen
|
|||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
def self.render(text)
|
def self.render(text)
|
||||||
::Kramdown::Document.new(text.to_s, OPTIONS).to_html
|
html = ::Kramdown::Document.new(text.to_s, OPTIONS).to_html
|
||||||
|
wrap_figures(html)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Wrap every `<img title="...">` inside a `<figure>` with `<figcaption>`.
|
||||||
|
# Images without a title are left untouched.
|
||||||
|
def self.wrap_figures(html)
|
||||||
|
doc = REXML::Document.new("<root>#{html}</root>")
|
||||||
|
|
||||||
|
REXML::XPath.each(doc, "//img[@title]") do |img|
|
||||||
|
caption = img.attributes["title"]
|
||||||
|
img.attributes.delete("title")
|
||||||
|
|
||||||
|
figure = REXML::Element.new("figure")
|
||||||
|
figure.add_element(img.deep_clone)
|
||||||
|
|
||||||
|
info = REXML::Element.new("div")
|
||||||
|
info.add_attribute("class", "fig-info")
|
||||||
|
info.add_attribute("aria-hidden", "true")
|
||||||
|
info.text = "i"
|
||||||
|
figure.add_element(info)
|
||||||
|
|
||||||
|
figcaption = REXML::Element.new("figcaption")
|
||||||
|
figcaption.text = caption
|
||||||
|
figure.add_element(figcaption)
|
||||||
|
|
||||||
|
img.parent.replace_child(img, figure)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Strip the wrapping <root> we added.
|
||||||
|
result = +""
|
||||||
|
doc.root.children.each { |child| result << child.to_s }
|
||||||
|
result
|
||||||
|
rescue REXML::ParseException
|
||||||
|
html
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -58,6 +58,10 @@ module Volumen
|
|||||||
metadata["cover"]
|
metadata["cover"]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def cover_alt
|
||||||
|
metadata["cover_alt"]
|
||||||
|
end
|
||||||
|
|
||||||
def fediverse_creator
|
def fediverse_creator
|
||||||
value = metadata["fediverse_creator"]
|
value = metadata["fediverse_creator"]
|
||||||
return nil if value.nil?
|
return nil if value.nil?
|
||||||
@@ -128,6 +132,7 @@ module Volumen
|
|||||||
"author" => author,
|
"author" => author,
|
||||||
"fediverse_creator" => fediverse_creator,
|
"fediverse_creator" => fediverse_creator,
|
||||||
"cover" => cover,
|
"cover" => cover,
|
||||||
|
"cover_alt" => cover_alt,
|
||||||
"reading_time" => reading_time,
|
"reading_time" => reading_time,
|
||||||
"translations" => translations,
|
"translations" => translations,
|
||||||
"url" => "/api/volumen/posts/#{slug}"
|
"url" => "/api/volumen/posts/#{slug}"
|
||||||
|
|||||||
@@ -376,7 +376,14 @@ module Volumen
|
|||||||
end
|
end
|
||||||
|
|
||||||
def post_from_params(http_params, existing: nil)
|
def post_from_params(http_params, existing: nil)
|
||||||
metadata = {
|
metadata = base_metadata(http_params, existing: existing)
|
||||||
|
post = Post.new(metadata: clean_metadata(metadata), body: http_params["body"].to_s)
|
||||||
|
post.path = existing.path if existing
|
||||||
|
post
|
||||||
|
end
|
||||||
|
|
||||||
|
def base_metadata(http_params, existing:)
|
||||||
|
{
|
||||||
"title" => presence(http_params["title"]),
|
"title" => presence(http_params["title"]),
|
||||||
"slug" => presence(http_params["slug"]) || existing&.slug,
|
"slug" => presence(http_params["slug"]) || existing&.slug,
|
||||||
"lang" => presence(http_params["lang"]),
|
"lang" => presence(http_params["lang"]),
|
||||||
@@ -387,12 +394,10 @@ module Volumen
|
|||||||
"tags" => parse_tags(http_params["tags"]),
|
"tags" => parse_tags(http_params["tags"]),
|
||||||
"excerpt" => presence(http_params["excerpt"]),
|
"excerpt" => presence(http_params["excerpt"]),
|
||||||
"cover" => presence(http_params["cover"]),
|
"cover" => presence(http_params["cover"]),
|
||||||
|
"cover_alt" => presence(http_params["cover_alt"]),
|
||||||
"draft" => http_params["draft"] == "on",
|
"draft" => http_params["draft"] == "on",
|
||||||
"all_langs" => http_params["all_langs"] == "on"
|
"all_langs" => http_params["all_langs"] == "on"
|
||||||
}
|
}
|
||||||
post = Post.new(metadata: clean_metadata(metadata), body: http_params["body"].to_s)
|
|
||||||
post.path = existing.path if existing
|
|
||||||
post
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def fediverse_creator_error(post)
|
def fediverse_creator_error(post)
|
||||||
|
|||||||
@@ -46,6 +46,8 @@
|
|||||||
<button type="button" id="cover-upload">Upload</button>
|
<button type="button" id="cover-upload">Upload</button>
|
||||||
<button type="button" id="cover-clear">Clear</button>
|
<button type="button" id="cover-clear">Clear</button>
|
||||||
</div>
|
</div>
|
||||||
|
<input type="text" id="cover-alt-input" name="cover_alt" value="<%= h(@post.cover_alt) %>"
|
||||||
|
placeholder="ALT text (describe the image for accessibility)">
|
||||||
<img id="cover-preview" class="cover-preview" alt="" hidden>
|
<img id="cover-preview" class="cover-preview" alt="" hidden>
|
||||||
<input type="file" id="cover-file" accept="image/*" hidden>
|
<input type="file" id="cover-file" accept="image/*" hidden>
|
||||||
</div>
|
</div>
|
||||||
@@ -149,7 +151,10 @@
|
|||||||
if (tag === "code") { return "`" + node.textContent + "`"; }
|
if (tag === "code") { return "`" + node.textContent + "`"; }
|
||||||
if (tag === "a") { return "[" + inner + "](" + (node.getAttribute("href") || "") + ")"; }
|
if (tag === "a") { return "[" + inner + "](" + (node.getAttribute("href") || "") + ")"; }
|
||||||
if (tag === "img") {
|
if (tag === "img") {
|
||||||
return " || "") + ")";
|
var title = node.getAttribute("title") || "";
|
||||||
|
var imgMd = " || "") + ")";
|
||||||
|
if (title) { imgMd = imgMd.replace(")", ' "' + title + '")'); }
|
||||||
|
return imgMd;
|
||||||
}
|
}
|
||||||
if (tag === "br") { return "\n"; }
|
if (tag === "br") { return "\n"; }
|
||||||
return inner;
|
return inner;
|
||||||
|
|||||||
Reference in New Issue
Block a user