Files
SnapOtter/apps/landing/src/components/FeatureHighlights.astro
T
SnapOtterandGitHub 17726ae59d docs: multi-modality rebrand, 2.0 architecture accuracy, and full OpenAPI coverage (#254)
* docs: rebrand from image-only to multi-modality across docs and metadata

SnapOtter expanded from image-only to 157 tools across 5 modalities
(image, video, audio, document/PDF, data). Update all product-level
copy, metadata, and i18n that still framed it as an image-only tool.

- README, package.json, root llms.txt: multi-modality framing, 157 tools
- OpenAPI info + tags, generated /llms.txt tagline (docs.ts)
- VitePress docs site: hero, getting-started, architecture, security,
  deployment, configuration, developer, supported-formats
- i18n: 10 product keys across all 21 locales (hero, app description,
  privacy notes, AI features, progress messages, getting-started)
- web/demo/landing meta + privacy copy, COMMUNITY_GUIDE, .env.example

Stale tool counts (53/50+/52/70+/35) corrected to 157 throughout.
Database/container deployment claims left unchanged (out of scope).

* docs: fix stale post-rebrand test assertions and README language list

- tests/e2e-docs/homepage.spec.ts: assert the current docs homepage (file toolkit, 157 tools, 5 modalities) instead of the old image-only strings
- tests/unit/api/docs-route.test.ts: sync the reproduced llms.txt tagline with docs.ts
- README.md: 21 languages with the correct list (add Swedish and Chinese Traditional, drop Czech which is not supported)

* docs: correct 2.0 architecture references (Postgres 17 + Redis 8, 3-container stack)

The docs and metadata still described the 1.x stack (SQLite, single container, p-queue). Update them to the current 2.0 reality.

- README: replace the broken single-container `docker run` quick-start with the real Docker Compose stack (app + Postgres 17 + Redis 8); fix the "no Redis, no Postgres" feature bullet
- package.json: description no longer claims a single container
- apps/docs: rewrite database.md for Postgres; configuration.md DB_PATH -> DATABASE_URL + REDIS_URL; architecture.md SQLite/p-queue/better-sqlite3 -> Postgres/BullMQ/pg and add media-engine + doc-engine; developer/security/deployment/docker-tags/getting-started/contributing compose examples now include postgres + redis; index.md + api/ai.md AI count 16 -> 19
- SECURITY.md: Drizzle (SQLite) -> (PostgreSQL)
- landing: enterprise/FeatureHighlights single-container wording; TrustSignals/ToolGrid 150+ -> 157 (dynamic); Pricing/FAQ 15 -> 19 AI tools

* docs(api): document all video, audio, document, and data tool endpoints in OpenAPI

The spec covered only image tools; the Scalar UI and the generated /llms.txt and /llms-full.txt inherited that gap. Add the 104 missing tool endpoints so the API docs match the code.

- Video: 29 endpoints (most long/async; auto-subtitles is AI)
- Audio: 17 (transcribe-audio is AI)
- Document/PDF: 36 (ocr-pdf is AI; conversions are long/async)
- Data: 10
- Image: 12 newer tools (background-replace, blur-background AI; histogram/lqip-placeholder/sprite-sheet custom responses; barcode-generate uses a JSON body)

Each schema is derived from the tool's Zod validator and executionHint (fast -> 200, long -> 202+SSE, AI adds 501 FeatureNotInstalledError, multi-file inputs as arrays), referencing the existing shared schemas. Tool path entries: 64 -> 168. Spec parses as valid YAML with no duplicate paths and only known $refs.
2026-06-16 18:04:52 +08:00

214 lines
11 KiB
Plaintext

---
const aiTools = [
"Remove Background",
"Image Upscaling",
"Face / PII Blur",
"Object Eraser",
"OCR / Text Extraction",
"PDF OCR",
"AI Colorization",
"Face Enhancement",
"Noise Removal",
"Smart Crop",
"Red Eye Removal",
"Photo Restoration",
"Passport Photo",
"PNG Transparency Fixer",
"AI Canvas Expand",
"Transcribe Audio",
"Auto Subtitles",
"Background Replace",
"Blur Background",
];
const aiPillColors = [
"border-purple-200 bg-purple-50 text-purple-700",
"border-blue-200 bg-blue-50 text-blue-700",
"border-emerald-200 bg-emerald-50 text-emerald-700",
"border-amber-200 bg-amber-50 text-amber-700",
"border-rose-200 bg-rose-50 text-rose-700",
"border-slate-200 bg-slate-50 text-slate-700",
];
---
<section class="px-6 py-20 md:py-28">
<div class="reveal mx-auto mb-16 max-w-3xl text-center md:mb-20">
<span class="inline-flex items-center gap-2 rounded-full border border-primary/20 bg-primary/5 px-4 py-1.5 text-xs font-semibold uppercase tracking-[0.2em] text-primary-dark">
<svg class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
Data sovereignty
</span>
<h2 class="mt-6 font-display text-3xl font-bold tracking-tight sm:text-4xl md:text-5xl">
Built for regulated environments.
</h2>
<p class="mt-4 text-lg leading-relaxed text-muted md:text-xl">
Your files stay on your infrastructure. No third-party processing, no data transfer risk.
</p>
</div>
<div class="mx-auto max-w-6xl space-y-20 md:space-y-28">
<!-- Feature 1: Data Sovereignty — text left, visual right -->
<div class="reveal grid grid-cols-1 md:grid-cols-2 gap-8 md:gap-16 items-center">
<div class="md:order-1">
<span class="font-mono text-xs font-semibold uppercase tracking-[0.15em] text-primary">01 / Architecture</span>
<h3 class="mt-3 font-display text-2xl font-bold tracking-tight sm:text-3xl">
Compliant by architecture
</h3>
<p class="mt-4 text-base leading-relaxed text-muted">
No vendor DPAs. No data processing agreements. No third-party risk assessments.
Files are processed on your infrastructure and never transmitted to external services.
GDPR, HIPAA, and CCPA compliant from day one.
</p>
</div>
<div class="md:order-2">
<div class="rounded-xl border border-border bg-surface p-6 md:p-8">
<!-- Outer infrastructure box -->
<div class="rounded-lg border-2 border-dashed border-primary/30 p-5">
<span class="mb-4 block text-xs font-semibold uppercase tracking-wider text-primary">Your Infrastructure</span>
<div class="grid grid-cols-2 gap-4">
<div class="flex flex-col items-center gap-2 rounded-lg bg-primary-subtle p-4">
<svg class="h-8 w-8 text-primary" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<rect x="2" y="3" width="20" height="18" rx="2" />
<path d="M8 12h8M8 16h5" />
<circle cx="18" cy="7" r="1" fill="currentColor" />
</svg>
<span class="text-xs font-semibold text-primary">SnapOtter</span>
</div>
<div class="flex flex-col items-center gap-2 rounded-lg bg-primary-subtle p-4">
<svg class="h-8 w-8 text-primary" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z" />
<polyline points="13 2 13 9 20 9" />
</svg>
<span class="text-xs font-semibold text-primary">Your Files</span>
</div>
</div>
<!-- Internal arrows -->
<div class="mt-4 flex items-center justify-center gap-2">
<svg class="h-4 w-20 text-primary/50" viewBox="0 0 80 16" fill="none" stroke="currentColor" stroke-width="1.5">
<line x1="8" y1="8" x2="32" y2="8" />
<polygon points="30,4 38,8 30,12" fill="currentColor" stroke="none" />
<line x1="42" y1="8" x2="66" y2="8" />
<polygon points="48,12 40,8 48,4" fill="currentColor" stroke="none" />
</svg>
</div>
<p class="mt-3 text-center text-xs text-muted">Data never leaves this boundary</p>
</div>
<!-- External cloud — blocked -->
<div class="mt-4 flex items-center justify-center gap-3 rounded-lg border border-danger/20 bg-danger/5 px-4 py-2.5">
<svg class="h-4 w-4 shrink-0 text-danger" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10" /><line x1="4.93" y1="4.93" x2="19.07" y2="19.07" />
</svg>
<span class="text-xs text-danger">No external cloud connections</span>
</div>
</div>
</div>
</div>
<!-- Feature 2: REST API — visual left, text right -->
<div class="reveal grid grid-cols-1 md:grid-cols-2 gap-8 md:gap-16 items-center">
<div class="md:order-2">
<span class="font-mono text-xs font-semibold uppercase tracking-[0.15em] text-primary">02 / Automation</span>
<h3 class="mt-3 font-display text-2xl font-bold tracking-tight sm:text-3xl">
Integrate with your existing workflows
</h3>
<p class="mt-4 text-base leading-relaxed text-muted">
Full REST API with OpenAPI documentation for every tool. Chain tools into automated
pipelines. Batch process thousands of files. Trigger via webhooks and integrate
with your internal systems.
</p>
</div>
<div class="md:order-1">
<pre class="code-block p-5"><span class="code-comment"># Resize an image via the API</span>
<span class="code-url">$</span> curl <span class="code-flag">-X POST</span> <span class="code-url">localhost:1349/api/v1/tools/resize</span> \
<span class="code-flag">-F</span> <span class="code-string">"file=@photo.jpg"</span> \
<span class="code-flag">-F</span> <span class="code-string">'settings=&#123;"width":800&#125;'</span>
<span class="code-comment"># Response</span>
&#123;
<span class="code-flag">"jobId"</span>: <span class="code-string">"abc123"</span>,
<span class="code-flag">"downloadUrl"</span>: <span class="code-string">"/api/v1/jobs/abc123/download"</span>
&#125;</pre>
</div>
</div>
<!-- Feature 3: AI Models — text left, visual right -->
<div class="reveal grid grid-cols-1 md:grid-cols-2 gap-8 md:gap-16 items-center">
<div class="md:order-1">
<span class="font-mono text-xs font-semibold uppercase tracking-[0.15em] text-primary">03 / Local AI</span>
<h3 class="mt-3 font-display text-2xl font-bold tracking-tight sm:text-3xl">
AI processing without external APIs
</h3>
<p class="mt-4 text-base leading-relaxed text-muted">
19 local AI tools for OCR, transcription, background removal, upscaling, and more.
No external API keys, no per-request charges, no data sent to third-party AI providers.
Runs on your GPU or CPU within your own infrastructure.
</p>
</div>
<div class="md:order-2">
<div class="flex flex-wrap gap-2.5">
{aiTools.map((tool, i) => (
<span class:list={["inline-flex items-center rounded-full border px-3.5 py-1.5 text-sm font-medium", aiPillColors[i % aiPillColors.length]]}>
{tool}
</span>
))}
</div>
</div>
</div>
<!-- Feature 4: Deployment — visual left, text right -->
<div class="reveal grid grid-cols-1 md:grid-cols-2 gap-8 md:gap-16 items-center">
<div class="md:order-2">
<span class="font-mono text-xs font-semibold uppercase tracking-[0.15em] text-primary">04 / Deployment</span>
<h3 class="mt-3 font-display text-2xl font-bold tracking-tight sm:text-3xl">
Fits your infrastructure
</h3>
<p class="mt-4 text-base leading-relaxed text-muted">
One Docker Compose stack: app, Postgres, and Redis. Deploy on Kubernetes,
bare metal, or cloud VMs. ARM and x86 architectures. Works in air-gapped and classified
environments with zero internet dependency.
</p>
</div>
<div class="md:order-1">
<div class="grid grid-cols-3 gap-3">
<!-- Docker -->
<div class="flex flex-col items-center gap-2 rounded-xl border border-border bg-surface p-4 transition-shadow hover:shadow-md">
<img src="/logos/docker.svg" alt="Docker" class="h-8 w-8" width="32" height="32" />
<span class="text-xs font-medium">Docker</span>
</div>
<!-- Kubernetes -->
<div class="flex flex-col items-center gap-2 rounded-xl border border-border bg-surface p-4 transition-shadow hover:shadow-md">
<img src="/logos/kubernetes.svg" alt="Kubernetes" class="h-8 w-8" width="32" height="32" />
<span class="text-xs font-medium">Kubernetes</span>
</div>
<!-- AWS -->
<div class="flex flex-col items-center gap-2 rounded-xl border border-border bg-surface p-4 transition-shadow hover:shadow-md">
<img src="/logos/aws.svg" alt="AWS" class="h-8 w-8" width="32" height="32" />
<span class="text-xs font-medium">AWS</span>
</div>
<!-- Linux -->
<div class="flex flex-col items-center gap-2 rounded-xl border border-border bg-surface p-4 transition-shadow hover:shadow-md">
<img src="/logos/linux.svg" alt="Linux" class="h-8 w-8" width="32" height="32" />
<span class="text-xs font-medium">Linux</span>
</div>
<!-- macOS -->
<div class="flex flex-col items-center gap-2 rounded-xl border border-border bg-surface p-4 transition-shadow hover:shadow-md">
<svg class="h-8 w-8 text-foreground" viewBox="0 0 24 24" fill="currentColor">
<path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.8-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"/>
</svg>
<span class="text-xs font-medium">macOS</span>
</div>
<!-- Air-Gapped -->
<div class="flex flex-col items-center gap-2 rounded-xl border border-border bg-surface p-4 transition-shadow hover:shadow-md">
<svg class="h-8 w-8 text-primary" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<rect x="3" y="11" width="18" height="11" rx="2" ry="2" />
<path d="M7 11V7a5 5 0 0 1 10 0v4" />
</svg>
<span class="text-xs font-medium">Air-Gapped</span>
</div>
</div>
</div>
</div>
</div>
</section>