Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 29 additions & 6 deletions src/components/CTA.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,45 @@
import { Link } from "@tanstack/react-router";
import { useProduct } from "@/context/ProductContext";

interface CTAProps {
ctaHref?: string;
}

export const CTA = ({ ctaHref }: CTAProps) => {
const { ctaHeadline, ctaButtonText, docsBasePath } = useProduct();
const href = ctaHref || `${docsBasePath}/installation`;
const isExternal = href.startsWith("http://") || href.startsWith("https://");

export const CTA = () => {
return (
<section className="py-32 lg:py-48 border-b border-border relative overflow-hidden">
<div className="absolute inset-0 grid-bg" aria-hidden />
<div className="container mx-auto px-6 relative text-center">
<h2 className="font-mono font-light tracking-tight leading-[1.05] text-foreground text-[14vw] md:text-[10vw] lg:text-[140px] mb-12">
Ship.
{ctaHeadline}
</h2>
<p className="text-base text-foreground/70 max-w-xl mx-auto mb-10">
Join thousands of engineers who replaced six dashboards with one CLI.
Install in 30 seconds. Cancel anytime.
</p>
<div className="flex flex-wrap justify-center gap-3">
<a href="#" className="btn-mono px-6 py-3 bg-foreground text-background hover:bg-foreground/90 transition-colors">
Start Now
</a>
<Link to="/docs" className="btn-mono px-6 py-3 border border-[rgba(255,255,255,0.2)] text-foreground hover:bg-[rgba(255,255,255,0.05)] transition-colors">
{isExternal ? (
<a
href={href}
target="_blank"
rel="noopener noreferrer"
className="btn-mono px-6 py-3 bg-foreground text-background hover:bg-foreground/90 transition-colors"
>
{ctaButtonText}
</a>
) : (
<Link
to={href}
className="btn-mono px-6 py-3 bg-foreground text-background hover:bg-foreground/90 transition-colors"
>
{ctaButtonText}
</Link>
)}
<Link to={docsBasePath} className="btn-mono px-6 py-3 border border-[rgba(255,255,255,0.2)] text-foreground hover:bg-[rgba(255,255,255,0.05)] transition-colors">
Read the docs
</Link>
</div>
Expand Down
107 changes: 34 additions & 73 deletions src/components/Commands.tsx
Original file line number Diff line number Diff line change
@@ -1,54 +1,13 @@
import { useState } from "react";
import { CommandExample } from "@/config/products/types";

const commands = [
{
name: "show",
sig: "kdm show <target>",
desc: "List running runners, pods, containers, or minikube clusters.",
output: `> kdm show pods
NAMESPACE NAME READY STATUS AGE
default api-server-7d4f8b 1/1 Running 3d
default worker-queue-2c9a1 1/1 Running 3d
ingress traefik-controller-x9 1/1 Running 14d
monitoring prometheus-0 2/2 Running 21d`,
},
{
name: "health",
sig: "kdm health <target>",
desc: "Detailed health, liveness, and resource pressure diagnostics.",
output: `> kdm health pods
[ok] api-server-7d4f8b healthy cpu 12% mem 248Mi
[ok] worker-queue-2c9a1 healthy cpu 34% mem 512Mi
[warn] log-shipper-abc12 degraded restarts: 3 (last 1h)
[fail] batch-runner-99fa1 failing OOMKilled · 2x in 5m`,
},
{
name: "watch",
sig: "kdm watch",
desc: "Live monitoring dashboard right inside your terminal.",
output: `> kdm watch
+- Live - 14:02:31 ----------------------------+
| pods: 24 running · 1 pending · 0 failed |
| cpu: ######======== 62% |
| mem: ####========== 41% |
| net: in 124 MB/s out 38 MB/s |
+----------------------------------------------+`,
},
{
name: "logs",
sig: "kdm logs <name>",
desc: "Tail logs with structured parsing and instant search.",
output: `> kdm logs api-server-7d4f8b -f
14:02:30 INFO request GET /api/users 200 12ms
14:02:30 INFO request POST /api/auth 201 48ms
14:02:31 WARN cache miss key=user:8821
14:02:31 INFO request GET /api/orders 200 18ms`,
},
];
export interface CommandsProps {
commands: CommandExample[];
}

export const Commands = () => {
export const Commands = ({ commands }: CommandsProps) => {
const [active, setActive] = useState(0);
const cmd = commands[active];
const cmd = commands[active] || { sig: "", output: "" };

return (
<section id="commands" className="py-24 lg:py-32 border-b border-border">
Expand All @@ -62,35 +21,37 @@ export const Commands = () => {
</h2>
</div>

<div className="grid lg:grid-cols-[320px_1fr] gap-6">
<div className="flex lg:flex-col gap-px bg-border overflow-x-auto">
{commands.map((c, i) => (
<button
key={c.name}
onClick={() => setActive(i)}
className={`text-left p-5 transition-colors whitespace-nowrap lg:whitespace-normal flex-1 min-h-[44px] ${
active === i
? "bg-[rgba(255,255,255,0.05)] border-l-2 border-l-foreground"
: "bg-background hover:bg-[rgba(255,255,255,0.03)]"
}`}
>
<div className="font-mono text-sm mb-1">{c.sig}</div>
<div className="text-xs text-foreground/70 hidden lg:block">{c.desc}</div>
</button>
))}
</div>

<div className="border border-border bg-[rgba(255,255,255,0.03)] overflow-hidden">
<div className="flex items-center gap-2 px-4 py-3 border-b border-border">
<span className="font-mono text-xs uppercase tracking-[1px] text-foreground/50">
{cmd.sig}
</span>
{commands.length > 0 && (
<div className="grid lg:grid-cols-[320px_1fr] gap-6">
<div className="flex lg:flex-col gap-px bg-border overflow-x-auto">
{commands.map((c, i) => (
<button
key={c.name}
onClick={() => setActive(i)}
className={`text-left p-5 transition-colors whitespace-nowrap lg:whitespace-normal flex-1 min-h-[44px] ${
active === i
? "bg-[rgba(255,255,255,0.05)] border-l-2 border-l-foreground"
: "bg-background hover:bg-[rgba(255,255,255,0.03)]"
}`}
>
<div className="font-mono text-sm mb-1">{c.sig}</div>
<div className="text-xs text-foreground/70 hidden lg:block">{c.desc}</div>
</button>
))}
</div>
<pre className="p-6 font-mono text-sm leading-relaxed text-foreground/90 overflow-x-auto whitespace-pre">

<div className="border border-border bg-[rgba(255,255,255,0.03)] overflow-hidden">
<div className="flex items-center gap-2 px-4 py-3 border-b border-border">
<span className="font-mono text-xs uppercase tracking-[1px] text-foreground/50">
{cmd.sig}
</span>
</div>
<pre className="p-6 font-mono text-sm leading-relaxed text-foreground/90 overflow-x-auto whitespace-pre">
{cmd.output}
</pre>
</pre>
</div>
</div>
</div>
)}
</div>
</section>
);
Expand Down
48 changes: 7 additions & 41 deletions src/components/Features.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,11 @@
import { Eye, HeartPulse, Radio, ScrollText, Cloud, Shield } from "lucide-react";
import { Feature } from "@/config/products/types";

const features = [
{
icon: Eye,
title: "Unified visibility",
desc: "Show running pods, containers, runners, and Minikube nodes — across clouds — in a single command.",
cmd: "kdm show <target>",
},
{
icon: HeartPulse,
title: "Health diagnostics",
desc: "Probe liveness, readiness, restarts, and resource pressure with intelligent severity scoring.",
cmd: "kdm health <target>",
},
{
icon: Radio,
title: "Live watch mode",
desc: "Stream metrics in real time with millisecond updates. Pin services, filter noise, alert on drift.",
cmd: "kdm watch",
},
{
icon: ScrollText,
title: "Smart log tailing",
desc: "Tail container or pod logs with structured parsing, multi-line stitching, and instant search.",
cmd: "kdm logs <name>",
},
{
icon: Cloud,
title: "Cloud-synced state",
desc: "Securely sync cluster state across your team. Share dashboards, runbooks, and incident timelines.",
cmd: "cloud · auto",
},
{
icon: Shield,
title: "Zero-trust by default",
desc: "Read-only kubeconfig context, scoped tokens, and end-to-end encrypted streams. SOC 2 ready.",
cmd: "built-in",
},
];
export interface FeaturesProps {
features: Feature[];
productName: string;
}

export const Features = () => {
export const Features = ({ features, productName }: FeaturesProps) => {
return (
<section id="features" className="py-24 lg:py-32 border-b border-border">
<div className="container mx-auto px-6">
Expand All @@ -51,7 +17,7 @@ export const Features = () => {
One CLI for the entire stack.
</h2>
<p className="text-base text-foreground/70">
From local Minikube clusters to multi-region production, KDM gives you the same crisp
From local Minikube clusters to multi-region production, {productName.toUpperCase()} gives you the same crisp
experience whether you're debugging on a laptop or paging at 3 AM.
</p>
</div>
Expand Down
17 changes: 12 additions & 5 deletions src/components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import { useOptionalProduct } from "@/context/ProductContext";
import { Link } from "@tanstack/react-router";

export const Footer = () => {
const product = useOptionalProduct();

return (
<footer className="py-12">
<footer className="py-12 border-t border-border mt-auto">
<div className="container mx-auto px-6">
<div className="flex flex-col md:flex-row items-center justify-between gap-4">
<div className="flex items-center gap-4">
<span className="font-mono text-sm uppercase tracking-[1.4px]">kdm</span>
<span className="text-xs text-foreground/50">© 2026 KDM Labs</span>
<span className="font-mono text-sm uppercase tracking-[1.4px]">
{product ? product.displayName : "KDM Labs"}
</span>
<span className="text-xs text-foreground/50">
© 2026 {product ? `${product.displayName} Labs` : "KDM Labs"}
</span>
</div>
<div className="flex gap-6 font-mono text-xs uppercase tracking-[1px] text-foreground/70">
<Link to="/privacy" className="hover:text-foreground/40 transition-colors">
Expand All @@ -17,15 +24,15 @@ export const Footer = () => {
Terms
</Link>
<a
href="https://github.com/KDM-cli/kdm-cli"
href={product ? `${product.githubUrl}/actions` : "https://github.com/KDM-cli/kdm-cli/actions"}
target="_blank"
rel="noreferrer"
className="hover:text-foreground/40 transition-colors"
>
Status
</a>
<a
href="https://github.com/KDM-cli/kdm-cli"
href={product ? product.githubUrl : "https://github.com/KDM-cli/kdm-cli"}
target="_blank"
rel="noreferrer"
className="hover:text-foreground/40 transition-colors"
Expand Down
64 changes: 41 additions & 23 deletions src/components/Hero.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,38 @@
import { Copy, Check } from "lucide-react";
import { useState } from "react";
import { Terminal } from "./Terminal";
import { useProduct } from "@/context/ProductContext";

export const Hero = () => {
const { displayName, tagline, description, installCommand, githubUrl, stats, terminalLines } = useProduct();
const [copied, setCopied] = useState(false);
const copy = () => {
navigator.clipboard.writeText("npm install -g kdm-cli");
setCopied(true);
setTimeout(() => setCopied(false), 1500);
navigator.clipboard.writeText(installCommand)
.then(() => {
setCopied(true);
setTimeout(() => setCopied(false), 1500);
})
.catch((err) => {
console.error("Failed to copy text using clipboard API: ", err);
try {
const textarea = document.createElement("textarea");
textarea.value = installCommand;
textarea.style.position = "fixed";
document.body.appendChild(textarea);
textarea.focus();
textarea.select();
const successful = document.execCommand("copy");
document.body.removeChild(textarea);
if (successful) {
setCopied(true);
setTimeout(() => setCopied(false), 1500);
} else {
console.error("execCommand copy fallback failed");
}
} catch (fallbackErr) {
console.error("Copy fallback execution failed: ", fallbackErr);
}
});
};
Comment thread
coderabbitai[bot] marked this conversation as resolved.

return (
Expand All @@ -21,55 +46,48 @@ export const Hero = () => {
</div>

<h1 className="font-mono font-light tracking-tight leading-[1.05] text-foreground text-[14vw] md:text-[12vw] lg:text-[180px] xl:text-[220px] mb-12">
kdm.
{displayName.toLowerCase()}.
</h1>

<div className="grid lg:grid-cols-2 gap-16 items-start">
<div className="space-y-8">
<h2 className="text-2xl lg:text-3xl font-normal leading-tight max-w-xl">
Monitor every pod and container from your terminal.
{tagline}
</h2>
<p className="text-base text-foreground/70 max-w-xl leading-relaxed">
KDM is a cloud-native CLI that streams real-time health, logs, and metrics
for Kubernetes clusters, Docker hosts, and Minikube — all from one unified command.
{description}
</p>

<button
onClick={copy}
className="group flex items-center justify-between gap-3 px-4 py-3 w-full max-w-md border border-[rgba(255,255,255,0.2)] font-mono text-sm hover:border-foreground/50 transition-colors"
>
<span className="text-foreground/50">$</span>
<span className="flex-1 text-left">npm install -g kdm-cli</span>
<span className="flex-1 text-left">{installCommand}</span>
{copied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4 text-foreground/50 group-hover:text-foreground transition-colors" />}
</button>

<div className="flex flex-wrap gap-3">
<a href="#" className="btn-mono px-6 py-3 bg-foreground text-background hover:bg-foreground/90 transition-colors">
Try KDM
Try {displayName.toUpperCase()}
</a>
<a href="https://github.com/KDM-cli/kdm-cli" className="btn-mono px-6 py-3 border border-[rgba(255,255,255,0.2)] text-foreground hover:bg-[rgba(255,255,255,0.05)] transition-colors">
<a href={githubUrl} target="_blank" rel="noreferrer" className="btn-mono px-6 py-3 border border-[rgba(255,255,255,0.2)] text-foreground hover:bg-[rgba(255,255,255,0.05)] transition-colors">
View on GitHub
</a>
</div>

<div className="grid grid-cols-1 sm:grid-cols-3 gap-6 pt-8 border-t border-border">
<div>
<div className="font-mono text-2xl sm:text-3xl font-light">12k+</div>
<div className="text-xs text-foreground/50 mt-1">Active clusters</div>
</div>
<div>
<div className="font-mono text-2xl sm:text-3xl font-light">99.99%</div>
<div className="text-xs text-foreground/50 mt-1">Uptime SLA</div>
</div>
<div>
<div className="font-mono text-2xl sm:text-3xl font-light">&lt;50ms</div>
<div className="text-xs text-foreground/50 mt-1">Stream latency</div>
</div>
{stats.map((stat, i) => (
<div key={i}>
<div className="font-mono text-2xl sm:text-3xl font-light">{stat.value}</div>
<div className="text-xs text-foreground/50 mt-1">{stat.label}</div>
</div>
))}
</div>
</div>

<div className="relative">
<Terminal />
<Terminal lines={terminalLines} productName={displayName} />
</div>
</div>
</div>
Expand Down
Loading
Loading