@gymmymac/bob-widget 2.0.0 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +20 -4
  2. package/dist/BobProvider.d.ts +6 -0
  3. package/dist/components/BobSuggestions.d.ts +15 -0
  4. package/dist/components/ChatInterface.d.ts +3 -0
  5. package/dist/components/MatrixProductLoader.d.ts +14 -0
  6. package/dist/components/ProductBadge.d.ts +15 -0
  7. package/dist/components/ProductTile.d.ts +20 -0
  8. package/dist/components/SparkDealBanner.d.ts +14 -0
  9. package/dist/components/SwipeableBob.d.ts +24 -0
  10. package/dist/components/mobile/ContainedChatDrawer.d.ts +2 -2
  11. package/dist/components/mobile/MobileBobCharacter.d.ts +12 -1
  12. package/dist/components/mobile/MobileBobLayoutCore.d.ts +39 -0
  13. package/dist/components/mobile/MobileChatDrawer.d.ts +3 -0
  14. package/dist/components/mobile/MobileProductColumn.d.ts +1 -0
  15. package/dist/components/mobile/ServicePackageDetailView.d.ts +31 -0
  16. package/dist/components/mobile/index.d.ts +2 -0
  17. package/dist/hooks/useBobAnimation.d.ts +12 -2
  18. package/dist/hooks/useBobAnimationData.d.ts +1 -0
  19. package/dist/hooks/useBobStateTransitions.d.ts +2 -0
  20. package/dist/hooks/usePositionFactors.d.ts +28 -0
  21. package/dist/hooks/useReturningUser.d.ts +10 -0
  22. package/dist/hooks/useSparkDeals.d.ts +27 -0
  23. package/dist/hooks/useSpeechSynthesis.d.ts +2 -1
  24. package/dist/hooks/useThemeSettings.d.ts +22 -0
  25. package/dist/hooks/useViewportSize.d.ts +2 -0
  26. package/dist/index.d.ts +15 -3
  27. package/dist/index.js +35 -13
  28. package/dist/index.mjs +5443 -2991
  29. package/dist/style.css +1 -1
  30. package/dist/styles/carfix-tokens.d.ts +166 -0
  31. package/dist/styles/glass.d.ts +49 -0
  32. package/dist/types/index.d.ts +1 -1
  33. package/dist/types/message.d.ts +6 -3
  34. package/dist/types/product.d.ts +96 -9
  35. package/dist/utils/debug.d.ts +23 -0
  36. package/package.json +2 -2
package/dist/style.css CHANGED
@@ -1 +1 @@
1
- .bob-widget-root{position:relative;display:flex;flex-direction:column;width:100%;height:100%;font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,sans-serif;font-size:16px;line-height:1.5;color:#1a1a1a;background:transparent;isolation:isolate;contain:layout style;box-sizing:border-box;--border: transparent !important;--border-border: transparent !important}.bob-widget-root *,.bob-widget-root *:before,.bob-widget-root *:after{box-sizing:border-box}.bob-widget-root button,.bob-widget-root [role=button]{min-height:unset!important;min-width:unset!important}.bob-widget-root input,.bob-widget-root textarea{font-size:16px!important}.bob-widget-root *:focus-visible{outline:2px solid #2563eb!important;outline-offset:2px!important}.bob-widget-root a{color:inherit;text-decoration:none}.bob-widget-root svg{display:inline-block;vertical-align:middle}.bob-widget-root img{max-width:100%;height:auto;display:block}.bob-widget-root ul,.bob-widget-root ol{list-style:none;margin:0;padding:0}.bob-widget-root h1,.bob-widget-root h2,.bob-widget-root h3,.bob-widget-root h4,.bob-widget-root h5,.bob-widget-root h6{font-size:inherit;font-weight:inherit;margin:0}.bob-widget-root p{margin:0}.bob-widget-root .bob-flex{display:flex}.bob-widget-root .bob-flex-col{flex-direction:column}.bob-widget-root .bob-items-center{align-items:center}.bob-widget-root .bob-justify-center{justify-content:center}.bob-widget-root .bob-gap-2{gap:.5rem}.bob-widget-root .bob-gap-3{gap:.75rem}@keyframes ptt-pulse{0%,to{box-shadow:0 0 #22c55e80}50%{box-shadow:0 0 0 16px #22c55e00}}@keyframes ptt-wave{0%{transform:translate(-50%,-50%) scale(1);opacity:.6}to{transform:translate(-50%,-50%) scale(2);opacity:0}}@keyframes ptt-dot{0%{opacity:.4;transform:scale(.8)}to{opacity:1;transform:scale(1)}}
1
+ .bob-widget-root{position:relative;display:flex;flex-direction:column;width:100%;height:100%;font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,sans-serif;font-size:16px;line-height:1.5;color:#1a1a1a;background:transparent;isolation:isolate;contain:layout style;box-sizing:border-box;--border: transparent !important;--border-border: transparent !important}.bob-widget-root *,.bob-widget-root *:before,.bob-widget-root *:after{box-sizing:border-box}.bob-widget-root button,.bob-widget-root [role=button]{min-height:unset!important;min-width:unset!important}.bob-widget-root input,.bob-widget-root textarea{font-size:16px!important}.bob-widget-root *:focus-visible{outline:2px solid #2563eb!important;outline-offset:2px!important}.bob-widget-root a{color:inherit;text-decoration:none}.bob-widget-root svg{display:inline-block;vertical-align:middle}.bob-widget-root img{max-width:100%;height:auto;display:block}.bob-widget-root ul,.bob-widget-root ol{list-style:none;margin:0;padding:0}.bob-widget-root h1,.bob-widget-root h2,.bob-widget-root h3,.bob-widget-root h4,.bob-widget-root h5,.bob-widget-root h6{font-size:inherit;font-weight:inherit;margin:0}.bob-widget-root p{margin:0}.bob-widget-root .bob-flex{display:flex}.bob-widget-root .bob-flex-col{flex-direction:column}.bob-widget-root .bob-items-center{align-items:center}.bob-widget-root .bob-justify-center{justify-content:center}.bob-widget-root .bob-gap-2{gap:.5rem}.bob-widget-root .bob-gap-3{gap:.75rem}@keyframes ptt-pulse{0%,to{box-shadow:0 0 #0066cc80}50%{box-shadow:0 0 0 16px #06c0}}@keyframes ptt-wave{0%{transform:translate(-50%,-50%) scale(1);opacity:.6}to{transform:translate(-50%,-50%) scale(2);opacity:0}}@keyframes ptt-dot{0%{opacity:.4;transform:scale(.8)}to{opacity:1;transform:scale(1)}}.bob-widget-root .glass-card{transition:transform .3s cubic-bezier(.34,1.56,.64,1),box-shadow .3s ease-out,border-color .3s ease}.bob-widget-root .glass-card:active{transform:scale(.98) translateY(0)!important}.bob-widget-root .glass-button{transition:transform .2s cubic-bezier(.34,1.56,.64,1),box-shadow .2s ease-out,background .2s ease}.bob-widget-root .glass-button:active{transform:scale(.95) translateY(0)!important}.bob-widget-root .product-scroll::-webkit-scrollbar{display:none}.bob-widget-root .product-scroll{scrollbar-width:none;-ms-overflow-style:none}.bob-widget-root .glass-scroll::-webkit-scrollbar{display:none}.bob-widget-root .glass-scroll{scrollbar-width:none;-ms-overflow-style:none}.bob-widget-root .high-contrast-input::placeholder{color:#ffffffb3}.bob-widget-root .high-contrast-input:focus::placeholder{color:#ffffff80}
@@ -0,0 +1,166 @@
1
+ /**
2
+ * CARFIX Design Tokens for Bob Widget
3
+ * Matches the CARFIX website styling exactly
4
+ */
5
+ export declare const CARFIX_COLORS: {
6
+ readonly primary: "#0052CC";
7
+ readonly primaryHover: "#0047B3";
8
+ readonly secondary: "#38BDF8";
9
+ readonly foreground: "#0F172A";
10
+ readonly accent: "#FF8C00";
11
+ readonly success: "#22C55E";
12
+ readonly destructive: "#EF4444";
13
+ readonly performanceRed: "#DC2626";
14
+ readonly mutedForeground: "#64748B";
15
+ readonly background: "#FAFAFA";
16
+ readonly card: "#FFFFFF";
17
+ readonly border: "#E2E8F0";
18
+ readonly slateEconomy: "#475569";
19
+ };
20
+ export declare const QUALITY_TIER_CONFIG: {
21
+ readonly Economy: {
22
+ readonly icon: "DollarSign";
23
+ readonly emoji: "💰";
24
+ readonly textColor: "#475569";
25
+ readonly background: "#F1F5F9";
26
+ readonly border: "#CBD5E1";
27
+ readonly description: "Smart savings";
28
+ };
29
+ readonly Standard: {
30
+ readonly icon: "Star";
31
+ readonly emoji: "⭐";
32
+ readonly textColor: "#0052CC";
33
+ readonly background: "rgba(0,82,204,0.1)";
34
+ readonly border: "rgba(0,82,204,0.3)";
35
+ readonly description: "Best value";
36
+ readonly isRecommended: true;
37
+ };
38
+ readonly Premium: {
39
+ readonly icon: "Award";
40
+ readonly emoji: "🏆";
41
+ readonly textColor: "#D97706";
42
+ readonly background: "#FEF3C7";
43
+ readonly border: "#FCD34D";
44
+ readonly description: "Superior quality";
45
+ };
46
+ readonly Performance: {
47
+ readonly icon: "Zap";
48
+ readonly emoji: "⚡";
49
+ readonly textColor: "#DC2626";
50
+ readonly background: "#FEE2E2";
51
+ readonly border: "#FCA5A5";
52
+ readonly description: "Maximum power";
53
+ };
54
+ };
55
+ /**
56
+ * Service Package Descriptions - Problem → Benefit → CFX Pack format
57
+ */
58
+ export declare const SERVICE_PACKAGE_DESCRIPTIONS: Record<string, string>;
59
+ export declare const DEFAULT_SERVICE_DESCRIPTION = "Keep your vehicle running safely and efficiently with quality parts matched to your specific make and model. Each CFX Service Pack includes everything you need for this essential maintenance.";
60
+ export declare const IMAGE_URLS: {
61
+ readonly productImage: (sku: string) => string;
62
+ readonly brandLogo: (brandName: string) => string;
63
+ readonly carfixImage: (sku: string) => string;
64
+ };
65
+ export declare const BADGE_CONFIG: {
66
+ readonly fitsVehicle: {
67
+ readonly background: "rgba(34,197,94,0.1)";
68
+ readonly text: "#22C55E";
69
+ readonly border: "rgba(34,197,94,0.2)";
70
+ readonly icon: "Check";
71
+ readonly label: "Fits Your Vehicle";
72
+ };
73
+ readonly viscosity: {
74
+ readonly background: "#EFF6FF";
75
+ readonly text: "#1D4ED8";
76
+ readonly border: "#BFDBFE";
77
+ readonly icon: "Gauge";
78
+ };
79
+ readonly volume: {
80
+ readonly background: "#ECFEFF";
81
+ readonly text: "#0E7490";
82
+ readonly border: "#A5F3FC";
83
+ readonly icon: "Droplets";
84
+ };
85
+ readonly quantity: {
86
+ readonly background: "#F1F5F9";
87
+ readonly text: "#475569";
88
+ readonly border: "#CBD5E1";
89
+ };
90
+ readonly partNumber: {
91
+ readonly background: "transparent";
92
+ readonly text: "#64748B";
93
+ readonly border: "transparent";
94
+ };
95
+ readonly carfixValue: {
96
+ readonly background: "rgba(0,82,204,0.1)";
97
+ readonly text: "#0052CC";
98
+ readonly border: "rgba(0,82,204,0.2)";
99
+ readonly icon: "Sparkles";
100
+ readonly label: "CARFIX Value";
101
+ };
102
+ readonly rotorPair: {
103
+ readonly background: "#EFF6FF";
104
+ readonly text: "#1D4ED8";
105
+ readonly border: "#BFDBFE";
106
+ readonly label: "Pair";
107
+ };
108
+ };
109
+ export declare const TYPOGRAPHY: {
110
+ readonly packageTitle: {
111
+ readonly fontSize: "20px";
112
+ readonly fontWeight: 700;
113
+ };
114
+ readonly partslotName: {
115
+ readonly fontSize: "14px";
116
+ readonly fontWeight: 600;
117
+ };
118
+ readonly productName: {
119
+ readonly fontSize: "14px";
120
+ readonly fontWeight: 500;
121
+ };
122
+ readonly description: {
123
+ readonly fontSize: "14px";
124
+ readonly fontWeight: 400;
125
+ };
126
+ readonly priceMain: {
127
+ readonly fontSize: "20px";
128
+ readonly fontWeight: 700;
129
+ };
130
+ readonly priceSecondary: {
131
+ readonly fontSize: "12px";
132
+ readonly fontWeight: 400;
133
+ };
134
+ readonly tierLabel: {
135
+ readonly fontSize: "14px";
136
+ readonly fontWeight: 500;
137
+ };
138
+ readonly badgeText: {
139
+ readonly fontSize: "12px";
140
+ readonly fontWeight: 500;
141
+ };
142
+ };
143
+ /**
144
+ * Check if a product is a rotor (sold in pairs)
145
+ */
146
+ export declare function isRotorProduct(product: {
147
+ name?: string;
148
+ partslotDescription?: string;
149
+ partslot_description?: string;
150
+ per_car_qty?: number;
151
+ }): boolean;
152
+ /**
153
+ * Get display price for a product (handles rotor pair pricing)
154
+ */
155
+ export declare function getDisplayPrice(unitPrice: number, isRotor: boolean): {
156
+ displayPrice: number;
157
+ unitPriceLabel?: string;
158
+ };
159
+ /**
160
+ * Format price as NZD currency
161
+ */
162
+ export declare function formatNZD(price: number): string;
163
+ /**
164
+ * Get service package description with fallback
165
+ */
166
+ export declare function getServicePackageDescription(title: string): string;
@@ -0,0 +1,49 @@
1
+ import { CSSProperties } from 'react';
2
+
3
+ /** Base glass panel (cards, tiles) - Dark variant for text visibility
4
+ * NOTE: Removed backdrop-filter to prevent GPU rendering artifacts on scroll with many items
5
+ */
6
+ export declare const glassCard: CSSProperties;
7
+ /** Premium/Spotlighted card variant
8
+ * NOTE: Using solid gradient instead of backdrop-filter for scroll performance
9
+ */
10
+ export declare const glassCardPremium: CSSProperties;
11
+ /** Glass button base */
12
+ export declare const glassButton: CSSProperties;
13
+ /** Primary CTA button - CARFIX Orange accent */
14
+ export declare const glassButtonPrimary: CSSProperties;
15
+ /** CARFIX Blue primary button */
16
+ export declare const glassButtonBlue: CSSProperties;
17
+ /** Glass input field */
18
+ export declare const glassInput: CSSProperties;
19
+ /** High-contrast glass input - CARFIX blue for readability */
20
+ export declare const glassInputHighContrast: CSSProperties;
21
+ /** Glass panel/container */
22
+ export declare const glassPanel: CSSProperties;
23
+ /** Hover transform styles - apply on :hover state */
24
+ export declare const glassHoverTransform: {
25
+ scale: number;
26
+ translateY: number;
27
+ boxShadow: string;
28
+ };
29
+ /** Text styles for glass surfaces - High contrast for readability */
30
+ export declare const glassText: {
31
+ primary: {
32
+ color: string;
33
+ textShadow: string;
34
+ };
35
+ secondary: {
36
+ color: string;
37
+ textShadow: string;
38
+ };
39
+ price: {
40
+ color: string;
41
+ textShadow: string;
42
+ };
43
+ };
44
+ /** Image container on glass surface - Solid background for performance */
45
+ export declare const glassImageContainer: CSSProperties;
46
+ /** Badge on glass surface */
47
+ export declare const glassBadge: CSSProperties;
48
+ /** Scroll indicator dot */
49
+ export declare const glassScrollDot: CSSProperties;
@@ -3,5 +3,5 @@
3
3
  */
4
4
  export type { HostContext, HostUserContext, HostVehicleContext, HostCartContext, HostHistoryContext, BobConfig, HostApiConfig, BobCallbacks, BobProviderConfig, CartItem as ContextCartItem, SavedItem, PurchaseRecord, Vehicle as ContextVehicle, } from './context';
5
5
  export type { Vehicle } from './vehicle';
6
- export type { Product, APIPart, CartItem, ServicePackage } from './product';
6
+ export type { Product, APIPart, CartItem, ServicePackage, PreparedTier, PreparedTierProduct, PreparedTierBrand } from './product';
7
7
  export type { Message, HighlightedProduct } from './message';
@@ -1,9 +1,12 @@
1
- /**
2
- * Message types for the Bob widget chat
3
- */
1
+ import { Product } from './product';
2
+
4
3
  export interface Message {
5
4
  role: "user" | "assistant";
6
5
  content: string;
6
+ /** Products Bob is specifically recommending in this message */
7
+ suggestedProducts?: Product[];
8
+ /** Custom header for suggestions (e.g., "Wipers for your 2018 Toyota Rav 4") */
9
+ suggestionsTitle?: string;
7
10
  }
8
11
  export interface HighlightedProduct {
9
12
  brand: string;
@@ -16,24 +16,34 @@ export interface Product {
16
16
  image?: string;
17
17
  quantity?: number;
18
18
  }
19
+ /**
20
+ * API response format from CARFIX retrieve-parts endpoint
21
+ *
22
+ * CANONICAL FIELDS (preferred):
23
+ * - sku, brand, partslot_description, part_number, price, image_url
24
+ *
25
+ * LEGACY FIELDS (backward compatibility):
26
+ * - SKU, Brand, Price, Image, "Part Product Type", "Part Number", "Metro Retail Price"
27
+ */
19
28
  export interface APIPart {
20
- SKU?: string;
21
29
  sku?: string;
22
- "Part Product Type"?: string;
23
- partslot_description?: string;
24
- Brand?: string;
25
30
  brand?: string;
26
- "Part Number"?: string;
31
+ partslot_description?: string;
27
32
  part_number?: string;
28
- "Metro Retail Price"?: number;
29
33
  price?: number;
30
- "Per Car Qty"?: number;
34
+ image_url?: string;
31
35
  per_car_qty?: number;
32
36
  vehicle_id?: number;
33
37
  volume?: string | null;
34
38
  viscosity?: string | null;
35
- image_url?: string;
36
39
  web_description?: string | null;
40
+ SKU?: string;
41
+ Brand?: string;
42
+ Price?: number;
43
+ "Part Product Type"?: string;
44
+ "Part Number"?: string;
45
+ "Metro Retail Price"?: number;
46
+ "Per Car Qty"?: number;
37
47
  }
38
48
  export interface CartItem {
39
49
  product_id: string;
@@ -50,5 +60,82 @@ export interface ServicePackage {
50
60
  title: string;
51
61
  description: string;
52
62
  from_price: number;
53
- partslots?: unknown[];
63
+ estimated_time?: string;
64
+ difficulty_level?: string;
65
+ bundle_discount_percentage?: number;
66
+ carfixValueTier?: string;
67
+ carfixValueProducts?: string[];
68
+ partslots?: Partslot[];
69
+ icon_url?: string;
70
+ preparedTiers?: PreparedTier[];
71
+ }
72
+ /** Server-prepared tier data - ready to render */
73
+ export interface PreparedTier {
74
+ tierName: string;
75
+ displayName: string;
76
+ description: string;
77
+ isRecommended: boolean;
78
+ isHidden: boolean;
79
+ totalPrice: number;
80
+ productCount: number;
81
+ dominantBrand: string | null;
82
+ brands: PreparedTierBrand[];
83
+ products: PreparedTierProduct[];
84
+ }
85
+ export interface PreparedTierBrand {
86
+ name: string;
87
+ fullName: string;
88
+ imageUrl: string;
89
+ }
90
+ export interface PreparedTierProduct {
91
+ partslotId: number;
92
+ partslotName: string;
93
+ sku: string;
94
+ name: string;
95
+ brand: string;
96
+ brandFullName: string;
97
+ brandImageUrl: string;
98
+ productImageUrl: string;
99
+ price: number;
100
+ unitPrice: number;
101
+ displayPrice: number;
102
+ isRotor: boolean;
103
+ isMultiQty: boolean;
104
+ perCarQty: number;
105
+ partNumber: string | null;
106
+ webDescription: string | null;
107
+ viscosity: string | null;
108
+ volume: number | null;
109
+ }
110
+ export interface Partslot {
111
+ id: number;
112
+ name: string;
113
+ description?: string;
114
+ products: {
115
+ quality_tiers: QualityTiers;
116
+ };
117
+ }
118
+ export interface QualityTiers {
119
+ Economy?: Part[];
120
+ Standard?: Part[];
121
+ Premium?: Part[];
122
+ Performance?: Part[];
123
+ }
124
+ export interface Part {
125
+ sku: string;
126
+ name: string;
127
+ brand: string;
128
+ price: number;
129
+ is_on_sale?: boolean;
130
+ sale_price?: number;
131
+ was_price?: number;
132
+ discount_percentage?: number;
133
+ image_url?: string;
134
+ part_number?: string;
135
+ web_description?: string;
136
+ classification?: {
137
+ primary_tier: string;
138
+ sub_tier: string;
139
+ display_name: string;
140
+ };
54
141
  }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Debug utilities for Bob Widget
3
+ *
4
+ * Enable debug mode by:
5
+ * - Setting localStorage: localStorage.setItem('bob_debug', 'true')
6
+ * - Adding URL param: ?bob_debug=true
7
+ */
8
+ /**
9
+ * Log debug messages (only in debug mode)
10
+ */
11
+ export declare const bobLog: (...args: unknown[]) => void;
12
+ /**
13
+ * Log warnings (only in debug mode)
14
+ */
15
+ export declare const bobWarn: (...args: unknown[]) => void;
16
+ /**
17
+ * Log errors (always logged, not gated by debug mode)
18
+ */
19
+ export declare const bobError: (...args: unknown[]) => void;
20
+ /**
21
+ * Check if debug mode is enabled
22
+ */
23
+ export declare const isBobDebug: () => boolean;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gymmymac/bob-widget",
3
- "version": "2.0.0",
4
- "description": "Bob - AI-powered automotive parts assistant widget with full immersive experience and GA4 analytics",
3
+ "version": "3.0.1",
4
+ "description": "Bob - AI-powered automotive parts assistant widget with multi-tenant support, RAF animations, swipeable interactions, and GA4 analytics",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
7
7
  "types": "dist/index.d.ts",