From 3b44e6db233e0b2534fc5ef647610710f62ed7b5 Mon Sep 17 00:00:00 2001 From: barkz Date: Fri, 11 Sep 2026 22:05:34 -0500 Subject: [PATCH] feat: link every heading and finish the terminal type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Headings were already ids, so they were addressable but not clickable — you had to read the HTML to find the fragment. Each heading is now a link to itself, and carries its markdown level as a dim prefix: ## before a section, ### before a subsection. That reads as the source the page is generated from, and the marker encodes real information, namely the depth. Type: the headings shared the mono face with the tagline but not its weight, sitting at 700 against the tagline's 500. They now match, and table headers — headers too — move from bold sans to the same mono. A heading that already contains a link is left alone rather than nesting one anchor inside another. Tests: 1,103 -> 1,108. Co-Authored-By: Claude Opus 5 (1M context) --- .github/pages/build.py | 7 ++++++- .github/pages/template.html | 22 ++++++++++++++++++---- CLAUDE.md | 2 +- README.md | 2 +- docs/TESTING.md | 4 ++-- tests/test_pages_build.py | 27 ++++++++++++++++++++++++++- 6 files changed, 54 insertions(+), 10 deletions(-) diff --git a/.github/pages/build.py b/.github/pages/build.py index b8074af..20dc645 100644 --- a/.github/pages/build.py +++ b/.github/pages/build.py @@ -224,7 +224,12 @@ def render(markdown): if heading: level = len(heading.group(1)) text = heading.group(2).strip() - out.append('%s' % (level, slug(text), inline(text), level)) + anchor = slug(text) + body = inline(text) + if anchor and "%s' % (level, anchor, body, level)) seen_prose = True i += 1 continue diff --git a/.github/pages/template.html b/.github/pages/template.html index 3e76614..8379663 100644 --- a/.github/pages/template.html +++ b/.github/pages/template.html @@ -103,15 +103,25 @@ /* ---- prose ---- */ h1, h2, h3 { - font-family: var(--mono); font-weight: 700; - letter-spacing: -0.035em; line-height: 1.25; text-wrap: balance; + font-family: var(--mono); font-weight: 500; + letter-spacing: -0.03em; line-height: 1.25; text-wrap: balance; } + + /* headings link to themselves, prefixed with their markdown level */ + .anchor { color: inherit; text-decoration: none; } + h2 .anchor::before, h3 .anchor::before { + color: var(--ink-3); font-weight: 400; margin-right: .5ch; + } + h2 .anchor::before { content: "##"; } + h3 .anchor::before { content: "###"; } + .anchor:hover { text-decoration: none; } + .anchor:hover::before { color: var(--accent-ink); } h1 { font-size: clamp(1.9rem, 4.6vw, 2.3rem); margin: 0 0 .3em; } h2 { font-size: 1.26rem; margin: 2.2em 0 1em; padding-bottom: .4em; border-bottom: 1px solid var(--rule); } - h3 { font-size: 1rem; font-weight: 500; letter-spacing: -0.025em; margin: 1.5em 0 .45em; } + h3 { font-size: 1rem; letter-spacing: -0.025em; margin: 1.5em 0 .45em; } /* the wordmark leads the page; the h1 stays for readers and search */ .sr-only { @@ -209,7 +219,11 @@ border: 1px solid var(--rule); border-radius: 8px; } .doc th, .doc td { text-align: left; padding: 15px 18px; vertical-align: top; } - .doc thead th { background: var(--surface-2); font-weight: 600; border-bottom: 1px solid var(--rule); } + .doc thead th { + background: var(--surface-2); border-bottom: 1px solid var(--rule); + font-family: var(--mono); font-weight: 500; font-size: .84rem; + letter-spacing: -0.02em; color: var(--ink-2); + } .doc tbody tr + tr td { border-top: 1px solid var(--rule-soft); } .doc td:first-child { font-weight: 600; } /* key/value rows: hold the value column to a readable measure */ diff --git a/CLAUDE.md b/CLAUDE.md index da9e256..7e2ac07 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -18,7 +18,7 @@ python3 install.py # --cli-only, --dev, --prefix, --verify, --uninstal # Pipe a single command (non-interactive; cli.py detects a non-tty stdin) echo '/search "q2 plan"' | python3 -m glean_code -# Run the full test suite (1,103 tests, stdlib unittest — works with or without pytest) +# Run the full test suite (1,108 tests, stdlib unittest — works with or without pytest) python3 -m pytest tests/ python3 -m unittest discover tests/ diff --git a/README.md b/README.md index f8767ee..44f3f28 100644 --- a/README.md +++ b/README.md @@ -176,7 +176,7 @@ The full Glean Code REPL — slash commands, status bar, mock/live switching, se | 🔐 **[SSO / OAuth](docs/SSO_OAUTH.md)** · **[Secure tokens](docs/SECURE_TOKENS.md)** | Browser sign-in, secure refs, the masking matrix | | 🔌 **[MCP server](docs/MCP.md)** | Glean as native tools in Claude Code, Claude Desktop, Cursor | | 🏛️ **[Architecture](docs/ARCHITECTURE.md)** · **[REST paths](docs/REST_PATHS.md)** | Module map, request flow, endpoints, how to add a command | -| ✅ **[Testing](docs/TESTING.md)** | Running the 1,103-test suite and what it covers | +| ✅ **[Testing](docs/TESTING.md)** | Running the 1,108-test suite and what it covers | | 🛟 **[Support](SUPPORT.md)** · **[Changelog](CHANGELOG.md)** | How to report a bug · release history | > [!NOTE] diff --git a/docs/TESTING.md b/docs/TESTING.md index fac4262..39c71c0 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -21,13 +21,13 @@ files, and they outrank the `Glean Code.app` launcher in `Cmd+Space`: export PYTHONPYCACHEPREFIX="$HOME/.cache/python" ``` -1,103 tests covering the client and every mock response, commands and dispatch, config, UI, auth, completion, help docs, the mock corpus, indexing-walk, scaffold, the installer, the MCP server, the flow mapper, the Pages site builder, and Glean Personal (text extraction, the index, the content graph, ranking explanations, local mode, and the local MCP tools). +1,108 tests covering the client and every mock response, commands and dispatch, config, UI, auth, completion, help docs, the mock corpus, indexing-walk, scaffold, the installer, the MCP server, the flow mapper, the Pages site builder, and Glean Personal (text extraction, the index, the content graph, ranking explanations, local mode, and the local MCP tools). ## Development notes Notes on the test suite added during development of glean-code-cli. -All 1,103 tests pass. Here's what was added across the development passes: +All 1,108 tests pass. Here's what was added across the development passes: `tests/test_commands_extended.py` (155 new tests) — covers all previously untested commands: diff --git a/tests/test_pages_build.py b/tests/test_pages_build.py index c023855..b978e93 100644 --- a/tests/test_pages_build.py +++ b/tests/test_pages_build.py @@ -88,6 +88,21 @@ class TestBlocks(unittest.TestCase): def test_heading_gets_an_anchor_id(self): self.assertIn('

', build.render("## Quickstart")) + def test_heading_links_to_itself(self): + out = build.render("## Quickstart") + self.assertEqual( + out, '

Quickstart

') + + def test_every_heading_level_gets_an_anchor(self): + out = build.render("# One\n\n## Two\n\n### Three") + self.assertEqual(out.count('class="anchor"'), 3) + self.assertIn('href="#three"', out) + + def test_a_heading_that_already_holds_a_link_is_not_double_wrapped(self): + out = build.render("## See [the docs](docs/COMMANDS.md)") + self.assertNotIn('class="anchor"', out) + self.assertEqual(out.count("' && x\n```") self.assertIn('
', out)
@@ -136,7 +151,8 @@ def test_note_alert(self):
     def test_raw_html_passes_through_untouched(self):
         out = build.render('
\n\n# Glean Code\n\n
') self.assertIn('
', out) - self.assertIn('

Glean Code

', out) + self.assertIn( + '

Glean Code

', out) self.assertIn("
", out) def test_horizontal_rule(self): @@ -283,6 +299,15 @@ def test_tagline_is_set_in_the_mono_face(self): rule = self.css[self.css.index('[align="center"] .banner + p {'):] self.assertIn("font-family: var(--mono)", rule[:rule.index("}")]) + def test_headings_carry_their_markdown_level(self): + self.assertIn('h2 .anchor::before { content: "##"; }', self.css) + self.assertIn('h3 .anchor::before { content: "###"; }', self.css) + + def test_headings_and_table_headers_use_the_mono_face(self): + for selector in ("h1, h2, h3 {", ".doc thead th {"): + rule = self.css[self.css.index(selector):] + self.assertIn("font-family: var(--mono)", rule[:rule.index("}")], selector) + def test_image_rows_do_not_stretch_their_items(self): row = self.css[self.css.index("p.imgrow {"):] self.assertIn("align-items: center", row[:row.index("}")])