fix: responsive design

This commit is contained in:
rishikanthc
2025-12-15 13:34:19 -08:00
committed by Rishikanth Chandrasekaran
parent aba5562624
commit 3bbcbcfd63
13 changed files with 211 additions and 54 deletions

View File

@@ -1044,6 +1044,26 @@ const docTemplate = `{
}
}
},
"/api/v1/events": {
"get": {
"description": "Subscribe to server-sent events",
"produces": [
"text/event-stream"
],
"tags": [
"events"
],
"summary": "SSE Events",
"responses": {
"200": {
"description": "stream",
"schema": {
"type": "string"
}
}
}
}
},
"/api/v1/llm/config": {
"get": {
"security": [

View File

@@ -1038,6 +1038,26 @@
}
}
},
"/api/v1/events": {
"get": {
"description": "Subscribe to server-sent events",
"produces": [
"text/event-stream"
],
"tags": [
"events"
],
"summary": "SSE Events",
"responses": {
"200": {
"description": "stream",
"schema": {
"type": "string"
}
}
}
}
},
"/api/v1/llm/config": {
"get": {
"security": [

View File

@@ -1427,6 +1427,19 @@ paths:
summary: Validate OpenAI API Key
tags:
- config
/api/v1/events:
get:
description: Subscribe to server-sent events
produces:
- text/event-stream
responses:
"200":
description: stream
schema:
type: string
summary: SSE Events
tags:
- events
/api/v1/llm/config:
get:
description: Get the current active LLM configuration

View File

@@ -23,8 +23,6 @@ import (
"scriberr/internal/transcription/adapters"
"scriberr/internal/transcription/registry"
"scriberr/pkg/logger"
_ "scriberr/api-docs" // Import generated Swagger docs
)
// Version information (set by GoReleaser)

View File

@@ -7,8 +7,6 @@ import (
"scriberr/pkg/middleware"
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
)
// SetupRoutes sets up all API routes
@@ -46,9 +44,6 @@ func SetupRoutes(handler *Handler, authService *auth.AuthService) *gin.Engine {
// Health check endpoint (no auth required)
router.GET("/health", handler.HealthCheck)
// Swagger documentation
router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
// CLI install script alias (root level for easier access)
router.GET("/install.sh", handler.GetInstallScript)
router.GET("/install-cli.sh", handler.GetInstallScript)

View File

@@ -4,7 +4,7 @@ install:
npm install
generate-api:
cd ../.. && swag init -g server/main.go -o api-docs --dir cmd,internal
cd ../.. && swag init -g server/main.go -o web/project-site/public/api --outputTypes json --dir cmd,internal
build: generate-api
npm run build

View File

@@ -4,7 +4,7 @@
"version": "0.0.0",
"type": "module",
"scripts": {
"sync:spec": "mkdir -p public/api && cp -f ../../api-docs/swagger.json public/api/swagger.json || echo 'No swagger.json found, skipping copy'",
"sync:spec": "echo 'Swagger spec is generated by make generate-api'",
"sync:undoc": "node ./scripts/gen-endpoints.mjs",
"dev": "npm run sync:spec && npm run sync:undoc && vite",
"build": "npm run sync:spec && npm run sync:undoc && tsc -b && vite build",

View File

@@ -1,8 +1,11 @@
import React from 'react';
import { Link, useLocation } from 'react-router-dom';
import { ScriberrLogo } from './components/ScriberrLogo';
import { Github, Book, Code } from 'lucide-react';
import { Button } from './components/ui/Button';
import { Github, Book, Menu, X } from 'lucide-react';
import { useState } from 'react';
import { GithubBadge } from './components/GithubBadge';
interface LayoutProps {
children: React.ReactNode;
@@ -11,6 +14,7 @@ interface LayoutProps {
export function Layout({ children }: LayoutProps) {
const location = useLocation();
const isDocs = location.pathname.startsWith('/docs');
const [isMenuOpen, setIsMenuOpen] = useState(false);
return (
<div className="min-h-screen flex flex-col font-sans selection:bg-[#FF6D20] selection:text-white">
@@ -22,33 +26,55 @@ export function Layout({ children }: LayoutProps) {
<ScriberrLogo />
</Link>
{isDocs && (
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-semibold bg-orange-100 text-[#FF6D20] border border-orange-200">
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-semibold bg-orange-100 text-[#FF6D20] border border-orange-200 font-heading">
Docs
</span>
)}
</div>
<nav className="hidden md:flex items-center gap-8">
<Link to="/#features" className="text-sm font-medium text-gray-600 hover:text-gray-900 transition-colors">Features</Link>
<Link to="/docs/intro" className="text-sm font-medium text-gray-600 hover:text-gray-900 transition-colors">Documentation</Link>
<Link to="/api" className="text-sm font-medium text-gray-600 hover:text-gray-900 transition-colors">API</Link>
<Link to="/docs/intro" className="text-sm text-gray-600 hover:text-gray-900 transition-colors font-heading">Documentation</Link>
<Link to="/api" className="text-sm text-gray-600 hover:text-gray-900 transition-colors font-heading">API</Link>
</nav>
<div className="flex items-center gap-4">
<a
href="https://github.com/rishikanthc/Scriberr"
target="_blank"
rel="noopener noreferrer"
className="p-2 text-gray-500 hover:text-gray-900 transition-colors"
aria-label="GitHub"
>
<Github className="w-5 h-5" />
</a>
<div className="hidden sm:block">
<Button variant="primary" size="sm">Get Started</Button>
<div className="hidden md:block">
<GithubBadge />
</div>
<button
onClick={() => setIsMenuOpen(!isMenuOpen)}
className="md:hidden flex items-center justify-center w-10 h-10 rounded-xl bg-white/80 border border-gray-200 shadow-sm text-gray-600 hover:text-[#FF6D20] hover:border-orange-200 hover:bg-orange-50 transition-all duration-200"
aria-label="Toggle menu"
>
{isMenuOpen ? <X className="w-5 h-5" strokeWidth={2.5} /> : <Menu className="w-5 h-5" strokeWidth={2.5} />}
</button>
</div>
</div>
{/* Mobile Menu Overlay */}
{isMenuOpen && (
<div className="md:hidden absolute top-16 left-0 right-0 bg-white border-b border-gray-100 shadow-lg animate-fade-in z-40">
<nav className="flex flex-col p-4 space-y-4">
<Link
to="/docs/intro"
className="text-sm font-medium text-gray-600 hover:text-gray-900 py-2 border-b border-gray-50 font-heading"
onClick={() => setIsMenuOpen(false)}
>
Documentation
</Link>
<Link
to="/api"
className="text-sm font-medium text-gray-600 hover:text-gray-900 py-2 border-b border-gray-50 font-heading"
onClick={() => setIsMenuOpen(false)}
>
API
</Link>
<div className="pt-2">
<GithubBadge />
</div>
</nav>
</div>
)}
</header>
{/* Main Content */}
@@ -57,21 +83,23 @@ export function Layout({ children }: LayoutProps) {
</main>
{/* Footer */}
<footer className="border-t border-gray-100 bg-gray-50 py-12">
<footer className="border-t border-gray-100 bg-gray-50 py-6">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex flex-col md:flex-row justify-between items-center gap-6">
<div className="flex items-center gap-2">
<span className="text-sm text-gray-500">© 2025 Scriberr. All rights reserved.</span>
</div>
<div className="flex items-center gap-6">
<a href="#" className="text-gray-400 hover:text-gray-600 transition-colors">
<a
href="https://github.com/rishikanthc/scriberr"
target="_blank"
rel="noopener noreferrer"
className="text-gray-400 hover:text-gray-600 transition-colors"
>
<Github className="w-5 h-5" />
</a>
<Link to="/docs" className="text-gray-400 hover:text-gray-600 transition-colors">
<Link to="/docs/intro" className="text-gray-400 hover:text-gray-600 transition-colors">
<Book className="w-5 h-5" />
</Link>
<a href="#" className="text-gray-400 hover:text-gray-600 transition-colors">
<Code className="w-5 h-5" />
</a>
</div>
</div>
</footer>

View File

@@ -0,0 +1,56 @@
import { useEffect, useState } from 'react';
import { Github, Star, GitFork } from 'lucide-react';
interface RepoStats {
stargazers_count: number;
forks_count: number;
}
export function GithubBadge() {
const [stats, setStats] = useState<RepoStats | null>(null);
useEffect(() => {
fetch('https://api.github.com/repos/rishikanthc/scriberr')
.then(res => res.json())
.then(data => {
setStats({
stargazers_count: data.stargazers_count,
forks_count: data.forks_count
});
})
.catch(err => console.error('Failed to fetch repo stats:', err));
}, []);
const formatCount = (count: number) => {
if (count >= 1000) {
return (count / 1000).toFixed(1) + 'k';
}
return count;
};
return (
<a
href="https://github.com/rishikanthc/scriberr"
target="_blank"
rel="noopener noreferrer"
className="group flex items-center gap-0 rounded-md overflow-hidden border border-gray-200 shadow-sm hover:shadow transition-all duration-200"
>
<div className="flex items-center gap-2 px-3 py-1.5 bg-gray-50 group-hover:bg-gray-100 transition-colors border-r border-gray-200">
<Github className="w-4 h-4 text-gray-700" />
<span className="text-xs font-semibold text-gray-700 font-heading">Star</span>
</div>
{stats && (
<div className="flex items-center bg-white">
<div className="flex items-center gap-1 px-2 py-1.5 border-r border-gray-100 last:border-0 hover:bg-gray-50 transition-colors">
<Star className="w-3.5 h-3.5 text-amber-400 fill-amber-400" />
<span className="text-xs font-medium text-gray-600 font-mono">{formatCount(stats.stargazers_count)}</span>
</div>
<div className="flex items-center gap-1 px-2 py-1.5 hover:bg-gray-50 transition-colors">
<GitFork className="w-3.5 h-3.5 text-gray-400" />
<span className="text-xs font-medium text-gray-600 font-mono">{formatCount(stats.forks_count)}</span>
</div>
</div>
)}
</a>
);
}

View File

@@ -16,7 +16,7 @@ export function Button({
...props
}: ButtonProps) {
const baseStyles = "inline-flex items-center justify-center font-medium transition-all duration-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed";
const baseStyles = "cursor-pointer inline-flex items-center justify-center font-medium transition-all duration-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed";
const variants = {
primary: "bg-gray-900 text-white shadow-lg hover:shadow-xl hover:-translate-y-0.5 focus:ring-gray-900",

View File

@@ -1,3 +1,4 @@
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&family=Work+Sans:wght@300;400;500;600&display=swap');
@import "tailwindcss";
@theme {
@@ -5,6 +6,8 @@
--image-brand-gradient: var(--brand-gradient);
--font-sans: Inter, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
--font-heading: "Plus Jakarta Sans", sans-serif;
--font-body: 'Work Sans', sans-serif;
--animate-fade-in: fadeIn 0.5s ease-out;
--animate-slide-up: slideUp 0.5s ease-out;
@@ -41,8 +44,6 @@
--color-bg-primary: #ffffff;
--color-bg-secondary: #f9fafb;
--color-text-primary: #111827;
--font-heading: 'Plus Jakarta Sans', sans-serif;
--font-body: 'Work Sans', sans-serif;
}
body {

View File

@@ -3,6 +3,8 @@ import { Link, useLocation } from 'react-router-dom';
import { Layout } from '../Layout';
import { Section } from '../components/ui/Section';
import { TableOfContents } from '../components/TableOfContents';
import { useState } from 'react';
import { Menu, X } from 'lucide-react';
interface DocsLayoutProps {
children: React.ReactNode;
@@ -10,10 +12,10 @@ interface DocsLayoutProps {
export function DocsLayout({ children }: DocsLayoutProps) {
const location = useLocation();
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
const navItems = [
{ path: '/docs/intro', label: 'Introduction' },
{ path: '/docs/features', label: 'Features' },
{ path: '/docs/installation', label: 'Installation' },
{ path: '/docs/configuration', label: 'Configuration' },
{ path: '/docs/usage', label: 'Usage Guide' },
@@ -23,9 +25,20 @@ export function DocsLayout({ children }: DocsLayoutProps) {
<Layout>
<Section className="py-0">
<div className="flex w-full max-w-7xl mx-auto">
{/* Sidebar Navigation - Fixed & Sticky */}
<aside className="hidden lg:block w-64 shrink-0 fixed top-24 bottom-0 overflow-y-auto pr-8">
<div className="py-2 space-y-8">
{/* Mobile Sidebar Backdrop */}
{isSidebarOpen && (
<div
className="fixed inset-0 bg-black/20 z-30 lg:hidden"
onClick={() => setIsSidebarOpen(false)}
/>
)}
{/* Sidebar Navigation */}
<aside className={`
fixed top-16 bottom-0 left-0 w-64 bg-white border-r border-gray-100 z-40 transform transition-transform duration-300 lg:translate-x-0 lg:static lg:border-r-0 lg:block lg:z-auto
${isSidebarOpen ? 'translate-x-0' : '-translate-x-full'}
`}>
<div className="h-full overflow-y-auto px-4 lg:px-0 py-6 lg:py-2 space-y-8">
<div>
<ul className="space-y-1">
{navItems.map((item) => {
@@ -34,6 +47,7 @@ export function DocsLayout({ children }: DocsLayoutProps) {
<li key={item.path}>
<Link
to={item.path}
onClick={() => setIsSidebarOpen(false)}
className={`block px-3 py-1.5 rounded-md text-sm font-[family-name:var(--font-heading)] transition-colors duration-200 ${isActive
? 'text-[#FF6D20] font-medium bg-orange-50'
: 'text-gray-500 hover:text-gray-900 hover:bg-gray-50'
@@ -49,16 +63,32 @@ export function DocsLayout({ children }: DocsLayoutProps) {
</div>
</aside>
{/* Main Content Area - Fluid with max-width - Offset for fixed sidebar */}
<div className="flex-1 min-w-0 lg:pl-72 lg:pr-8 pt-0 pb-12">
<article className="docs-content max-w-3xl">
{/* Main Content Area */}
<div className="flex-1 min-w-0 px-4 sm:px-6 lg:pl-12 lg:pr-8 pt-6 lg:pt-0 pb-12 w-full">
{/* Mobile Sidebar Toggle */}
<div className="lg:hidden mb-6 flex items-center justify-between sticky top-0 bg-white/80 backdrop-blur-md py-4 z-20 border-b border-gray-100 -mx-4 px-4 sm:-mx-6 sm:px-6">
<button
onClick={() => setIsSidebarOpen(!isSidebarOpen)}
className="flex items-center gap-2 px-3 py-1.5 rounded-lg bg-orange-50 border border-orange-100 text-[#FF6D20] font-semibold text-sm hover:bg-orange-100 transition-colors"
>
{isSidebarOpen ? <X className="w-4 h-4" strokeWidth={2.5} /> : <Menu className="w-4 h-4" strokeWidth={2.5} />}
<span>Menu</span>
</button>
<div className="text-xs font-bold text-gray-900 uppercase tracking-wider bg-gray-100 px-2 py-1 rounded">
{navItems.find(i => i.path === location.pathname)?.label || 'Docs'}
</div>
</div>
<article className="docs-content max-w-3xl mx-auto lg:mx-0">
{children}
</article>
</div>
{/* Right Sidebar - For TOC */}
<div className="hidden xl:block shrink-0">
<TableOfContents />
<div className="hidden xl:block w-64 shrink-0">
<div className="sticky top-24">
<TableOfContents />
</div>
</div>
</div>
</Section>

View File

@@ -22,7 +22,7 @@ export function Home() {
return (
<div className="space-y-12">
{/* Hero Section */}
<Section className="min-h-[80vh] flex flex-col items-center justify-center text-center">
<Section className="!py-12 sm:py-10 min-h-[auto] md:min-h-[80vh] flex flex-col items-center justify-center text-center">
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
@@ -34,7 +34,7 @@ export function Home() {
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-orange-400 opacity-75"></span>
<span className="relative inline-flex rounded-full h-2 w-2 bg-orange-500"></span>
</span>
v1.0 Now Available
v1.2.0 Now Available
</div>
<Heading level={1}>
@@ -47,12 +47,9 @@ export function Home() {
State-of-the-art AI models, running entirely on your machine.
</Paragraph>
<div className="flex flex-col sm:flex-row items-center justify-center gap-4 pt-4">
<Link to="/docs/installation">
<Button size="lg">Download Now</Button>
</Link>
<div className="flex items-center justify-center pt-4">
<Link to="/docs/intro">
<Button variant="secondary" size="lg">View Documentation</Button>
<Button size="lg" className="px-8">Get Started</Button>
</Link>
</div>
</motion.div>
@@ -62,7 +59,7 @@ export function Home() {
initial={{ opacity: 0, y: 50 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 1, delay: 0.2 }}
className="relative w-full max-w-6xl mx-auto mt-20 perspective-1000"
className="relative w-full max-w-6xl mx-auto mt-8 md:mt-20 perspective-1000"
>
{/* Gradient Glow */}
<div className="absolute inset-0 bg-[image:var(--image-brand-gradient)] opacity-20 rounded-[2rem] blur-3xl -z-10 transform scale-90"></div>
@@ -75,11 +72,10 @@ export function Home() {
/>
</div>
{/* Floating Mobile Mockup */}
<motion.div
animate={{ y: [0, -10, 0] }}
transition={{ duration: 6, repeat: Infinity, ease: "easeInOut" }}
className="absolute -bottom-12 -right-4 md:-right-12 w-[240px] md:w-[300px] rounded-[2.5rem] border-8 border-gray-900 overflow-hidden shadow-2xl bg-white"
className="hidden md:block absolute -bottom-12 -right-12 w-[300px] rounded-[2.5rem] border-8 border-gray-900 overflow-hidden shadow-2xl bg-white"
>
<img
src="/screenshots/mobile-transcript-light.PNG"