feat: implement comprehensive UI/UX improvements

This commit implements 7 requested improvements:

1. Add footer component to all pages
   - Created reusable AppFooter component
   - Added to layout.tsx with flex-1 main container
   - Includes links to Legal, Disclaimer, Imprint, GitHub

2. Change search highlight from yellow to more discreet color
   - Updated mark styling with purple theme colors
   - Uses color-mix for theme-aware transparency
   - Added subtle border-bottom for better visibility

3. Strip markdown from search results
   - Created stripMarkdown function
   - Removes HTML tags, markdown links, images, formatting
   - Shows clean text descriptions only

4. Add page number links to pagination
   - Created getPageNumbers function with smart ellipsis
   - Shows current page ±2 pages with first/last always visible
   - Example: 1 ... 5 6 [7] 8 9 ... 20

5. Adjust README badge display to be inline
   - Custom marked renderer detects badges (shields.io, badgen, etc.)
   - Applies inline-block with !my-0 !mx-1 align-middle classes
   - Badges now display inline in paragraph flow

6. Fix relative image URLs in READMEs
   - Custom image renderer converts relative to absolute GitHub URLs
   - Handles ./path and path patterns
   - Converts to raw.githubusercontent.com URLs
   - Also handles /blob/ URLs conversion

7. Fix command menu highlight contrast
   - Reuses mark styling from search highlights
   - Consistent purple theme colors across app

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
valknarness
2025-10-29 09:27:01 +01:00
parent 109d82ed23
commit bbe7f9a22d
8 changed files with 179 additions and 47 deletions

View File

@@ -0,0 +1,37 @@
import Link from 'next/link'
export function AppFooter() {
return (
<footer className="border-t border-border/40 px-6 py-12 lg:px-8">
<div className="mx-auto max-w-7xl">
<div className="flex flex-col items-center justify-between gap-4 sm:flex-row">
<div className="text-center sm:text-left">
<div className="gradient-text mb-2 text-xl font-bold">Awesome</div>
<p className="text-sm text-muted-foreground">
Built with 💜💗💛 and maximum awesomeness
</p>
</div>
<div className="flex flex-wrap justify-center gap-6 text-sm">
<Link href="/legal" className="text-muted-foreground hover:text-primary">
Legal
</Link>
<Link href="/disclaimer" className="text-muted-foreground hover:text-primary">
Disclaimer
</Link>
<Link href="/imprint" className="text-muted-foreground hover:text-primary">
Imprint
</Link>
<a
href="https://github.com/sindresorhus/awesome"
target="_blank"
rel="noopener noreferrer"
className="text-muted-foreground hover:text-primary"
>
GitHub
</a>
</div>
</div>
</div>
</footer>
)
}

View File

@@ -8,9 +8,10 @@ import 'highlight.js/styles/github-dark.css'
interface ReadmeViewerProps {
content: string
repositoryUrl?: string
}
export function ReadmeViewer({ content }: ReadmeViewerProps) {
export function ReadmeViewer({ content, repositoryUrl }: ReadmeViewerProps) {
const [html, setHtml] = React.useState('')
React.useEffect(() => {
@@ -30,13 +31,59 @@ export function ReadmeViewer({ content }: ReadmeViewerProps) {
breaks: true,
})
// Custom renderer to fix relative image URLs
if (repositoryUrl) {
const renderer = {
image(href: string, title: string | null, text: string) {
let imgSrc = href
// Convert GitHub URLs to raw content URLs
if (repositoryUrl.includes('github.com')) {
const match = repositoryUrl.match(/github\.com\/([^\/]+)\/([^\/]+)/)
if (match) {
const [, owner, repo] = match
const cleanRepo = repo.replace(/\.git$/, '')
// Handle relative URLs
if (!imgSrc.startsWith('http://') && !imgSrc.startsWith('https://') && !imgSrc.startsWith('//')) {
// Remove leading ./
imgSrc = imgSrc.replace(/^\.\//, '')
// Build raw GitHub URL (main/master branch assumed)
imgSrc = `https://raw.githubusercontent.com/${owner}/${cleanRepo}/master/${imgSrc}`
}
// Handle GitHub blob URLs - convert to raw
else if (imgSrc.includes('github.com') && imgSrc.includes('/blob/')) {
imgSrc = imgSrc.replace('github.com', 'raw.githubusercontent.com').replace('/blob/', '/')
}
}
}
const titleAttr = title ? ` title="${title}"` : ''
// Check if it's a badge (shields.io, badgen, etc.)
const isBadge = imgSrc.includes('shields.io') ||
imgSrc.includes('badgen.net') ||
imgSrc.includes('badge') ||
imgSrc.includes('img.shields') ||
imgSrc.match(/\/badges?\//)
if (isBadge) {
return `<img src="${imgSrc}" alt="${text}" ${titleAttr} class="inline-block !my-0 !mx-1 align-middle" />`
}
return `<img src="${imgSrc}" alt="${text}" ${titleAttr} />`
}
}
marked.use({ renderer })
}
// Parse markdown
const parseMarkdown = async () => {
const result = await marked.parse(content)
setHtml(result)
}
parseMarkdown()
}, [content])
}, [content, repositoryUrl])
return (
<article