@intentai/react 2.1.2 → 2.2.0
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.
- package/dist/index.d.mts +150 -6
- package/dist/index.d.ts +150 -6
- package/dist/index.js +385 -20
- package/dist/index.mjs +379 -19
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -31,6 +31,149 @@ interface FeedbackData {
|
|
|
31
31
|
}
|
|
32
32
|
declare const PremiumVoiceWidget: React.FC<WidgetConfig>;
|
|
33
33
|
|
|
34
|
+
interface IntentAIProviderConfig {
|
|
35
|
+
/** Your Intent AI API key (required) */
|
|
36
|
+
apiKey: string;
|
|
37
|
+
/** API URL (defaults to production) */
|
|
38
|
+
apiUrl?: string;
|
|
39
|
+
/** User information for identifying feedback */
|
|
40
|
+
user?: IntentAIUser$1;
|
|
41
|
+
/** Additional metadata to attach to all feedback */
|
|
42
|
+
metadata?: Record<string, unknown>;
|
|
43
|
+
}
|
|
44
|
+
interface IntentAIUser$1 {
|
|
45
|
+
id?: string;
|
|
46
|
+
email?: string;
|
|
47
|
+
name?: string;
|
|
48
|
+
[key: string]: unknown;
|
|
49
|
+
}
|
|
50
|
+
interface IntentAIContextValue {
|
|
51
|
+
/** Identify the current user */
|
|
52
|
+
identify: (user: IntentAIUser$1) => void;
|
|
53
|
+
/** Set metadata to attach to feedback */
|
|
54
|
+
setMetadata: (metadata: Record<string, unknown>) => void;
|
|
55
|
+
/** Clear the current user */
|
|
56
|
+
clearUser: () => void;
|
|
57
|
+
/** Whether the provider is configured */
|
|
58
|
+
isConfigured: boolean;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Hook to access Intent AI context for user identification and metadata
|
|
62
|
+
*/
|
|
63
|
+
declare function useIntentAI(): IntentAIContextValue;
|
|
64
|
+
interface IntentAIProviderProps extends IntentAIProviderConfig {
|
|
65
|
+
/** Widget configuration options */
|
|
66
|
+
widgetConfig?: Omit<WidgetConfig, 'onSubmit'>;
|
|
67
|
+
/** Children components */
|
|
68
|
+
children?: React.ReactNode;
|
|
69
|
+
/** Disable the widget (only provide context) */
|
|
70
|
+
disableWidget?: boolean;
|
|
71
|
+
/** Callback when feedback is submitted successfully */
|
|
72
|
+
onFeedbackSubmitted?: () => void;
|
|
73
|
+
/** Callback when there's an error */
|
|
74
|
+
onError?: (error: Error) => void;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Intent AI Provider - Wraps your app with feedback collection capabilities
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```tsx
|
|
81
|
+
* import { IntentAIProvider } from '@intentai/react';
|
|
82
|
+
*
|
|
83
|
+
* function App() {
|
|
84
|
+
* return (
|
|
85
|
+
* <IntentAIProvider
|
|
86
|
+
* apiKey="your-api-key"
|
|
87
|
+
* user={{ id: 'user-123', email: 'user@example.com' }}
|
|
88
|
+
* >
|
|
89
|
+
* <YourApp />
|
|
90
|
+
* </IntentAIProvider>
|
|
91
|
+
* );
|
|
92
|
+
* }
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
declare function IntentAIProvider({ apiKey, apiUrl, user: initialUser, metadata: initialMetadata, widgetConfig, children, disableWidget, onFeedbackSubmitted, onError, }: IntentAIProviderProps): React.ReactElement;
|
|
96
|
+
/**
|
|
97
|
+
* Hook to identify users - convenience wrapper around useIntentAI
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```tsx
|
|
101
|
+
* function UserProfile({ user }) {
|
|
102
|
+
* const identify = useIdentify();
|
|
103
|
+
*
|
|
104
|
+
* useEffect(() => {
|
|
105
|
+
* identify({ id: user.id, email: user.email, name: user.name });
|
|
106
|
+
* }, [user]);
|
|
107
|
+
*
|
|
108
|
+
* return <div>...</div>;
|
|
109
|
+
* }
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
declare function useIdentify(): (user: IntentAIUser$1) => void;
|
|
113
|
+
/**
|
|
114
|
+
* Hook to set metadata - convenience wrapper around useIntentAI
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```tsx
|
|
118
|
+
* function Dashboard({ plan }) {
|
|
119
|
+
* const setMetadata = useSetMetadata();
|
|
120
|
+
*
|
|
121
|
+
* useEffect(() => {
|
|
122
|
+
* setMetadata({ plan, feature: 'dashboard' });
|
|
123
|
+
* }, [plan]);
|
|
124
|
+
*
|
|
125
|
+
* return <div>...</div>;
|
|
126
|
+
* }
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
declare function useSetMetadata(): (metadata: Record<string, unknown>) => void;
|
|
130
|
+
|
|
131
|
+
interface FeedbackButtonProps {
|
|
132
|
+
/** Button variant style */
|
|
133
|
+
variant?: 'primary' | 'secondary' | 'ghost' | 'minimal';
|
|
134
|
+
/** Button size */
|
|
135
|
+
size?: 'sm' | 'md' | 'lg';
|
|
136
|
+
/** Show icon */
|
|
137
|
+
showIcon?: boolean;
|
|
138
|
+
/** Custom icon (React element) */
|
|
139
|
+
icon?: React.ReactNode;
|
|
140
|
+
/** Button label */
|
|
141
|
+
label?: string;
|
|
142
|
+
/** Additional class name */
|
|
143
|
+
className?: string;
|
|
144
|
+
/** Custom styles */
|
|
145
|
+
style?: React.CSSProperties;
|
|
146
|
+
/** Primary color */
|
|
147
|
+
primaryColor?: string;
|
|
148
|
+
/** Click handler - if provided, will be called instead of opening widget */
|
|
149
|
+
onClick?: () => void;
|
|
150
|
+
/** Disabled state */
|
|
151
|
+
disabled?: boolean;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Customizable feedback button that can trigger the widget or custom actions
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* ```tsx
|
|
158
|
+
* // Basic usage - triggers widget
|
|
159
|
+
* <FeedbackButton />
|
|
160
|
+
*
|
|
161
|
+
* // Custom label and style
|
|
162
|
+
* <FeedbackButton
|
|
163
|
+
* label="Give Feedback"
|
|
164
|
+
* variant="secondary"
|
|
165
|
+
* size="lg"
|
|
166
|
+
* />
|
|
167
|
+
*
|
|
168
|
+
* // With custom click handler
|
|
169
|
+
* <FeedbackButton
|
|
170
|
+
* label="Report Issue"
|
|
171
|
+
* onClick={() => console.log('clicked')}
|
|
172
|
+
* />
|
|
173
|
+
* ```
|
|
174
|
+
*/
|
|
175
|
+
declare function FeedbackButton({ variant, size, showIcon, icon, label, className, style, primaryColor, onClick, disabled, }: FeedbackButtonProps): React.ReactElement;
|
|
176
|
+
|
|
34
177
|
interface IntentAIUser {
|
|
35
178
|
id?: string;
|
|
36
179
|
email?: string;
|
|
@@ -102,29 +245,30 @@ declare global {
|
|
|
102
245
|
*/
|
|
103
246
|
declare function IntentAIWidget({ apiKey, apiUrl, widgetUrl, position, theme, primaryColor, allowVoice, allowText, allowScreenshot, customMetadata, user, onOpen: _onOpen, onClose: _onClose, onFeedbackSubmitted: _onFeedbackSubmitted, onError, }: IntentAIProps): null;
|
|
104
247
|
/**
|
|
105
|
-
* Hook to control the
|
|
248
|
+
* Hook to control the script-tag widget programmatically
|
|
249
|
+
* @deprecated Use useIntentAI from IntentAIProvider instead for React apps
|
|
106
250
|
*
|
|
107
251
|
* @example
|
|
108
252
|
* ```tsx
|
|
109
|
-
* import { IntentAIWidget,
|
|
253
|
+
* import { IntentAIWidget, useWidgetControls } from '@intentai/react';
|
|
110
254
|
*
|
|
111
255
|
* function App() {
|
|
112
|
-
* const { open, close, identify } =
|
|
256
|
+
* const { open, close, identify } = useWidgetControls();
|
|
113
257
|
*
|
|
114
258
|
* return (
|
|
115
259
|
* <>
|
|
116
|
-
* <IntentAIWidget apiKey="your-api-key" />
|
|
260
|
+
* <IntentAIWidget apiKey="your-api-key" widgetUrl="..." apiUrl="..." />
|
|
117
261
|
* <button onClick={open}>Open Feedback</button>
|
|
118
262
|
* </>
|
|
119
263
|
* );
|
|
120
264
|
* }
|
|
121
265
|
* ```
|
|
122
266
|
*/
|
|
123
|
-
declare function
|
|
267
|
+
declare function useWidgetControls(): {
|
|
124
268
|
open: () => void;
|
|
125
269
|
close: () => void;
|
|
126
270
|
identify: (user: IntentAIUser) => void;
|
|
127
271
|
setMetadata: (metadata: Record<string, any>) => void;
|
|
128
272
|
};
|
|
129
273
|
|
|
130
|
-
export { type IntentAIConfig, type IntentAIProps, type IntentAIUser, IntentAIWidget, PremiumVoiceWidget, type WidgetConfig as PremiumWidgetConfig, IntentAIWidget as default, useIntentAI };
|
|
274
|
+
export { FeedbackButton, type FeedbackButtonProps, type IntentAIConfig, type IntentAIContextValue, type IntentAIProps, IntentAIProvider, type IntentAIProviderConfig, type IntentAIProviderProps, type IntentAIUser, IntentAIWidget, PremiumVoiceWidget, type WidgetConfig as PremiumWidgetConfig, IntentAIWidget as default, useIdentify, useIntentAI, useSetMetadata, useWidgetControls };
|
package/dist/index.d.ts
CHANGED
|
@@ -31,6 +31,149 @@ interface FeedbackData {
|
|
|
31
31
|
}
|
|
32
32
|
declare const PremiumVoiceWidget: React.FC<WidgetConfig>;
|
|
33
33
|
|
|
34
|
+
interface IntentAIProviderConfig {
|
|
35
|
+
/** Your Intent AI API key (required) */
|
|
36
|
+
apiKey: string;
|
|
37
|
+
/** API URL (defaults to production) */
|
|
38
|
+
apiUrl?: string;
|
|
39
|
+
/** User information for identifying feedback */
|
|
40
|
+
user?: IntentAIUser$1;
|
|
41
|
+
/** Additional metadata to attach to all feedback */
|
|
42
|
+
metadata?: Record<string, unknown>;
|
|
43
|
+
}
|
|
44
|
+
interface IntentAIUser$1 {
|
|
45
|
+
id?: string;
|
|
46
|
+
email?: string;
|
|
47
|
+
name?: string;
|
|
48
|
+
[key: string]: unknown;
|
|
49
|
+
}
|
|
50
|
+
interface IntentAIContextValue {
|
|
51
|
+
/** Identify the current user */
|
|
52
|
+
identify: (user: IntentAIUser$1) => void;
|
|
53
|
+
/** Set metadata to attach to feedback */
|
|
54
|
+
setMetadata: (metadata: Record<string, unknown>) => void;
|
|
55
|
+
/** Clear the current user */
|
|
56
|
+
clearUser: () => void;
|
|
57
|
+
/** Whether the provider is configured */
|
|
58
|
+
isConfigured: boolean;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Hook to access Intent AI context for user identification and metadata
|
|
62
|
+
*/
|
|
63
|
+
declare function useIntentAI(): IntentAIContextValue;
|
|
64
|
+
interface IntentAIProviderProps extends IntentAIProviderConfig {
|
|
65
|
+
/** Widget configuration options */
|
|
66
|
+
widgetConfig?: Omit<WidgetConfig, 'onSubmit'>;
|
|
67
|
+
/** Children components */
|
|
68
|
+
children?: React.ReactNode;
|
|
69
|
+
/** Disable the widget (only provide context) */
|
|
70
|
+
disableWidget?: boolean;
|
|
71
|
+
/** Callback when feedback is submitted successfully */
|
|
72
|
+
onFeedbackSubmitted?: () => void;
|
|
73
|
+
/** Callback when there's an error */
|
|
74
|
+
onError?: (error: Error) => void;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Intent AI Provider - Wraps your app with feedback collection capabilities
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```tsx
|
|
81
|
+
* import { IntentAIProvider } from '@intentai/react';
|
|
82
|
+
*
|
|
83
|
+
* function App() {
|
|
84
|
+
* return (
|
|
85
|
+
* <IntentAIProvider
|
|
86
|
+
* apiKey="your-api-key"
|
|
87
|
+
* user={{ id: 'user-123', email: 'user@example.com' }}
|
|
88
|
+
* >
|
|
89
|
+
* <YourApp />
|
|
90
|
+
* </IntentAIProvider>
|
|
91
|
+
* );
|
|
92
|
+
* }
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
declare function IntentAIProvider({ apiKey, apiUrl, user: initialUser, metadata: initialMetadata, widgetConfig, children, disableWidget, onFeedbackSubmitted, onError, }: IntentAIProviderProps): React.ReactElement;
|
|
96
|
+
/**
|
|
97
|
+
* Hook to identify users - convenience wrapper around useIntentAI
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```tsx
|
|
101
|
+
* function UserProfile({ user }) {
|
|
102
|
+
* const identify = useIdentify();
|
|
103
|
+
*
|
|
104
|
+
* useEffect(() => {
|
|
105
|
+
* identify({ id: user.id, email: user.email, name: user.name });
|
|
106
|
+
* }, [user]);
|
|
107
|
+
*
|
|
108
|
+
* return <div>...</div>;
|
|
109
|
+
* }
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
declare function useIdentify(): (user: IntentAIUser$1) => void;
|
|
113
|
+
/**
|
|
114
|
+
* Hook to set metadata - convenience wrapper around useIntentAI
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```tsx
|
|
118
|
+
* function Dashboard({ plan }) {
|
|
119
|
+
* const setMetadata = useSetMetadata();
|
|
120
|
+
*
|
|
121
|
+
* useEffect(() => {
|
|
122
|
+
* setMetadata({ plan, feature: 'dashboard' });
|
|
123
|
+
* }, [plan]);
|
|
124
|
+
*
|
|
125
|
+
* return <div>...</div>;
|
|
126
|
+
* }
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
declare function useSetMetadata(): (metadata: Record<string, unknown>) => void;
|
|
130
|
+
|
|
131
|
+
interface FeedbackButtonProps {
|
|
132
|
+
/** Button variant style */
|
|
133
|
+
variant?: 'primary' | 'secondary' | 'ghost' | 'minimal';
|
|
134
|
+
/** Button size */
|
|
135
|
+
size?: 'sm' | 'md' | 'lg';
|
|
136
|
+
/** Show icon */
|
|
137
|
+
showIcon?: boolean;
|
|
138
|
+
/** Custom icon (React element) */
|
|
139
|
+
icon?: React.ReactNode;
|
|
140
|
+
/** Button label */
|
|
141
|
+
label?: string;
|
|
142
|
+
/** Additional class name */
|
|
143
|
+
className?: string;
|
|
144
|
+
/** Custom styles */
|
|
145
|
+
style?: React.CSSProperties;
|
|
146
|
+
/** Primary color */
|
|
147
|
+
primaryColor?: string;
|
|
148
|
+
/** Click handler - if provided, will be called instead of opening widget */
|
|
149
|
+
onClick?: () => void;
|
|
150
|
+
/** Disabled state */
|
|
151
|
+
disabled?: boolean;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Customizable feedback button that can trigger the widget or custom actions
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* ```tsx
|
|
158
|
+
* // Basic usage - triggers widget
|
|
159
|
+
* <FeedbackButton />
|
|
160
|
+
*
|
|
161
|
+
* // Custom label and style
|
|
162
|
+
* <FeedbackButton
|
|
163
|
+
* label="Give Feedback"
|
|
164
|
+
* variant="secondary"
|
|
165
|
+
* size="lg"
|
|
166
|
+
* />
|
|
167
|
+
*
|
|
168
|
+
* // With custom click handler
|
|
169
|
+
* <FeedbackButton
|
|
170
|
+
* label="Report Issue"
|
|
171
|
+
* onClick={() => console.log('clicked')}
|
|
172
|
+
* />
|
|
173
|
+
* ```
|
|
174
|
+
*/
|
|
175
|
+
declare function FeedbackButton({ variant, size, showIcon, icon, label, className, style, primaryColor, onClick, disabled, }: FeedbackButtonProps): React.ReactElement;
|
|
176
|
+
|
|
34
177
|
interface IntentAIUser {
|
|
35
178
|
id?: string;
|
|
36
179
|
email?: string;
|
|
@@ -102,29 +245,30 @@ declare global {
|
|
|
102
245
|
*/
|
|
103
246
|
declare function IntentAIWidget({ apiKey, apiUrl, widgetUrl, position, theme, primaryColor, allowVoice, allowText, allowScreenshot, customMetadata, user, onOpen: _onOpen, onClose: _onClose, onFeedbackSubmitted: _onFeedbackSubmitted, onError, }: IntentAIProps): null;
|
|
104
247
|
/**
|
|
105
|
-
* Hook to control the
|
|
248
|
+
* Hook to control the script-tag widget programmatically
|
|
249
|
+
* @deprecated Use useIntentAI from IntentAIProvider instead for React apps
|
|
106
250
|
*
|
|
107
251
|
* @example
|
|
108
252
|
* ```tsx
|
|
109
|
-
* import { IntentAIWidget,
|
|
253
|
+
* import { IntentAIWidget, useWidgetControls } from '@intentai/react';
|
|
110
254
|
*
|
|
111
255
|
* function App() {
|
|
112
|
-
* const { open, close, identify } =
|
|
256
|
+
* const { open, close, identify } = useWidgetControls();
|
|
113
257
|
*
|
|
114
258
|
* return (
|
|
115
259
|
* <>
|
|
116
|
-
* <IntentAIWidget apiKey="your-api-key" />
|
|
260
|
+
* <IntentAIWidget apiKey="your-api-key" widgetUrl="..." apiUrl="..." />
|
|
117
261
|
* <button onClick={open}>Open Feedback</button>
|
|
118
262
|
* </>
|
|
119
263
|
* );
|
|
120
264
|
* }
|
|
121
265
|
* ```
|
|
122
266
|
*/
|
|
123
|
-
declare function
|
|
267
|
+
declare function useWidgetControls(): {
|
|
124
268
|
open: () => void;
|
|
125
269
|
close: () => void;
|
|
126
270
|
identify: (user: IntentAIUser) => void;
|
|
127
271
|
setMetadata: (metadata: Record<string, any>) => void;
|
|
128
272
|
};
|
|
129
273
|
|
|
130
|
-
export { type IntentAIConfig, type IntentAIProps, type IntentAIUser, IntentAIWidget, PremiumVoiceWidget, type WidgetConfig as PremiumWidgetConfig, IntentAIWidget as default, useIntentAI };
|
|
274
|
+
export { FeedbackButton, type FeedbackButtonProps, type IntentAIConfig, type IntentAIContextValue, type IntentAIProps, IntentAIProvider, type IntentAIProviderConfig, type IntentAIProviderProps, type IntentAIUser, IntentAIWidget, PremiumVoiceWidget, type WidgetConfig as PremiumWidgetConfig, IntentAIWidget as default, useIdentify, useIntentAI, useSetMetadata, useWidgetControls };
|
package/dist/index.js
CHANGED
|
@@ -21,13 +21,18 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
// src/index.tsx
|
|
22
22
|
var index_exports = {};
|
|
23
23
|
__export(index_exports, {
|
|
24
|
+
FeedbackButton: () => FeedbackButton,
|
|
25
|
+
IntentAIProvider: () => IntentAIProvider,
|
|
24
26
|
IntentAIWidget: () => IntentAIWidget,
|
|
25
27
|
PremiumVoiceWidget: () => PremiumVoiceWidget,
|
|
26
28
|
default: () => index_default,
|
|
27
|
-
|
|
29
|
+
useIdentify: () => useIdentify,
|
|
30
|
+
useIntentAI: () => useIntentAI,
|
|
31
|
+
useSetMetadata: () => useSetMetadata,
|
|
32
|
+
useWidgetControls: () => useWidgetControls
|
|
28
33
|
});
|
|
29
34
|
module.exports = __toCommonJS(index_exports);
|
|
30
|
-
var
|
|
35
|
+
var import_react4 = require("react");
|
|
31
36
|
|
|
32
37
|
// src/components/PremiumVoiceWidget.tsx
|
|
33
38
|
var import_react = require("react");
|
|
@@ -129,8 +134,9 @@ var useAudioLevel = (isRecording) => {
|
|
|
129
134
|
streamRef.current.getTracks().forEach((track) => track.stop());
|
|
130
135
|
streamRef.current = null;
|
|
131
136
|
}
|
|
132
|
-
if (audioContextRef.current) {
|
|
133
|
-
audioContextRef.current.close()
|
|
137
|
+
if (audioContextRef.current && audioContextRef.current.state !== "closed") {
|
|
138
|
+
audioContextRef.current.close().catch(() => {
|
|
139
|
+
});
|
|
134
140
|
audioContextRef.current = null;
|
|
135
141
|
}
|
|
136
142
|
analyserRef.current = null;
|
|
@@ -168,8 +174,9 @@ var useAudioLevel = (isRecording) => {
|
|
|
168
174
|
if (streamRef.current) {
|
|
169
175
|
streamRef.current.getTracks().forEach((track) => track.stop());
|
|
170
176
|
}
|
|
171
|
-
if (audioContextRef.current) {
|
|
172
|
-
audioContextRef.current.close()
|
|
177
|
+
if (audioContextRef.current && audioContextRef.current.state !== "closed") {
|
|
178
|
+
audioContextRef.current.close().catch(() => {
|
|
179
|
+
});
|
|
173
180
|
}
|
|
174
181
|
};
|
|
175
182
|
}, [isRecording]);
|
|
@@ -390,12 +397,12 @@ var AmbientOrb = ({ audioLevel, isRecording }) => {
|
|
|
390
397
|
]
|
|
391
398
|
}
|
|
392
399
|
) }),
|
|
393
|
-
particles.map((particle) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
400
|
+
particles.filter((p) => p.size != null).map((particle) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
394
401
|
import_framer_motion.motion.circle,
|
|
395
402
|
{
|
|
396
403
|
cx: 70,
|
|
397
404
|
cy: 70,
|
|
398
|
-
r: particle.size,
|
|
405
|
+
r: particle.size || 3,
|
|
399
406
|
fill: particle.color,
|
|
400
407
|
initial: { opacity: 0.8, scale: 1 },
|
|
401
408
|
animate: {
|
|
@@ -1642,6 +1649,359 @@ function adjustColor(color, amount) {
|
|
|
1642
1649
|
return `#${(r << 16 | g << 8 | b).toString(16).padStart(6, "0")}`;
|
|
1643
1650
|
}
|
|
1644
1651
|
|
|
1652
|
+
// src/components/IntentAIProvider.tsx
|
|
1653
|
+
var import_react2 = require("react");
|
|
1654
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1655
|
+
var IntentAIContext = (0, import_react2.createContext)(null);
|
|
1656
|
+
function useIntentAI() {
|
|
1657
|
+
const context = (0, import_react2.useContext)(IntentAIContext);
|
|
1658
|
+
if (!context) {
|
|
1659
|
+
throw new Error("useIntentAI must be used within an IntentAIProvider");
|
|
1660
|
+
}
|
|
1661
|
+
return context;
|
|
1662
|
+
}
|
|
1663
|
+
function generateSessionId() {
|
|
1664
|
+
return `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
1665
|
+
}
|
|
1666
|
+
function getDeviceInfo() {
|
|
1667
|
+
const ua = navigator.userAgent;
|
|
1668
|
+
let browser = "Unknown";
|
|
1669
|
+
let browserVersion = "Unknown";
|
|
1670
|
+
if (ua.includes("Firefox/")) {
|
|
1671
|
+
browser = "Firefox";
|
|
1672
|
+
const match = ua.match(/Firefox\/(\d+(\.\d+)?)/);
|
|
1673
|
+
if (match) browserVersion = match[1];
|
|
1674
|
+
} else if (ua.includes("Edg/")) {
|
|
1675
|
+
browser = "Edge";
|
|
1676
|
+
const match = ua.match(/Edg\/(\d+(\.\d+)?)/);
|
|
1677
|
+
if (match) browserVersion = match[1];
|
|
1678
|
+
} else if (ua.includes("Chrome/")) {
|
|
1679
|
+
browser = "Chrome";
|
|
1680
|
+
const match = ua.match(/Chrome\/(\d+(\.\d+)?)/);
|
|
1681
|
+
if (match) browserVersion = match[1];
|
|
1682
|
+
} else if (ua.includes("Safari/") && !ua.includes("Chrome")) {
|
|
1683
|
+
browser = "Safari";
|
|
1684
|
+
const match = ua.match(/Version\/(\d+(\.\d+)?)/);
|
|
1685
|
+
if (match) browserVersion = match[1];
|
|
1686
|
+
}
|
|
1687
|
+
let os = "Unknown";
|
|
1688
|
+
if (ua.includes("Windows NT 10")) os = "Windows 10";
|
|
1689
|
+
else if (ua.includes("Windows")) os = "Windows";
|
|
1690
|
+
else if (ua.includes("Mac OS X")) os = "macOS";
|
|
1691
|
+
else if (ua.includes("Android")) os = "Android";
|
|
1692
|
+
else if (ua.includes("iPhone") || ua.includes("iPad")) os = "iOS";
|
|
1693
|
+
else if (ua.includes("Linux")) os = "Linux";
|
|
1694
|
+
let device = "Desktop";
|
|
1695
|
+
if (/iPhone|iPad|iPod/.test(ua)) device = "iOS Device";
|
|
1696
|
+
else if (/Android.*Mobile/.test(ua)) device = "Android Phone";
|
|
1697
|
+
else if (/Android/.test(ua)) device = "Android Tablet";
|
|
1698
|
+
return { browser, browserVersion, os, device };
|
|
1699
|
+
}
|
|
1700
|
+
var DEFAULT_API_URL = "https://api-production-5b88.up.railway.app";
|
|
1701
|
+
function IntentAIProvider({
|
|
1702
|
+
apiKey,
|
|
1703
|
+
apiUrl = DEFAULT_API_URL,
|
|
1704
|
+
user: initialUser,
|
|
1705
|
+
metadata: initialMetadata,
|
|
1706
|
+
widgetConfig,
|
|
1707
|
+
children,
|
|
1708
|
+
disableWidget = false,
|
|
1709
|
+
onFeedbackSubmitted,
|
|
1710
|
+
onError
|
|
1711
|
+
}) {
|
|
1712
|
+
const userRef = (0, import_react2.useRef)(initialUser);
|
|
1713
|
+
const metadataRef = (0, import_react2.useRef)(initialMetadata || {});
|
|
1714
|
+
const sessionIdRef = (0, import_react2.useRef)(generateSessionId());
|
|
1715
|
+
(0, import_react2.useEffect)(() => {
|
|
1716
|
+
if (initialUser) {
|
|
1717
|
+
userRef.current = initialUser;
|
|
1718
|
+
}
|
|
1719
|
+
}, [initialUser]);
|
|
1720
|
+
(0, import_react2.useEffect)(() => {
|
|
1721
|
+
if (initialMetadata) {
|
|
1722
|
+
metadataRef.current = { ...metadataRef.current, ...initialMetadata };
|
|
1723
|
+
}
|
|
1724
|
+
}, [initialMetadata]);
|
|
1725
|
+
const identify = (0, import_react2.useCallback)((user) => {
|
|
1726
|
+
userRef.current = { ...userRef.current, ...user };
|
|
1727
|
+
}, []);
|
|
1728
|
+
const setMetadata = (0, import_react2.useCallback)((metadata) => {
|
|
1729
|
+
metadataRef.current = { ...metadataRef.current, ...metadata };
|
|
1730
|
+
}, []);
|
|
1731
|
+
const clearUser = (0, import_react2.useCallback)(() => {
|
|
1732
|
+
userRef.current = void 0;
|
|
1733
|
+
}, []);
|
|
1734
|
+
const handleSubmit = (0, import_react2.useCallback)(async (feedback) => {
|
|
1735
|
+
const deviceInfo = getDeviceInfo();
|
|
1736
|
+
const user = userRef.current;
|
|
1737
|
+
const customMetadata = metadataRef.current;
|
|
1738
|
+
const categoryMap = {
|
|
1739
|
+
bug: "BUG",
|
|
1740
|
+
feature: "FEATURE",
|
|
1741
|
+
improvement: "IMPROVEMENT",
|
|
1742
|
+
praise: "PRAISE",
|
|
1743
|
+
other: "OTHER"
|
|
1744
|
+
};
|
|
1745
|
+
const payload = {
|
|
1746
|
+
sessionId: sessionIdRef.current,
|
|
1747
|
+
type: feedback.type === "voice" ? "VOICE" : "TEXT",
|
|
1748
|
+
category: categoryMap[feedback.category.toLowerCase()] || feedback.category.toUpperCase(),
|
|
1749
|
+
rawText: feedback.type === "text" ? feedback.content : void 0,
|
|
1750
|
+
transcription: feedback.type === "voice" ? feedback.transcription || feedback.content : void 0,
|
|
1751
|
+
pageUrl: typeof window !== "undefined" ? window.location.href : "",
|
|
1752
|
+
pageTitle: typeof document !== "undefined" ? document.title : "",
|
|
1753
|
+
userId: user?.id,
|
|
1754
|
+
userEmail: user?.email,
|
|
1755
|
+
userName: user?.name,
|
|
1756
|
+
customData: {
|
|
1757
|
+
...customMetadata,
|
|
1758
|
+
...feedback.metadata
|
|
1759
|
+
},
|
|
1760
|
+
browser: deviceInfo.browser,
|
|
1761
|
+
browserVersion: deviceInfo.browserVersion,
|
|
1762
|
+
os: deviceInfo.os,
|
|
1763
|
+
device: deviceInfo.device,
|
|
1764
|
+
screenWidth: typeof window !== "undefined" ? window.screen.width : void 0,
|
|
1765
|
+
screenHeight: typeof window !== "undefined" ? window.screen.height : void 0
|
|
1766
|
+
};
|
|
1767
|
+
try {
|
|
1768
|
+
const response = await fetch(`${apiUrl}/feedback`, {
|
|
1769
|
+
method: "POST",
|
|
1770
|
+
headers: {
|
|
1771
|
+
"Content-Type": "application/json",
|
|
1772
|
+
"X-API-Key": apiKey
|
|
1773
|
+
},
|
|
1774
|
+
body: JSON.stringify(payload)
|
|
1775
|
+
});
|
|
1776
|
+
if (!response.ok) {
|
|
1777
|
+
const errorData = await response.json().catch(() => ({}));
|
|
1778
|
+
throw new Error(errorData.error || `Failed to submit feedback: ${response.status}`);
|
|
1779
|
+
}
|
|
1780
|
+
onFeedbackSubmitted?.();
|
|
1781
|
+
} catch (error) {
|
|
1782
|
+
const err = error instanceof Error ? error : new Error("Failed to submit feedback");
|
|
1783
|
+
onError?.(err);
|
|
1784
|
+
throw err;
|
|
1785
|
+
}
|
|
1786
|
+
}, [apiKey, apiUrl, onFeedbackSubmitted, onError]);
|
|
1787
|
+
const contextValue = (0, import_react2.useMemo)(() => ({
|
|
1788
|
+
identify,
|
|
1789
|
+
setMetadata,
|
|
1790
|
+
clearUser,
|
|
1791
|
+
isConfigured: Boolean(apiKey)
|
|
1792
|
+
}), [identify, setMetadata, clearUser, apiKey]);
|
|
1793
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(IntentAIContext.Provider, { value: contextValue, children: [
|
|
1794
|
+
children,
|
|
1795
|
+
!disableWidget && apiKey && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1796
|
+
PremiumVoiceWidget,
|
|
1797
|
+
{
|
|
1798
|
+
...widgetConfig,
|
|
1799
|
+
onSubmit: handleSubmit
|
|
1800
|
+
}
|
|
1801
|
+
)
|
|
1802
|
+
] });
|
|
1803
|
+
}
|
|
1804
|
+
function useIdentify() {
|
|
1805
|
+
const { identify } = useIntentAI();
|
|
1806
|
+
return identify;
|
|
1807
|
+
}
|
|
1808
|
+
function useSetMetadata() {
|
|
1809
|
+
const { setMetadata } = useIntentAI();
|
|
1810
|
+
return setMetadata;
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
// src/components/FeedbackButton.tsx
|
|
1814
|
+
var import_react3 = require("react");
|
|
1815
|
+
var import_framer_motion2 = require("framer-motion");
|
|
1816
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1817
|
+
var FeedbackIcon = ({ size }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1818
|
+
"svg",
|
|
1819
|
+
{
|
|
1820
|
+
width: size,
|
|
1821
|
+
height: size,
|
|
1822
|
+
viewBox: "0 0 24 24",
|
|
1823
|
+
fill: "none",
|
|
1824
|
+
stroke: "currentColor",
|
|
1825
|
+
strokeWidth: "2",
|
|
1826
|
+
strokeLinecap: "round",
|
|
1827
|
+
strokeLinejoin: "round",
|
|
1828
|
+
children: [
|
|
1829
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }),
|
|
1830
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "9", cy: "10", r: "1", fill: "currentColor" }),
|
|
1831
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "15", cy: "10", r: "1", fill: "currentColor" })
|
|
1832
|
+
]
|
|
1833
|
+
}
|
|
1834
|
+
);
|
|
1835
|
+
function FeedbackButton({
|
|
1836
|
+
variant = "primary",
|
|
1837
|
+
size = "md",
|
|
1838
|
+
showIcon = true,
|
|
1839
|
+
icon,
|
|
1840
|
+
label = "Feedback",
|
|
1841
|
+
className,
|
|
1842
|
+
style,
|
|
1843
|
+
primaryColor = "#10b981",
|
|
1844
|
+
onClick,
|
|
1845
|
+
disabled = false
|
|
1846
|
+
}) {
|
|
1847
|
+
const [isHovered, setIsHovered] = (0, import_react3.useState)(false);
|
|
1848
|
+
const [isPressed, setIsPressed] = (0, import_react3.useState)(false);
|
|
1849
|
+
const buttonRef = (0, import_react3.useRef)(null);
|
|
1850
|
+
const sizes = {
|
|
1851
|
+
sm: {
|
|
1852
|
+
padding: "8px 14px",
|
|
1853
|
+
fontSize: 13,
|
|
1854
|
+
iconSize: 14,
|
|
1855
|
+
gap: 6,
|
|
1856
|
+
borderRadius: 8
|
|
1857
|
+
},
|
|
1858
|
+
md: {
|
|
1859
|
+
padding: "10px 18px",
|
|
1860
|
+
fontSize: 14,
|
|
1861
|
+
iconSize: 16,
|
|
1862
|
+
gap: 8,
|
|
1863
|
+
borderRadius: 10
|
|
1864
|
+
},
|
|
1865
|
+
lg: {
|
|
1866
|
+
padding: "14px 24px",
|
|
1867
|
+
fontSize: 15,
|
|
1868
|
+
iconSize: 18,
|
|
1869
|
+
gap: 10,
|
|
1870
|
+
borderRadius: 12
|
|
1871
|
+
}
|
|
1872
|
+
};
|
|
1873
|
+
const sizeConfig = sizes[size];
|
|
1874
|
+
const variants = {
|
|
1875
|
+
primary: {
|
|
1876
|
+
background: `linear-gradient(145deg, ${primaryColor} 0%, ${adjustColor2(primaryColor, -20)} 100%)`,
|
|
1877
|
+
color: "white",
|
|
1878
|
+
border: "none",
|
|
1879
|
+
boxShadow: `0 4px 16px -4px ${primaryColor}66, 0 2px 4px rgba(0, 0, 0, 0.1)`
|
|
1880
|
+
},
|
|
1881
|
+
secondary: {
|
|
1882
|
+
background: "white",
|
|
1883
|
+
color: "#374151",
|
|
1884
|
+
border: "1px solid #e5e7eb",
|
|
1885
|
+
boxShadow: "0 2px 4px rgba(0, 0, 0, 0.05)"
|
|
1886
|
+
},
|
|
1887
|
+
ghost: {
|
|
1888
|
+
background: "transparent",
|
|
1889
|
+
color: "#6b7280",
|
|
1890
|
+
border: "1px solid transparent",
|
|
1891
|
+
boxShadow: "none"
|
|
1892
|
+
},
|
|
1893
|
+
minimal: {
|
|
1894
|
+
background: "transparent",
|
|
1895
|
+
color: primaryColor,
|
|
1896
|
+
border: "none",
|
|
1897
|
+
boxShadow: "none"
|
|
1898
|
+
}
|
|
1899
|
+
};
|
|
1900
|
+
const variantStyle = variants[variant];
|
|
1901
|
+
const handleClick = (0, import_react3.useCallback)(() => {
|
|
1902
|
+
if (disabled) return;
|
|
1903
|
+
if (onClick) {
|
|
1904
|
+
onClick();
|
|
1905
|
+
return;
|
|
1906
|
+
}
|
|
1907
|
+
const widget = document.querySelector(".fiq-premium-widget");
|
|
1908
|
+
if (widget) {
|
|
1909
|
+
const trigger = widget.querySelector('button[aria-label="Open feedback widget"]');
|
|
1910
|
+
if (trigger) {
|
|
1911
|
+
trigger.click();
|
|
1912
|
+
return;
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
const scriptWidget = document.getElementById("feedbackiq-widget");
|
|
1916
|
+
if (scriptWidget?.shadowRoot) {
|
|
1917
|
+
const trigger = scriptWidget.shadowRoot.querySelector(".fiq-trigger");
|
|
1918
|
+
if (trigger) {
|
|
1919
|
+
trigger.click();
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
}, [onClick, disabled]);
|
|
1923
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1924
|
+
import_framer_motion2.motion.button,
|
|
1925
|
+
{
|
|
1926
|
+
ref: buttonRef,
|
|
1927
|
+
onClick: handleClick,
|
|
1928
|
+
onMouseEnter: () => setIsHovered(true),
|
|
1929
|
+
onMouseLeave: () => {
|
|
1930
|
+
setIsHovered(false);
|
|
1931
|
+
setIsPressed(false);
|
|
1932
|
+
},
|
|
1933
|
+
onMouseDown: () => setIsPressed(true),
|
|
1934
|
+
onMouseUp: () => setIsPressed(false),
|
|
1935
|
+
disabled,
|
|
1936
|
+
animate: {
|
|
1937
|
+
scale: isPressed ? 0.97 : isHovered ? 1.02 : 1,
|
|
1938
|
+
y: isHovered ? -1 : 0
|
|
1939
|
+
},
|
|
1940
|
+
transition: {
|
|
1941
|
+
type: "spring",
|
|
1942
|
+
stiffness: 400,
|
|
1943
|
+
damping: 25
|
|
1944
|
+
},
|
|
1945
|
+
className,
|
|
1946
|
+
style: {
|
|
1947
|
+
display: "inline-flex",
|
|
1948
|
+
alignItems: "center",
|
|
1949
|
+
justifyContent: "center",
|
|
1950
|
+
gap: sizeConfig.gap,
|
|
1951
|
+
padding: sizeConfig.padding,
|
|
1952
|
+
fontSize: sizeConfig.fontSize,
|
|
1953
|
+
fontWeight: 600,
|
|
1954
|
+
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
1955
|
+
borderRadius: sizeConfig.borderRadius,
|
|
1956
|
+
cursor: disabled ? "not-allowed" : "pointer",
|
|
1957
|
+
opacity: disabled ? 0.5 : 1,
|
|
1958
|
+
transition: "all 0.2s ease",
|
|
1959
|
+
position: "relative",
|
|
1960
|
+
overflow: "hidden",
|
|
1961
|
+
...variantStyle,
|
|
1962
|
+
...style
|
|
1963
|
+
},
|
|
1964
|
+
children: [
|
|
1965
|
+
variant === "primary" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1966
|
+
import_framer_motion2.motion.div,
|
|
1967
|
+
{
|
|
1968
|
+
initial: { opacity: 0 },
|
|
1969
|
+
animate: { opacity: isHovered ? 0.1 : 0 },
|
|
1970
|
+
style: {
|
|
1971
|
+
position: "absolute",
|
|
1972
|
+
inset: 0,
|
|
1973
|
+
background: "white",
|
|
1974
|
+
pointerEvents: "none"
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
),
|
|
1978
|
+
variant === "primary" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1979
|
+
"div",
|
|
1980
|
+
{
|
|
1981
|
+
style: {
|
|
1982
|
+
position: "absolute",
|
|
1983
|
+
inset: 0,
|
|
1984
|
+
background: "linear-gradient(180deg, rgba(255,255,255,0.2) 0%, rgba(255,255,255,0) 50%)",
|
|
1985
|
+
borderRadius: "inherit",
|
|
1986
|
+
pointerEvents: "none"
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
),
|
|
1990
|
+
showIcon && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { display: "flex", alignItems: "center", position: "relative", zIndex: 1 }, children: icon || /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FeedbackIcon, { size: sizeConfig.iconSize }) }),
|
|
1991
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { position: "relative", zIndex: 1 }, children: label })
|
|
1992
|
+
]
|
|
1993
|
+
}
|
|
1994
|
+
);
|
|
1995
|
+
}
|
|
1996
|
+
function adjustColor2(color, amount) {
|
|
1997
|
+
const hex = color.replace("#", "");
|
|
1998
|
+
const num = parseInt(hex, 16);
|
|
1999
|
+
const r = Math.min(255, Math.max(0, (num >> 16) + amount));
|
|
2000
|
+
const g = Math.min(255, Math.max(0, (num >> 8 & 255) + amount));
|
|
2001
|
+
const b = Math.min(255, Math.max(0, (num & 255) + amount));
|
|
2002
|
+
return `#${(r << 16 | g << 8 | b).toString(16).padStart(6, "0")}`;
|
|
2003
|
+
}
|
|
2004
|
+
|
|
1645
2005
|
// src/index.tsx
|
|
1646
2006
|
function IntentAIWidget({
|
|
1647
2007
|
apiKey,
|
|
@@ -1660,9 +2020,9 @@ function IntentAIWidget({
|
|
|
1660
2020
|
onFeedbackSubmitted: _onFeedbackSubmitted,
|
|
1661
2021
|
onError
|
|
1662
2022
|
}) {
|
|
1663
|
-
const widgetRef = (0,
|
|
1664
|
-
const scriptLoadedRef = (0,
|
|
1665
|
-
const initWidget = (0,
|
|
2023
|
+
const widgetRef = (0, import_react4.useRef)(null);
|
|
2024
|
+
const scriptLoadedRef = (0, import_react4.useRef)(false);
|
|
2025
|
+
const initWidget = (0, import_react4.useCallback)(() => {
|
|
1666
2026
|
if (!window.FeedbackIQ || widgetRef.current) return;
|
|
1667
2027
|
try {
|
|
1668
2028
|
widgetRef.current = new window.FeedbackIQ({
|
|
@@ -1685,7 +2045,7 @@ function IntentAIWidget({
|
|
|
1685
2045
|
void _onClose;
|
|
1686
2046
|
void _onFeedbackSubmitted;
|
|
1687
2047
|
}, [apiKey, apiUrl, widgetUrl, position, theme, primaryColor, allowVoice, allowText, allowScreenshot, customMetadata, user, onError]);
|
|
1688
|
-
(0,
|
|
2048
|
+
(0, import_react4.useEffect)(() => {
|
|
1689
2049
|
if (!widgetUrl) {
|
|
1690
2050
|
onError?.(new Error("widgetUrl is required for IntentAIWidget"));
|
|
1691
2051
|
return;
|
|
@@ -1716,39 +2076,39 @@ function IntentAIWidget({
|
|
|
1716
2076
|
}
|
|
1717
2077
|
};
|
|
1718
2078
|
}, [initWidget, onError, widgetUrl]);
|
|
1719
|
-
(0,
|
|
2079
|
+
(0, import_react4.useEffect)(() => {
|
|
1720
2080
|
if (widgetRef.current && user) {
|
|
1721
2081
|
widgetRef.current.identify(user);
|
|
1722
2082
|
}
|
|
1723
2083
|
}, [user]);
|
|
1724
|
-
(0,
|
|
2084
|
+
(0, import_react4.useEffect)(() => {
|
|
1725
2085
|
if (widgetRef.current && customMetadata) {
|
|
1726
2086
|
widgetRef.current.setMetadata(customMetadata);
|
|
1727
2087
|
}
|
|
1728
2088
|
}, [customMetadata]);
|
|
1729
2089
|
return null;
|
|
1730
2090
|
}
|
|
1731
|
-
function
|
|
1732
|
-
const open = (0,
|
|
2091
|
+
function useWidgetControls() {
|
|
2092
|
+
const open = (0, import_react4.useCallback)(() => {
|
|
1733
2093
|
const widget = document.getElementById("feedbackiq-widget");
|
|
1734
2094
|
if (widget) {
|
|
1735
2095
|
const trigger = widget.shadowRoot?.querySelector(".fiq-trigger");
|
|
1736
2096
|
trigger?.click();
|
|
1737
2097
|
}
|
|
1738
2098
|
}, []);
|
|
1739
|
-
const close = (0,
|
|
2099
|
+
const close = (0, import_react4.useCallback)(() => {
|
|
1740
2100
|
const widget = document.getElementById("feedbackiq-widget");
|
|
1741
2101
|
if (widget) {
|
|
1742
2102
|
const closeBtn = widget.shadowRoot?.querySelector(".fiq-close");
|
|
1743
2103
|
closeBtn?.click();
|
|
1744
2104
|
}
|
|
1745
2105
|
}, []);
|
|
1746
|
-
const identify = (0,
|
|
2106
|
+
const identify = (0, import_react4.useCallback)((user) => {
|
|
1747
2107
|
if (window.IntentAI?.widget) {
|
|
1748
2108
|
window.IntentAI.widget.identify(user);
|
|
1749
2109
|
}
|
|
1750
2110
|
}, []);
|
|
1751
|
-
const setMetadata = (0,
|
|
2111
|
+
const setMetadata = (0, import_react4.useCallback)((metadata) => {
|
|
1752
2112
|
if (window.IntentAI?.widget) {
|
|
1753
2113
|
window.IntentAI.widget.setMetadata(metadata);
|
|
1754
2114
|
}
|
|
@@ -1758,7 +2118,12 @@ function useIntentAI() {
|
|
|
1758
2118
|
var index_default = IntentAIWidget;
|
|
1759
2119
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1760
2120
|
0 && (module.exports = {
|
|
2121
|
+
FeedbackButton,
|
|
2122
|
+
IntentAIProvider,
|
|
1761
2123
|
IntentAIWidget,
|
|
1762
2124
|
PremiumVoiceWidget,
|
|
1763
|
-
|
|
2125
|
+
useIdentify,
|
|
2126
|
+
useIntentAI,
|
|
2127
|
+
useSetMetadata,
|
|
2128
|
+
useWidgetControls
|
|
1764
2129
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
// src/index.tsx
|
|
4
|
-
import { useEffect as
|
|
4
|
+
import { useEffect as useEffect3, useRef as useRef4, useCallback as useCallback4 } from "react";
|
|
5
5
|
|
|
6
6
|
// src/components/PremiumVoiceWidget.tsx
|
|
7
7
|
import {
|
|
@@ -111,8 +111,9 @@ var useAudioLevel = (isRecording) => {
|
|
|
111
111
|
streamRef.current.getTracks().forEach((track) => track.stop());
|
|
112
112
|
streamRef.current = null;
|
|
113
113
|
}
|
|
114
|
-
if (audioContextRef.current) {
|
|
115
|
-
audioContextRef.current.close()
|
|
114
|
+
if (audioContextRef.current && audioContextRef.current.state !== "closed") {
|
|
115
|
+
audioContextRef.current.close().catch(() => {
|
|
116
|
+
});
|
|
116
117
|
audioContextRef.current = null;
|
|
117
118
|
}
|
|
118
119
|
analyserRef.current = null;
|
|
@@ -150,8 +151,9 @@ var useAudioLevel = (isRecording) => {
|
|
|
150
151
|
if (streamRef.current) {
|
|
151
152
|
streamRef.current.getTracks().forEach((track) => track.stop());
|
|
152
153
|
}
|
|
153
|
-
if (audioContextRef.current) {
|
|
154
|
-
audioContextRef.current.close()
|
|
154
|
+
if (audioContextRef.current && audioContextRef.current.state !== "closed") {
|
|
155
|
+
audioContextRef.current.close().catch(() => {
|
|
156
|
+
});
|
|
155
157
|
}
|
|
156
158
|
};
|
|
157
159
|
}, [isRecording]);
|
|
@@ -372,12 +374,12 @@ var AmbientOrb = ({ audioLevel, isRecording }) => {
|
|
|
372
374
|
]
|
|
373
375
|
}
|
|
374
376
|
) }),
|
|
375
|
-
particles.map((particle) => /* @__PURE__ */ jsx(
|
|
377
|
+
particles.filter((p) => p.size != null).map((particle) => /* @__PURE__ */ jsx(
|
|
376
378
|
motion.circle,
|
|
377
379
|
{
|
|
378
380
|
cx: 70,
|
|
379
381
|
cy: 70,
|
|
380
|
-
r: particle.size,
|
|
382
|
+
r: particle.size || 3,
|
|
381
383
|
fill: particle.color,
|
|
382
384
|
initial: { opacity: 0.8, scale: 1 },
|
|
383
385
|
animate: {
|
|
@@ -1624,6 +1626,359 @@ function adjustColor(color, amount) {
|
|
|
1624
1626
|
return `#${(r << 16 | g << 8 | b).toString(16).padStart(6, "0")}`;
|
|
1625
1627
|
}
|
|
1626
1628
|
|
|
1629
|
+
// src/components/IntentAIProvider.tsx
|
|
1630
|
+
import { createContext as createContext2, useContext as useContext2, useCallback as useCallback2, useMemo as useMemo2, useRef as useRef2, useEffect as useEffect2 } from "react";
|
|
1631
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1632
|
+
var IntentAIContext = createContext2(null);
|
|
1633
|
+
function useIntentAI() {
|
|
1634
|
+
const context = useContext2(IntentAIContext);
|
|
1635
|
+
if (!context) {
|
|
1636
|
+
throw new Error("useIntentAI must be used within an IntentAIProvider");
|
|
1637
|
+
}
|
|
1638
|
+
return context;
|
|
1639
|
+
}
|
|
1640
|
+
function generateSessionId() {
|
|
1641
|
+
return `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
1642
|
+
}
|
|
1643
|
+
function getDeviceInfo() {
|
|
1644
|
+
const ua = navigator.userAgent;
|
|
1645
|
+
let browser = "Unknown";
|
|
1646
|
+
let browserVersion = "Unknown";
|
|
1647
|
+
if (ua.includes("Firefox/")) {
|
|
1648
|
+
browser = "Firefox";
|
|
1649
|
+
const match = ua.match(/Firefox\/(\d+(\.\d+)?)/);
|
|
1650
|
+
if (match) browserVersion = match[1];
|
|
1651
|
+
} else if (ua.includes("Edg/")) {
|
|
1652
|
+
browser = "Edge";
|
|
1653
|
+
const match = ua.match(/Edg\/(\d+(\.\d+)?)/);
|
|
1654
|
+
if (match) browserVersion = match[1];
|
|
1655
|
+
} else if (ua.includes("Chrome/")) {
|
|
1656
|
+
browser = "Chrome";
|
|
1657
|
+
const match = ua.match(/Chrome\/(\d+(\.\d+)?)/);
|
|
1658
|
+
if (match) browserVersion = match[1];
|
|
1659
|
+
} else if (ua.includes("Safari/") && !ua.includes("Chrome")) {
|
|
1660
|
+
browser = "Safari";
|
|
1661
|
+
const match = ua.match(/Version\/(\d+(\.\d+)?)/);
|
|
1662
|
+
if (match) browserVersion = match[1];
|
|
1663
|
+
}
|
|
1664
|
+
let os = "Unknown";
|
|
1665
|
+
if (ua.includes("Windows NT 10")) os = "Windows 10";
|
|
1666
|
+
else if (ua.includes("Windows")) os = "Windows";
|
|
1667
|
+
else if (ua.includes("Mac OS X")) os = "macOS";
|
|
1668
|
+
else if (ua.includes("Android")) os = "Android";
|
|
1669
|
+
else if (ua.includes("iPhone") || ua.includes("iPad")) os = "iOS";
|
|
1670
|
+
else if (ua.includes("Linux")) os = "Linux";
|
|
1671
|
+
let device = "Desktop";
|
|
1672
|
+
if (/iPhone|iPad|iPod/.test(ua)) device = "iOS Device";
|
|
1673
|
+
else if (/Android.*Mobile/.test(ua)) device = "Android Phone";
|
|
1674
|
+
else if (/Android/.test(ua)) device = "Android Tablet";
|
|
1675
|
+
return { browser, browserVersion, os, device };
|
|
1676
|
+
}
|
|
1677
|
+
var DEFAULT_API_URL = "https://api-production-5b88.up.railway.app";
|
|
1678
|
+
function IntentAIProvider({
|
|
1679
|
+
apiKey,
|
|
1680
|
+
apiUrl = DEFAULT_API_URL,
|
|
1681
|
+
user: initialUser,
|
|
1682
|
+
metadata: initialMetadata,
|
|
1683
|
+
widgetConfig,
|
|
1684
|
+
children,
|
|
1685
|
+
disableWidget = false,
|
|
1686
|
+
onFeedbackSubmitted,
|
|
1687
|
+
onError
|
|
1688
|
+
}) {
|
|
1689
|
+
const userRef = useRef2(initialUser);
|
|
1690
|
+
const metadataRef = useRef2(initialMetadata || {});
|
|
1691
|
+
const sessionIdRef = useRef2(generateSessionId());
|
|
1692
|
+
useEffect2(() => {
|
|
1693
|
+
if (initialUser) {
|
|
1694
|
+
userRef.current = initialUser;
|
|
1695
|
+
}
|
|
1696
|
+
}, [initialUser]);
|
|
1697
|
+
useEffect2(() => {
|
|
1698
|
+
if (initialMetadata) {
|
|
1699
|
+
metadataRef.current = { ...metadataRef.current, ...initialMetadata };
|
|
1700
|
+
}
|
|
1701
|
+
}, [initialMetadata]);
|
|
1702
|
+
const identify = useCallback2((user) => {
|
|
1703
|
+
userRef.current = { ...userRef.current, ...user };
|
|
1704
|
+
}, []);
|
|
1705
|
+
const setMetadata = useCallback2((metadata) => {
|
|
1706
|
+
metadataRef.current = { ...metadataRef.current, ...metadata };
|
|
1707
|
+
}, []);
|
|
1708
|
+
const clearUser = useCallback2(() => {
|
|
1709
|
+
userRef.current = void 0;
|
|
1710
|
+
}, []);
|
|
1711
|
+
const handleSubmit = useCallback2(async (feedback) => {
|
|
1712
|
+
const deviceInfo = getDeviceInfo();
|
|
1713
|
+
const user = userRef.current;
|
|
1714
|
+
const customMetadata = metadataRef.current;
|
|
1715
|
+
const categoryMap = {
|
|
1716
|
+
bug: "BUG",
|
|
1717
|
+
feature: "FEATURE",
|
|
1718
|
+
improvement: "IMPROVEMENT",
|
|
1719
|
+
praise: "PRAISE",
|
|
1720
|
+
other: "OTHER"
|
|
1721
|
+
};
|
|
1722
|
+
const payload = {
|
|
1723
|
+
sessionId: sessionIdRef.current,
|
|
1724
|
+
type: feedback.type === "voice" ? "VOICE" : "TEXT",
|
|
1725
|
+
category: categoryMap[feedback.category.toLowerCase()] || feedback.category.toUpperCase(),
|
|
1726
|
+
rawText: feedback.type === "text" ? feedback.content : void 0,
|
|
1727
|
+
transcription: feedback.type === "voice" ? feedback.transcription || feedback.content : void 0,
|
|
1728
|
+
pageUrl: typeof window !== "undefined" ? window.location.href : "",
|
|
1729
|
+
pageTitle: typeof document !== "undefined" ? document.title : "",
|
|
1730
|
+
userId: user?.id,
|
|
1731
|
+
userEmail: user?.email,
|
|
1732
|
+
userName: user?.name,
|
|
1733
|
+
customData: {
|
|
1734
|
+
...customMetadata,
|
|
1735
|
+
...feedback.metadata
|
|
1736
|
+
},
|
|
1737
|
+
browser: deviceInfo.browser,
|
|
1738
|
+
browserVersion: deviceInfo.browserVersion,
|
|
1739
|
+
os: deviceInfo.os,
|
|
1740
|
+
device: deviceInfo.device,
|
|
1741
|
+
screenWidth: typeof window !== "undefined" ? window.screen.width : void 0,
|
|
1742
|
+
screenHeight: typeof window !== "undefined" ? window.screen.height : void 0
|
|
1743
|
+
};
|
|
1744
|
+
try {
|
|
1745
|
+
const response = await fetch(`${apiUrl}/feedback`, {
|
|
1746
|
+
method: "POST",
|
|
1747
|
+
headers: {
|
|
1748
|
+
"Content-Type": "application/json",
|
|
1749
|
+
"X-API-Key": apiKey
|
|
1750
|
+
},
|
|
1751
|
+
body: JSON.stringify(payload)
|
|
1752
|
+
});
|
|
1753
|
+
if (!response.ok) {
|
|
1754
|
+
const errorData = await response.json().catch(() => ({}));
|
|
1755
|
+
throw new Error(errorData.error || `Failed to submit feedback: ${response.status}`);
|
|
1756
|
+
}
|
|
1757
|
+
onFeedbackSubmitted?.();
|
|
1758
|
+
} catch (error) {
|
|
1759
|
+
const err = error instanceof Error ? error : new Error("Failed to submit feedback");
|
|
1760
|
+
onError?.(err);
|
|
1761
|
+
throw err;
|
|
1762
|
+
}
|
|
1763
|
+
}, [apiKey, apiUrl, onFeedbackSubmitted, onError]);
|
|
1764
|
+
const contextValue = useMemo2(() => ({
|
|
1765
|
+
identify,
|
|
1766
|
+
setMetadata,
|
|
1767
|
+
clearUser,
|
|
1768
|
+
isConfigured: Boolean(apiKey)
|
|
1769
|
+
}), [identify, setMetadata, clearUser, apiKey]);
|
|
1770
|
+
return /* @__PURE__ */ jsxs2(IntentAIContext.Provider, { value: contextValue, children: [
|
|
1771
|
+
children,
|
|
1772
|
+
!disableWidget && apiKey && /* @__PURE__ */ jsx2(
|
|
1773
|
+
PremiumVoiceWidget,
|
|
1774
|
+
{
|
|
1775
|
+
...widgetConfig,
|
|
1776
|
+
onSubmit: handleSubmit
|
|
1777
|
+
}
|
|
1778
|
+
)
|
|
1779
|
+
] });
|
|
1780
|
+
}
|
|
1781
|
+
function useIdentify() {
|
|
1782
|
+
const { identify } = useIntentAI();
|
|
1783
|
+
return identify;
|
|
1784
|
+
}
|
|
1785
|
+
function useSetMetadata() {
|
|
1786
|
+
const { setMetadata } = useIntentAI();
|
|
1787
|
+
return setMetadata;
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
// src/components/FeedbackButton.tsx
|
|
1791
|
+
import { useCallback as useCallback3, useState as useState2, useRef as useRef3 } from "react";
|
|
1792
|
+
import { motion as motion2 } from "framer-motion";
|
|
1793
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1794
|
+
var FeedbackIcon = ({ size }) => /* @__PURE__ */ jsxs3(
|
|
1795
|
+
"svg",
|
|
1796
|
+
{
|
|
1797
|
+
width: size,
|
|
1798
|
+
height: size,
|
|
1799
|
+
viewBox: "0 0 24 24",
|
|
1800
|
+
fill: "none",
|
|
1801
|
+
stroke: "currentColor",
|
|
1802
|
+
strokeWidth: "2",
|
|
1803
|
+
strokeLinecap: "round",
|
|
1804
|
+
strokeLinejoin: "round",
|
|
1805
|
+
children: [
|
|
1806
|
+
/* @__PURE__ */ jsx3("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }),
|
|
1807
|
+
/* @__PURE__ */ jsx3("circle", { cx: "9", cy: "10", r: "1", fill: "currentColor" }),
|
|
1808
|
+
/* @__PURE__ */ jsx3("circle", { cx: "15", cy: "10", r: "1", fill: "currentColor" })
|
|
1809
|
+
]
|
|
1810
|
+
}
|
|
1811
|
+
);
|
|
1812
|
+
function FeedbackButton({
|
|
1813
|
+
variant = "primary",
|
|
1814
|
+
size = "md",
|
|
1815
|
+
showIcon = true,
|
|
1816
|
+
icon,
|
|
1817
|
+
label = "Feedback",
|
|
1818
|
+
className,
|
|
1819
|
+
style,
|
|
1820
|
+
primaryColor = "#10b981",
|
|
1821
|
+
onClick,
|
|
1822
|
+
disabled = false
|
|
1823
|
+
}) {
|
|
1824
|
+
const [isHovered, setIsHovered] = useState2(false);
|
|
1825
|
+
const [isPressed, setIsPressed] = useState2(false);
|
|
1826
|
+
const buttonRef = useRef3(null);
|
|
1827
|
+
const sizes = {
|
|
1828
|
+
sm: {
|
|
1829
|
+
padding: "8px 14px",
|
|
1830
|
+
fontSize: 13,
|
|
1831
|
+
iconSize: 14,
|
|
1832
|
+
gap: 6,
|
|
1833
|
+
borderRadius: 8
|
|
1834
|
+
},
|
|
1835
|
+
md: {
|
|
1836
|
+
padding: "10px 18px",
|
|
1837
|
+
fontSize: 14,
|
|
1838
|
+
iconSize: 16,
|
|
1839
|
+
gap: 8,
|
|
1840
|
+
borderRadius: 10
|
|
1841
|
+
},
|
|
1842
|
+
lg: {
|
|
1843
|
+
padding: "14px 24px",
|
|
1844
|
+
fontSize: 15,
|
|
1845
|
+
iconSize: 18,
|
|
1846
|
+
gap: 10,
|
|
1847
|
+
borderRadius: 12
|
|
1848
|
+
}
|
|
1849
|
+
};
|
|
1850
|
+
const sizeConfig = sizes[size];
|
|
1851
|
+
const variants = {
|
|
1852
|
+
primary: {
|
|
1853
|
+
background: `linear-gradient(145deg, ${primaryColor} 0%, ${adjustColor2(primaryColor, -20)} 100%)`,
|
|
1854
|
+
color: "white",
|
|
1855
|
+
border: "none",
|
|
1856
|
+
boxShadow: `0 4px 16px -4px ${primaryColor}66, 0 2px 4px rgba(0, 0, 0, 0.1)`
|
|
1857
|
+
},
|
|
1858
|
+
secondary: {
|
|
1859
|
+
background: "white",
|
|
1860
|
+
color: "#374151",
|
|
1861
|
+
border: "1px solid #e5e7eb",
|
|
1862
|
+
boxShadow: "0 2px 4px rgba(0, 0, 0, 0.05)"
|
|
1863
|
+
},
|
|
1864
|
+
ghost: {
|
|
1865
|
+
background: "transparent",
|
|
1866
|
+
color: "#6b7280",
|
|
1867
|
+
border: "1px solid transparent",
|
|
1868
|
+
boxShadow: "none"
|
|
1869
|
+
},
|
|
1870
|
+
minimal: {
|
|
1871
|
+
background: "transparent",
|
|
1872
|
+
color: primaryColor,
|
|
1873
|
+
border: "none",
|
|
1874
|
+
boxShadow: "none"
|
|
1875
|
+
}
|
|
1876
|
+
};
|
|
1877
|
+
const variantStyle = variants[variant];
|
|
1878
|
+
const handleClick = useCallback3(() => {
|
|
1879
|
+
if (disabled) return;
|
|
1880
|
+
if (onClick) {
|
|
1881
|
+
onClick();
|
|
1882
|
+
return;
|
|
1883
|
+
}
|
|
1884
|
+
const widget = document.querySelector(".fiq-premium-widget");
|
|
1885
|
+
if (widget) {
|
|
1886
|
+
const trigger = widget.querySelector('button[aria-label="Open feedback widget"]');
|
|
1887
|
+
if (trigger) {
|
|
1888
|
+
trigger.click();
|
|
1889
|
+
return;
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
const scriptWidget = document.getElementById("feedbackiq-widget");
|
|
1893
|
+
if (scriptWidget?.shadowRoot) {
|
|
1894
|
+
const trigger = scriptWidget.shadowRoot.querySelector(".fiq-trigger");
|
|
1895
|
+
if (trigger) {
|
|
1896
|
+
trigger.click();
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
}, [onClick, disabled]);
|
|
1900
|
+
return /* @__PURE__ */ jsxs3(
|
|
1901
|
+
motion2.button,
|
|
1902
|
+
{
|
|
1903
|
+
ref: buttonRef,
|
|
1904
|
+
onClick: handleClick,
|
|
1905
|
+
onMouseEnter: () => setIsHovered(true),
|
|
1906
|
+
onMouseLeave: () => {
|
|
1907
|
+
setIsHovered(false);
|
|
1908
|
+
setIsPressed(false);
|
|
1909
|
+
},
|
|
1910
|
+
onMouseDown: () => setIsPressed(true),
|
|
1911
|
+
onMouseUp: () => setIsPressed(false),
|
|
1912
|
+
disabled,
|
|
1913
|
+
animate: {
|
|
1914
|
+
scale: isPressed ? 0.97 : isHovered ? 1.02 : 1,
|
|
1915
|
+
y: isHovered ? -1 : 0
|
|
1916
|
+
},
|
|
1917
|
+
transition: {
|
|
1918
|
+
type: "spring",
|
|
1919
|
+
stiffness: 400,
|
|
1920
|
+
damping: 25
|
|
1921
|
+
},
|
|
1922
|
+
className,
|
|
1923
|
+
style: {
|
|
1924
|
+
display: "inline-flex",
|
|
1925
|
+
alignItems: "center",
|
|
1926
|
+
justifyContent: "center",
|
|
1927
|
+
gap: sizeConfig.gap,
|
|
1928
|
+
padding: sizeConfig.padding,
|
|
1929
|
+
fontSize: sizeConfig.fontSize,
|
|
1930
|
+
fontWeight: 600,
|
|
1931
|
+
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
1932
|
+
borderRadius: sizeConfig.borderRadius,
|
|
1933
|
+
cursor: disabled ? "not-allowed" : "pointer",
|
|
1934
|
+
opacity: disabled ? 0.5 : 1,
|
|
1935
|
+
transition: "all 0.2s ease",
|
|
1936
|
+
position: "relative",
|
|
1937
|
+
overflow: "hidden",
|
|
1938
|
+
...variantStyle,
|
|
1939
|
+
...style
|
|
1940
|
+
},
|
|
1941
|
+
children: [
|
|
1942
|
+
variant === "primary" && /* @__PURE__ */ jsx3(
|
|
1943
|
+
motion2.div,
|
|
1944
|
+
{
|
|
1945
|
+
initial: { opacity: 0 },
|
|
1946
|
+
animate: { opacity: isHovered ? 0.1 : 0 },
|
|
1947
|
+
style: {
|
|
1948
|
+
position: "absolute",
|
|
1949
|
+
inset: 0,
|
|
1950
|
+
background: "white",
|
|
1951
|
+
pointerEvents: "none"
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
),
|
|
1955
|
+
variant === "primary" && /* @__PURE__ */ jsx3(
|
|
1956
|
+
"div",
|
|
1957
|
+
{
|
|
1958
|
+
style: {
|
|
1959
|
+
position: "absolute",
|
|
1960
|
+
inset: 0,
|
|
1961
|
+
background: "linear-gradient(180deg, rgba(255,255,255,0.2) 0%, rgba(255,255,255,0) 50%)",
|
|
1962
|
+
borderRadius: "inherit",
|
|
1963
|
+
pointerEvents: "none"
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
),
|
|
1967
|
+
showIcon && /* @__PURE__ */ jsx3("span", { style: { display: "flex", alignItems: "center", position: "relative", zIndex: 1 }, children: icon || /* @__PURE__ */ jsx3(FeedbackIcon, { size: sizeConfig.iconSize }) }),
|
|
1968
|
+
label && /* @__PURE__ */ jsx3("span", { style: { position: "relative", zIndex: 1 }, children: label })
|
|
1969
|
+
]
|
|
1970
|
+
}
|
|
1971
|
+
);
|
|
1972
|
+
}
|
|
1973
|
+
function adjustColor2(color, amount) {
|
|
1974
|
+
const hex = color.replace("#", "");
|
|
1975
|
+
const num = parseInt(hex, 16);
|
|
1976
|
+
const r = Math.min(255, Math.max(0, (num >> 16) + amount));
|
|
1977
|
+
const g = Math.min(255, Math.max(0, (num >> 8 & 255) + amount));
|
|
1978
|
+
const b = Math.min(255, Math.max(0, (num & 255) + amount));
|
|
1979
|
+
return `#${(r << 16 | g << 8 | b).toString(16).padStart(6, "0")}`;
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1627
1982
|
// src/index.tsx
|
|
1628
1983
|
function IntentAIWidget({
|
|
1629
1984
|
apiKey,
|
|
@@ -1642,9 +1997,9 @@ function IntentAIWidget({
|
|
|
1642
1997
|
onFeedbackSubmitted: _onFeedbackSubmitted,
|
|
1643
1998
|
onError
|
|
1644
1999
|
}) {
|
|
1645
|
-
const widgetRef =
|
|
1646
|
-
const scriptLoadedRef =
|
|
1647
|
-
const initWidget =
|
|
2000
|
+
const widgetRef = useRef4(null);
|
|
2001
|
+
const scriptLoadedRef = useRef4(false);
|
|
2002
|
+
const initWidget = useCallback4(() => {
|
|
1648
2003
|
if (!window.FeedbackIQ || widgetRef.current) return;
|
|
1649
2004
|
try {
|
|
1650
2005
|
widgetRef.current = new window.FeedbackIQ({
|
|
@@ -1667,7 +2022,7 @@ function IntentAIWidget({
|
|
|
1667
2022
|
void _onClose;
|
|
1668
2023
|
void _onFeedbackSubmitted;
|
|
1669
2024
|
}, [apiKey, apiUrl, widgetUrl, position, theme, primaryColor, allowVoice, allowText, allowScreenshot, customMetadata, user, onError]);
|
|
1670
|
-
|
|
2025
|
+
useEffect3(() => {
|
|
1671
2026
|
if (!widgetUrl) {
|
|
1672
2027
|
onError?.(new Error("widgetUrl is required for IntentAIWidget"));
|
|
1673
2028
|
return;
|
|
@@ -1698,39 +2053,39 @@ function IntentAIWidget({
|
|
|
1698
2053
|
}
|
|
1699
2054
|
};
|
|
1700
2055
|
}, [initWidget, onError, widgetUrl]);
|
|
1701
|
-
|
|
2056
|
+
useEffect3(() => {
|
|
1702
2057
|
if (widgetRef.current && user) {
|
|
1703
2058
|
widgetRef.current.identify(user);
|
|
1704
2059
|
}
|
|
1705
2060
|
}, [user]);
|
|
1706
|
-
|
|
2061
|
+
useEffect3(() => {
|
|
1707
2062
|
if (widgetRef.current && customMetadata) {
|
|
1708
2063
|
widgetRef.current.setMetadata(customMetadata);
|
|
1709
2064
|
}
|
|
1710
2065
|
}, [customMetadata]);
|
|
1711
2066
|
return null;
|
|
1712
2067
|
}
|
|
1713
|
-
function
|
|
1714
|
-
const open =
|
|
2068
|
+
function useWidgetControls() {
|
|
2069
|
+
const open = useCallback4(() => {
|
|
1715
2070
|
const widget = document.getElementById("feedbackiq-widget");
|
|
1716
2071
|
if (widget) {
|
|
1717
2072
|
const trigger = widget.shadowRoot?.querySelector(".fiq-trigger");
|
|
1718
2073
|
trigger?.click();
|
|
1719
2074
|
}
|
|
1720
2075
|
}, []);
|
|
1721
|
-
const close =
|
|
2076
|
+
const close = useCallback4(() => {
|
|
1722
2077
|
const widget = document.getElementById("feedbackiq-widget");
|
|
1723
2078
|
if (widget) {
|
|
1724
2079
|
const closeBtn = widget.shadowRoot?.querySelector(".fiq-close");
|
|
1725
2080
|
closeBtn?.click();
|
|
1726
2081
|
}
|
|
1727
2082
|
}, []);
|
|
1728
|
-
const identify =
|
|
2083
|
+
const identify = useCallback4((user) => {
|
|
1729
2084
|
if (window.IntentAI?.widget) {
|
|
1730
2085
|
window.IntentAI.widget.identify(user);
|
|
1731
2086
|
}
|
|
1732
2087
|
}, []);
|
|
1733
|
-
const setMetadata =
|
|
2088
|
+
const setMetadata = useCallback4((metadata) => {
|
|
1734
2089
|
if (window.IntentAI?.widget) {
|
|
1735
2090
|
window.IntentAI.widget.setMetadata(metadata);
|
|
1736
2091
|
}
|
|
@@ -1739,8 +2094,13 @@ function useIntentAI() {
|
|
|
1739
2094
|
}
|
|
1740
2095
|
var index_default = IntentAIWidget;
|
|
1741
2096
|
export {
|
|
2097
|
+
FeedbackButton,
|
|
2098
|
+
IntentAIProvider,
|
|
1742
2099
|
IntentAIWidget,
|
|
1743
2100
|
PremiumVoiceWidget,
|
|
1744
2101
|
index_default as default,
|
|
1745
|
-
|
|
2102
|
+
useIdentify,
|
|
2103
|
+
useIntentAI,
|
|
2104
|
+
useSetMetadata,
|
|
2105
|
+
useWidgetControls
|
|
1746
2106
|
};
|
package/package.json
CHANGED