mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-30 14:58:08 +00:00
The owner's spec for a landing page is the models bento's: monochrome, the words left and the art right, no extra prose. The folds carried two to four sentences under each heading, and some a claim, a lede and a list besides. Each now says one line: ProductLanding the tagline where a page states one (the lede still describes it to search), a claim alone where the claim is the line (/payments, /treasury, /zap, the extension), and elsewhere the sentence that says what the thing is. /philosophy's two points, /dns' claim that repeated its title, /calculator's capabilities line and the Spare clauses on /research and /learn go.
229 lines
9.7 KiB
TypeScript
229 lines
9.7 KiB
TypeScript
'use client'
|
|
|
|
/**
|
|
* /functions — serverless handlers on a cluster you already run.
|
|
*
|
|
* NO TAILWIND. The four section frames are `components/marketing/band`'s `Band`
|
|
* and `Head`; @hanzo/ui's marketing components carry the type and the controls
|
|
* (`Display`, `Lede`, `Title`, `Chip`, `Action`, `Line`); everything left is
|
|
* gui props over @hanzo/design's tokens. The six capability cards and four use
|
|
* cases are DATA — they were the same markup written ten times.
|
|
*
|
|
* Three things the swap moves, each deliberate, each the shared shape winning
|
|
* over this page's own copy of it:
|
|
*
|
|
* - the section headings take `Title quiet` and stop stating
|
|
* `text-3xl font-bold`, so they step 3xl -> 4xl at 640 and are CENTRED.
|
|
* They were `ChromeText` inside a `text-center` block, and ChromeText's own
|
|
* `flex flex-col items-start` wrapper shrink-wrapped each one to the LEFT
|
|
* while the paragraph under it centred.
|
|
* - the closing rule takes `Band`'s `--neutral-900` hairline rather than this
|
|
* page's `--neutral-800`, and its gutter steps at 640/1024 like every other
|
|
* band on the site.
|
|
* - `Action` is the one button shape at its two weights.
|
|
*
|
|
* The sample keeps ONE type rung. It was `text-sm sm:text-base`, and a code
|
|
* sample's size is not a role this site states at a breakpoint — the reference
|
|
* conversion (components/blockchain/BlockchainProductLayout) sets a single
|
|
* `--text-sm` on its source frames and this matches it. `Lattice` is
|
|
* @hanzo/ui's ruled backdrop, placed by its own props.
|
|
*/
|
|
|
|
import React, { type ReactNode } from 'react';
|
|
import { motion } from "@/components/motion";
|
|
import { Code, Terminal, Database, Server, Network, Shield } from "lucide-react";
|
|
|
|
import { ProductFooter } from "@/components/products/ProductFooter"
|
|
import { AgentPrompt } from '@/components/product/AgentPrompt'
|
|
import { Related } from '@/components/product/Related'
|
|
import { Box, XStack, YStack } from '@hanzo/ui'
|
|
import { Grid } from '@hanzo/ui/grid'
|
|
import { Band, Head } from '@/components/marketing/band'
|
|
import { product } from '@/lib/product'
|
|
import { DOCS } from '@/components/home/nav-data'
|
|
import { Action, Line } from '@hanzo/ui/marketing'
|
|
import { Fold } from '@/components/marketing/fold'
|
|
import { Win } from '@/components/home/chrome'
|
|
import { Code2 } from 'lucide-react'
|
|
import { useI18n } from '@hanzo/i18n/react'
|
|
import { phrase } from '@hanzo/i18n'
|
|
|
|
/** What the two shared blocks say about this product: the audited command, the
|
|
* route the catalog proves it by, and the docs page or null. One table answers
|
|
* for every product page, so this one cannot say something its neighbours do
|
|
* not. */
|
|
const PRODUCT = product('functions')
|
|
|
|
/** How narrow a column may get before a grid drops one, per shape. */
|
|
const DECK = { min: 304, max: 3 } as const
|
|
const DUO = { min: 320, max: 2 } as const
|
|
|
|
/** The inks this page draws body copy and marks in. */
|
|
const SOFT = 'color-mix(in oklab, var(--foreground) 80%, transparent)'
|
|
const WASH = 'color-mix(in oklab, var(--primary) 10%, transparent)'
|
|
const EDGE = 'color-mix(in oklab, var(--pure-white) 30%, transparent)'
|
|
|
|
/** A `color-mix()` ink is not a value gui's colour props type, so a frame's
|
|
* ground and edge ride `$platform-web`, which gui still writes as a class. */
|
|
const CARD = { p: 24, rounded: 'var(--radius-xl)', borderWidth: 1, '$platform-web': { borderColor: EDGE, backgroundColor: WASH } } as const
|
|
const PANEL = {
|
|
p: 32,
|
|
rounded: 'var(--radius-xl)',
|
|
borderWidth: 1,
|
|
borderColor: 'var(--border)',
|
|
'$platform-web': { backgroundColor: 'color-mix(in oklab, var(--primary) 5%, transparent)' },
|
|
} as const
|
|
|
|
/** The pair of controls in the hero, filled and outlined. */
|
|
const CENTER = { justify: "center" } as const
|
|
const OUTLINE = { justify: "center", borderColor: EDGE } as const
|
|
|
|
const FEATURES = [
|
|
{ icon: Code, title: 'Python, Go, Node.js, Rust', body: phrase('Name a runtime and point at a handler file. When the language you want is not one of them, bring a container you built yourself and deploy that instead.') },
|
|
{ icon: Terminal, title: phrase('Warm before the request'), body: phrase('Pools are prewarmed, so a function is not booting on the request that needed it. Scale to zero is still the default when nothing is calling.') },
|
|
{ icon: Database, title: phrase('On your own cluster'), body: phrase('Install it with Helm into Kubernetes you already run. Functions are ordinary workloads there, so quotas, node pools and network policy keep meaning what they meant.') },
|
|
{ icon: Server, title: phrase('A GPU is a normal ask'), body: phrase('A function can request an NVIDIA GPU and be scheduled onto a node that has one. That is what lets model work be a function instead of a service somebody has to keep alive.') },
|
|
{ icon: Network, title: phrase('Triggers, not just HTTP'), body: phrase('An HTTP route, a cron schedule, a Kafka topic, a RabbitMQ queue. The trigger is its own object, so the same handler can answer to several without knowing about any of them.') },
|
|
{ icon: Shield, title: phrase('Versions you can move between'), body: phrase('Blue/green and canary between versions of a function, so backing out a bad deploy is a traffic decision rather than another build.') },
|
|
] as const
|
|
|
|
const USE_CASES = [
|
|
{
|
|
title: phrase('API Backends'),
|
|
body: phrase('Build complete REST or GraphQL APIs with automatic scaling and no server management.'),
|
|
points: [
|
|
phrase('Custom middleware for authentication and validation'),
|
|
phrase('API versioning and documentation generation'),
|
|
phrase('Automatic OpenAPI schema generation'),
|
|
],
|
|
},
|
|
{
|
|
title: phrase('Real-time Processing'),
|
|
body: phrase('Process events in real-time with automatic scaling for traffic spikes.'),
|
|
points: [
|
|
phrase('Webhook handlers for third-party services'),
|
|
phrase('Event-driven data transformation and enrichment'),
|
|
phrase('Real-time analytics processing'),
|
|
],
|
|
},
|
|
{
|
|
title: phrase('AI/ML Inference'),
|
|
body: phrase('Deploy machine learning models for inference with built-in GPU acceleration.'),
|
|
points: [
|
|
phrase('Image and text classification'),
|
|
phrase('AI chatbots and virtual assistants'),
|
|
phrase('Real-time recommendations'),
|
|
],
|
|
},
|
|
{
|
|
title: phrase('Scheduled Tasks'),
|
|
body: phrase('Run tasks on a schedule without maintaining a dedicated server.'),
|
|
points: [
|
|
phrase('Periodic data cleanup and maintenance'),
|
|
phrase('Scheduled reports and notifications'),
|
|
phrase('Recurring data integration and ETL processes'),
|
|
],
|
|
},
|
|
] as const
|
|
|
|
const Functions = ({ handler }: { handler: ReactNode }) => {
|
|
const { t } = useI18n()
|
|
return (
|
|
<Box minH="100vh" $platform-web={{ color: 'var(--foreground)' }}>
|
|
|
|
<Fold
|
|
label="Serverless Computing"
|
|
title="Hanzo Functions"
|
|
lede="Functions that run on Kubernetes."
|
|
actions={
|
|
<>
|
|
<Action href="/login" fill {...CENTER}>
|
|
{t('Try Hanzo')}
|
|
</Action>
|
|
<Action href={`${DOCS}/docs/cli/function`} {...OUTLINE}>
|
|
{t('View Documentation')}
|
|
</Action>
|
|
</>
|
|
}
|
|
preview={
|
|
<Win title="handler.js" icon={<Code2 size={12} color="currentColor" />}>
|
|
{handler}
|
|
</Win>
|
|
}
|
|
/>
|
|
|
|
{PRODUCT && <AgentPrompt product={PRODUCT} />}
|
|
|
|
{/* Features */}
|
|
<Band rule={false} pad="$11" measure={1280}>
|
|
<Head
|
|
title={t('What you get')}
|
|
lede={t('A handler file, a trigger, and the cluster you already run')}
|
|
measure={768}
|
|
/>
|
|
|
|
<Grid columns={DECK} gap={32}>
|
|
{FEATURES.map(({ icon: Icon, title, body }, index) => (
|
|
<Box
|
|
key={title}
|
|
render={<motion.div initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} transition={{ duration: 0.5, delay: index * 0.1 }} viewport={{ once: true }} />}
|
|
{...CARD}
|
|
>
|
|
<Box mb={16}>
|
|
<Icon width={40} height={40} color="var(--foreground)" />
|
|
</Box>
|
|
<Line render="h3" display="block" mb={8} fontSize="$6" lineHeight="1.4" fontWeight="700" color="var(--text-primary)">
|
|
{t(title)}
|
|
</Line>
|
|
<Line render="p" display="block" color={SOFT}>
|
|
{t(body)}
|
|
</Line>
|
|
</Box>
|
|
))}
|
|
</Grid>
|
|
</Band>
|
|
|
|
{/* Use Cases */}
|
|
<Band
|
|
rule={false}
|
|
pad="$11"
|
|
measure={1280}
|
|
ground="linear-gradient(to bottom in oklab, var(--background), color-mix(in oklab, var(--pure-white) 10%, transparent))"
|
|
>
|
|
<Head
|
|
title={t('Common Use Cases')}
|
|
lede={t('Hanzo Functions powers a wide range of serverless applications')}
|
|
measure={768}
|
|
/>
|
|
|
|
<Grid columns={DUO} gap={32}>
|
|
{USE_CASES.map(({ title, body, points }) => (
|
|
<Box key={title} {...PANEL}>
|
|
<Line render="h3" display="block" mb={16} fontSize="$7" lineHeight="1.3333" fontWeight="700" color="var(--text-primary)">
|
|
{t(title)}
|
|
</Line>
|
|
<Line render="p" display="block" mb={16} color={SOFT}>
|
|
{t(body)}
|
|
</Line>
|
|
<YStack render="ul" gap={8}>
|
|
{points.map((point) => (
|
|
<XStack key={point} render="li" items="flex-start">
|
|
<Line marginInlineEnd={8} color="var(--foreground)">•</Line>
|
|
<Line color={SOFT}>{t(point)}</Line>
|
|
</XStack>
|
|
))}
|
|
</YStack>
|
|
</Box>
|
|
))}
|
|
</Grid>
|
|
</Band>
|
|
|
|
{PRODUCT && <Related product={PRODUCT} />}
|
|
|
|
<ProductFooter slug="functions" name="Functions" />
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
export default Functions;
|