diff --git a/app/javascript/styles/application.scss b/app/javascript/styles/application.scss index 81a040108e..1b2969c234 100644 --- a/app/javascript/styles/application.scss +++ b/app/javascript/styles/application.scss @@ -23,3 +23,4 @@ @import 'mastodon/dashboard'; @import 'mastodon/rtl'; @import 'mastodon/accessibility'; +@import 'mastodon/rich_text'; diff --git a/app/javascript/styles/mastodon/rich_text.scss b/app/javascript/styles/mastodon/rich_text.scss new file mode 100644 index 0000000000..a8258ee8cb --- /dev/null +++ b/app/javascript/styles/mastodon/rich_text.scss @@ -0,0 +1,97 @@ +.account__header__bio, +.status__content__text, +.e-content, +.reply-indicator__content { + pre, + blockquote { + margin-bottom: 20px; + white-space: pre-wrap; + unicode-bidi: plaintext; + + &:last-child { + margin-bottom: 0; + } + } + + code, pre { + border: 1px solid $ui-primary-color; + font-family: monospace; + } + + code { + margin: 0 4px; + } + + pre { + padding: 4px 8px; + + span.ellipsis::after { + content: ""; + } + + span.invisible { + display: inline; + font-size: inherit; + line-height: inherit; + width: inherit; + height: inherit; + position: static; + } + } + + blockquote { + padding-left: 10px; + border-left: 3px solid $darker-text-color; + color: $darker-text-color; + white-space: normal; + + p:last-child { + margin-bottom: 0; + } + } + + & > ul, + & > ol { + margin-bottom: 20px; + } + + b, + strong { + font-weight: 700; + } + + em, + i { + font-style: italic; + } + + ul, + ol { + margin-left: 2em; + + p { + margin: 0; + } + } + + ul { + list-style-type: disc; + } + + ol { + list-style-type: decimal; + } + + mark { + background-color: $ui-highlight-color; + color: $ui-secondary-color; + padding: 0 2px; + } +} + +.reply-indicator__content { + blockquote { + border-left-color: $inverted-text-color; + color: $inverted-text-color; + } +} diff --git a/app/lib/text_formatter.rb b/app/lib/text_formatter.rb index 48e2fc2338..8513b1f4ca 100644 --- a/app/lib/text_formatter.rb +++ b/app/lib/text_formatter.rb @@ -43,7 +43,9 @@ class TextFormatter end end - html = simple_format(html, {}, sanitize: false).delete("\n") if multiline? + html = markdownify(html) + + # html = simple_format(html, {}, sanitize: false).delete("\n") if multiline? html.html_safe # rubocop:disable Rails/OutputSafety end @@ -155,4 +157,50 @@ class TextFormatter def preloaded_accounts? preloaded_accounts.present? end + + def markdownify(html) + # not need filter_html because escape is already done + @htmlobj ||= MyMarkdownHTML.new( + filter_html: false, + hard_wrap: false, + no_styles: true + ) + @markdown ||= Redcarpet::Markdown.new(@htmlobj, + autolink: false, + tables: false, + underline: true, + disable_indented_code_blocks: false, + fenced_code_blocks: true, + highlight: false + ) + @markdown.render(html) + end + + class MyMarkdownHTML < Redcarpet::Render::HTML + def link(link, title, content) + nil + end + + def linebreak + nil + end + + def block_code(code, language) + "
#{code}
" + end + + def codespan(code) + "#{escape_tags(code)}" + end + + def header(text, header_level) + "

#{text}

" + end + + private + + def escape_tags(code) + code.gsub(//, '>') + end + end end