From 9d3ce9ca53cb3b660323903a1db1ea6a31c1a03e Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Mon, 24 Aug 2026 10:38:51 +0200 Subject: feat: pad code blocks, make the copy link a hover button The copy link was a word-plus-glyph sitting above the block, always at full strength, with no feedback once clicked. It is now an icon-only button under the block, since Qt cannot float it over the tint. Qt draws almost no box properties on
: padding and vertical margins
are ignored, so the inset is margin-left/right and the room above and
below is a tinted spacer line. The glyph alone renders small whatever
font-size says, so the button is a tinted pill instead: grey at rest,
the highlight colour under the pointer, and a tick on green once used.
Qt rich text has no :hover, so hovering is QTextBrowser.highlighted()
driving a re-render, the same way the toggles already re-render.

The tick reverts after a moment rather than standing indefinitely, which
would claim the clipboard still holds that block long after it stopped
being true. Copying a second block first cancels the first one's revert.
---
 test_llamachat.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 48 insertions(+)

(limited to 'test_llamachat.py')

diff --git a/test_llamachat.py b/test_llamachat.py
index 7a84608..407c5dc 100755
--- a/test_llamachat.py
+++ b/test_llamachat.py
@@ -501,6 +501,10 @@ def test_code_block_copy_links():
     fragment = _markdown_to_fragment(markdown, 3)
     assert _re.findall(r"x-llamachat-copy:([0-9.]+)", fragment) == ["3.0", "3.1"]
 
+    # Icon only, and under its block: Qt cannot float it over the tint.
+    assert "⧉" in fragment and "copy<" not in fragment
+    assert fragment.rindex("⧉") > fragment.rindex("
") + # The indices the links carry must select the blocks the parser found. blocks = _code_blocks(_close_fences(markdown)) assert blocks == ["AAA", "BBB"], blocks @@ -518,6 +522,49 @@ def test_code_block_copy_links(): print("ok code block copy links") +def test_code_block_copy_states(): + """The copy link dims, lights on hover, and ticks once used.""" + import os + + os.environ.setdefault("QT_QPA_PLATFORM", "offscreen") + from PySide6.QtWidgets import QApplication + + from llamachat.ui import _PRE_SPACER, _markdown_to_fragment + + app = QApplication.instance() or QApplication([]) + assert app is not None + + markdown = "```py\nAAA\n```\n" + + def link_style(fragment: str, glyph: str) -> str: + return fragment.split(glyph)[0].rsplit(", so a tinted spacer stands in above and + # below; without it the tint sits flush against the code. + assert plain.count(_PRE_SPACER) == 2 + print("ok code block copy states") + + def test_system_qt_theme_guard(): """The plugin path is only borrowed when the Qt versions agree.""" import os @@ -3339,4 +3386,5 @@ if __name__ == "__main__": test_title_prompt() test_code_block_wrapping() test_code_block_copy_links() + test_code_block_copy_states() print("\nall checks passed") -- cgit v1.2.3