Files
zeekay 6c65e3e941 @hanzo/app 0.2.0: the app SDK, renamed from @hanzo/rooms
Every import, the catalog pin and the docs name @hanzo/app and
github.com/hanzoai/app. lib/rooms.ts is lib/app.ts and
components/rooms.tsx is components/app.tsx.
2026-10-22 19:07:20 -07:00

101 lines
3.9 KiB
TypeScript

'use client'
// The "try it" chat at the foot of /models. The conversation is the SAME one
// /chat offers — `Ask` from the workspace — so there is one chat surface on this
// site with one consent semantic. This section only says what sits under it
// while nobody is signed in.
//
// A SIGNED-IN READER CHATS IN THE APP: they are sent to /chat, which stands
// behind the organization gate (app/_web.tsx), so a team on Free is asked for
// its plan there rather than handed a thread here. A visitor who is not signed
// in keeps the thread.
//
// The provider is mounted HERE because this page has no shell above it to own
// one; on /chat the shell owns it. Whoever hosts the surface supplies the client.
import { AiProvider } from '@hanzo/ai/react'
import { useIam } from '@hanzo/iam/react'
import { Box, XStack } from '@hanzo/ui'
import { Card } from '@/components/marketing/band'
import { Ask } from '@hanzo/app/Chat'
import { useAi } from '@hanzo/app/lib/ai'
import { useHydrated } from '@hanzo/app/lib/hydrated'
import { hasSession } from '@hanzo/app/lib/session'
import { CHAT, KEYS } from '@/components/home/nav-data'
import { Action, Lede } from '@hanzo/ui/marketing'
export function TryChat() {
const { client } = useAi()
const { isAuthenticated, isLoading } = useIam()
const hydrated = useHydrated()
// Signed in by the same reading `Ask` makes; until it can be made, both draw the sign-in card.
if (hydrated && !isLoading && (isAuthenticated || hasSession())) return <Open />
return (
<Box
className="try-chat-container"
position="relative"
width="100%"
>
<style jsx global>{`
.try-chat-container [data-slot="crew"],
.try-chat-container [aria-label="Agents"] {
display: none !important;
}
`}</style>
<AiProvider client={client ?? undefined}>
{/* No placeholder: the composer says its own ("Type / for skills"),
and @hanzo/app' Ask no longer draws one it is handed. */}
<Ask idle={<SignIn />} />
</AiProvider>
</Box>
)
}
// `Card` carries the rounded border and the translucent fill the hand-written
// box drew; the paragraph is `Lede` and the two actions are `Action` and
// `Action fill`, the two weights every call-to-action on the site takes.
// The card steps its padding 32->48 at $md as the original did; the children
// centre themselves (the lede on its own measure, the actions in a full-width
// row) rather than the card being a flex container it never was.
function SignIn() {
return (
<Card mt="$5" p="$6" $md={{ p: '$8' }}>
<Lede
maxW={512}
mx="auto"
mb={32}
text="center"
>
Sign in to chat with Enso, Zen, or any model on the endpoint. Pick a model, start a thread,
billed to your account and kept private.
</Lede>
<XStack width="100%" flexWrap="wrap" justify="center" gap="$3">
<Action href={KEYS} fill>
Get free API key
</Action>
<Action href={CHAT}>
Open Hanzo Chat
</Action>
</XStack>
</Card>
)
}
/** A signed-in reader's way in: the app at /chat. */
function Open() {
return (
<Card mt="$5" p="$6" $md={{ p: '$8' }} data-slot="try-open">
<Lede maxW={512} mx="auto" mb={32} text="center">
Chat with Enso, Zen, or any model on the endpoint in Hanzo Chat, billed to your organization.
</Lede>
<XStack width="100%" flexWrap="wrap" justify="center" gap="$3">
<Action href={CHAT} fill>
Open Hanzo Chat
</Action>
</XStack>
</Card>
)
}
export default TryChat