mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-29 04:14:47 +00:00
120 lines
4.4 KiB
TypeScript
120 lines
4.4 KiB
TypeScript
'use client'
|
|
|
|
/**
|
|
* NO TAILWIND. The section frame is `components/marketing/band`'s `Band` and
|
|
* `Head` — the rule, the rhythm, the gutter, the measure and the centred head
|
|
* this file used to write out by hand — and the cards are gui props over
|
|
* @hanzo/design's tokens.
|
|
*
|
|
* `Grid` here is lucide's ICON of that name, so gui's grid comes in as
|
|
* `Columns`. A `Fit` is one floor and a cap rather than a pair of breakpoints:
|
|
* 224 lands the widest step (3-up) on the 768 the source asked for.
|
|
*/
|
|
|
|
import React from "react";
|
|
import { motion } from "@/components/motion";
|
|
import { Zap, Scale, Shield, Grid, DatabaseIcon, Activity } from "lucide-react";
|
|
import { View, XStack } from '@hanzo/ui'
|
|
import { Grid as Columns } from '@hanzo/ui/grid'
|
|
import { Band, Head, Words } from '@/components/marketing/band'
|
|
|
|
const FeatureCard = ({
|
|
icon: Icon,
|
|
title,
|
|
description,
|
|
delay
|
|
}: {
|
|
icon: React.ElementType;
|
|
title: string;
|
|
description: string;
|
|
delay: number;
|
|
}) => (
|
|
<View
|
|
render={<motion.div initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ duration: 0.4, delay }} />}
|
|
display="block"
|
|
rounded="var(--radius-xl)"
|
|
borderWidth={1}
|
|
borderColor="var(--neutral-800)"
|
|
background="color-mix(in oklab, var(--neutral-900) 20%, transparent)"
|
|
p={32}
|
|
>
|
|
<XStack
|
|
width={48}
|
|
height={48}
|
|
items="center"
|
|
justify="center"
|
|
mb={24}
|
|
rounded="var(--radius-lg)"
|
|
bg="$raised"
|
|
>
|
|
<Icon width={24} height={24} color="color-mix(in oklab, var(--foreground) 70%, transparent)" />
|
|
</XStack>
|
|
{/* A card's own heading and its paragraph — not a section head, so not a role. */}
|
|
<Words render="h3" mb={16} fontSize="$6" lineHeight="1.4" weight="700" color="var(--text-primary)">
|
|
{title}
|
|
</Words>
|
|
<Words render="p" color="var(--muted-foreground)">
|
|
{description}
|
|
</Words>
|
|
</View>
|
|
);
|
|
|
|
const KeyFeatures = () => {
|
|
const features = [
|
|
{
|
|
icon: Zap,
|
|
title: "Columns, not rows",
|
|
description: "A table is stored one column at a time, so a query reads only the columns it names. Each column gets a codec suited to what it holds — deltas for a rising timestamp, dictionaries for a repeated label, general compression over the result."
|
|
},
|
|
{
|
|
icon: Scale,
|
|
title: "Shard it and it keeps up",
|
|
description: "Tables are partitioned and spread across nodes; add nodes and both the storage and the scan spread with them. Parts merge in the background, so ingest never stops for a compaction window."
|
|
},
|
|
{
|
|
icon: Shield,
|
|
title: "Replicas that agree",
|
|
description: "Replicated tables coordinate through a Raft quorum running inside the server itself — there is no separate coordination service to stand up beside the database. A replica that falls behind catches up part by part."
|
|
},
|
|
{
|
|
icon: Grid,
|
|
title: "Local disks, or object storage",
|
|
description: "Run a shared-nothing cluster on local NVMe, or put the table's data on object storage and scale query nodes on their own. Old partitions expire on a TTL you write into the schema."
|
|
},
|
|
{
|
|
icon: DatabaseIcon,
|
|
title: "Events, metrics and logs are one shape",
|
|
description: "Append-heavy, partitioned by time, queried by range — the same engine serves all three. Materialized views keep rollups current as rows land, so the summary exists before anyone asks for it."
|
|
},
|
|
{
|
|
icon: Activity,
|
|
title: "SQL, on several protocols",
|
|
description: "Query over HTTP on 8123, on the native protocol on 9000, or through the MySQL and PostgreSQL wire ports — which is how BI tools and drivers that know nothing about it still connect. A CSV, a Parquet file or a JSON stream loads without a conversion step first."
|
|
}
|
|
];
|
|
|
|
return (
|
|
<Band id="features" rule={false} pad="$14" measure={1152} ground="var(--black)">
|
|
<Head
|
|
title="What it does"
|
|
lede="Scan a great many rows. Return a small answer. Do it while the rows are still arriving."
|
|
measure={768}
|
|
/>
|
|
|
|
<Columns columns={{ min: 224, max: 3 }} gap={32}>
|
|
{features.map((feature, index) => (
|
|
<FeatureCard
|
|
key={feature.title}
|
|
icon={feature.icon}
|
|
title={feature.title}
|
|
description={feature.description}
|
|
delay={0.1 * (index + 1)}
|
|
/>
|
|
))}
|
|
</Columns>
|
|
</Band>
|
|
);
|
|
};
|
|
|
|
export default KeyFeatures;
|