@keak/sdk 1.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.
- package/README.md +131 -0
- package/dist/Conversion.d.ts +13 -0
- package/dist/Conversion.d.ts.map +1 -0
- package/dist/KeakToolbarShadow.d.ts +21 -0
- package/dist/KeakToolbarShadow.d.ts.map +1 -0
- package/dist/components/ui/card.d.ts +9 -0
- package/dist/components/ui/card.d.ts.map +1 -0
- package/dist/components/ui/html-preview.d.ts +9 -0
- package/dist/components/ui/html-preview.d.ts.map +1 -0
- package/dist/components/ui/simple-tabs.d.ts +26 -0
- package/dist/components/ui/simple-tabs.d.ts.map +1 -0
- package/dist/components/ui/spinner.d.ts +6 -0
- package/dist/components/ui/spinner.d.ts.map +1 -0
- package/dist/components/ui/tabs.d.ts +13 -0
- package/dist/components/ui/tabs.d.ts.map +1 -0
- package/dist/components/ui/textarea.d.ts +6 -0
- package/dist/components/ui/textarea.d.ts.map +1 -0
- package/dist/index.cjs.js +17407 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +101 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17395 -0
- package/dist/index.js.map +1 -0
- package/dist/runtime/sourceInjector.d.ts +2 -0
- package/dist/runtime/sourceInjector.d.ts.map +1 -0
- package/dist/scripts/sourcePathInjection.d.ts +11 -0
- package/dist/scripts/sourcePathInjection.d.ts.map +1 -0
- package/dist/services/index.d.ts +7 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/telemetry/index.d.ts +20 -0
- package/dist/services/telemetry/index.d.ts.map +1 -0
- package/dist/services/telemetry/telemetryService.d.ts +66 -0
- package/dist/services/telemetry/telemetryService.d.ts.map +1 -0
- package/dist/services/telemetry/types.d.ts +64 -0
- package/dist/services/telemetry/types.d.ts.map +1 -0
- package/dist/toolbar/AIPromptPanel.d.ts +9 -0
- package/dist/toolbar/AIPromptPanel.d.ts.map +1 -0
- package/dist/toolbar/ElementSelector.d.ts +8 -0
- package/dist/toolbar/ElementSelector.d.ts.map +1 -0
- package/dist/toolbar/ExperimentPanel.d.ts +9 -0
- package/dist/toolbar/ExperimentPanel.d.ts.map +1 -0
- package/dist/toolbar/KeakToolbar.d.ts +10 -0
- package/dist/toolbar/KeakToolbar.d.ts.map +1 -0
- package/dist/toolbar/MetricsPanel.d.ts +7 -0
- package/dist/toolbar/MetricsPanel.d.ts.map +1 -0
- package/dist/toolbar/PageScanPanel.d.ts +15 -0
- package/dist/toolbar/PageScanPanel.d.ts.map +1 -0
- package/dist/toolbar/components/PrimaryButton.d.ts +12 -0
- package/dist/toolbar/components/PrimaryButton.d.ts.map +1 -0
- package/dist/toolbar/components/WarningButton.d.ts +12 -0
- package/dist/toolbar/components/WarningButton.d.ts.map +1 -0
- package/dist/toolbar/components/icons/index.d.ts +13 -0
- package/dist/toolbar/components/icons/index.d.ts.map +1 -0
- package/dist/toolbar/components/ui/Badge.d.ts +10 -0
- package/dist/toolbar/components/ui/Badge.d.ts.map +1 -0
- package/dist/toolbar/components/ui/Button.d.ts +12 -0
- package/dist/toolbar/components/ui/Button.d.ts.map +1 -0
- package/dist/toolbar/components/ui/Progress.d.ts +5 -0
- package/dist/toolbar/components/ui/Progress.d.ts.map +1 -0
- package/dist/toolbar/components/ui/Tabs.d.ts +44 -0
- package/dist/toolbar/components/ui/Tabs.d.ts.map +1 -0
- package/dist/toolbar/components/ui/dropdown-menu.d.ts +28 -0
- package/dist/toolbar/components/ui/dropdown-menu.d.ts.map +1 -0
- package/dist/toolbar/lib/utils.d.ts +3 -0
- package/dist/toolbar/lib/utils.d.ts.map +1 -0
- package/dist/toolbar/utils/fiberSource.d.ts +64 -0
- package/dist/toolbar/utils/fiberSource.d.ts.map +1 -0
- package/dist/toolbar/utils/keakCodeClient.d.ts +104 -0
- package/dist/toolbar/utils/keakCodeClient.d.ts.map +1 -0
- package/dist/toolbar.css +1 -0
- package/dist/toolbar.js +1257 -0
- package/dist/toolbar.js.map +1 -0
- package/dist/utils/generateElementId.d.ts +44 -0
- package/dist/utils/generateElementId.d.ts.map +1 -0
- package/dist/utils/injectDataId.d.ts +33 -0
- package/dist/utils/injectDataId.d.ts.map +1 -0
- package/package.json +152 -0
- package/src/cli/ai-helper.js +206 -0
- package/src/cli/code-transformer.js +354 -0
- package/src/cli/conversion-detector.js +716 -0
- package/src/cli/framework-config.js +477 -0
- package/src/cli/install.js +618 -0
- package/src/cli/keak-setup.js +43 -0
- package/src/cli/revert-conversions.js +264 -0
- package/src/cli/safe-transformer.js +456 -0
- package/src/cli/simple-transformer.js +339 -0
- package/src/plugins/README.md +131 -0
- package/src/plugins/babel-source-inject.cjs +170 -0
- package/src/plugins/next.cjs +145 -0
|
@@ -0,0 +1,716 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import SafeTransformer from './safe-transformer.js';
|
|
6
|
+
|
|
7
|
+
class ConversionDetector {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.codeTransformer = new SafeTransformer();
|
|
10
|
+
this.conversionSelectors = [
|
|
11
|
+
// Button elements
|
|
12
|
+
'button',
|
|
13
|
+
'input[type="button"]',
|
|
14
|
+
'input[type="submit"]',
|
|
15
|
+
'[role="button"]',
|
|
16
|
+
|
|
17
|
+
// Link elements
|
|
18
|
+
'a[href]',
|
|
19
|
+
|
|
20
|
+
// Common CTA patterns
|
|
21
|
+
'[class*="cta"]',
|
|
22
|
+
'[class*="button"]',
|
|
23
|
+
'[class*="btn"]',
|
|
24
|
+
'[id*="cta"]',
|
|
25
|
+
'[id*="button"]',
|
|
26
|
+
'[id*="btn"]',
|
|
27
|
+
|
|
28
|
+
// Form submit elements
|
|
29
|
+
'form button',
|
|
30
|
+
'form input[type="submit"]',
|
|
31
|
+
|
|
32
|
+
// Common conversion text patterns
|
|
33
|
+
'[class*="buy"]',
|
|
34
|
+
'[class*="purchase"]',
|
|
35
|
+
'[class*="checkout"]',
|
|
36
|
+
'[class*="sign-up"]',
|
|
37
|
+
'[class*="signup"]',
|
|
38
|
+
'[class*="subscribe"]',
|
|
39
|
+
'[class*="download"]',
|
|
40
|
+
'[class*="get-started"]',
|
|
41
|
+
'[class*="learn-more"]',
|
|
42
|
+
'[class*="contact"]',
|
|
43
|
+
'[class*="demo"]',
|
|
44
|
+
'[class*="trial"]',
|
|
45
|
+
|
|
46
|
+
// E-commerce specific
|
|
47
|
+
'[class*="add-to-cart"]',
|
|
48
|
+
'[class*="add-cart"]',
|
|
49
|
+
'[class*="cart"]',
|
|
50
|
+
'[class*="wishlist"]',
|
|
51
|
+
|
|
52
|
+
// Navigation that might lead to conversions
|
|
53
|
+
'nav a',
|
|
54
|
+
'header a',
|
|
55
|
+
'.navigation a',
|
|
56
|
+
'.menu a'
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
this.conversionTextPatterns = [
|
|
60
|
+
/buy\s+now/i,
|
|
61
|
+
/get\s+started/i,
|
|
62
|
+
/sign\s+up/i,
|
|
63
|
+
/subscribe/i,
|
|
64
|
+
/download/i,
|
|
65
|
+
/try\s+free/i,
|
|
66
|
+
/free\s+trial/i,
|
|
67
|
+
/learn\s+more/i,
|
|
68
|
+
/contact\s+us/i,
|
|
69
|
+
/request\s+demo/i,
|
|
70
|
+
/book\s+demo/i,
|
|
71
|
+
/add\s+to\s+cart/i,
|
|
72
|
+
/checkout/i,
|
|
73
|
+
/purchase/i,
|
|
74
|
+
/order\s+now/i,
|
|
75
|
+
/join\s+now/i,
|
|
76
|
+
/register/i,
|
|
77
|
+
/apply\s+now/i,
|
|
78
|
+
/get\s+quote/i,
|
|
79
|
+
/call\s+now/i,
|
|
80
|
+
/schedule/i,
|
|
81
|
+
/book\s+now/i
|
|
82
|
+
];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
generateConversionScript() {
|
|
86
|
+
return `
|
|
87
|
+
// Keak Conversion Tracking - Auto-generated
|
|
88
|
+
(function() {
|
|
89
|
+
'use strict';
|
|
90
|
+
|
|
91
|
+
// Configuration
|
|
92
|
+
const CONVERSION_SELECTORS = ${JSON.stringify(this.conversionSelectors, null, 2)};
|
|
93
|
+
const CONVERSION_TEXT_PATTERNS = [
|
|
94
|
+
${this.conversionTextPatterns.map(pattern =`/${pattern.source}/${pattern.flags}`).join(',\n ')}
|
|
95
|
+
];
|
|
96
|
+
|
|
97
|
+
// Conversion tracking state
|
|
98
|
+
let trackedElements = new Set();
|
|
99
|
+
let conversionGoals = new Map();
|
|
100
|
+
|
|
101
|
+
// Initialize conversion tracking
|
|
102
|
+
function initConversionTracking() {
|
|
103
|
+
console.log('šÆ Keak Conversion Tracking initialized');
|
|
104
|
+
|
|
105
|
+
// Tag existing elements
|
|
106
|
+
tagConversionElements();
|
|
107
|
+
|
|
108
|
+
// Set up mutation observer for dynamic content
|
|
109
|
+
observeNewElements();
|
|
110
|
+
|
|
111
|
+
// Set up periodic re-scanning
|
|
112
|
+
setInterval(tagConversionElements, 5000);
|
|
113
|
+
|
|
114
|
+
console.log(\`š Tagged \${trackedElements.size} conversion elements\`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Main function to detect and tag conversion elements
|
|
118
|
+
function tagConversionElements() {
|
|
119
|
+
const newElements = new Set();
|
|
120
|
+
|
|
121
|
+
// Find elements by selectors
|
|
122
|
+
CONVERSION_SELECTORS.forEach(selector ={
|
|
123
|
+
try {
|
|
124
|
+
const elements = document.querySelectorAll(selector);
|
|
125
|
+
elements.forEach(element ={
|
|
126
|
+
if (!trackedElements.has(element) && isConversionElement(element)) {
|
|
127
|
+
tagElement(element);
|
|
128
|
+
newElements.add(element);
|
|
129
|
+
trackedElements.add(element);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
} catch (e) {
|
|
133
|
+
console.warn('Invalid selector:', selector, e);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Find elements by text content
|
|
138
|
+
const allClickableElements = document.querySelectorAll('a, button, [onclick], [role="button"], input[type="button"], input[type="submit"]');
|
|
139
|
+
allClickableElements.forEach(element ={
|
|
140
|
+
if (!trackedElements.has(element) && hasConversionText(element)) {
|
|
141
|
+
tagElement(element);
|
|
142
|
+
newElements.add(element);
|
|
143
|
+
trackedElements.add(element);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
if (newElements.size 0) {
|
|
148
|
+
console.log(\`š Found \${newElements.size} new conversion elements\`);
|
|
149
|
+
|
|
150
|
+
// Show visual indicators
|
|
151
|
+
showConversionIndicators();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Check if element is likely a conversion element
|
|
156
|
+
function isConversionElement(element) {
|
|
157
|
+
// Skip if already tracked
|
|
158
|
+
if (trackedElements.has(element)) return false;
|
|
159
|
+
|
|
160
|
+
// Skip hidden elements
|
|
161
|
+
if (!isElementVisible(element)) return false;
|
|
162
|
+
|
|
163
|
+
// Check if it's clickable
|
|
164
|
+
if (!isClickable(element)) return false;
|
|
165
|
+
|
|
166
|
+
// Check text content
|
|
167
|
+
if (hasConversionText(element)) return true;
|
|
168
|
+
|
|
169
|
+
// Check classes and IDs
|
|
170
|
+
const className = element.className || '';
|
|
171
|
+
const id = element.id || '';
|
|
172
|
+
const combined = (className + ' ' + id).toLowerCase();
|
|
173
|
+
|
|
174
|
+
const conversionKeywords = [
|
|
175
|
+
'cta', 'button', 'btn', 'buy', 'purchase', 'checkout',
|
|
176
|
+
'signup', 'sign-up', 'subscribe', 'download', 'trial',
|
|
177
|
+
'demo', 'contact', 'get-started', 'learn-more', 'cart'
|
|
178
|
+
];
|
|
179
|
+
|
|
180
|
+
return conversionKeywords.some(keyword =combined.includes(keyword));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Check if element has conversion-related text
|
|
184
|
+
function hasConversionText(element) {
|
|
185
|
+
const text = (element.textContent || element.value || element.alt || element.title || '').toLowerCase();
|
|
186
|
+
return CONVERSION_TEXT_PATTERNS.some(pattern =pattern.test(text));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Check if element is visible
|
|
190
|
+
function isElementVisible(element) {
|
|
191
|
+
const style = window.getComputedStyle(element);
|
|
192
|
+
return style.display !== 'none' &&
|
|
193
|
+
style.visibility !== 'hidden' &&
|
|
194
|
+
style.opacity !== '0' &&
|
|
195
|
+
element.offsetWidth 0 &&
|
|
196
|
+
element.offsetHeight 0;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Check if element is clickable
|
|
200
|
+
function isClickable(element) {
|
|
201
|
+
const clickableTypes = ['a', 'button', 'input'];
|
|
202
|
+
const hasClickableRole = element.getAttribute('role') === 'button';
|
|
203
|
+
const hasClickHandler = element.onclick || element.getAttribute('onclick');
|
|
204
|
+
const isClickableInput = element.type === 'button' || element.type === 'submit';
|
|
205
|
+
|
|
206
|
+
return clickableTypes.includes(element.tagName.toLowerCase()) ||
|
|
207
|
+
hasClickableRole ||
|
|
208
|
+
hasClickHandler ||
|
|
209
|
+
isClickableInput;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Tag an element for conversion tracking
|
|
213
|
+
function tagElement(element) {
|
|
214
|
+
// Add data attributes
|
|
215
|
+
element.setAttribute('data-keak-conversion', 'true');
|
|
216
|
+
element.setAttribute('data-keak-conversion-id', generateConversionId());
|
|
217
|
+
element.setAttribute('data-keak-conversion-type', getConversionType(element));
|
|
218
|
+
|
|
219
|
+
// Add event listener for tracking
|
|
220
|
+
element.addEventListener('click', handleConversionClick, { passive: true });
|
|
221
|
+
|
|
222
|
+
// Add visual indicator class
|
|
223
|
+
element.classList.add('keak-conversion-element');
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Generate unique conversion ID
|
|
227
|
+
function generateConversionId() {
|
|
228
|
+
return 'conv_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Determine conversion type based on element
|
|
232
|
+
function getConversionType(element) {
|
|
233
|
+
const text = (element.textContent || element.value || '').toLowerCase();
|
|
234
|
+
const className = (element.className || '').toLowerCase();
|
|
235
|
+
const id = (element.id || '').toLowerCase();
|
|
236
|
+
const combined = text + ' ' + className + ' ' + id;
|
|
237
|
+
|
|
238
|
+
if (/buy|purchase|checkout|order/.test(combined)) return 'purchase';
|
|
239
|
+
if (/sign.?up|register|join/.test(combined)) return 'signup';
|
|
240
|
+
if (/subscribe/.test(combined)) return 'subscription';
|
|
241
|
+
if (/download/.test(combined)) return 'download';
|
|
242
|
+
if (/demo|trial/.test(combined)) return 'demo';
|
|
243
|
+
if (/contact|call|phone/.test(combined)) return 'contact';
|
|
244
|
+
if (/cart/.test(combined)) return 'add-to-cart';
|
|
245
|
+
if (/learn.?more|read.?more/.test(combined)) return 'engagement';
|
|
246
|
+
|
|
247
|
+
return 'click';
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Handle conversion click events
|
|
251
|
+
function handleConversionClick(event) {
|
|
252
|
+
const element = event.currentTarget;
|
|
253
|
+
const conversionId = element.getAttribute('data-keak-conversion-id');
|
|
254
|
+
const conversionType = element.getAttribute('data-keak-conversion-type');
|
|
255
|
+
|
|
256
|
+
// Track the conversion event
|
|
257
|
+
if (window.keak && window.keak.trackEvent) {
|
|
258
|
+
window.keak.trackEvent('conversion', {
|
|
259
|
+
conversionId,
|
|
260
|
+
conversionType,
|
|
261
|
+
elementTag: element.tagName.toLowerCase(),
|
|
262
|
+
elementText: (element.textContent || element.value || '').substring(0, 100),
|
|
263
|
+
elementClass: element.className,
|
|
264
|
+
elementId: element.id,
|
|
265
|
+
href: element.href,
|
|
266
|
+
timestamp: new Date().toISOString(),
|
|
267
|
+
url: window.location.href,
|
|
268
|
+
referrer: document.referrer
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
console.log(\`šÆ Conversion tracked: \${conversionType}\`, {
|
|
272
|
+
id: conversionId,
|
|
273
|
+
element: element.tagName.toLowerCase(),
|
|
274
|
+
text: element.textContent?.substring(0, 50)
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Visual feedback
|
|
279
|
+
showConversionFeedback(element);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Show visual feedback for conversion
|
|
283
|
+
function showConversionFeedback(element) {
|
|
284
|
+
const feedback = document.createElement('div');
|
|
285
|
+
feedback.innerHTML = 'šÆ';
|
|
286
|
+
feedback.style.cssText = \`
|
|
287
|
+
position: fixed;
|
|
288
|
+
z-index: 999999;
|
|
289
|
+
pointer-events: none;
|
|
290
|
+
font-size: 24px;
|
|
291
|
+
animation: keakConversionPing 0.8s ease-out;
|
|
292
|
+
\`;
|
|
293
|
+
|
|
294
|
+
const rect = element.getBoundingClientRect();
|
|
295
|
+
feedback.style.left = (rect.left + rect.width / 2) + 'px';
|
|
296
|
+
feedback.style.top = (rect.top + rect.height / 2) + 'px';
|
|
297
|
+
|
|
298
|
+
document.body.appendChild(feedback);
|
|
299
|
+
|
|
300
|
+
setTimeout(() => {
|
|
301
|
+
feedback.remove();
|
|
302
|
+
}, 800);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Show visual indicators for all conversion elements
|
|
306
|
+
function showConversionIndicators() {
|
|
307
|
+
// Add CSS for conversion indicators
|
|
308
|
+
if (!document.getElementById('keak-conversion-styles')) {
|
|
309
|
+
const style = document.createElement('style');
|
|
310
|
+
style.id = 'keak-conversion-styles';
|
|
311
|
+
style.textContent = \`
|
|
312
|
+
.keak-conversion-element {
|
|
313
|
+
position: relative !important;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.keak-conversion-element::after {
|
|
317
|
+
content: 'šÆ';
|
|
318
|
+
position: absolute !important;
|
|
319
|
+
top: -8px !important;
|
|
320
|
+
right: -8px !important;
|
|
321
|
+
width: 20px !important;
|
|
322
|
+
height: 20px !important;
|
|
323
|
+
background: #ff6b35 !important;
|
|
324
|
+
border-radius: 50% !important;
|
|
325
|
+
display: flex !important;
|
|
326
|
+
align-items: center !important;
|
|
327
|
+
justify-content: center !important;
|
|
328
|
+
font-size: 10px !important;
|
|
329
|
+
z-index: 999998 !important;
|
|
330
|
+
animation: keakConversionBadge 2s infinite !important;
|
|
331
|
+
pointer-events: none !important;
|
|
332
|
+
border: 2px solid white !important;
|
|
333
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.2) !important;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
.keak-conversion-element:hover::after {
|
|
337
|
+
animation: keakConversionHover 0.3s ease !important;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
@keyframes keakConversionBadge {
|
|
341
|
+
0% { transform: scale(1); opacity: 1; }
|
|
342
|
+
50% { transform: scale(1.1); opacity: 0.8; }
|
|
343
|
+
100% { transform: scale(1); opacity: 1; }
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
@keyframes keakConversionHover {
|
|
347
|
+
0% { transform: scale(1); }
|
|
348
|
+
50% { transform: scale(1.3); }
|
|
349
|
+
100% { transform: scale(1.1); }
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
@keyframes keakConversionPing {
|
|
353
|
+
0% {
|
|
354
|
+
transform: scale(1) translate(-50%, -50%);
|
|
355
|
+
opacity: 1;
|
|
356
|
+
}
|
|
357
|
+
100% {
|
|
358
|
+
transform: scale(2) translate(-50%, -50%);
|
|
359
|
+
opacity: 0;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
.keak-conversion-summary {
|
|
364
|
+
position: fixed !important;
|
|
365
|
+
top: 20px !important;
|
|
366
|
+
right: 20px !important;
|
|
367
|
+
background: #2d3748 !important;
|
|
368
|
+
color: white !important;
|
|
369
|
+
padding: 12px 16px !important;
|
|
370
|
+
border-radius: 8px !important;
|
|
371
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif !important;
|
|
372
|
+
font-size: 14px !important;
|
|
373
|
+
z-index: 999999 !important;
|
|
374
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.3) !important;
|
|
375
|
+
border: 1px solid #4a5568 !important;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.keak-conversion-summary h4 {
|
|
379
|
+
margin: 0 0 8px 0 !important;
|
|
380
|
+
font-size: 16px !important;
|
|
381
|
+
font-weight: 600 !important;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
.keak-conversion-summary ul {
|
|
385
|
+
margin: 0 !important;
|
|
386
|
+
padding: 0 !important;
|
|
387
|
+
list-style: none !important;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.keak-conversion-summary li {
|
|
391
|
+
margin: 4px 0 !important;
|
|
392
|
+
font-size: 12px !important;
|
|
393
|
+
opacity: 0.9 !important;
|
|
394
|
+
}
|
|
395
|
+
\`;
|
|
396
|
+
document.head.appendChild(style);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Show summary
|
|
400
|
+
showConversionSummary();
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Show conversion tracking summary
|
|
404
|
+
function showConversionSummary() {
|
|
405
|
+
const existingSummary = document.getElementById('keak-conversion-summary');
|
|
406
|
+
if (existingSummary) {
|
|
407
|
+
existingSummary.remove();
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
const typeCount = {};
|
|
411
|
+
trackedElements.forEach(element ={
|
|
412
|
+
const type = element.getAttribute('data-keak-conversion-type');
|
|
413
|
+
typeCount[type] = (typeCount[type] || 0) + 1;
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
const summary = document.createElement('div');
|
|
417
|
+
summary.id = 'keak-conversion-summary';
|
|
418
|
+
summary.className = 'keak-conversion-summary';
|
|
419
|
+
summary.innerHTML = \`
|
|
420
|
+
<h4šÆ Conversion Tracking Active</h4<div\${trackedElements.size} elements tagged</div<ul\${Object.entries(typeCount).map(([type, count]) =\`<li\${type}: \${count}</li\`
|
|
421
|
+
).join('')}
|
|
422
|
+
</ul\`;
|
|
423
|
+
|
|
424
|
+
document.body.appendChild(summary);
|
|
425
|
+
|
|
426
|
+
// Auto-hide after 10 seconds
|
|
427
|
+
setTimeout(() => {
|
|
428
|
+
if (summary.parentNode) {
|
|
429
|
+
summary.style.transition = 'opacity 0.5s';
|
|
430
|
+
summary.style.opacity = '0';
|
|
431
|
+
setTimeout(() =summary.remove(), 500);
|
|
432
|
+
}
|
|
433
|
+
}, 10000);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Observe for new elements (dynamic content)
|
|
437
|
+
function observeNewElements() {
|
|
438
|
+
const observer = new MutationObserver((mutations) => {
|
|
439
|
+
let hasNewElements = false;
|
|
440
|
+
|
|
441
|
+
mutations.forEach((mutation) => {
|
|
442
|
+
if (mutation.type === 'childList') {
|
|
443
|
+
mutation.addedNodes.forEach((node) => {
|
|
444
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
445
|
+
hasNewElements = true;
|
|
446
|
+
}
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
if (hasNewElements) {
|
|
452
|
+
// Debounce the tagging
|
|
453
|
+
clearTimeout(window.keakTaggingTimeout);
|
|
454
|
+
window.keakTaggingTimeout = setTimeout(tagConversionElements, 1000);
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
observer.observe(document.body, {
|
|
459
|
+
childList: true,
|
|
460
|
+
subtree: true
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Public API for manual control
|
|
465
|
+
window.keakConversions = {
|
|
466
|
+
scan: tagConversionElements,
|
|
467
|
+
show: showConversionIndicators,
|
|
468
|
+
hide: () => {
|
|
469
|
+
document.querySelectorAll('.keak-conversion-element').forEach(el ={
|
|
470
|
+
el.classList.remove('keak-conversion-element');
|
|
471
|
+
});
|
|
472
|
+
const summary = document.getElementById('keak-conversion-summary');
|
|
473
|
+
if (summary) summary.remove();
|
|
474
|
+
},
|
|
475
|
+
getTracked: () =Array.from(trackedElements),
|
|
476
|
+
getStats: () => {
|
|
477
|
+
const typeCount = {};
|
|
478
|
+
trackedElements.forEach(element ={
|
|
479
|
+
const type = element.getAttribute('data-keak-conversion-type');
|
|
480
|
+
typeCount[type] = (typeCount[type] || 0) + 1;
|
|
481
|
+
});
|
|
482
|
+
return { total: trackedElements.size, byType: typeCount };
|
|
483
|
+
}
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
// Initialize when DOM is ready
|
|
487
|
+
if (document.readyState === 'loading') {
|
|
488
|
+
document.addEventListener('DOMContentLoaded', initConversionTracking);
|
|
489
|
+
} else {
|
|
490
|
+
initConversionTracking();
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// Also initialize after a short delay to catch any late-loading content
|
|
494
|
+
setTimeout(initConversionTracking, 2000);
|
|
495
|
+
|
|
496
|
+
})();
|
|
497
|
+
`;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
generateResultsHTML(report, results) {
|
|
501
|
+
const modifiedFiles = results.filter(r => r.success && r.modified);
|
|
502
|
+
const errors = results.filter(r => !r.success);
|
|
503
|
+
|
|
504
|
+
return `<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Keak Conversion Tracking Setup Complete</title><style>body {
|
|
505
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
506
|
+
max-width: 800px;
|
|
507
|
+
margin: 0 auto;
|
|
508
|
+
padding: 20px;
|
|
509
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
510
|
+
color: #333;
|
|
511
|
+
min-height: 100vh;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
.container {
|
|
515
|
+
background: white;
|
|
516
|
+
border-radius: 12px;
|
|
517
|
+
padding: 40px;
|
|
518
|
+
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
.header {
|
|
522
|
+
text-align: center;
|
|
523
|
+
margin-bottom: 40px;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
.logo {
|
|
527
|
+
font-size: 48px;
|
|
528
|
+
margin-bottom: 16px;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
h1 {
|
|
532
|
+
color: #2d3748;
|
|
533
|
+
margin-bottom: 8px;
|
|
534
|
+
font-size: 28px;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
.subtitle {
|
|
538
|
+
color: #718096;
|
|
539
|
+
font-size: 18px;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
.feature-grid {
|
|
543
|
+
display: grid;
|
|
544
|
+
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
545
|
+
gap: 20px;
|
|
546
|
+
margin: 40px 0;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
.feature {
|
|
550
|
+
padding: 20px;
|
|
551
|
+
border: 1px solid #e2e8f0;
|
|
552
|
+
border-radius: 8px;
|
|
553
|
+
background: #f7fafc;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
.feature h3 {
|
|
557
|
+
color: #2d3748;
|
|
558
|
+
margin-bottom: 8px;
|
|
559
|
+
display: flex;
|
|
560
|
+
align-items: center;
|
|
561
|
+
gap: 8px;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
.instructions {
|
|
565
|
+
background: #edf2f7;
|
|
566
|
+
padding: 24px;
|
|
567
|
+
border-radius: 8px;
|
|
568
|
+
margin: 20px 0;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
.code {
|
|
572
|
+
background: #2d3748;
|
|
573
|
+
color: #e2e8f0;
|
|
574
|
+
padding: 16px;
|
|
575
|
+
border-radius: 8px;
|
|
576
|
+
font-family: 'SF Mono', Monaco, monospace;
|
|
577
|
+
margin: 16px 0;
|
|
578
|
+
overflow-x: auto;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
.success {
|
|
582
|
+
background: #f0fff4;
|
|
583
|
+
border: 1px solid #9ae6b4;
|
|
584
|
+
color: #22543d;
|
|
585
|
+
padding: 16px;
|
|
586
|
+
border-radius: 8px;
|
|
587
|
+
margin: 20px 0;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
.button {
|
|
591
|
+
background: #4299e1;
|
|
592
|
+
color: white;
|
|
593
|
+
border: none;
|
|
594
|
+
padding: 12px 24px;
|
|
595
|
+
border-radius: 8px;
|
|
596
|
+
font-size: 16px;
|
|
597
|
+
cursor: pointer;
|
|
598
|
+
transition: background 0.2s;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
.button:hover {
|
|
602
|
+
background: #3182ce;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
.status {
|
|
606
|
+
font-weight: 600;
|
|
607
|
+
color: #38a169;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
.stats, .modified-files, .errors {
|
|
611
|
+
background: #f7fafc;
|
|
612
|
+
padding: 20px;
|
|
613
|
+
border-radius: 8px;
|
|
614
|
+
margin: 20px 0;
|
|
615
|
+
border-left: 4px solid #4299e1;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
.errors {
|
|
619
|
+
border-left-color: #f56565;
|
|
620
|
+
background: #fed7d7;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
.stats ul, .modified-files ul, .errors ul {
|
|
624
|
+
margin: 8px 0;
|
|
625
|
+
padding-left: 20px;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
.stats li, .modified-files li, .errors li {
|
|
629
|
+
margin: 4px 0;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
.backup {
|
|
633
|
+
color: #718096;
|
|
634
|
+
font-size: 12px;
|
|
635
|
+
}
|
|
636
|
+
</style></head><body><div class="container"><div class="header"><div class="logo">šÆ</div><h1>Conversion Tracking Setup Complete</h1><p class="subtitle">Your React components have been automatically wrapped with conversion tracking</p></div><div class="success"><strong>ā
Success!</strong>Conversion tracking has been integrated into your codebase.
|
|
637
|
+
All clickable elements are now wrapped with <Conversion> components for automatic telemetry.
|
|
638
|
+
</div><div class="stats"><h3>š Transformation Results</h3><ul><li><strong>Files processed:</strong> ${report.stats.filesProcessed}</li><li><strong>Files modified:</strong> ${report.stats.filesModified}</li><li><strong>Elements wrapped:</strong> ${report.stats.elementsWrapped}</li></ul></div>${modifiedFiles.length > 0 ? `
|
|
639
|
+
<div class="modified-files"><h3>š Modified Files</h3><ul>${modifiedFiles.map(file => `
|
|
640
|
+
<li><code>${path.basename(file.filePath)}</code>${file.backup ? ` <span class="backup">(backup: ${path.basename(file.backup)})</span>` : ''}
|
|
641
|
+
</li>`).join('')}
|
|
642
|
+
</ul></div>` : ''}
|
|
643
|
+
|
|
644
|
+
${errors.length > 0 ? `
|
|
645
|
+
<div class="errors"><h3>ā ļø Issues Found</h3><ul>${errors.map(error => `
|
|
646
|
+
<li><code>${path.basename(error.filePath)}</code>: ${error.error}</li>`).join('')}
|
|
647
|
+
</ul></div>` : ''}
|
|
648
|
+
|
|
649
|
+
<div class="feature-grid"><div class="feature"><h3>š Auto-Detection</h3><p>Automatically finds buttons, CTAs, forms, and links that could be conversion points</p></div><div class="feature"><h3>šÆ Smart Tagging</h3><p>Uses AI-powered patterns to identify conversion-worthy elements based on text and context</p></div><div class="feature"><h3>š Real-time Tracking</h3><p>Tracks conversion events in real-time and sends data to your Keak dashboard</p></div><div class="feature"><h3>šļø Visual Indicators</h3><p>Shows small badges on tracked elements so you can see what's being monitored</p></div></div><div class="instructions"><h3>How to use:</h3><ol><li><strong>Automatic:</strong> The tracking script is already running and detecting elements</li><li><strong>Manual control:</strong> Use the browser console commands for manual control</li><li><strong>View tracked elements:</strong> Look for šÆ badges on your conversion elements</li><li><strong>Analytics:</strong> Check your Keak dashboard for conversion data</li></ol></div><div class="instructions"><h3>Browser Console Commands:</h3><div class="code">// View tracking statistics
|
|
650
|
+
keakConversions.getStats()
|
|
651
|
+
|
|
652
|
+
// Manually scan for new elements
|
|
653
|
+
keakConversions.scan()
|
|
654
|
+
|
|
655
|
+
// Show/hide visual indicators
|
|
656
|
+
keakConversions.show()
|
|
657
|
+
keakConversions.hide()
|
|
658
|
+
|
|
659
|
+
// Get all tracked elements
|
|
660
|
+
keakConversions.getTracked()
|
|
661
|
+
</div></div><div style="text-align: center; margin-top: 40px;"><p class="status">Conversion tracking is now active! š</p><p>Visit your website to see the conversion elements highlighted with šÆ badges.</p></div></div></body></html>`;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
createConversionFiles(targetDir) {
|
|
665
|
+
try {
|
|
666
|
+
console.log(`š Scanning for React files in ${targetDir}...`);
|
|
667
|
+
|
|
668
|
+
// Use code transformer to automatically wrap elements
|
|
669
|
+
const results = this.codeTransformer.processDirectory(targetDir);
|
|
670
|
+
const report = this.codeTransformer.generateReport();
|
|
671
|
+
|
|
672
|
+
// Create a success page with results
|
|
673
|
+
const htmlPath = path.join(targetDir, 'conversion-setup-complete.html');
|
|
674
|
+
fs.writeFileSync(htmlPath, this.generateResultsHTML(report, results));
|
|
675
|
+
|
|
676
|
+
// Show any errors
|
|
677
|
+
const errors = results.filter(r => !r.success);
|
|
678
|
+
|
|
679
|
+
return {
|
|
680
|
+
htmlPath,
|
|
681
|
+
success: true,
|
|
682
|
+
results,
|
|
683
|
+
report,
|
|
684
|
+
errors
|
|
685
|
+
};
|
|
686
|
+
} catch (error) {
|
|
687
|
+
console.error('Error setting up conversion tracking:', error);
|
|
688
|
+
return { success: false, error: error.message };
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
export default ConversionDetector;
|
|
694
|
+
|
|
695
|
+
// CLI execution
|
|
696
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
697
|
+
const detector = new ConversionDetector();
|
|
698
|
+
const targetDir = process.argv[2] || process.cwd();
|
|
699
|
+
|
|
700
|
+
console.log('šÆ Setting up conversion tracking...');
|
|
701
|
+
|
|
702
|
+
const result = detector.createConversionFiles(targetDir);
|
|
703
|
+
|
|
704
|
+
if (result.success) {
|
|
705
|
+
console.log('ā
Conversion tracking files created successfully!');
|
|
706
|
+
console.log('š Script:', result.scriptPath);
|
|
707
|
+
console.log('š Setup page:', result.htmlPath);
|
|
708
|
+
console.log('\nš§ Next steps:');
|
|
709
|
+
console.log('1. Add the script to your website');
|
|
710
|
+
console.log('2. The system will automatically detect conversion elements');
|
|
711
|
+
console.log('3. Check the setup page for more details');
|
|
712
|
+
} else {
|
|
713
|
+
console.error('ā Failed to create conversion files:', result.error);
|
|
714
|
+
process.exit(1);
|
|
715
|
+
}
|
|
716
|
+
}
|