mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat: add live GitHub star count, legal footer section, FAQ page
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
"use client";
|
||||
|
||||
import { ChevronDown } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
|
||||
import { FadeIn } from "@/components/fade-in";
|
||||
import { Footer } from "@/components/footer";
|
||||
import { Navbar } from "@/components/navbar";
|
||||
|
||||
const faqs = [
|
||||
{
|
||||
question: "Are my files safe and private?",
|
||||
answer:
|
||||
"All processing happens on your own server. Your images are never sent to any external service. SnapOtter runs entirely on your infrastructure.",
|
||||
},
|
||||
{
|
||||
question: "Is SnapOtter really free?",
|
||||
answer:
|
||||
"Yes. SnapOtter is open source under AGPL-3.0 with no hidden fees, no subscription plans, no usage limits, and no premium-only features. A commercial license is available for organizations that need proprietary use.",
|
||||
},
|
||||
{
|
||||
question: "Do I need an internet connection?",
|
||||
answer:
|
||||
"No. SnapOtter runs fully offline once deployed. No external API calls are made. It works in completely air-gapped environments.",
|
||||
},
|
||||
{
|
||||
question: "Are there any file size or usage limitations?",
|
||||
answer:
|
||||
"No artificial limits. You can process as many images as you want, at any size. The only constraint is your server's hardware resources.",
|
||||
},
|
||||
{
|
||||
question: "What AI features are included?",
|
||||
answer:
|
||||
"SnapOtter includes 14 local AI models for background removal, upscaling, OCR, face detection, colorization, noise removal, photo restoration, and more. All models run on your hardware.",
|
||||
},
|
||||
{
|
||||
question: "What technology does SnapOtter use?",
|
||||
answer:
|
||||
"The backend uses Node.js with Sharp for high-performance image processing. AI features use Python with models like RealESRGAN, rembg, and PaddleOCR. The frontend is React with a REST API.",
|
||||
},
|
||||
{
|
||||
question: "Can I use SnapOtter in my company?",
|
||||
answer:
|
||||
"Yes. Under AGPL-3.0, you can use it freely as long as you comply with the license terms. For proprietary or closed-source use, a commercial license is available.",
|
||||
},
|
||||
{
|
||||
question: "How do I update SnapOtter?",
|
||||
answer:
|
||||
"Pull the latest Docker image and restart the container. Your data persists in the mounted volume.",
|
||||
},
|
||||
];
|
||||
|
||||
function FaqItem({ question, answer }: { question: string; answer: string }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="border-b border-border">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen(!open)}
|
||||
className="flex w-full items-center justify-between py-5 text-left"
|
||||
>
|
||||
<span className="text-base font-medium">{question}</span>
|
||||
<ChevronDown
|
||||
size={18}
|
||||
className={`shrink-0 text-muted transition-transform duration-200 ${open ? "rotate-180" : ""}`}
|
||||
/>
|
||||
</button>
|
||||
{open && <p className="pb-5 text-sm leading-relaxed text-muted">{answer}</p>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function FaqPage() {
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
<main className="pt-16">
|
||||
<section className="px-6 py-24 md:py-32">
|
||||
<div className="mx-auto max-w-3xl">
|
||||
<FadeIn>
|
||||
<div className="text-center">
|
||||
<h1 className="text-4xl font-bold tracking-tight md:text-5xl">
|
||||
Frequently Asked Questions
|
||||
</h1>
|
||||
<p className="mt-6 text-lg text-muted">
|
||||
Everything you need to know about SnapOtter.
|
||||
</p>
|
||||
</div>
|
||||
</FadeIn>
|
||||
|
||||
<FadeIn delay={0.1}>
|
||||
<div className="mt-16 border-t border-border">
|
||||
{faqs.map((faq) => (
|
||||
<FaqItem key={faq.question} question={faq.question} answer={faq.answer} />
|
||||
))}
|
||||
</div>
|
||||
</FadeIn>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -34,12 +34,21 @@ const columns = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Legal",
|
||||
links: [
|
||||
{ label: "Licensing", href: "/#pricing" },
|
||||
{ label: "Terms and Conditions", href: "/terms" },
|
||||
{ label: "Privacy Policy", href: "/privacy" },
|
||||
{ label: "FAQ", href: "/faq" },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export function Footer() {
|
||||
return (
|
||||
<footer className="border-t border-border bg-background-alt px-6 py-16">
|
||||
<div className="mx-auto grid max-w-6xl gap-12 md:grid-cols-4">
|
||||
<div className="mx-auto grid max-w-6xl gap-12 md:grid-cols-5">
|
||||
<div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex h-8 w-8 items-center justify-center rounded-lg bg-accent text-accent-foreground text-sm font-bold">
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
"use client";
|
||||
|
||||
import { Github, Menu, Star, X } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
function formatStarCount(count: number): string {
|
||||
if (count >= 1000) {
|
||||
const k = count / 1000;
|
||||
return `${k % 1 === 0 ? k.toFixed(0) : k.toFixed(1)}k`;
|
||||
}
|
||||
return count.toString();
|
||||
}
|
||||
|
||||
const links = [
|
||||
{ label: "Features", href: "#features" },
|
||||
@@ -12,6 +20,18 @@ const links = [
|
||||
|
||||
export function Navbar() {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [stars, setStars] = useState<string>("Star");
|
||||
|
||||
useEffect(() => {
|
||||
fetch("https://api.github.com/repos/ashim-hq/ashim")
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
if (typeof data.stargazers_count === "number") {
|
||||
setStars(formatStarCount(data.stargazers_count));
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<nav className="fixed top-0 left-0 right-0 z-50 bg-background/60 backdrop-blur-xl after:absolute after:bottom-0 after:left-0 after:h-px after:w-full after:bg-gradient-to-r after:from-transparent after:via-accent/30 after:to-transparent">
|
||||
@@ -47,7 +67,7 @@ export function Navbar() {
|
||||
>
|
||||
<Github size={16} />
|
||||
<Star size={12} className="text-accent" />
|
||||
<span>Star</span>
|
||||
<span>{stars}</span>
|
||||
</a>
|
||||
<a
|
||||
href="/contact"
|
||||
|
||||
Reference in New Issue
Block a user