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