From 85d270abcb5b0f2a553de0b5776d8eafcfe5e5c9 Mon Sep 17 00:00:00 2001 From: Jammy2211 Date: Wed, 7 Oct 2026 18:48:50 +0100 Subject: [PATCH] feat: align dashboard Markdown source icons to the right --- board/_theme.py | 56 ++++++++++++++++++++++++++++++++-- docs/board-navigation.md | 10 ++++++ tests/test_board.py | 2 +- tests/test_intake_dashboard.py | 4 +-- 4 files changed, 66 insertions(+), 6 deletions(-) diff --git a/board/_theme.py b/board/_theme.py index 5c8a2ac..792a48c 100644 --- a/board/_theme.py +++ b/board/_theme.py @@ -760,6 +760,13 @@ def mark(key): background:var(--warn)} a.go:hover{opacity:.9;text-decoration:none} .mdsrc{font-size:.85em} +.board-source-link{display:inline-flex;align-items:center;justify-content:center;flex:none;width:44px;height:44px;border-radius:6px;color:var(--muted);text-decoration:none} +.board-source-link:hover{color:var(--accent);background:var(--btn)} +.board-source-link:focus-visible{outline:3px solid var(--accent);outline-offset:2px} +.board-source-link svg{width:18px;height:18px;pointer-events:none} +p:has(>.board-source-link){display:flex;align-items:center;gap:.5rem;max-width:none} +p>.board-source-link{order:1;margin-left:auto} +.board-section>summary>.board-source-link{margin-left:auto} """ # One organ chip colour per scheme, appended to the sheet so the family @@ -1244,8 +1251,9 @@ def portable_prompt(payload): def section_layout(page, summaries=None): """Compose an owner's rendered board into the shared disclosure layout. - Preserve the owner's HTML verbatim, including IDs and copy payloads. Only - major heading groups are wrapped; existing details and card headings are + Preserve owner IDs, destinations and copy payloads. Markdown source links + become accessible icons; major heading groups are wrapped, while existing + details and card headings are left alone. Optional summaries map section IDs to owner-supplied count, status and label fields. This helper never infers health from prose. """ @@ -1296,6 +1304,41 @@ def handle_endtag(self, tag): break tree = Tree(page) + # Normalize the existing source-link convention once for every consumer. + # Match the complete label, never arbitrary links to Markdown documents. + source_edits = [] + for node in tree.nodes: + if node['tag'] != 'a' or not node['end'] or not node['attrs'].get('href'): + continue + label = _html.unescape(_re.sub('<[^>]*>', '', page[node['inner']:node['close']])).strip() + if label.lower() != 'markdown version': + continue + attrs = dict(node['attrs']) + attrs['class'] = (attrs.get('class', '') + ' board-source-link').strip() + attrs['title'] = attrs['aria-label'] = 'Markdown version' + attributes = ''.join(' ' + key + (f'="{_html.escape(value, quote=True)}"' + if value is not None else '') for key, value in attrs.items()) + icon = ('' + '' + '') + start, end = node['start'], node['end'] + parent = node['parent'] + if parent and parent['tag'] == 'p': + # Page-level links share a utility row; its separator is redundant + # once the source link moves to the far right. + trailing = _re.match(r'\s*·\s*', page[end:parent['close']]) + preceding = _re.search(r'\s*·\s*$', page[parent['inner']:start]) + if trailing: + end += trailing.end() + elif preceding: + start = parent['inner'] + preceding.start() + source_edits.append((start, end, '' + icon + '')) + for start, end, replacement in reversed(source_edits): + page = page[:start] + replacement + page[end:] + if source_edits: + tree = Tree(page) summaries = summaries or {} def classes(node): @@ -1387,8 +1430,15 @@ def headings(node): badges += (f'' + _html.escape(str(info.get('label') or status)) + '') title = page[heading['start']:heading['end']] + sources = [n for n in tree.nodes if n['tag'] == 'a' and n['end'] + and 'board-source-link' in classes(n) + and heading['inner'] <= n['start'] < heading['close']] + source_links = ''.join(page[n['start']:n['end']] for n in sources) + for node in reversed(sources): + left, right = node['start'] - heading['start'], node['end'] - heading['start'] + title = title[:left] + title[right:] content = page[start:heading['start']] + page[heading['end']:end] - replacement = ('
' + title + badges + replacement = ('
' + title + badges + source_links + '
' + content + '
') edits.append((start, end, replacement)) covered_until = end diff --git a/docs/board-navigation.md b/docs/board-navigation.md index 6a0c3e1..b872ef9 100644 --- a/docs/board-navigation.md +++ b/docs/board-navigation.md @@ -108,3 +108,13 @@ synthetic test data, not current operational state. ![Brain desktop navigation](board-navigation/brain-desktop.png) ![Cortex phone navigation](board-navigation/cortex-phone.png) + +### Markdown source links + +The shared section layout renders links labelled “markdown version” as small +document icons, preserving the owner’s URL. Section icons sit at the right of +the summary after its count/status badges; page-level source icons sit at the +right of their utility row. Each link has a “Markdown version” tooltip and +accessible name, visible keyboard focus and a 44px click target. Following the +link does not toggle the disclosure. Other Markdown links retain their labels. +This presentation is applied when a consuming board is next regenerated. diff --git a/tests/test_board.py b/tests/test_board.py index 5cecd62..4edf620 100644 --- a/tests/test_board.py +++ b/tests/test_board.py @@ -543,7 +543,7 @@ def test_html_is_self_contained_with_one_tap_payloads(tmp_path): assert 'data-cmd="Use the intake skill."' in page # The header line every sibling board carries: the markdown twin and the # way back to the repository front door on github.com. - assert 'markdown version' in page + assert 'GitHub Page') in page diff --git a/tests/test_intake_dashboard.py b/tests/test_intake_dashboard.py index f45307b..0672ec4 100644 --- a/tests/test_intake_dashboard.py +++ b/tests/test_intake_dashboard.py @@ -623,8 +623,8 @@ def test_html_sections_link_their_markdown_source(tmp_path): "repos.yaml": REPOS_YAML}) html = _html(mind) for src in ("active.md", "epics.md", "planned.md"): - assert f'/blob/main/{src}">markdown version' in html, src - assert '/tree/main/draft">markdown version' in html + assert f'/blob/main/{src}" title="Markdown version" aria-label="Markdown version">GitHub Page' in html