Files
hanzo.ai/components/product/AgentPrompt.tsx
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

77 lines
3.3 KiB
TypeScript

/**
* The sentence a reader hands to an agent, on every product page.
*
* ONE sentence, and it names the manifest rather than the API: an agent reads
* https://hanzo.ai/skill.md and takes the routes, the key and the shapes from
* there, so this block does not restate what that document already says and
* cannot drift from it.
*
* A COMMAND APPEARS ONLY WHERE ONE WAS VERIFIED. `cli` is the audited command
* for this product or it is absent, and absent renders the sentence alone.
* There is no branch that composes a command out of the product's name, and
* none that writes a sentence about the command a product does not have.
*
* The clipboard is `components/workspace/copy`. The block holds no state of its
* own; `Band` and `Head` are client components and `Head` enters on scroll.
*/
import { Box, Text, XStack } from '@/components/ui'
import { Band, Head } from '@/components/marketing/band'
import { Take } from '@hanzo/app/copy'
import type { Product } from '@/lib/product'
import { Line } from '@hanzo/ui/marketing'
/** What an agent reads to learn what Hanzo serves and how to call it. */
const SKILL = 'https://hanzo.ai/skill.md'
/** The sentence. A command joins it only when the product has one. */
function ask({ name, cli }: Product): string {
const line = `Read ${SKILL} and use Hanzo ${name} in my project.`
return cli ? `${line} Start with: ${cli}` : line
}
/** A label in the chrome, small mono on its own leading. */
const LABEL = { fontFamily: '$mono', fontSize: '$1', lineHeight: '1.3333' } as const
export function AgentPrompt({ product }: { product: Product }) {
const prompt = ask(product)
return (
<Band measure={896}>
<Head
eyebrow="Paste and ship"
lede={`Paste this into any agent. It reads the skill manifest and calls ${product.name} from there.`}
/>
<Box overflow="hidden" rounded="var(--radius-2xl)" borderWidth={1} borderColor="var(--border)" bg="var(--card)">
{/* Wraps: a long route beside the name and the control is wider than a
phone's frame, and a clipped chrome hides the control. */}
<XStack borderBottomWidth={1} borderColor="var(--border)" bg="var(--muted)" px={16} py={8} items="center" flexWrap="wrap" gap={12}>
<Line {...LABEL} color="var(--text-secondary)">Agent prompt</Line>
{/* The namespace, one ink quieter than the panel's own name. `--muted-foreground`
is the quietest rung that holds 4.5:1 on `--muted` in both themes. */}
{product.api ? <Line {...LABEL} color="var(--muted-foreground)">api {product.api}</Line> : null}
<Box ml="auto">
<Take text={prompt} says="the agent prompt" label="Copy" />
</Box>
</XStack>
{/* `pre` carries a UA margin the frame would show as a gap under the sentence.
It wraps rather than scrolls: this is one sentence, not a listing, and the
URL in it is the part a narrow screen has to be able to break. */}
<Line
render="pre"
display="block"
m={0}
p={20}
fontFamily="$mono"
fontSize="$2"
color="var(--text-secondary)"
whiteSpace="pre-wrap"
lineHeight="var(--leading-relaxed)" $platform-web={{ overflowWrap: 'anywhere' }}
>
{prompt}
</Line>
</Box>
</Band>
)
}