// ==========================================================================
// HANDOFF CHECKLIST — for Claude Code wiring the real backend
// ==========================================================================
// 1. `src/api.js` — every function carries a TODO[backend] marker with the
// endpoint shape + return contract. Replace the stub bodies with real
// fetches / SSE streams. Artifact shapes are the contract; don't change
// them or the UI breaks.
//
// 2. MOAT — the `chat()` function in api.js has a big comment block above
// it covering `consulting-brand` and `consulting-data`. These are the
// two retrieval skills that MUST run first on every turn. Implement:
// POST /api/skills/consulting-brand { orgId, threadId, query }
// POST /api/skills/consulting-data { orgId, threadId, query, horizon }
// Results get injected into every downstream skill's system prompt so
// the model never invents claims the brand can't back.
//
// 3. IMAGE PIPELINE — search this file for `TODO[image-pipeline`. Two cards
// (AdConceptGrid, VariantMatrix) render with a CSS `bg` placeholder;
// extend the concept/variant shapes to include imageUrl / imagePrompt /
// imageModel and swap the placeholder for the generated asset. Per-org
// brand-LoRA routing is expected — tag each artifact with the LoRA
// version for audit.
//
// 4. BRAND ENGINE — the `parsed-files` artifact carries parsed uploads;
// the real brand engine should (a) chunk + embed uploaded brand docs
// into the per-org vector store, (b) extract brand facts (tone, palette,
// approved claims), (c) return them via consulting-brand for every turn.
// Ingest pipeline is out of scope of this frontend.
//
// 5. AGENTS — scheduled agents (api.listAgents, getLatestBriefing) need a
// real scheduler (cron / Temporal) + a run history store. The briefing
// shape in getLatestBriefing() is the contract the real backend should
// match.
// ==========================================================================
function InkuLogo({ size = 22 }) {
// an abstract "I" with a seed — a droplet within a column.
return (
);
}
function Icon({ name, size = 16, stroke = 1.5 }) {
const s = { width: size, height: size, fill: 'none', stroke: 'currentColor', strokeWidth: stroke, strokeLinecap: 'round', strokeLinejoin: 'round' };
switch (name) {
case 'upload': return ;
case 'send': return ;
case 'sparkle': return ;
case 'check': return ;
case 'plus': return ;
case 'chat': return ;
case 'clock': return ;
case 'grid': return ;
case 'folder': return ;
case 'arrow': return ;
case 'arrow-up-right':
return ;
case 'x': return ;
case 'pdf': return ;
case 'csv': return ;
case 'dot': return ;
case 'bell': return ;
case 'pause': return ;
case 'play': return ;
case 'refresh': return ;
case 'eye': return ;
case 'more': return ;
case 'route': return ;
case 'calendar': return ;
case 'meta': return ;
case 'tiktok': return ;
case 'pinterest':return ;
case 'ooh': return ;
case 'mail': return ;
case 'youtube': return ;
case 'search': return ;
case 'arrow-up': return ;
case 'chevron-right': return ;
case 'users': return ;
case 'message': return ;
case 'bar-chart':return ;
case 'edit': return ;
case 'flask': return ;
case 'file': return ;
case 'palette': return ;
case 'shop': return ;
case 'rocket': return ;
case 'sparkles': return ;
default: return null;
}
}
function ChannelIcon({ name, size = 14 }) {
const map = { Meta: 'meta', TikTok: 'tiktok', Pinterest: 'pinterest', OOH: 'ooh', Email: 'mail', YouTube: 'youtube' };
const base = name.split(' ')[0];
return