From eda6d65818d4eb80c35b25be43ce8de9588d7161 Mon Sep 17 00:00:00 2001 From: D <dj8793@gmail.com> Date: Sat, 19 Apr 2025 14:53:39 +0900 Subject: [PATCH] markup: improve code block readability and isolate copy button (#34009) Fix #33197 Improve the rendering of code blocks in markdown content for better readability and UI stability across screen sizes. Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> --- modules/markup/markdown/markdown.go | 9 +--- templates/devtest/markup-render.tmpl | 71 ++++++++++++++++++++++++++++ web_src/css/markup/codecopy.css | 9 +--- web_src/css/markup/content.css | 27 +++++++---- web_src/js/markup/codecopy.ts | 4 +- 5 files changed, 95 insertions(+), 25 deletions(-) create mode 100644 templates/devtest/markup-render.tmpl diff --git a/modules/markup/markdown/markdown.go b/modules/markup/markdown/markdown.go index 0d7180c6b1..79df547c2c 100644 --- a/modules/markup/markdown/markdown.go +++ b/modules/markup/markdown/markdown.go @@ -86,20 +86,15 @@ func (r *GlodmarkRender) highlightingRenderer(w util.BufWriter, c highlighting.C preClasses += " is-loading" } - err := r.ctx.RenderInternal.FormatWithSafeAttrs(w, `<pre class="%s">`, preClasses) - if err != nil { - return - } - // include language-x class as part of commonmark spec, "chroma" class is used to highlight the code // the "display" class is used by "js/markup/math.ts" to render the code element as a block // the "math.ts" strictly depends on the structure: <pre class="code-block is-loading"><code class="language-math display">...</code></pre> - err = r.ctx.RenderInternal.FormatWithSafeAttrs(w, `<code class="chroma language-%s display">`, languageStr) + err := r.ctx.RenderInternal.FormatWithSafeAttrs(w, `<div class="code-block-container code-overflow-scroll"><pre class="%s"><code class="chroma language-%s display">`, preClasses, languageStr) if err != nil { return } } else { - _, err := w.WriteString("</code></pre>") + _, err := w.WriteString("</code></pre></div>") if err != nil { return } diff --git a/templates/devtest/markup-render.tmpl b/templates/devtest/markup-render.tmpl new file mode 100644 index 0000000000..69d29d7829 --- /dev/null +++ b/templates/devtest/markup-render.tmpl @@ -0,0 +1,71 @@ +{{template "devtest/devtest-header"}} +<div class="page-content devtest ui container"> + {{$longCode := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"}} + <div class="tw-flex"> + <div class="tw-w-[50%] tw-p-4"> + <div class="markup render-content"> + Inline <code>code</code> content + </div> + + <div class="divider"></div> + + <div class="markup render-content"> + <p>content before</p> + <pre><code>Very long line with no code block or container: {{$longCode}}</code></pre> + <p>content after</p> + </div> + + <div class="divider"></div> + + <div class="markup render-content"> + <p>content before</p> + <div class="code-block-container code-overflow-wrap"> + <pre class="code-block"><code>Very long line with wrap: {{$longCode}}</code></pre> + </div> + <p>content after</p> + </div> + + <div class="divider"></div> + + <div class="markup render-content"> + <p>content before</p> + <div class="code-block-container code-overflow-scroll"> + <pre class="code-block"><code>Short line in scroll container</code></pre> + </div> + <div class="code-block-container code-overflow-scroll"> + <pre class="code-block"><code>Very long line with scroll: {{$longCode}}</code></pre> + </div> + <p>content after</p> + </div> + </div> + + <div class="tw-w-[50%] tw-p-4"> + <div class="markup render-content"> + <p>content before</p> + <div class="code-block-container"> + <pre class="code-block"><code class="language-math"> + \lim\limits_{n\rightarrow\infty}{\left(1+\frac{1}{n}\right)^n} + </code></pre> + </div> + <p>content after</p> + </div> + + <div class="divider"></div> + + <div class="markup render-content"> + <p>content before</p> + <div class="code-block-container"> + <pre class="code-block"><code class="language-mermaid is-loading"> + graph LR + A[Square Rect] -- Link text --> B((Circle)) + A --> C(Round Rect) + B --> D{Rhombus} + C --> D + </code></pre> + </div> + <p>content after</p> + </div> + </div> + </div> +</div> +{{template "devtest/devtest-footer"}} diff --git a/web_src/css/markup/codecopy.css b/web_src/css/markup/codecopy.css index e3017ae962..5a7b9955e7 100644 --- a/web_src/css/markup/codecopy.css +++ b/web_src/css/markup/codecopy.css @@ -1,8 +1,3 @@ -.markup .code-block, -.markup .mermaid-block { - position: relative; -} - .markup .code-copy { position: absolute; top: 8px; @@ -28,8 +23,8 @@ background: var(--color-secondary-dark-1) !important; } -.markup .code-block:hover .code-copy, -.markup .mermaid-block:hover .code-copy { +.markup .code-block-container:hover .code-copy, +.markup .code-block:hover .code-copy { visibility: visible; animation: fadein 0.2s both; } diff --git a/web_src/css/markup/content.css b/web_src/css/markup/content.css index 937224a9d7..8291539b95 100644 --- a/web_src/css/markup/content.css +++ b/web_src/css/markup/content.css @@ -443,13 +443,25 @@ } .markup pre > code { - padding: 0; - margin: 0; font-size: 100%; +} + +.markup .code-block, +.markup .code-block-container { + position: relative; +} + +.markup .code-block-container.code-overflow-wrap pre > code { white-space: pre-wrap; - overflow-wrap: anywhere; - background: transparent; - border: 0; +} + +.markup .code-block-container.code-overflow-scroll pre { + overflow-x: auto; +} + +.markup .code-block-container.code-overflow-scroll pre > code { + white-space: pre; + overflow-wrap: normal; } .markup .highlight { @@ -470,16 +482,11 @@ word-break: normal; } -.markup pre { - word-wrap: normal; -} - .markup pre code, .markup pre tt { display: inline; padding: 0; line-height: inherit; - word-wrap: normal; background-color: transparent; border: 0; } diff --git a/web_src/js/markup/codecopy.ts b/web_src/js/markup/codecopy.ts index 67284bad55..b37aa3a236 100644 --- a/web_src/js/markup/codecopy.ts +++ b/web_src/js/markup/codecopy.ts @@ -15,6 +15,8 @@ export function initMarkupCodeCopy(elMarkup: HTMLElement): void { const btn = makeCodeCopyButton(); // remove final trailing newline introduced during HTML rendering btn.setAttribute('data-clipboard-text', el.textContent.replace(/\r?\n$/, '')); - el.after(btn); + // we only want to use `.code-block-container` if it exists, no matter `.code-block` exists or not. + const btnContainer = el.closest('.code-block-container') ?? el.closest('.code-block'); + btnContainer.append(btn); }); }