Browse Source

fix navigation and responsiveness

main
Silberengel 4 weeks ago
parent
commit
7f5ccdfb1d
  1. 9
      src/app.css
  2. 104
      src/lib/components/Footer.svelte
  3. 250
      src/lib/components/NavBar.svelte
  4. 6
      src/routes/+layout.svelte
  5. 59
      src/routes/+page.svelte
  6. 3
      src/routes/docs/+page.svelte
  7. 1
      src/routes/docs/nip34/+page.svelte
  8. 14
      src/routes/repos/[npub]/[repo]/+page.svelte
  9. 1
      src/routes/repos/[npub]/[repo]/settings/+page.svelte
  10. 1
      src/routes/search/+page.svelte
  11. 1
      src/routes/users/[npub]/+page.svelte

9
src/app.css

@ -153,6 +153,15 @@ a:hover {
padding: 2rem; padding: 2rem;
} }
/* Mobile responsive for containers */
@media (max-width: 768px) {
.container,
.container-narrow,
.container-wide {
padding: 1rem;
}
}
/* Header and Navigation */ /* Header and Navigation */
header { header {
display: flex; display: flex;

104
src/lib/components/Footer.svelte

@ -0,0 +1,104 @@
<script lang="ts">
function scrollToTop() {
window.scrollTo({ top: 0, behavior: 'smooth' });
}
</script>
<footer class="site-footer">
<div class="footer-content">
<button onclick={scrollToTop} class="back-to-top-button">↑ Back to Top</button>
<div class="footer-copyright">
<a href="https://gitcitadel.com" class="copyright-link" target="_blank" rel="noopener noreferrer">
GitCitadel LLC
</a> ©2026 All rights reserved.
</div>
</div>
</footer>
<style>
.site-footer {
margin-top: 4rem;
padding: 2rem 0;
border-top: 1px solid var(--border-color);
background: var(--bg-primary);
}
.footer-content {
max-width: 1200px;
margin: 0 auto;
padding: 0 2rem;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 1rem;
box-sizing: border-box;
width: 100%;
}
.back-to-top-button {
background: var(--button-primary);
color: white;
border: none;
padding: 0.5rem 1rem;
border-radius: 0.375rem;
cursor: pointer;
font-size: 0.875rem;
font-family: 'IBM Plex Serif', serif;
transition: background 0.2s ease, transform 0.2s ease;
}
.back-to-top-button:hover {
background: var(--button-primary-hover);
transform: translateY(-2px);
}
.back-to-top-button:active {
transform: translateY(0);
}
.footer-copyright {
font-size: 0.875rem;
color: var(--text-muted);
}
.copyright-link {
color: var(--link-color);
text-decoration: none;
transition: color 0.2s ease;
}
.copyright-link:hover {
color: var(--link-hover);
text-decoration: underline;
}
@media (max-width: 768px) {
.site-footer {
padding: 1.5rem 0;
margin-top: 2rem;
}
.footer-content {
flex-direction: column;
text-align: center;
padding: 0 1rem;
gap: 1rem;
}
.back-to-top-button {
width: 100%;
max-width: 200px;
}
}
@media (max-width: 480px) {
.footer-content {
padding: 0 0.75rem;
}
.footer-copyright {
font-size: 0.75rem;
}
}
</style>

250
src/lib/components/NavBar.svelte

@ -0,0 +1,250 @@
<script lang="ts">
import { page } from '$app/stores';
import { getPublicKeyWithNIP07, isNIP07Available } from '../services/nostr/nip07-signer.js';
import { nip19 } from 'nostr-tools';
import ThemeToggle from './ThemeToggle.svelte';
import UserBadge from './UserBadge.svelte';
import { onMount } from 'svelte';
let userPubkey = $state<string | null>(null);
onMount(async () => {
await checkAuth();
});
async function checkAuth() {
try {
if (isNIP07Available()) {
userPubkey = await getPublicKeyWithNIP07();
}
} catch (err) {
console.log('NIP-07 not available or user not connected');
userPubkey = null;
}
}
async function login() {
try {
if (!isNIP07Available()) {
alert('NIP-07 extension not found. Please install a Nostr extension like Alby or nos2x.');
return;
}
userPubkey = await getPublicKeyWithNIP07();
} catch (err) {
console.error('Login error:', err);
}
}
function logout() {
userPubkey = null;
}
function isActive(path: string): boolean {
return $page.url.pathname === path || $page.url.pathname.startsWith(path + '/');
}
</script>
<header class="site-header">
<div class="header-container">
<a href="/" class="header-logo">
<img src="/GR_logo.png" alt="GitRepublic Logo" class="main-logo" />
<h1>gitrepublic</h1>
</a>
<nav>
<div class="nav-links">
<a href="/" class:active={isActive('/') && $page.url.pathname === '/'}>Repositories</a>
<a href="/search" class:active={isActive('/search')}>Search</a>
<a href="/signup" class:active={isActive('/signup')}>Sign Up</a>
<a href="/docs" class:active={isActive('/docs')}>Docs</a>
</div>
</nav>
<div class="auth-section">
<ThemeToggle />
{#if userPubkey}
<UserBadge pubkey={userPubkey} />
<button onclick={logout} class="logout-button">Logout</button>
{:else}
<button onclick={login} class="login-button" disabled={!isNIP07Available()}>
{isNIP07Available() ? 'Login' : 'NIP-07 Not Available'}
</button>
{/if}
</div>
</div>
</header>
<style>
.site-header {
border-bottom: 1px solid var(--border-color);
margin-bottom: 2rem;
background: var(--bg-primary);
}
.header-container {
max-width: 1200px;
margin: 0 auto;
padding: 1rem 2rem;
display: flex;
align-items: center;
justify-content: space-between;
gap: 2rem;
box-sizing: border-box;
width: 100%;
}
.header-logo {
display: flex;
align-items: center;
gap: 1rem;
text-decoration: none;
color: inherit;
transition: opacity 0.2s ease;
flex-shrink: 0;
}
.header-logo:hover {
opacity: 0.8;
}
.main-logo {
height: 48px;
width: 48px;
object-fit: cover;
border-radius: 50%;
}
.header-logo h1 {
margin: 0;
font-size: 1.75rem;
font-weight: 600;
color: var(--text-primary);
}
nav {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
}
.nav-links {
display: flex;
gap: 1rem;
align-items: center;
}
.nav-links a {
text-decoration: none;
color: var(--link-color);
transition: color 0.2s ease;
padding: 0.5rem 0.75rem;
border-radius: 0.375rem;
position: relative;
white-space: nowrap;
}
.nav-links a:hover {
color: var(--link-hover);
}
.nav-links a.active {
color: var(--accent);
font-weight: 600;
background: var(--bg-secondary);
}
.nav-links a.active::after {
content: '';
position: absolute;
bottom: -1rem;
left: 50%;
transform: translateX(-50%);
width: 80%;
height: 2px;
background: var(--accent);
}
.auth-section {
display: flex;
align-items: center;
gap: 0.5rem;
flex-shrink: 0;
}
/* Mobile responsive styles */
@media (max-width: 768px) {
.header-container {
flex-direction: column;
padding: 1rem;
gap: 1rem;
}
.header-logo h1 {
font-size: 1.5rem;
}
.main-logo {
height: 40px;
width: 40px;
}
nav {
width: 100%;
}
.nav-links {
flex-wrap: wrap;
justify-content: center;
gap: 0.5rem;
width: 100%;
}
.nav-links a {
font-size: 0.875rem;
padding: 0.4rem 0.6rem;
}
.nav-links a.active::after {
display: none;
}
.auth-section {
width: 100%;
justify-content: center;
flex-wrap: wrap;
}
.auth-section button {
font-size: 0.875rem;
padding: 0.4rem 0.8rem;
}
}
@media (max-width: 480px) {
.header-container {
padding: 0.75rem;
}
.header-logo {
gap: 0.5rem;
}
.header-logo h1 {
font-size: 1.25rem;
}
.main-logo {
height: 32px;
width: 32px;
}
.nav-links {
flex-direction: column;
width: 100%;
}
.nav-links a {
width: 100%;
text-align: center;
}
}
</style>

6
src/routes/+layout.svelte

@ -1,6 +1,8 @@
<script lang="ts"> <script lang="ts">
import '../app.css'; import '../app.css';
import { onMount, setContext } from 'svelte'; import { onMount, setContext } from 'svelte';
import Footer from '$lib/components/Footer.svelte';
import NavBar from '$lib/components/NavBar.svelte';
// Theme management - default to dark // Theme management - default to dark
let theme: 'light' | 'dark' = 'dark'; let theme: 'light' | 'dark' = 'dark';
@ -48,4 +50,8 @@
}); });
</script> </script>
<NavBar />
<slot /> <slot />
<Footer />

59
src/routes/+page.svelte

@ -6,15 +6,11 @@
import { KIND } from '../lib/types/nostr.js'; import { KIND } from '../lib/types/nostr.js';
import type { NostrEvent } from '../lib/types/nostr.js'; import type { NostrEvent } from '../lib/types/nostr.js';
import { nip19 } from 'nostr-tools'; import { nip19 } from 'nostr-tools';
import { getPublicKeyWithNIP07, isNIP07Available } from '../lib/services/nostr/nip07-signer.js';
import { ForkCountService } from '../lib/services/nostr/fork-count-service.js'; import { ForkCountService } from '../lib/services/nostr/fork-count-service.js';
import ThemeToggle from '../lib/components/ThemeToggle.svelte';
import UserBadge from '../lib/components/UserBadge.svelte';
let repos = $state<NostrEvent[]>([]); let repos = $state<NostrEvent[]>([]);
let loading = $state(true); let loading = $state(true);
let error = $state<string | null>(null); let error = $state<string | null>(null);
let userPubkey = $state<string | null>(null);
let forkCounts = $state<Map<string, number>>(new Map()); let forkCounts = $state<Map<string, number>>(new Map());
import { DEFAULT_NOSTR_RELAYS } from '../lib/config.js'; import { DEFAULT_NOSTR_RELAYS } from '../lib/config.js';
@ -24,37 +20,8 @@
onMount(async () => { onMount(async () => {
await loadRepos(); await loadRepos();
await checkAuth();
}); });
async function checkAuth() {
try {
if (isNIP07Available()) {
userPubkey = await getPublicKeyWithNIP07();
}
} catch (err) {
console.log('NIP-07 not available or user not connected');
userPubkey = null;
}
}
async function login() {
try {
if (!isNIP07Available()) {
alert('NIP-07 extension not found. Please install a Nostr extension like Alby or nos2x.');
return;
}
userPubkey = await getPublicKeyWithNIP07();
} catch (err) {
error = err instanceof Error ? err.message : 'Failed to connect';
console.error('Login error:', err);
}
}
function logout() {
userPubkey = null;
}
async function loadRepos() { async function loadRepos() {
loading = true; loading = true;
error = null; error = null;
@ -255,32 +222,6 @@
</svelte:head> </svelte:head>
<div class="container"> <div class="container">
<header>
<a href="/" class="header-logo">
<img src="/GR_logo.png" alt="GitRepublic Logo" class="main-logo" />
<h1>gitrepublic</h1>
</a>
<nav>
<div class="nav-links">
<a href="/">Repositories</a>
<a href="/search">Search</a>
<a href="/signup">Sign Up</a>
<a href="/docs">Docs</a>
</div>
<div class="auth-section">
<ThemeToggle />
{#if userPubkey}
<UserBadge pubkey={userPubkey} />
<button onclick={logout} class="logout-button">Logout</button>
{:else}
<button onclick={login} class="login-button" disabled={!isNIP07Available()}>
{isNIP07Available() ? 'Login' : 'NIP-07 Not Available'}
</button>
{/if}
</div>
</nav>
</header>
<main> <main>
<div class="repos-header"> <div class="repos-header">
<h2>Repositories on {$page.data.gitDomain || 'localhost:6543'}</h2> <h2>Repositories on {$page.data.gitDomain || 'localhost:6543'}</h2>

3
src/routes/docs/+page.svelte

@ -42,8 +42,7 @@
<div class="container-wide"> <div class="container-wide">
<header> <header>
<a href="/" class="back-link">← Back to Repositories</a> <h1>Documentation</h1>
<h1>GitRepublic Documentation</h1>
<p class="subtitle">Complete tutorial and walkthrough for decentralized git hosting</p> <p class="subtitle">Complete tutorial and walkthrough for decentralized git hosting</p>
</header> </header>

1
src/routes/docs/nip34/+page.svelte

@ -42,7 +42,6 @@
<div class="container-wide"> <div class="container-wide">
<header> <header>
<a href="/" class="back-link">← Back to Repositories</a>
<h1>GitRepublic Documentation</h1> <h1>GitRepublic Documentation</h1>
<p class="subtitle">Complete tutorial and walkthrough for decentralized git hosting</p> <p class="subtitle">Complete tutorial and walkthrough for decentralized git hosting</p>
</header> </header>

14
src/routes/repos/[npub]/[repo]/+page.svelte

@ -1085,7 +1085,6 @@
</div> </div>
{/if} {/if}
<div class="header-left"> <div class="header-left">
<a href="/" class="back-link">← Back to Repositories</a>
<div class="repo-title-section"> <div class="repo-title-section">
{#if repoImage} {#if repoImage}
<img src={repoImage} alt="" class="repo-image" /> <img src={repoImage} alt="" class="repo-image" />
@ -1100,7 +1099,7 @@
<span class="npub"> <span class="npub">
by <a href={`/users/${npub}`}>{npub.slice(0, 16)}...</a> by <a href={`/users/${npub}`}>{npub.slice(0, 16)}...</a>
</span> </span>
<a href="/docs" class="docs-link" target="_blank" title="GitRepublic Documentation">📖</a> <a href="/docs" class="docs-link" target="_blank" title="Documentation">📖</a>
{#if forkInfo?.isFork && forkInfo.originalRepo} {#if forkInfo?.isFork && forkInfo.originalRepo}
<span class="fork-badge">Forked from <a href={`/repos/${forkInfo.originalRepo.npub}/${forkInfo.originalRepo.repo}`}>{forkInfo.originalRepo.repo}</a></span> <span class="fork-badge">Forked from <a href={`/repos/${forkInfo.originalRepo.npub}/${forkInfo.originalRepo.repo}`}>{forkInfo.originalRepo.repo}</a></span>
{/if} {/if}
@ -1861,17 +1860,6 @@
text-decoration: underline; text-decoration: underline;
} }
.back-link {
color: var(--link-color);
text-decoration: none;
font-size: 0.875rem;
transition: color 0.2s ease;
}
.back-link:hover {
color: var(--link-hover);
text-decoration: underline;
}
header h1 { header h1 {
margin: 0; margin: 0;

1
src/routes/repos/[npub]/[repo]/settings/+page.svelte

@ -116,7 +116,6 @@
<div class="container"> <div class="container">
<header> <header>
<a href={`/repos/${npub}/${repo}`} class="back-link">← Back to Repository</a>
<h1>Repository Settings</h1> <h1>Repository Settings</h1>
</header> </header>

1
src/routes/search/+page.svelte

@ -44,7 +44,6 @@
<div class="container"> <div class="container">
<header> <header>
<a href="/" class="back-link">← Back to Repositories</a>
<h1>Search</h1> <h1>Search</h1>
</header> </header>

1
src/routes/users/[npub]/+page.svelte

@ -104,7 +104,6 @@
<div class="container"> <div class="container">
<header> <header>
<a href="/" class="back-link">← Back to Repositories</a>
<div class="profile-header"> <div class="profile-header">
{#if userProfile?.picture} {#if userProfile?.picture}
<img src={userProfile.picture} alt="Profile" class="profile-picture" /> <img src={userProfile.picture} alt="Profile" class="profile-picture" />

Loading…
Cancel
Save