Refine the store assistant UI and gate it behind a flag

- Gate the assistant on NEXT_PUBLIC_ENABLE_AI=1 (UI only; the /api/chat
  route still responds if called directly)
- Chat model is openai/gpt-5.6-luna-pro with reasoning requested and
  sendReasoning enabled, rendered via the Reasoning component
- Suggestion chips and reasoning come from ai-elements; attachments gain
  hover-card previews, left alignment, and render under the user message
- Launcher: plain Button when closed, magicui RainbowButton when open
- Tool results replace the collapsible Tool UI with a shimmer while
  running and a muted icon + summary line after, plus product previews
- Commerce icons in place of the folder icon for collections
- Add a thin announcement bar above the header; footer splits links and
  socials onto separate rows and clears the launcher corner
- Add the shadcn base layer so Tailwind v4's bare `border` picks up the
  theme colour instead of currentColor

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016jWbNNJLksC1QG8z8845FX
This commit is contained in:
Rami Bitar
2026-08-01 14:55:04 -04:00
co-authored by Claude Opus 5
parent 107959a4c3
commit 7c6abb4648
14 changed files with 1389 additions and 148 deletions
+7 -3
View File
@@ -13,7 +13,7 @@ import {
// server-side on each tool call.
export const maxDuration = 30;
const MODEL = process.env.OPENROUTER_MODEL ?? 'anthropic/claude-sonnet-4.5';
const MODEL = process.env.OPENROUTER_MODEL ?? 'openai/gpt-5.6-luna-pro';
const SYSTEM_PROMPT = `You are the shopping assistant for an online store built on Shopify.
@@ -84,7 +84,9 @@ export async function POST(req: Request) {
const openrouter = createOpenRouter({ apiKey });
const result = streamText({
model: openrouter(MODEL),
// `reasoning` asks OpenRouter to stream the model's thinking; the UI
// renders it via the Reasoning component.
model: openrouter(MODEL, { reasoning: { enabled: true, effort: 'medium' } }),
system: SYSTEM_PROMPT,
messages: await convertToModelMessages(messages),
// Let the model call a tool, read the result, then answer.
@@ -193,5 +195,7 @@ export async function POST(req: Request) {
},
});
return result.toUIMessageStreamResponse();
// sendReasoning forwards reasoning parts to the client; without it the
// stream carries text and tool calls only.
return result.toUIMessageStreamResponse({ sendReasoning: true });
}
+44
View File
@@ -1,5 +1,7 @@
@import 'tailwindcss';
@custom-variant dark (&:is(.dark *));
@theme {
/* Refined neutral modern palette */
--color-background: hsl(0 0% 100%);
@@ -56,6 +58,15 @@
}
/* Base styles for light, modern Shopify storefront */
/* Tailwind v4 defaults an unqualified `border` to currentColor. shadcn
components (e.g. Button's outline variant) rely on this base layer to pick up
the theme's border colour instead of the text colour. */
@layer base {
* {
border-color: var(--color-border);
}
}
body {
font-feature-settings:
'kern' 1,
@@ -184,3 +195,36 @@ button,
.price-display {
font-feature-settings: 'tnum';
}
@theme inline {
--animate-rainbow: rainbow var(--speed, 2s) infinite linear;
--color-color-5: var(--color-5);
--color-color-4: var(--color-4);
--color-color-3: var(--color-3);
--color-color-2: var(--color-2);
--color-color-1: var(--color-1);
@keyframes rainbow {
0% {
background-position: 0%;
}
100% {
background-position: 200%;
}
}
}
:root {
--color-1: oklch(66.2% 0.225 25.9);
--color-2: oklch(60.4% 0.26 302);
--color-3: oklch(69.6% 0.165 251);
--color-4: oklch(80.2% 0.134 225);
--color-5: oklch(90.7% 0.231 133);
}
.dark {
--color-1: oklch(66.2% 0.225 25.9);
--color-2: oklch(60.4% 0.26 302);
--color-3: oklch(69.6% 0.165 251);
--color-4: oklch(80.2% 0.134 225);
--color-5: oklch(90.7% 0.231 133);
}