diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bf82c4c..b0516d95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ ## [Unreleased] +## [v0.51.580] — 2026-06-22 — Release UM (wrap markdown source previews) + +### Fixed + +- **Markdown source previews now wrap normal prose instead of forcing horizontal scroll.** Fenced code blocks tagged `md`, `markdown`, or `mdx` (the inline viewer with the small `MD` header and Copy button) now render in a dedicated wrapping source-preview style, so ordinary markdown text flows to the message width while true code/diff blocks keep their existing horizontal-scroll behavior. Thanks @TomBanksAU. (#4669) + ## [v0.51.579] — 2026-06-22 — Release UL (deflake TLS end-to-end tests) ### Fixed diff --git a/static/style.css b/static/style.css index b81ce66e..4ee51c2b 100644 --- a/static/style.css +++ b/static/style.css @@ -1687,6 +1687,9 @@ .msg-body code{font-family:"SF Mono","Fira Code",ui-monospace,monospace;font-size:var(--message-code-font-size);background:var(--code-inline-bg);padding:1px 5px;border-radius:4px;color:var(--code-text);} .msg-body pre{background:var(--code-bg);border:1px solid var(--border);border-radius:10px;padding:14px 16px;overflow-x:auto;margin:10px 0;} .msg-body pre code{background:none;padding:0;border-radius:0;color:var(--pre-text);font-size:var(--message-pre-code-font-size);line-height:1.6;} + .msg-body pre.md-source-block,.preview-md pre.md-source-block{white-space:pre-wrap;overflow-x:hidden;overflow-wrap:anywhere;} + .msg-body pre.md-source-block code,.preview-md pre.md-source-block code{display:block;white-space:inherit;overflow-wrap:anywhere;word-break:break-word;} + .msg-body pre.md-source-block code .token,.preview-md pre.md-source-block code .token{white-space:inherit;overflow-wrap:anywhere;word-break:inherit;} .provider-error-details{margin:12px 0 0;border:1px solid var(--border);border-radius:10px;background:var(--surface);overflow:hidden;} .provider-error-details>summary{cursor:pointer;color:var(--muted);font-size:12px;font-weight:600;padding:8px 12px;} .provider-error-details>pre{margin:0;border:0;border-top:1px solid var(--border);border-radius:0;max-height:220px;} diff --git a/static/ui.js b/static/ui.js index d530bb47..7db23b26 100644 --- a/static/ui.js +++ b/static/ui.js @@ -349,6 +349,7 @@ function _renderUserFencedBlocks(text){ if(code.endsWith('\n')) code=code.slice(0,-1); const h=lang?`
${colored}`);
} else {
- stash.push(`${h}${esc(code)}`);
+ stash.push(`${h}${esc(code)}`);
}
return lead+'\x00UF'+(stash.length-1)+'\x00';
});
@@ -4856,6 +4857,7 @@ function renderMd(raw){
} else {
const h=lang?`| ${esc(h)} | `).join('')}
|---|
${esc(code.replace(/\n$/,''))}`);
+ _preBlock_stash.push(`${h}${esc(code.replace(/\n$/,''))}`);
}
} else {
- _preBlock_stash.push(`${h}${esc(code.replace(/\n$/,''))}`);
+ _preBlock_stash.push(`${h}${esc(code.replace(/\n$/,''))}`);
}
}
return lead+'\x00P'+(_preBlock_stash.length-1)+'\x00';
diff --git a/tests/test_1325_user_fenced_code.py b/tests/test_1325_user_fenced_code.py
index be918a4b..240e4560 100644
--- a/tests/test_1325_user_fenced_code.py
+++ b/tests/test_1325_user_fenced_code.py
@@ -126,7 +126,7 @@ class TestUserFencedBlocks:
def test_four_backtick_outer_fence_preserves_inner_triple_fence(self):
"""User-message code fences should follow CommonMark fence-length matching too."""
out = _run_user_render("````md\n```inner\nfoo\n```\n````")
- assert out.count("") == 1
+ assert out.count("") == 1
assert out.count("") == 1
assert 'md' in out
assert "```inner" in out
diff --git a/tests/test_csv_table_rendering.py b/tests/test_csv_table_rendering.py
index a2afc3da..b86c413f 100644
--- a/tests/test_csv_table_rendering.py
+++ b/tests/test_csv_table_rendering.py
@@ -41,7 +41,9 @@ def test_csv_fence_fallback_for_insufficient_rows():
src = f.read()
fence_section = src[src.find("lang==='csv'"):src.find("lang==='csv'") + 800]
assert 'rows.length>=2' in fence_section, "Should check for at least 2 rows"
- assert '"
+ assert '' in fence_section, (
+ "Fallback should render via the shared preClass-aware code-block template"
+ )
def test_csv_media_file_handler():
diff --git a/tests/test_issue_code_syntax_highlight.py b/tests/test_issue_code_syntax_highlight.py
index d83617a6..d6dedd3d 100644
--- a/tests/test_issue_code_syntax_highlight.py
+++ b/tests/test_issue_code_syntax_highlight.py
@@ -17,9 +17,10 @@ def test_fenced_code_blocks_add_prism_language_class():
def test_fenced_code_blocks_keep_existing_pre_header_layout():
js = _read_ui_js()
# The fenced code rendering was moved into the stash callback (#1154 fix).
- # The template string now uses `lang` instead of `normalizedLang`.
- assert '${h}${esc(code.replace(/\\n$/,' in js, (
- "The syntax-highlight fix should preserve the existing fenced code block layout"
+ # The template string now uses `lang` instead of `normalizedLang`, and
+ # markdown-source fences may add a dedicated md-source-block class.
+ assert '${h}${esc(code.replace(/\\n$/,' in js, (
+ "The syntax-highlight fix should preserve the shared fenced code block layout"
)
assert '' not in js, (
"This fix should not introduce a new wrapper around fenced code blocks"
diff --git a/tests/test_markdown_source_code_blocks_wrap.py b/tests/test_markdown_source_code_blocks_wrap.py
new file mode 100644
index 00000000..14c5e603
--- /dev/null
+++ b/tests/test_markdown_source_code_blocks_wrap.py
@@ -0,0 +1,18 @@
+from pathlib import Path
+
+
+ROOT = Path(__file__).resolve().parents[1]
+UI_JS = (ROOT / "static" / "ui.js").read_text(encoding="utf-8")
+CSS = (ROOT / "static" / "style.css").read_text(encoding="utf-8")
+
+
+def test_markdown_source_code_blocks_get_dedicated_pre_class_in_both_renderers():
+ expected = "const preClass=/^(md|markdown|mdx)$/.test(lang)?' class=\"md-source-block\"':'';"
+
+ assert UI_JS.count(expected) >= 2, "Expected both fenced-code renderers to tag markdown source blocks"
+
+
+def test_markdown_source_code_blocks_have_desktop_wrap_css():
+ assert ".msg-body pre.md-source-block,.preview-md pre.md-source-block{white-space:pre-wrap;overflow-x:hidden;overflow-wrap:anywhere;}" in CSS
+ assert ".msg-body pre.md-source-block code,.preview-md pre.md-source-block code{display:block;white-space:inherit;overflow-wrap:anywhere;word-break:break-word;}" in CSS
+ assert ".msg-body pre.md-source-block code .token,.preview-md pre.md-source-block code .token{white-space:inherit;overflow-wrap:anywhere;word-break:inherit;}" in CSS
diff --git a/tests/test_renderer_js_behaviour.py b/tests/test_renderer_js_behaviour.py
index 2a84905a..0c63cc6e 100644
--- a/tests/test_renderer_js_behaviour.py
+++ b/tests/test_renderer_js_behaviour.py
@@ -397,7 +397,7 @@ class TestFencedCodeFenceLength:
"That is much more correct than pretending"
)
out = _render(driver_path, src)
- assert out.count("") == 1
+ assert out.count("") == 1
assert out.count("") == 1
assert 'md' in out
assert "```novelcrafter" in out
@@ -408,7 +408,7 @@ class TestFencedCodeFenceLength:
def test_four_backtick_outer_fence_preserves_inner_triple_fence(self, driver_path):
out = _render(driver_path, "````md\n```inner\nfoo\n```\n````\n")
- assert out.count("") == 1
+ assert out.count("") == 1
assert out.count("") == 1
assert 'md' in out
assert "```inner" in out