Files
hanzo.ai/components/models/GuestKeyBanner.tsx
zeekay e981114824 chat: a signed-out reader asks the buying agent, and it routes them to pay
/chat signed out is the buying agent (components/sales/Guest, Agent): the
question the home composer carried in ?q= is asked of cloud's
POST /v1/ai/sales, the reply is drawn as text, and each next step the
server built is a control: a call (the lead is filed first, then the
calendar opens on it), the sign-up, a plan's card checkout, Add funds. A
quiet line says it is a limited preview and how many messages are left; at
the ceiling the sign-up takes the composer's place. A conversation's own
address still signs in, and a signed-in reader keeps the app.

/buying-specialist is the same agent; its keyword script, its typed prices
and its claims to see an account are gone. The model pages' no-key card is
the real lane, POST /v1/chat/public, not a guest key that answers 401.

Measured in the site's names: chat_started, cta_clicked {cta: kind, plan,
agent: sales}, sales_contacted. The floating privacy button is hidden as
room.css meant: the rule names its tag to outrank gui's atomic class.
2026-10-27 01:29:38 -07:00

130 lines
3.8 KiB
TypeScript

'use client'
import { useState, type ReactNode } from 'react'
import { Check, Copy, Terminal, Zap } from 'lucide-react'
import { Box, Text, XStack } from '@/components/ui'
interface GuestKeyBannerProps {
title?: string
/** The request, as text for the clipboard. */
command: string
/** The same request highlighted by the server page (components/models/guest). */
sample: ReactNode
}
/** The card's lift off the page: a soft drop in the black of the palette. */
const DROP = '0 4px 20px -2px color-mix(in srgb, var(--pure-black) 30%, transparent)'
/** The mono caption every small label in the card shares. */
const CAPTION = { fontFamily: '$mono', fontSize: '$1', color: 'var(--muted-foreground)' } as const
export function GuestKeyBanner({ title = 'Try it without a key', command, sample }: GuestKeyBannerProps) {
const [copied, setCopied] = useState(false)
const handleCopy = async () => {
try {
await navigator.clipboard.writeText(command)
setCopied(true)
setTimeout(() => setCopied(false), 2000)
} catch {
// Fallback
}
}
return (
<Box
rounded="var(--radius-2xl)"
borderWidth={1}
borderColor="var(--border)"
overflow="hidden"
p="$4"
$sm={{ p: '$5' }}
bg="var(--card)"
boxShadow={DROP}
>
<XStack items="center" justify="space-between" mb="$3" flexWrap="wrap" gap="$2">
<XStack items="center" gap="$2">
<Text
display="inline-flex"
items="center"
justify="center"
width={22}
height={22}
rounded="var(--radius-full)"
bg="var(--glass-strong)"
color="var(--state-online)"
>
<Zap size={13} fill="currentColor" />
</Text>
{/* --tracking-tight at this size: -0.025em of 13px. */}
<Text fontSize="$2" fontWeight="600" color="var(--foreground)" letterSpacing={-0.33}>
{title}
</Text>
<Text
{...CAPTION}
px={8}
py={2}
rounded="var(--radius-full)"
bg="var(--glass)"
borderWidth={1}
borderColor="var(--border)"
>
no key
</Text>
</XStack>
<Text fontSize="$1" color="var(--muted-foreground)">
No account needed
</Text>
</XStack>
<Text render="p" display="block" fontSize="$1" color="var(--muted-foreground)" mb={12}>
A few free answers a day from one address, on the free model. A free account gets a key and more:
</Text>
<Box
position="relative"
rounded="var(--radius-xl)"
borderWidth={1}
borderColor="var(--border)"
bg="var(--background)"
overflow="hidden"
>
<XStack
items="center"
justify="space-between"
borderBottomWidth={1}
borderColor="var(--border)"
bg="$panel"
px="$3"
py="$1.5"
>
<XStack items="center" gap="$2">
<Terminal size={13} color="var(--muted-foreground)" />
<Text {...CAPTION}>cURL ยท no key</Text>
</XStack>
<Text
render={<button type="button" onClick={handleCopy} title="Copy command to clipboard" />}
{...CAPTION}
color={copied ? 'var(--state-online)' : 'var(--muted-foreground)'}
display="inline-flex"
items="center"
gap={4}
px={8}
py={2}
rounded="var(--radius-md)"
borderWidth={1}
borderColor="var(--border)"
bg="var(--glass)"
cursor="pointer"
>
{copied ? <Check size={12} /> : <Copy size={12} />}
{copied ? 'Copied' : 'Copy'}
</Text>
</XStack>
{sample}
</Box>
</Box>
)
}