AI chat
Picante chat launcher, AI support, contact form, and live-team handoff behavior.
AI chat
The public storefront includes Picante, a playful pepper-penguin AI companion for product, fundraising, wholesale, and order-support questions. The animated mascot at the bottom-right of the page is also the chat launcher.
Launcher behavior
- Picante walks within a bounded track along the bottom-right portion of the viewport.
- Clicking or keyboard-activating Picante opens the existing chat panel.
- Picante returns to an idle pose while the panel is open.
- Visitors who request reduced motion see a stationary idle pose.
- The launcher remains inside mobile safe areas and does not block the rest of the page.
Chat behavior
Picante introduces himself by name and responds with a concise, upbeat character voice that uses occasional pepper and penguin wordplay. Accuracy and clarity take priority over jokes, and Picante avoids mascot banter during sensitive support or human handoffs. Business-hours lookup and live-team handoff continue through the existing chat APIs. Closing the panel returns Picante to the launcher state.
Replies from Picante and from the team render as Markdown (components/chat/chat-markdown.tsx): bold, italics, lists, links, headings, blockquotes, code and GFM tables show as formatted text. Raw HTML in a reply is never rendered, and the output passes through rehype-sanitize. Messages the visitor types are shown exactly as typed.
Knowledge sources
Before each reply, /api/ai-chat searches Picante's knowledge index (lib/ai-rag/indexer.ts, cached for five minutes) and adds the five best matches to the system prompt. The index holds:
- Active products, recipes, and retail locations from the database, plus the Find Us markdown.
- A few built-in pages (fundraising, wholesale, shipping, contact).
- Every document marked Ready under Admin → Training data (uploads, pasted text, and scraped URLs).
- The customer Help Center articles shown at
/help(apps/storefront/content/help), split into one entry per heading and ranked as store pages. - Every page of this documentation site (
apps/docs/content/docs), split into one entry per heading. - Published posts from the developer blog (
/developer/blog), but only while that blog is public (DEVELOPER_BLOG_PUBLICinapps/storefront/lib/developer/visibility.ts). It is off, so none are indexed.
Documentation entries rank at half weight (lib/ai-rag/retriever.ts), so a store page that matches equally well comes first. The system prompt tells Picante to use the docs to explain how features work and to leave out implementation details unless a staff member or developer asks. The /api/ai-chat route and the inbox-triage routes that share the index list ../docs/content/docs/**/*.mdx and ./content/help/** in outputFileTracingIncludes so the pages ship with their serverless functions; add the same entry to any new route that calls getIndexedContent().
Contact form
Below Picante's opening message sits an "Or fill out a contact form by clicking here" button, for visitors who would rather leave their details than have a conversation. It swaps the panel to a standard contact form:
| Field | Required |
|---|---|
| Full Name | Yes |
| Email Address | Yes |
| Company name | No |
| Phone number | No |
| Comments / questions | Yes |
Submitting posts to POST /api/send-email/contact with sourcePage: 'Picante Chat', the same endpoint the public /contact page uses. The submission is rate limited by IP (5 per minute), validated with Zod, stored as a Conversation, and emailed to the business inbox with the sender's address as reply-to. On success the panel shows a confirmation; closing it returns the visitor to Picante, and "Back to chat" does the same from the form.
The notification email (emails/contact-form.tsx) mirrors the layout the office already works from: logo, a "Contact form submission" heading, a labelled Details block, the comment body, and an "Open store" button. Its subject is <email> submitted the form from your <page> page, where the page comes from the optional sourcePage field. Because this email is an internal alert to the business inbox rather than a subscriber email, it carries no footer unsubscribe link; the List-Unsubscribe header applied by lib/email/client.ts is unchanged.
This is separate from the "Talk to a human" handoff, which opens a live chat thread during business hours instead of sending mail.
Asset
The storefront uses an optimized three-row derivative at public/images/ai-chat/picante-spritesheet.webp, containing only idle and left/right walking frames. The complete version 2 pet package remains the source asset outside the website bundle.
How is this guide?
Last updated on