import React, { type CSSProperties, useMemo } from 'react'; import { LuClipboard, LuInfo, LuLink } from 'react-icons/lu'; import { cn } from '../copilot/cn'; import { copyToClipboard } from '../../lib/clipboard'; import { parseProfileFields, type ProfileFieldSpec, type ProfilePlatformSpec, resolveProfileSpec, splitItems, } from './profile-platform-specs'; interface ProfilePreviewProps { markdown: string; platform: string | null | undefined; title: string; topic?: string | null; slug?: string | null; logoUrl?: string | null; onCopied?: (message: string) => void; } /* The card is intentionally LIGHT (real profiles are), like PagePreview, so it reads the same in a dark or light dashboard. These are its fixed colours. */ // The card is intentionally light (real profiles are) so it reads the same // whatever the dashboard theme is. const INK = '#10151c'; const BODY = '#2b3440'; const SUB = '#5b6572'; const MUT = '#8892a0'; const LINE = '#e6e9ee'; const PANEL = '#f8f9fb'; const PANEL_LINE = '#eef1f5'; const BTN_LINE = '#d3d9e0'; const AVATAR_RADIUS: Record = { circle: 'rounded-full', rounded: 'rounded-2xl', tile: 'rounded-lg', doc: 'rounded-md', }; const deriveHandle = (title: string, slug?: string | null): string => { // Prefer the brand name: a profile row's slug is its preset topic // ("instagram-bio"), which reads wrong as a handle. for (const source of [title, slug]) { const base = (source || '') .toLowerCase() .replace(/[^a-z0-9]+/g, '') .slice(0, 24); if (base) return `@${base}`; } return '@yourbrand'; }; const subtitleFor = ( spec: ProfilePlatformSpec, handle: string ): string | null => { switch (spec.identity) { case 'handle': return handle; case 'page': return 'Company · Page'; case 'channel': return `${handle} · Channel`; case 'store': return 'Store'; case 'business': return 'Business Account'; default: return null; } }; function Avatar({ spec, monogram, logoUrl, size, }: { spec: ProfilePlatformSpec; monogram: string; logoUrl?: string | null; size: number; }): JSX.Element { return ( ); } function ActionButtons({ spec, size = 'md', className, }: { spec: ProfilePlatformSpec; size?: 'sm' | 'md'; className?: string; }): JSX.Element | null { const chrome = spec.chrome; const buttons = chrome?.buttons; if (!buttons?.length) return null; const accent = chrome?.buttonAccent ?? spec.accent; const buttonStyle = (btn: { solid?: boolean; muted?: boolean; }): CSSProperties => { if (btn.solid) return { background: accent, color: '#fff', boxShadow: '0 1px 2px rgba(16,21,28,0.18)', }; if (btn.muted) return { background: '#eef1f5', color: INK }; if (chrome?.accentOutline) return { border: `1px solid ${accent}`, color: accent, background: '#fff', }; return { border: `1px solid ${BTN_LINE}`, color: INK, background: '#fff' }; }; return (
{buttons.map(btn => ( {btn.label} ))}
); } function TabsRow({ spec, centered, className, }: { spec: ProfilePlatformSpec; centered?: boolean; className?: string; }): JSX.Element | null { const tabs = spec.chrome?.tabs; if (!tabs?.length) return null; return (
{tabs.map((tab, index) => ( {tab} ))}
); } function GridHint({ spec }: { spec: ProfilePlatformSpec }): JSX.Element | null { const grid = spec.chrome?.grid; if (!grid) return null; return (