From 186f63a26f82e4c1e063fb1fff0e3505f357bafa Mon Sep 17 00:00:00 2001 From: Jammy2211 Date: Fri, 28 Aug 2026 10:53:03 -0400 Subject: [PATCH] fix(heart): give the worded copy chips the theme's text modifier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The plan line's "clear them all" and "command chain" buttons carry words, but `button.copy` is a fixed 2.6rem square drawn for a bare glyph. The labels wrapped inside 42px into a one-word-per-line column and spilled out of the box — reported from a laptop as a thin column of text under the gaps heading. Whitespace in the face is the test, because that is what makes a face a phrase rather than a glyph; the per-row glyph buttons keep their square. The `text` rule itself belongs to the family and lives in the shared theme. The regression test asserts per button — this face has words, so this button is a chip — rather than by position, so it survives the board reordering its tiers. Verified red with the modifier removed. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_018q6ZrgG6u8o8ENYZ7cq3Lr --- heart/dashboard.py | 9 ++++++++- tests/test_dashboard.py | 24 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/heart/dashboard.py b/heart/dashboard.py index d6fdfed..77866b9 100644 --- a/heart/dashboard.py +++ b/heart/dashboard.py @@ -1413,8 +1413,15 @@ def _copy_btn(payload: str, label: str = "copy", face: str = "📋") -> str: ``face`` is what the button shows: the bare 📋 for a chip beside a row, a short worded face (⌨ command chain) where the board offers more than one payload and the reader has to choose between them. + + A worded face takes the theme's `text` modifier. The base `button.copy` is + a fixed 2.6rem SQUARE — right for a bare glyph, and a trap for words: the + label wrapped inside 42px into a one-word-per-line column and spilled out + of its own box. Whitespace in the face is the test, because that is what + makes a face a phrase rather than a glyph. """ - return (f"") diff --git a/tests/test_dashboard.py b/tests/test_dashboard.py index 76667aa..191dd50 100644 --- a/tests/test_dashboard.py +++ b/tests/test_dashboard.py @@ -959,6 +959,30 @@ def test_surfaces_render_the_remedies_and_the_plan(): assert all("command" in b for b in d["blockers"]) +def test_a_worded_copy_face_is_a_chip_and_a_glyph_stays_a_square(): + """The theme sizes `button.copy` as a fixed 2.6rem SQUARE. A face carrying + words needs the `text` modifier or the label wraps inside 42px into a + one-word-per-line column and spills out of the box — which is what the + plan line did until it was caught on a laptop. + + Asserted per button rather than by position: the invariant is "this face + has words, so this button is a chip", which stays true however the board + reorders its tiers. + """ + v = _stale_verdict(GAPS, ["install_unknown", "test_unknown"]) + html = dashboard.render(make_snapshot(), v, fmt="html", now=FRESH_NOW) + + buttons = re.findall(r"", html) + assert buttons, "no copy buttons on the board at all" + for cls, face in buttons: + is_chip = "text" in cls.split() + assert is_chip == (len(face.split()) > 1), (cls, face) + + faces = [f for _, f in buttons] + assert "\U0001f4cb clear them all" in faces # a worded chip is on show + assert "\U0001f4cb" in faces # and a bare glyph beside it + + def test_the_plan_stays_off_a_board_showing_another_tier(): # The board shows one tier at a time; a plan for gaps the reader cannot see # is noise (the json surface still carries it as data).