Skip to content

feat: overview mode, expanded sitemap, and UI improvements#3168

Merged
MarkusNeusinger merged 3 commits intomainfrom
feature/frontend-design-changes
Jan 5, 2026
Merged

feat: overview mode, expanded sitemap, and UI improvements#3168
MarkusNeusinger merged 3 commits intomainfrom
feature/frontend-design-changes

Conversation

@MarkusNeusinger
Copy link
Copy Markdown
Owner

Summary

  • Overview/Detail Mode Navigation: New URL structure for spec pages
    • /{spec_id} → Overview grid showing all implementations
    • /{spec_id}/{library} → Detail view with carousel
  • Action Buttons on Overview: Copy code, download PNG, and open interactive buttons appear on hover
  • Library Tooltips: Shows library description and documentation URL (like home page)
  • Interactive Page Fix: postMessage origin now works correctly on localhost
  • Expanded Sitemap: Includes all implementation URLs (~1500 pages) for better SEO
  • Spacing Fixes: Consistent footer spacing across all pages
  • ClickAwayListener: Tooltips close when clicking outside

Test plan

  • Visit /scatter-basic - should show overview grid with all libraries
  • Click on an implementation - should navigate to /scatter-basic/matplotlib
  • Hover over grid cards - action buttons should appear
  • Click library name in overview - tooltip should open
  • Click elsewhere - tooltip should close
  • Check /sitemap.xml - should include implementation URLs
  • Test interactive page on localhost - iframe should load correctly
  • Verify footer spacing is consistent across /, /catalog, and spec pages

🤖 Generated with Claude Code

MarkusNeusinger and others added 2 commits January 5, 2026 15:09
- Implemented strip_noqa_comments utility to clean code display
- Updated SpecTabs component to support collapsible tabs
- Enhanced rendering of specifications with improved styling
- Added comprehensive tests for strip_noqa_comments utility
- Add overview/detail mode navigation for spec pages
  - /scatter-basic → overview grid showing all implementations
  - /scatter-basic/matplotlib → detail view with carousel
- Add action buttons (copy code, download, interactive) to overview grid
- Add library tooltip with description and doc URL in overview
- Fix interactive page postMessage origin for localhost development
- Update catalog links to go to overview page
- Expand sitemap to include all implementation URLs (~1500 pages)
- Fix spacing consistency between pages
- Add ClickAwayListener to close tooltips on outside click

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings January 5, 2026 14:57
@codecov
Copy link
Copy Markdown

codecov Bot commented Jan 5, 2026

Codecov Report

❌ Patch coverage is 95.45455% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
api/routers/proxy.py 91.66% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements a dual-mode navigation system for spec pages (overview grid vs. detail carousel), adds interactive action buttons on hover, improves library tooltips, fixes the interactive page postMessage origin for localhost, expands the sitemap to include all implementation URLs, and introduces a utility to strip noqa comments from displayed code.

Key Changes:

  • New URL structure: /{spec_id} shows grid of all implementations, /{spec_id}/{library} shows detail view with carousel
  • Action buttons (copy, download, interactive) appear on hover in overview mode
  • Sitemap now includes ~1500 pages (overview + all implementation URLs)

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
core/utils.py New utility function to strip noqa comments from code using regex
api/routers/specs.py Apply noqa stripping to code responses
api/routers/libraries.py Apply noqa stripping to library image code
api/routers/seo.py Expand sitemap to include implementation URLs alongside overview pages
api/routers/proxy.py Add origin parameter support for postMessage with localhost
app/src/pages/SpecPage.tsx Implement overview/detail mode with grid layout and library tooltips
app/src/pages/InteractivePage.tsx Add localhost:8000 to allowed origins
app/src/pages/CatalogPage.tsx Update navigation to link to overview pages
app/src/components/SpecTabs.tsx Add tab collapse/expand behavior and overview mode support
app/src/components/Footer.tsx Reduce footer spacing for consistency
tests/unit/core/test_utils.py Comprehensive tests for strip_noqa_comments function
tests/unit/api/test_routers.py Update sitemap test to verify implementation URLs

Comment thread core/utils.py
Comment on lines +3 to +10
import re


def strip_noqa_comments(code: str | None) -> str | None:
"""Remove # noqa: comments from code for cleaner user display."""
if not code:
return code
return re.sub(r"\s*# noqa:[^\n]*", "", code)
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The regex pattern r"\s*# noqa:[^\n]*" will remove noqa comments even when they appear inside string literals. While the test acknowledges this is "acceptable", it can lead to unintended modifications of code strings. Consider using a more robust approach such as:

  • Using Python's AST module to parse and modify only actual comments
  • Or at minimum, document this limitation clearly in the function's docstring

For example, code like message = "Error # noqa: F401" would become message = "Error", which changes the string's meaning.

Suggested change
import re
def strip_noqa_comments(code: str | None) -> str | None:
"""Remove # noqa: comments from code for cleaner user display."""
if not code:
return code
return re.sub(r"\s*# noqa:[^\n]*", "", code)
import io
import re
import tokenize
def strip_noqa_comments(code: str | None) -> str | None:
"""Remove ``# noqa:`` comments from code for cleaner user display.
This function removes only actual ``# noqa:...`` comment tokens, as identified
by Python's tokenizer. Substrings like ``"# noqa: F401"`` inside string
literals are preserved and will not be modified.
"""
if not code:
return code
# Use Python's tokenizer so we only remove real comments, not substrings
# inside string literals or other tokens.
lines = code.splitlines(keepends=True)
try:
tokens = tokenize.generate_tokens(io.StringIO(code).readline)
except (SyntaxError, tokenize.TokenError):
# If tokenization fails, fall back to returning the original code
# unchanged rather than risk corrupting it.
return code
for token in tokens:
if token.type != tokenize.COMMENT:
continue
comment_text = token.string.lstrip()
if not comment_text.startswith("# noqa:"):
continue
# Remove the noqa comment and any preceding whitespace on that line.
line_index = token.start[0] - 1 # token rows are 1-based
col = token.start[1]
original_line = lines[line_index]
newline = ""
if original_line.endswith(("\r\n", "\n", "\r")):
newline = original_line[len(original_line.rstrip("\r\n")) :]
# Keep content before the comment column, stripping trailing spaces.
kept = original_line[:col].rstrip()
lines[line_index] = kept + newline
return "".join(lines)

Copilot uses AI. Check for mistakes.
Comment thread core/utils.py Outdated


def strip_noqa_comments(code: str | None) -> str | None:
"""Remove # noqa: comments from code for cleaner user display."""
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function's docstring should document the known limitation that the regex-based approach will also remove noqa patterns inside string literals. This will help future maintainers understand the trade-off that was made. For example:

"Remove # noqa: comments from code for cleaner user display.

Note: Uses regex-based matching and will remove noqa patterns even within
string literals. This is acceptable for the current use case as noqa comments
in string literals are extremely rare in plot implementation code."

Suggested change
"""Remove # noqa: comments from code for cleaner user display."""
"""Remove ``# noqa:`` comments from code for cleaner user display.
Note:
This uses regex-based matching and will also remove ``noqa`` patterns
that appear inside string literals. This trade-off is acceptable for
the current use case, as ``noqa`` comments inside plot implementation
string literals are expected to be extremely rare.
"""

Copilot uses AI. Check for mistakes.
Comment on lines +517 to +534
<Typography
onClick={(e) => {
e.stopPropagation();
setOpenTooltip(isTooltipOpen ? null : tooltipId);
}}
sx={{
fontSize: '0.8rem',
fontWeight: 600,
fontFamily: '"MonoLisa", monospace',
color: isTooltipOpen ? '#3776AB' : '#9ca3af',
textTransform: 'lowercase',
cursor: 'pointer',
'&:hover': { color: '#3776AB' },
}}
>
{impl.library_id}
</Typography>
</Tooltip>
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The clickable library name tooltip lacks keyboard accessibility. Users who navigate via keyboard cannot activate the tooltip since it's disabled for focus events (disableFocusListener). Consider adding keyboard support by:

  1. Removing disableFocusListener or conditionally enabling it
  2. Adding onKeyDown handler to the Typography component to support Enter/Space key activation
  3. Adding appropriate ARIA attributes (role="button", tabIndex="0") to make it clear the element is interactive

Copilot uses AI. Check for mistakes.
Comment on lines +244 to +273
{!overviewMode && (
<Tab
icon={<CodeIcon sx={{ fontSize: '1.1rem' }} />}
iconPosition="start"
label="Code"
onClick={() => tabIndex === 0 && setTabIndex(null)}
/>
)}
<Tab
icon={<StarIcon sx={{ fontSize: '1.1rem', color: tabIndex === 3 ? '#3776AB' : '#f59e0b' }} />}
icon={<DescriptionIcon sx={{ fontSize: '1.1rem' }} />}
iconPosition="start"
label={qualityScore ? `${Math.round(qualityScore)}` : 'Quality'}
label="Spec"
onClick={() => tabIndex === specTabIndex && setTabIndex(null)}
/>
{!overviewMode && (
<Tab
icon={<ImageIcon sx={{ fontSize: '1.1rem' }} />}
iconPosition="start"
label="Impl"
onClick={() => tabIndex === 2 && setTabIndex(null)}
/>
)}
{!overviewMode && (
<Tab
icon={<StarIcon sx={{ fontSize: '1.1rem', color: tabIndex === 3 ? '#3776AB' : '#f59e0b' }} />}
iconPosition="start"
label={qualityScore ? `${Math.round(qualityScore)}` : 'Quality'}
onClick={() => tabIndex === 3 && setTabIndex(null)}
/>
)}
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Tab components have both onChange (on the parent Tabs) and individual onClick handlers. This creates redundant event handling and potential confusion. The onClick handlers on individual tabs (lines 249, 256, 263, 271) will fire before the onChange handler on the Tabs component (line 227), but both will execute.

The collapse logic in onClick is being duplicated in the handleTabChange function. Consider removing the individual onClick handlers and keeping all the logic in handleTabChange, which already handles the toggle behavior correctly.

Copilot uses AI. Check for mistakes.
Comment thread app/src/components/SpecTabs.tsx Outdated
onTrackEvent?.('tab_collapse', { library: libraryId });
} else {
setTabIndex(newValue);
const tabNames = ['code', 'specification', 'implementation', 'quality'];
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In overview mode, the tab index mapping is incorrect. When overviewMode is true, the Spec tab is at index 0, but the tabNames array at line 181 still includes 'code' as the first element. This means clicking the Spec tab in overview mode will track it as 'code' instead of 'specification'.

The array should be adjusted based on overview mode, or use a mapping approach that accounts for the actual tab configuration. For example:

const tabNames = overviewMode 
  ? ['specification'] 
  : ['code', 'specification', 'implementation', 'quality'];
Suggested change
const tabNames = ['code', 'specification', 'implementation', 'quality'];
const tabNames = overviewMode
? ['specification']
: ['code', 'specification', 'implementation', 'quality'];

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

@MarkusNeusinger MarkusNeusinger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Zusammenfassung der wichtigsten Änderungen

1. Overview/Detail Mode (SpecPage.tsx)

Die URL-Struktur wurde geändert:

  • /{spec_id} → Overview mit Grid aller Implementierungen
  • /{spec_id}/{library} → Detail-Ansicht mit Carousel

Das ermöglicht bessere SEO (jede Impl hat eigene URL) und bessere UX (Übersicht vor Detail).

2. PostMessage Origin Security (proxy.py)

Der target_origin Parameter für postMessage ist jetzt dynamisch statt hardcoded. Das ist wichtig weil:

  • Localhost-Entwicklung sonst nicht funktioniert (Origin mismatch)
  • Die Origin wird gegen eine Whitelist (ALLOWED_ORIGINS) validiert
  • Default ist Production (https://pyplots.ai)

3. Sitemap Expansion (seo.py)

Die Sitemap enthält jetzt alle Impl-URLs zusätzlich zu den Overview-URLs:

  • ~170 Overview-Seiten: /{spec_id}
  • ~1500 Impl-Seiten: /{spec_id}/{library}

Das verbessert die SEO für library-spezifische Suchen (z.B. "bokeh heatmap python").

4. Footer Spacing (Footer.tsx, SpecTabs.tsx)

  • Footer margin reduziert: mt: 8, pt: 5mt: 4, pt: 4
  • TabPanel padding: py: 2pt: 2 (kein bottom padding)

Das sorgt für konsistenten Abstand zwischen Content und Footer auf allen Seiten.

- Fix tabNames mapping in overview mode (was logging 'code' instead of 'specification')
- Remove redundant onClick handlers from Tab components (handleTabChange handles toggle)
- Document regex limitation in strip_noqa_comments docstring

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@MarkusNeusinger MarkusNeusinger merged commit ec44f62 into main Jan 5, 2026
6 of 8 checks passed
@MarkusNeusinger MarkusNeusinger deleted the feature/frontend-design-changes branch January 5, 2026 15:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants