Skip to content

Instantly share code, notes, and snippets.

@Pysis868
Forked from bitti/gfm-render
Last active November 16, 2023 18:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Pysis868/6a42c95cfb410d5f384c2e8a97ce44f9 to your computer and use it in GitHub Desktop.
Save Pysis868/6a42c95cfb410d5f384c2e8a97ce44f9 to your computer and use it in GitHub Desktop.
Offline renderer for GitHub flavoured markdown
#!/usr/bin/ruby
# Renders Markdown content offline by converting it to HTML using GitHub's own redcarpet renderer software, and their additional CSS.
# Does require a download of the additional GitHub CSS to a local cache.
# Replaces grip for preferred offline functionality: https://github.com/joeyespo/grip/issues/35
require 'optparse'
options = {
cache: false,
replace: false
}
OptionParser.new do |opts|
opts.banner = 'Usage: [cat <file.md> |] gfm-render [options] <file.md>'
opts.on('-c', '--cache', 'Only checks to locally cache the additional GitHub CSS file, if it does not exist already.') do
options[:cache] = true
end
opts.on('-f', '--force', 'Sames as -r|--replace.') do
options[:replace] = true
end
opts.on('-h', '--help', "Displays this usage text.") do
puts opts
exit
end
opts.on('-r', '--replace', 'Forces a re-download of the additional GitHub CSS file.') do
options[:replace] = true
end
end.parse!(ARGV)
gh_markdown_css_filename = File.join(ENV["XDG_CACHE_HOME"] || "#{ENV['HOME']}/.cache", "gfm-render/github-markdown.css")
unless File.exist?(gh_markdown_css_filename) && !options[:replace]
require 'net/http'
require 'pathname'
CSS_URL = 'https://raw.githubusercontent.com/sindresorhus/github-markdown-css/main/github-markdown.css'
STDERR.puts "Downloading \"#{CSS_URL}\" to the cache at \"#{gh_markdown_css_filename}\"."
Pathname.new(gh_markdown_css_filename).dirname.mkpath
File.open(gh_markdown_css_filename, "w") do |css|
css.write(Net::HTTP.get(URI(CSS_URL)))
end
end
if options[:cache]
return
end
gh_markdown_css = File.read(gh_markdown_css_filename)
input = ARGV[0] && ARGV[0] != "-" && File.open(ARGV[0]) || STDIN
output = ARGV[1] && File.open(ARGV[1], "w") || STDOUT
File.join(ENV["XDG_CACHE_HOME"] || "#{ENV['HOME']}/.cache", "gfm-render")
require 'redcarpet'
require 'erb'
class RenderWithTaskLists < Redcarpet::Render::HTML
def list_item(text, list_type)
if text.start_with?("[x]", "[X]")
text[0..2] = %(<input type="checkbox" class="task-list-item-checkbox" disabled="" checked="checked">)
elsif text.start_with?("[ ]")
text[0..2] = %(<input type="checkbox" class="task-list-item-checkbox" disabled="">)
end
%(<li class="task-list-item">#{text}</li>)
end
end
markdown_renderer = Redcarpet::Markdown.new(
RenderWithTaskLists,
no_intra_emphasis: true,
autolink: true,
tables: true,
fenced_code_blocks: true,
lax_spacing: true,
)
html_output = markdown_renderer.render(input.read)
name = input.is_a?(File) && File.basename(input) || "<stdin>"
erb = ERB.new(DATA.read)
output.write(erb.result)
__END__
<html>
<head>
<meta charset="utf-8">
<title>Rendered <%= name %></title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
<%= gh_markdown_css %>
@media (prefers-color-scheme: dark) {
body {
color: #c9d1d9;
background-color: #0d1117;
}
}
@media (prefers-color-scheme: light) {
body {
color: #24292f;
background-color: #ffffff;
}
}
.markdown-body {
box-sizing: border-box;
min-width: 200px;
max-width: 980px;
margin: 1 auto;
padding: 45px;
border: 1px solid #d0d7de;
border-radius: 6px;
}
@media (max-width: 767px) {
.markdown-body {
padding: 15px;
}
}
</style>
</head>
<body>
<article class="markdown-body">
<%= html_output %>
</article>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment