/** Configuration options for the PrimeStyle Try-On SDK */ export interface PrimeStyleConfig { /** Your API key (starts with ps_live_) */ apiKey: string; /** API base URL (defaults to https://myaifitting.com) */ apiUrl?: string; /** Product image URL to try on */ productImage?: string; /** Button text (defaults to "Virtual Try-On") */ buttonText?: string; /** Locale for i18n (defaults to "en") */ locale?: string; /** Show "Powered by PrimeStyle" badge (defaults to true) */ showPoweredBy?: boolean; /** Microsoft Clarity project id for SDK session replay. Defaults to PrimeStyleAI's SDK replay project. */ clarityProjectId?: string; } /** Button customization options */ export interface ButtonStyles { backgroundColor?: string; textColor?: string; borderRadius?: string; fontSize?: string; fontFamily?: string; fontWeight?: string; textDecoration?: string; padding?: string; paddingLeft?: string; paddingRight?: string; paddingTop?: string; paddingBottom?: string; border?: string; width?: string; minWidth?: string; maxWidth?: string; height?: string; hoverBackgroundColor?: string; hoverTextColor?: string; iconSize?: string; iconColor?: string; boxShadow?: string; displayMode?: "button" | "text"; } /** Modal customization options */ export interface ModalStyles { overlayColor?: string; backgroundColor?: string; textColor?: string; borderRadius?: string; width?: string; maxWidth?: string; fontFamily?: string; headerBackgroundColor?: string; headerTextColor?: string; closeButtonColor?: string; uploadBorderColor?: string; uploadBackgroundColor?: string; uploadTextColor?: string; uploadIconColor?: string; primaryButtonBackgroundColor?: string; primaryButtonTextColor?: string; primaryButtonBorderRadius?: string; loaderColor?: string; resultBorderRadius?: string; /** Accent color used throughout the SDK (default: #bb945c) */ accentColor?: string; /** Accent hover color (default: #a07d4e) */ accentHoverColor?: string; /** Accent light color for gradients (default: #d6ba7d) */ accentLightColor?: string; /** Primary text color (default: #fff) */ textPrimaryColor?: string; /** Secondary text color (default: #999) */ textSecondaryColor?: string; /** Muted text color (default: #666) */ textMutedColor?: string; /** Border color (default: #333) */ borderColor?: string; /** Secondary background (default: #1a1b1a) */ secondaryBackgroundColor?: string; /** Error color (default: #ef4444) */ errorColor?: string; /** Success color (default: #4ade80) */ successColor?: string; /** Logo height (default: 2.8vw) */ logoHeight?: string; } /** Try-on job submission response */ export interface TryOnResponse { jobId: string; status: string; tryOnsUsed: number; newBalance: number; streamUrl?: string; /** Opaque id of the user's photo as cached on the backend. Send this back * on subsequent regens (via TryOnContext.modelImageId) instead of re-uploading * the full image. Backend keeps the entry for 1 hour, refreshed on each use. */ modelImageId?: string; } export interface SavedTryOnResult { found: true; historyId: string; jobId: string; productId: string; productTitle: string | null; productUrl: string | null; recommendedSize: string; sizingResult: SizingResult | null; resultImageUrl: string | null; hasResultImage: boolean; imageStatus: "available" | "expired" | "missing"; imageExpiresAt: string | null; productLimitReached?: boolean; productLimitAttemptCount?: number; productLimitMaxAttempts?: number; storeLimitReached?: boolean; storeLimitAttemptCount?: number; storeLimitMaxAttempts?: number | null; ipAddressMasked?: string | null; createdAt: string; } export type SavedTryOnResultResponse = SavedTryOnResult | { found: false; productLimitReached?: boolean; productLimitAttemptCount?: number; productLimitMaxAttempts?: number; storeLimitReached?: boolean; storeLimitAttemptCount?: number; storeLimitMaxAttempts?: number | null; ipAddressMasked?: string | null; }; /** VTO status update from SSE */ export interface VtoUpdate { galleryId: string; status: "processing" | "completed" | "failed"; imageUrl: string | null; resultImageUrl?: string | null; outputUrl?: string | null; remoteOutputUrl?: string | null; error: string | null; timestamp: number; } /** Try-on job status (polling) */ export interface TryOnStatus { jobId: string; status: "processing" | "completed" | "failed"; imageUrl: string | null; resultImageUrl?: string | null; outputUrl?: string | null; remoteOutputUrl?: string | null; message: string; } /** Class name overrides for styling with Tailwind or custom CSS */ export interface PrimeStyleClassNames { /** Root wrapper element */ root?: string; /** Trigger button */ button?: string; /** Modal overlay backdrop */ overlay?: string; /** Modal container */ modal?: string; /** Modal header bar */ header?: string; /** Modal title text */ title?: string; /** Close (X) button */ closeButton?: string; /** Modal body area */ body?: string; /** Upload drop zone */ uploadZone?: string; /** Upload primary text */ uploadText?: string; /** Upload hint text (file types) */ uploadHint?: string; /** Preview image container */ preview?: string; /** Preview image element */ previewImage?: string; /** Remove preview button */ removeButton?: string; /** Submit / "Try It On" button */ submitButton?: string; /** Processing spinner element */ spinner?: string; /** Processing main text */ processingText?: string; /** Processing sub text */ processingSubText?: string; /** Result container */ result?: string; /** Result image element */ resultImage?: string; /** Result actions container */ resultActions?: string; /** Download button */ downloadButton?: string; /** Retry / "Try Another" button */ retryButton?: string; /** Error container */ error?: string; /** Error text */ errorText?: string; /** Powered by footer */ poweredBy?: string; } /** Pre-computed size guide data — pass directly to skip AI extraction */ export interface SizeGuideData { found: true; title?: string; headers: string[]; rows: string[][]; } /** A measurement field descriptor used by the sizing form */ export interface SizeGuideField { key: string; label: string; required: boolean; unit: "cm" | "size" | string; placeholder?: string; category: "body" | "shoe" | "other"; } /** A normalised section within a multi-garment size guide (e.g. tuxedo) */ export interface NormalizedSection { headers: string[]; rows: string[][]; requiredFields: SizeGuideField[]; } /** Fully normalised size guide ready for deterministic comparison */ export interface NormalizedSizeGuide { found: true; title?: string; headers: string[]; rows: string[][]; requiredFields: SizeGuideField[]; sections?: Record; } /** Match detail for a single measurement in a sizing recommendation */ export interface MatchDetail { measurement: string; userValue: string; chartRange: string; fit: "good" | "tight" | "loose"; } /** Per-section recommendation result (for multi-garment products) */ export interface SectionRecommendation { recommendedSize: string; matchDetails: MatchDetail[]; /** True when the user's measurements fit inside this section's chart. */ found?: boolean; } /** Full sizing recommendation result */ export interface SizingResult { recommendedSize: string; tightSize?: string | null; looseSize?: string | null; recommendedLength?: string | null; confidence: "high" | "medium" | "low" | string; reasoning: string; internationalSizes?: Record; matchDetails?: MatchDetail[]; method?: "deterministic" | "ai"; sections?: Record; /** Unit the matchDetails values (and chart columns used for matching) are in. */ unit?: "cm" | "in"; /** True when the backend found a size whose measurements actually fit the * user. False when the user's body is outside every row in the chart — * the SDK renders a "no size" message and disables Try-On. Optional for * backwards compatibility with older backends (treat `undefined` as true). */ found?: boolean; /** True when the backend's pre-flight consistency check determined the * user's photo doesn't match their entered weight, and the pipeline * switched to a free-measurement mode driven by the photo + landmarks * alone. The result view shows a polite "we measured from your photo" * notice when this is set. */ mismatchDetected?: boolean; /** Source of the body measurements: "inputs+photo" (formula+photo, the * default path) or "photo" (free-measurement mode triggered by a * detected mismatch between the photo and entered weight). */ measurementSource?: "inputs+photo" | "photo"; /** Per-size lookup: { sizeLabel: { measurement: { chartRange, fit } } }. * When the user clicks a non-recommended size pill, the SDK reads from * this map to swap the table cells + fit pills to that row's pre- * classified values. NO client-side recomputation — the backend's * matcher already classified every row + every measurement field. */ allSizes?: Record>; } /** Fit info for a body area — tells Gemini how the garment should render at this region */ export interface FitAreaInfo { /** Body area name (e.g. "Chest", "Waist", "Hips") */ area: string; /** Section name from the size guide (e.g. "Jacket Size", "Suit Pants", * "Vest Size — Big"). Backend uses this to disambiguate which garment * piece the area refers to (jacket chest vs vest chest, pants waistband * vs dress waist, etc.). Empty/unknown when the chart is single-section. */ section?: string; /** Fit classification */ fit: "good" | "tight" | "loose" | "a-bit-tight" | "a-bit-loose" | "too-tight" | "too-loose"; /** User's measurement value in the same unit as garmentRange */ userValue?: number; /** Garment's selected-size range for this area (e.g. "37-38 in") */ garmentRange?: string; /** Normalised body coordinate — vertical position (0 = top, 1 = bottom) */ y?: number; /** Normalised body coordinate — left edge of this region */ x1?: number; /** Normalised body coordinate — right edge of this region */ x2?: number; } /** Custom events emitted by the component */ export interface PrimeStyleEvents { "ps:open": CustomEvent; "ps:close": CustomEvent; "ps:upload": CustomEvent<{ file: File; }>; "ps:processing": CustomEvent<{ jobId: string; }>; "ps:complete": CustomEvent<{ jobId: string; imageUrl: string; }>; "ps:error": CustomEvent<{ message: string; code?: string; }>; "ps:product-detected": CustomEvent<{ imageUrl: string; }>; }