mirror of
https://github.com/veeso/termscp.git
synced 2026-06-13 03:59:37 +02:00
feat(site): landing page with dual-pane explorer hero
This commit is contained in:
63
site/src/components/ExplorerHero.astro
Normal file
63
site/src/components/ExplorerHero.astro
Normal file
@@ -0,0 +1,63 @@
|
||||
---
|
||||
import { localizePath, useTranslations, type Locale } from "../i18n/ui";
|
||||
interface Props { locale: Locale; }
|
||||
const { locale } = Astro.props;
|
||||
const t = useTranslations(locale);
|
||||
const p = (sub: string) => localizePath(locale, sub);
|
||||
|
||||
const local = [
|
||||
{ name: " ..", size: "DIR", kind: "dir" },
|
||||
{ name: " Documents/", size: "DIR", kind: "dir" },
|
||||
{ name: " backup.tar.gz", size: "1.2 GB", kind: "file" },
|
||||
{ name: " id_ed25519", size: "411 B", kind: "file" },
|
||||
{ name: " notes.md", size: "8.4 KB", kind: "file" },
|
||||
];
|
||||
const remote = [
|
||||
{ name: " ..", size: "DIR", kind: "dir" },
|
||||
{ name: " html/", size: "DIR", kind: "sel" },
|
||||
{ name: " .env", size: "220 B", kind: "file" },
|
||||
{ name: " docker-compose.yml", size: "1.7 KB", kind: "file" },
|
||||
{ name: " logs/", size: "DIR", kind: "dir" },
|
||||
];
|
||||
---
|
||||
<section class="mx-auto max-w-5xl px-4 pt-12">
|
||||
<p class="text-green text-sm tracking-widest uppercase">{t("hero.kicker")}</p>
|
||||
<div class="mt-4 overflow-hidden rounded-lg border border-line bg-mantle shadow-2xl">
|
||||
<div class="border-b border-line bg-crust px-3 py-2 text-xs text-overlay">
|
||||
{t("hero.tabs", { host: "192.168.1.10" })}<span class="blink ml-1"></span>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 bg-base">
|
||||
<div>
|
||||
<div class="border-b border-line bg-mantle px-3 py-1.5 text-xs text-teal">{t("hero.local")}</div>
|
||||
{local.map((r) => (
|
||||
<div class={`flex justify-between px-3 py-0.5 text-sm ${r.kind === "dir" ? "text-blue" : "text-text"}`}>
|
||||
<span>{r.name}</span><span class="text-overlay">{r.size}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div class="border-l border-line">
|
||||
<div class="border-b border-line bg-mantle px-3 py-1.5 text-xs text-mauve">{t("hero.remote")}</div>
|
||||
{remote.map((r) => (
|
||||
<div class={`flex justify-between px-3 py-0.5 text-sm ${
|
||||
r.kind === "sel" ? "bg-blue text-crust font-bold" : r.kind === "dir" ? "text-blue" : "text-text"
|
||||
}`}>
|
||||
<span>{r.name}</span>
|
||||
<span class={r.kind === "sel" ? "text-crust" : "text-overlay"}>{r.size}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-between border-t border-line bg-crust px-3 py-1.5 text-xs text-overlay">
|
||||
<span set:html={t("hero.status").replace(/(↹|↑↓|↵|SPACE)/g, '<span class="text-yellow">$1</span>')}></span>
|
||||
<span>4 files · 1.2 GB</span>
|
||||
</div>
|
||||
<div class="bg-base px-6 py-6 text-center">
|
||||
<h1 class="text-2xl font-bold text-text">{t("hero.title")}</h1>
|
||||
<p class="mx-auto mt-2 max-w-xl text-subtext">{t("hero.subtitle")}</p>
|
||||
<div class="mt-5 flex flex-wrap justify-center gap-3">
|
||||
<a href={p("/install")} class="rounded-lg bg-green px-5 py-2.5 font-bold text-crust">{t("cta.install")}</a>
|
||||
<a href={p("/user-manual")} class="rounded-lg border border-line px-5 py-2.5 text-text">{t("cta.manual")}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
8
site/src/components/FeatureCard.astro
Normal file
8
site/src/components/FeatureCard.astro
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
interface Props { title: string; body: string; }
|
||||
const { title, body } = Astro.props;
|
||||
---
|
||||
<div class="rounded-lg border border-line bg-mantle p-5">
|
||||
<h3 class="text-green">{title}</h3>
|
||||
<p class="mt-2 text-sm text-subtext">{body}</p>
|
||||
</div>
|
||||
15
site/src/components/ProtocolStrip.astro
Normal file
15
site/src/components/ProtocolStrip.astro
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
import { useTranslations, type Locale } from "../i18n/ui";
|
||||
interface Props { locale: Locale; }
|
||||
const { locale } = Astro.props;
|
||||
const t = useTranslations(locale);
|
||||
const protocols = ["SCP", "SFTP", "FTP/S", "S3", "Kube", "SMB", "WebDAV"];
|
||||
---
|
||||
<section class="mx-auto max-w-5xl px-4 py-12 text-center">
|
||||
<h2 class="text-overlay text-sm uppercase tracking-widest">{t("protocols.heading")}</h2>
|
||||
<div class="mt-4 flex flex-wrap justify-center gap-2">
|
||||
{protocols.map((proto) => (
|
||||
<span class="rounded-full border border-line px-4 py-1 text-sm text-text">{proto}</span>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
@@ -1,9 +1,34 @@
|
||||
---
|
||||
import "../styles/theme.css";
|
||||
import Base from "../layouts/Base.astro";
|
||||
import Nav from "../components/Nav.astro";
|
||||
import Footer from "../components/Footer.astro";
|
||||
import ExplorerHero from "../components/ExplorerHero.astro";
|
||||
import ProtocolStrip from "../components/ProtocolStrip.astro";
|
||||
import FeatureCard from "../components/FeatureCard.astro";
|
||||
import { useTranslations } from "../i18n/ui";
|
||||
|
||||
const locale = "en" as const;
|
||||
const t = useTranslations(locale);
|
||||
const features = ["handy", "cross", "custom", "bookmarks", "security", "performance"];
|
||||
---
|
||||
<html lang="en" data-theme="frappe">
|
||||
<head><meta charset="utf-8" /><title>termscp</title></head>
|
||||
<body class="bg-base text-text font-mono p-8">
|
||||
<h1 class="text-green text-2xl">termscp<span class="blink"></span></h1>
|
||||
</body>
|
||||
</html>
|
||||
<Base
|
||||
title="termscp — terminal file transfer & explorer for SCP/SFTP/FTP/S3/Kube/SMB/WebDAV"
|
||||
description="A feature-rich terminal UI file transfer and explorer with support for SCP/SFTP/FTP/Kube/S3/WebDAV/SMB."
|
||||
locale={locale}
|
||||
path="/"
|
||||
>
|
||||
<Nav locale={locale} path="/" />
|
||||
<main class="flex-1">
|
||||
<ExplorerHero locale={locale} />
|
||||
<section class="mx-auto max-w-5xl px-4 py-12">
|
||||
<h2 class="mb-6 text-center text-xl text-text">{t("features.heading")}</h2>
|
||||
<div class="grid gap-5 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{features.map((f) => (
|
||||
<FeatureCard title={t(`features.${f}.title`)} body={t(`features.${f}.body`)} />
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
<ProtocolStrip locale={locale} />
|
||||
</main>
|
||||
<Footer locale={locale} />
|
||||
</Base>
|
||||
|
||||
Reference in New Issue
Block a user