mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-29 07:20:43 +00:00
/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.
130 lines
3.8 KiB
TypeScript
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>
|
|
)
|
|
}
|