@keak/sdk 1.0.9 → 2.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 +102 -7
- package/dist/Conversion.d.ts.map +1 -1
- package/dist/index.cjs.js +199 -187
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +55 -22
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +201 -187
- package/dist/index.js.map +1 -1
- package/dist/toolbar/KeakToolbar.d.ts.map +1 -1
- package/dist/toolbar.js +41 -0
- package/dist/toolbar.js.map +1 -1
- package/package.json +2 -1
- package/src/cli/ai-helper.js +117 -39
- package/src/cli/install.js +251 -152
- package/src/plugins/babel-source-inject.cjs +55 -131
- package/src/plugins/next.cjs +48 -221
- package/src/plugins/webpack-loader-babel/index.js +43 -117
package/src/cli/install.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import fs from 'fs';
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import { execSync } from 'child_process';
|
|
6
|
-
import {
|
|
6
|
+
import { LocalAIHelper } from './ai-helper.js';
|
|
7
7
|
import ConversionDetector from './conversion-detector.js';
|
|
8
8
|
import { FrameworkConfigManager } from './framework-config.js';
|
|
9
9
|
|
|
@@ -33,23 +33,23 @@ ${colors.cyan}${colors.bright}
|
|
|
33
33
|
╚═══════════════════════════════════════╝
|
|
34
34
|
${colors.reset}
|
|
35
35
|
`);
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
const detector = new ConversionDetector();
|
|
38
38
|
const targetDir = args[1] || process.cwd();
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
console.log(`${colors.blue}🎯 Setting up conversion tracking...${colors.reset}\n`);
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
const result = detector.createConversionFiles(targetDir);
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
if (result.success) {
|
|
45
45
|
const report = result.report;
|
|
46
|
-
|
|
46
|
+
|
|
47
47
|
console.log(`${colors.green}✅ Conversion tracking setup complete!${colors.reset}`);
|
|
48
48
|
console.log(`${colors.cyan}🌐 Results page: ${result.htmlPath}${colors.reset}`);
|
|
49
49
|
console.log(`
|
|
50
50
|
${colors.blue}📊 Results:${colors.reset}
|
|
51
51
|
• Files processed: ${report.stats.filesProcessed}
|
|
52
|
-
• Files modified: ${report.stats.filesModified}
|
|
52
|
+
• Files modified: ${report.stats.filesModified}
|
|
53
53
|
• Elements wrapped: ${report.stats.elementsWrapped}
|
|
54
54
|
`);
|
|
55
55
|
|
|
@@ -60,7 +60,7 @@ ${colors.blue}📊 Results:${colors.reset}
|
|
|
60
60
|
console.log(`
|
|
61
61
|
${colors.yellow}🔧 What happened:${colors.reset}
|
|
62
62
|
1. All React files were scanned automatically
|
|
63
|
-
2. Clickable elements (buttons, links, etc.) were wrapped with components
|
|
63
|
+
2. Clickable elements (buttons, links, etc.) were wrapped with <Conversion> components
|
|
64
64
|
3. Backup files (.keak-backup) were created for all changes
|
|
65
65
|
4. Your components now automatically track conversion events
|
|
66
66
|
|
|
@@ -70,7 +70,7 @@ ${colors.green}🚀 Your conversion tracking is now active!${colors.reset}
|
|
|
70
70
|
console.error(`${colors.red}❌ Failed to setup conversion tracking: ${result.error}${colors.reset}`);
|
|
71
71
|
process.exit(1);
|
|
72
72
|
}
|
|
73
|
-
|
|
73
|
+
|
|
74
74
|
process.exit(0);
|
|
75
75
|
}
|
|
76
76
|
|
|
@@ -89,7 +89,6 @@ class KeakInstaller {
|
|
|
89
89
|
this.projectRoot = process.cwd();
|
|
90
90
|
this.framework = null;
|
|
91
91
|
this.entryFile = null;
|
|
92
|
-
this.aiApiKey = process.env.OPENAI_API_KEY || process.env.ANTHROPIC_API_KEY;
|
|
93
92
|
}
|
|
94
93
|
|
|
95
94
|
async run() {
|
|
@@ -139,14 +138,14 @@ class KeakInstaller {
|
|
|
139
138
|
|
|
140
139
|
detectFramework() {
|
|
141
140
|
const packageJsonPath = path.join(this.projectRoot, 'package.json');
|
|
142
|
-
|
|
141
|
+
|
|
143
142
|
if (!fs.existsSync(packageJsonPath)) {
|
|
144
143
|
throw new Error('No package.json found. Are you in a Node.js project?');
|
|
145
144
|
}
|
|
146
|
-
|
|
145
|
+
|
|
147
146
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
148
147
|
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
149
|
-
|
|
148
|
+
|
|
150
149
|
if (deps['next']) {
|
|
151
150
|
this.framework = 'nextjs';
|
|
152
151
|
console.log(`${colors.green}✓${colors.reset} Detected: Next.js`);
|
|
@@ -213,7 +212,7 @@ class KeakInstaller {
|
|
|
213
212
|
};
|
|
214
213
|
|
|
215
214
|
const entries = possibleEntries[this.framework] || possibleEntries.react;
|
|
216
|
-
|
|
215
|
+
|
|
217
216
|
for (const entry of entries) {
|
|
218
217
|
const fullPath = path.join(this.projectRoot, entry);
|
|
219
218
|
if (fs.existsSync(fullPath)) {
|
|
@@ -222,10 +221,10 @@ class KeakInstaller {
|
|
|
222
221
|
return;
|
|
223
222
|
}
|
|
224
223
|
}
|
|
225
|
-
|
|
224
|
+
|
|
226
225
|
// If no entry file found, try to find any file with ReactDOM.render or createRoot
|
|
227
226
|
this.entryFile = this.findReactDOMRenderFile();
|
|
228
|
-
|
|
227
|
+
|
|
229
228
|
if (!this.entryFile) {
|
|
230
229
|
throw new Error('Could not find React entry file. Please specify manually.');
|
|
231
230
|
}
|
|
@@ -233,16 +232,16 @@ class KeakInstaller {
|
|
|
233
232
|
|
|
234
233
|
findReactDOMRenderFile() {
|
|
235
234
|
const searchDirs = ['src', '.'];
|
|
236
|
-
|
|
235
|
+
|
|
237
236
|
for (const dir of searchDirs) {
|
|
238
237
|
const fullDir = path.join(this.projectRoot, dir);
|
|
239
238
|
if (!fs.existsSync(fullDir)) continue;
|
|
240
|
-
|
|
239
|
+
|
|
241
240
|
const files = this.getAllFiles(fullDir, ['.js', '.jsx', '.ts', '.tsx']);
|
|
242
|
-
|
|
241
|
+
|
|
243
242
|
for (const file of files) {
|
|
244
243
|
const content = fs.readFileSync(file, 'utf-8');
|
|
245
|
-
if (content.includes('ReactDOM.render') ||
|
|
244
|
+
if (content.includes('ReactDOM.render') ||
|
|
246
245
|
content.includes('ReactDOM.createRoot') ||
|
|
247
246
|
content.includes('createRoot(')) {
|
|
248
247
|
console.log(`${colors.green}✓${colors.reset} Found React render in: ${path.relative(this.projectRoot, file)}`);
|
|
@@ -250,46 +249,46 @@ class KeakInstaller {
|
|
|
250
249
|
}
|
|
251
250
|
}
|
|
252
251
|
}
|
|
253
|
-
|
|
252
|
+
|
|
254
253
|
return null;
|
|
255
254
|
}
|
|
256
255
|
|
|
257
256
|
getAllFiles(dir, extensions) {
|
|
258
257
|
const files = [];
|
|
259
|
-
|
|
258
|
+
|
|
260
259
|
const items = fs.readdirSync(dir);
|
|
261
260
|
for (const item of items) {
|
|
262
261
|
const fullPath = path.join(dir, item);
|
|
263
|
-
|
|
262
|
+
|
|
264
263
|
// Skip node_modules and other common directories
|
|
265
264
|
if (item === 'node_modules' || item === '.git' || item === 'dist' || item === 'build') {
|
|
266
265
|
continue;
|
|
267
266
|
}
|
|
268
|
-
|
|
267
|
+
|
|
269
268
|
const stat = fs.statSync(fullPath);
|
|
270
269
|
if (stat.isDirectory()) {
|
|
271
270
|
files.push(...this.getAllFiles(fullPath, extensions));
|
|
272
|
-
} else if (extensions.some(ext
|
|
271
|
+
} else if (extensions.some(ext => item.endsWith(ext))) {
|
|
273
272
|
files.push(fullPath);
|
|
274
273
|
}
|
|
275
274
|
}
|
|
276
|
-
|
|
275
|
+
|
|
277
276
|
return files;
|
|
278
277
|
}
|
|
279
278
|
|
|
280
279
|
isAlreadyInstalled() {
|
|
281
280
|
if (!this.entryFile) return false;
|
|
282
|
-
|
|
281
|
+
|
|
283
282
|
const content = fs.readFileSync(this.entryFile, 'utf-8');
|
|
284
|
-
return content.includes('
|
|
283
|
+
return content.includes('useKeak') || content.includes('@keak/sdk');
|
|
285
284
|
}
|
|
286
285
|
|
|
287
286
|
async autoInstall() {
|
|
288
287
|
console.log(`\n${colors.blue}🔧 Attempting automatic installation...${colors.reset}\n`);
|
|
289
|
-
|
|
288
|
+
|
|
290
289
|
const content = fs.readFileSync(this.entryFile, 'utf-8');
|
|
291
290
|
let modified = content;
|
|
292
|
-
|
|
291
|
+
|
|
293
292
|
try {
|
|
294
293
|
if (this.framework === 'nextjs') {
|
|
295
294
|
modified = this.wrapNextJs(content);
|
|
@@ -298,23 +297,23 @@ class KeakInstaller {
|
|
|
298
297
|
} else {
|
|
299
298
|
modified = this.wrapGenericReact(content);
|
|
300
299
|
}
|
|
301
|
-
|
|
300
|
+
|
|
302
301
|
if (modified !== content) {
|
|
303
302
|
// Create backup
|
|
304
303
|
const backupPath = `${this.entryFile}.backup`;
|
|
305
304
|
fs.writeFileSync(backupPath, content);
|
|
306
305
|
console.log(`${colors.cyan}📋 Created backup: ${path.basename(backupPath)}${colors.reset}`);
|
|
307
|
-
|
|
306
|
+
|
|
308
307
|
// Write modified file
|
|
309
308
|
fs.writeFileSync(this.entryFile, modified);
|
|
310
309
|
console.log(`${colors.green}✓${colors.reset} Modified: ${path.relative(this.projectRoot, this.entryFile)}`);
|
|
311
|
-
|
|
310
|
+
|
|
312
311
|
return true;
|
|
313
312
|
}
|
|
314
313
|
} catch (error) {
|
|
315
314
|
console.log(`${colors.yellow}⚠️ Auto-installation needs help with your setup${colors.reset}`);
|
|
316
315
|
}
|
|
317
|
-
|
|
316
|
+
|
|
318
317
|
return false;
|
|
319
318
|
}
|
|
320
319
|
|
|
@@ -323,10 +322,15 @@ class KeakInstaller {
|
|
|
323
322
|
|
|
324
323
|
if (isAppDir) {
|
|
325
324
|
// Next.js 13+ App Directory
|
|
326
|
-
if (!content.includes('
|
|
327
|
-
// Add
|
|
328
|
-
const importLine = "import { KeakProvider, KeakToolbar } from '@keak/sdk';\n";
|
|
325
|
+
if (!content.includes('useKeak')) {
|
|
326
|
+
// Add 'use client' directive if not present
|
|
329
327
|
let modified = content;
|
|
328
|
+
if (!modified.includes("'use client'") && !modified.includes('"use client"')) {
|
|
329
|
+
modified = "'use client';\n\n" + modified;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Add import
|
|
333
|
+
const importLine = "import { useKeak, KeakToolbar } from '@keak/sdk';\n";
|
|
330
334
|
|
|
331
335
|
// Add import after existing imports
|
|
332
336
|
const lastImportIndex = modified.lastIndexOf('import ');
|
|
@@ -337,26 +341,34 @@ class KeakInstaller {
|
|
|
337
341
|
modified = importLine + modified;
|
|
338
342
|
}
|
|
339
343
|
|
|
340
|
-
//
|
|
341
|
-
const
|
|
344
|
+
// Add useKeak() hook at the start of the layout component
|
|
345
|
+
const componentMatch = modified.match(/(export\s+default\s+function\s+\w+\s*\([^)]*\)\s*\{)\s*/);
|
|
346
|
+
if (componentMatch) {
|
|
347
|
+
modified = modified.replace(
|
|
348
|
+
componentMatch[0],
|
|
349
|
+
`${componentMatch[1]}
|
|
350
|
+
useKeak({ debug: true });
|
|
351
|
+
`
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Add KeakToolbar before closing body tag
|
|
356
|
+
const bodyMatch = modified.match(/(<\/body>)/);
|
|
342
357
|
if (bodyMatch) {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
${
|
|
347
|
-
|
|
348
|
-
</KeakProvider>
|
|
349
|
-
${bodyMatch[3]}`;
|
|
350
|
-
modified = modified.replace(bodyMatch[0], wrapped);
|
|
358
|
+
modified = modified.replace(
|
|
359
|
+
bodyMatch[0],
|
|
360
|
+
` <KeakToolbar position="bottom-right" />
|
|
361
|
+
${bodyMatch[0]}`
|
|
362
|
+
);
|
|
351
363
|
}
|
|
352
364
|
|
|
353
365
|
return modified;
|
|
354
366
|
}
|
|
355
367
|
} else {
|
|
356
|
-
// Next.js Pages Directory
|
|
357
|
-
if (!content.includes('
|
|
358
|
-
// Add import
|
|
359
|
-
const importLine = "import {
|
|
368
|
+
// Next.js Pages Directory (_app.tsx/jsx/js)
|
|
369
|
+
if (!content.includes('useKeak')) {
|
|
370
|
+
// Add import
|
|
371
|
+
const importLine = "import { useKeak, KeakToolbar } from '@keak/sdk';\n";
|
|
360
372
|
let modified = content;
|
|
361
373
|
|
|
362
374
|
const lastImportIndex = modified.lastIndexOf('import ');
|
|
@@ -367,16 +379,25 @@ ${bodyContent}
|
|
|
367
379
|
modified = importLine + modified;
|
|
368
380
|
}
|
|
369
381
|
|
|
370
|
-
//
|
|
371
|
-
const componentMatch = modified.match(/(
|
|
382
|
+
// Add useKeak() hook at the start of MyApp component
|
|
383
|
+
const componentMatch = modified.match(/(function\s+\w+\s*\([^)]*\)\s*\{)\s*/);
|
|
372
384
|
if (componentMatch) {
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
385
|
+
modified = modified.replace(
|
|
386
|
+
componentMatch[0],
|
|
387
|
+
`${componentMatch[1]}
|
|
388
|
+
useKeak({ debug: true });
|
|
389
|
+
`
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Add KeakToolbar after Component
|
|
394
|
+
const componentRenderMatch = modified.match(/(<Component\s+[^>]*\/>)/);
|
|
395
|
+
if (componentRenderMatch) {
|
|
396
|
+
modified = modified.replace(
|
|
397
|
+
componentRenderMatch[0],
|
|
398
|
+
`${componentRenderMatch[0]}
|
|
399
|
+
<KeakToolbar position="bottom-right" />`
|
|
400
|
+
);
|
|
380
401
|
}
|
|
381
402
|
|
|
382
403
|
return modified;
|
|
@@ -389,9 +410,9 @@ ${bodyContent}
|
|
|
389
410
|
wrapCRA(content) {
|
|
390
411
|
let modified = content;
|
|
391
412
|
|
|
392
|
-
// Add import
|
|
393
|
-
if (!content.includes('
|
|
394
|
-
const importLine = "import {
|
|
413
|
+
// Add import
|
|
414
|
+
if (!content.includes('useKeak')) {
|
|
415
|
+
const importLine = "import { useKeak, KeakToolbar } from '@keak/sdk';\n";
|
|
395
416
|
|
|
396
417
|
const lastImportIndex = modified.lastIndexOf('import ');
|
|
397
418
|
if (lastImportIndex !== -1) {
|
|
@@ -402,36 +423,122 @@ ${bodyContent}
|
|
|
402
423
|
}
|
|
403
424
|
}
|
|
404
425
|
|
|
405
|
-
//
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
<KeakToolbar />
|
|
414
|
-
</KeakProvider>
|
|
415
|
-
${renderMatch[3]}`;
|
|
416
|
-
modified = modified.replace(renderMatch[0], wrapped);
|
|
426
|
+
// Check if this is App component (has a component function)
|
|
427
|
+
const hasComponentFunction = content.match(/function\s+\w+\s*\(|const\s+\w+\s*=\s*\([^)]*\)\s*=>/);
|
|
428
|
+
|
|
429
|
+
if (hasComponentFunction) {
|
|
430
|
+
// This is App.tsx/jsx - add hook to component
|
|
431
|
+
const hookAdded = this.addUseKeakHookToComponent(modified);
|
|
432
|
+
if (hookAdded) {
|
|
433
|
+
modified = hookAdded;
|
|
417
434
|
}
|
|
418
|
-
|
|
419
|
-
//
|
|
420
|
-
const
|
|
421
|
-
if (
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
435
|
+
|
|
436
|
+
// Add toolbar before the closing tag of return
|
|
437
|
+
const returnMatch = modified.match(/(<\/(?:[^>]+)>)\s*\n?\s*(\);?\s*[}$])/m);
|
|
438
|
+
if (returnMatch) {
|
|
439
|
+
modified = modified.replace(
|
|
440
|
+
returnMatch[0],
|
|
441
|
+
`${returnMatch[1]}
|
|
442
|
+
<KeakToolbar position="bottom-right" />
|
|
443
|
+
${returnMatch[2]}`
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
} else {
|
|
447
|
+
// This is index.tsx/jsx - create wrapper component
|
|
448
|
+
// Find ReactDOM.render or createRoot and wrap with a component
|
|
449
|
+
if (content.includes('ReactDOM.render') || content.includes('createRoot')) {
|
|
450
|
+
// Add wrapper component before ReactDOM
|
|
451
|
+
const wrapperComponent = `
|
|
452
|
+
// Keak wrapper component with hook
|
|
453
|
+
function KeakWrapper({ children }) {
|
|
454
|
+
useKeak({ debug: true });
|
|
455
|
+
return (
|
|
456
|
+
<>
|
|
457
|
+
{children}
|
|
458
|
+
<KeakToolbar position="bottom-right" />
|
|
459
|
+
</>
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
`;
|
|
464
|
+
const reactDOMIndex = modified.search(/(ReactDOM\.render|createRoot)/);
|
|
465
|
+
if (reactDOMIndex !== -1) {
|
|
466
|
+
modified = modified.slice(0, reactDOMIndex) + wrapperComponent + modified.slice(reactDOMIndex);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// Wrap the rendered component
|
|
470
|
+
if (content.includes('ReactDOM.render')) {
|
|
471
|
+
// Old React 17 style
|
|
472
|
+
const renderMatch = modified.match(/(ReactDOM\.render\s*\(\s*)(<)/);
|
|
473
|
+
if (renderMatch) {
|
|
474
|
+
modified = modified.replace(
|
|
475
|
+
renderMatch[0],
|
|
476
|
+
`${renderMatch[1]}<KeakWrapper>
|
|
477
|
+
${renderMatch[2]}`
|
|
478
|
+
);
|
|
479
|
+
|
|
480
|
+
const closeMatch = modified.match(/(<\/(?:[^>]+)>)\s*\n?\s*(,\s*document\.getElementById)/);
|
|
481
|
+
if (closeMatch) {
|
|
482
|
+
modified = modified.replace(
|
|
483
|
+
closeMatch[0],
|
|
484
|
+
`${closeMatch[1]}
|
|
485
|
+
</KeakWrapper>${closeMatch[2]}`
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
} else if (content.includes('createRoot')) {
|
|
490
|
+
// React 18 style
|
|
491
|
+
const renderMatch = modified.match(/(root\.render\s*\(\s*)(<)/);
|
|
492
|
+
if (renderMatch) {
|
|
493
|
+
modified = modified.replace(
|
|
494
|
+
renderMatch[0],
|
|
495
|
+
`${renderMatch[1]}<KeakWrapper>
|
|
496
|
+
${renderMatch[2]}`
|
|
497
|
+
);
|
|
498
|
+
|
|
499
|
+
const closeMatch = modified.match(/(<\/(?:[^>]+)>)\s*\n?\s*(\))/);
|
|
500
|
+
if (closeMatch) {
|
|
501
|
+
modified = modified.replace(
|
|
502
|
+
closeMatch[0],
|
|
503
|
+
`${closeMatch[1]}
|
|
504
|
+
</KeakWrapper>${closeMatch[2]}`
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
429
509
|
}
|
|
430
510
|
}
|
|
431
511
|
|
|
432
512
|
return modified;
|
|
433
513
|
}
|
|
434
514
|
|
|
515
|
+
addUseKeakHookToComponent(content) {
|
|
516
|
+
// Try to add useKeak() hook at the start of the component
|
|
517
|
+
// Match function components
|
|
518
|
+
const functionMatch = content.match(/(function\s+\w+\s*\([^)]*\)\s*\{)\s*/);
|
|
519
|
+
if (functionMatch) {
|
|
520
|
+
return content.replace(
|
|
521
|
+
functionMatch[0],
|
|
522
|
+
`${functionMatch[1]}
|
|
523
|
+
useKeak({ debug: true });
|
|
524
|
+
`
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Match arrow function components
|
|
529
|
+
const arrowMatch = content.match(/(const\s+\w+\s*=\s*\([^)]*\)\s*=>\s*\{)\s*/);
|
|
530
|
+
if (arrowMatch) {
|
|
531
|
+
return content.replace(
|
|
532
|
+
arrowMatch[0],
|
|
533
|
+
`${arrowMatch[1]}
|
|
534
|
+
useKeak({ debug: true });
|
|
535
|
+
`
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
return null;
|
|
540
|
+
}
|
|
541
|
+
|
|
435
542
|
wrapGenericReact(content) {
|
|
436
543
|
// Generic React app wrapper
|
|
437
544
|
return this.wrapCRA(content);
|
|
@@ -439,52 +546,25 @@ ${bodyContent}
|
|
|
439
546
|
|
|
440
547
|
async aiAssistedInstall() {
|
|
441
548
|
console.log(`\n${colors.cyan}🤖 Using AI to help with installation...${colors.reset}\n`);
|
|
442
|
-
|
|
549
|
+
|
|
443
550
|
const content = fs.readFileSync(this.entryFile, 'utf-8');
|
|
444
|
-
const aiHelper = new AIHelper();
|
|
445
551
|
const localHelper = new LocalAIHelper();
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
// First try the Keak AI service (free, no API key needed)
|
|
449
|
-
console.log(`${colors.blue}Connecting to Keak AI service...${colors.reset}`);
|
|
450
|
-
const modifiedContent = await Promise.race([
|
|
451
|
-
aiHelper.getInstallationHelp(content, this.entryFile, this.framework),
|
|
452
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 5000))
|
|
453
|
-
]);
|
|
454
|
-
|
|
455
|
-
if (modifiedContent && modifiedContent !== content) {
|
|
456
|
-
// Create backup
|
|
457
|
-
const backupPath = `${this.entryFile}.backup`;
|
|
458
|
-
fs.writeFileSync(backupPath, content);
|
|
459
|
-
console.log(`${colors.cyan}📋 Created backup: ${path.basename(backupPath)}${colors.reset}`);
|
|
460
|
-
|
|
461
|
-
// Write modified file
|
|
462
|
-
fs.writeFileSync(this.entryFile, modifiedContent);
|
|
463
|
-
console.log(`${colors.green}✓${colors.reset} AI successfully updated: ${path.relative(this.projectRoot, this.entryFile)}`);
|
|
464
|
-
|
|
465
|
-
console.log(`\n${colors.green}${colors.bright}✨ Success! Keak has been installed with AI assistance.${colors.reset}`);
|
|
466
|
-
this.showPostInstallInstructions();
|
|
467
|
-
return;
|
|
468
|
-
}
|
|
469
|
-
} catch (error) {
|
|
470
|
-
console.log(`${colors.yellow}Online AI unavailable, using local intelligence...${colors.reset}`);
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
// Fallback to local AI helper
|
|
552
|
+
|
|
553
|
+
// Use local AI helper (updated to use hook-based format)
|
|
474
554
|
try {
|
|
475
555
|
const isTypeScript = this.entryFile.endsWith('.ts') || this.entryFile.endsWith('.tsx');
|
|
476
|
-
const modifiedContent = localHelper.
|
|
477
|
-
|
|
556
|
+
const modifiedContent = localHelper.addKeakHook(content, isTypeScript);
|
|
557
|
+
|
|
478
558
|
if (modifiedContent && modifiedContent !== content) {
|
|
479
559
|
// Create backup
|
|
480
560
|
const backupPath = `${this.entryFile}.backup`;
|
|
481
561
|
fs.writeFileSync(backupPath, content);
|
|
482
562
|
console.log(`${colors.cyan}📋 Created backup: ${path.basename(backupPath)}${colors.reset}`);
|
|
483
|
-
|
|
563
|
+
|
|
484
564
|
// Write modified file
|
|
485
565
|
fs.writeFileSync(this.entryFile, modifiedContent);
|
|
486
566
|
console.log(`${colors.green}✓${colors.reset} Successfully updated: ${path.relative(this.projectRoot, this.entryFile)}`);
|
|
487
|
-
|
|
567
|
+
|
|
488
568
|
console.log(`\n${colors.green}${colors.bright}✨ Success! Keak has been installed.${colors.reset}`);
|
|
489
569
|
this.showPostInstallInstructions();
|
|
490
570
|
return;
|
|
@@ -492,16 +572,16 @@ ${bodyContent}
|
|
|
492
572
|
} catch (error) {
|
|
493
573
|
console.log(`${colors.yellow}Automated installation needs manual help.${colors.reset}`);
|
|
494
574
|
}
|
|
495
|
-
|
|
575
|
+
|
|
496
576
|
// If all else fails, show manual instructions
|
|
497
577
|
this.showManualInstructions();
|
|
498
578
|
}
|
|
499
579
|
|
|
500
580
|
showManualInstructions() {
|
|
501
|
-
const relativeEntry = this.entryFile
|
|
581
|
+
const relativeEntry = this.entryFile
|
|
502
582
|
? path.relative(this.projectRoot, this.entryFile)
|
|
503
583
|
: 'your entry file (e.g., src/index.tsx or src/App.tsx)';
|
|
504
|
-
|
|
584
|
+
|
|
505
585
|
console.log(`
|
|
506
586
|
${colors.yellow}📝 Manual Installation Required${colors.reset}
|
|
507
587
|
|
|
@@ -510,10 +590,13 @@ Please add Keak to your app manually:
|
|
|
510
590
|
1. Open ${colors.cyan}${relativeEntry}${colors.reset}
|
|
511
591
|
|
|
512
592
|
2. Add this import at the top:
|
|
513
|
-
${colors.green}import {
|
|
593
|
+
${colors.green}import { useKeak, KeakToolbar } from '@keak/sdk';${colors.reset}
|
|
514
594
|
|
|
515
|
-
3.
|
|
516
|
-
${colors.green}
|
|
595
|
+
3. Add the hook in your component:
|
|
596
|
+
${colors.green}useKeak({ debug: true });${colors.reset}
|
|
597
|
+
|
|
598
|
+
4. Add the toolbar to your JSX:
|
|
599
|
+
${colors.green}<KeakToolbar position="bottom-right" />${colors.reset}
|
|
517
600
|
|
|
518
601
|
Example for ${this.framework}:
|
|
519
602
|
${this.getFrameworkExample()}
|
|
@@ -524,36 +607,52 @@ ${this.getFrameworkExample()}
|
|
|
524
607
|
const examples = {
|
|
525
608
|
'nextjs': `
|
|
526
609
|
// app/layout.tsx
|
|
527
|
-
|
|
610
|
+
'use client';
|
|
611
|
+
|
|
612
|
+
import { useKeak, KeakToolbar } from '@keak/sdk';
|
|
528
613
|
|
|
529
614
|
export default function RootLayout({ children }) {
|
|
615
|
+
useKeak({ debug: true });
|
|
616
|
+
|
|
530
617
|
return (
|
|
531
|
-
<html
|
|
532
|
-
<
|
|
618
|
+
<html>
|
|
619
|
+
<body>
|
|
533
620
|
{children}
|
|
534
|
-
|
|
535
|
-
|
|
621
|
+
<KeakToolbar position="bottom-right" />
|
|
622
|
+
</body>
|
|
623
|
+
</html>
|
|
624
|
+
);
|
|
536
625
|
}`,
|
|
537
626
|
'create-react-app': `
|
|
538
|
-
// src/
|
|
539
|
-
import {
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
627
|
+
// src/App.tsx
|
|
628
|
+
import { useKeak, KeakToolbar } from '@keak/sdk';
|
|
629
|
+
|
|
630
|
+
function App() {
|
|
631
|
+
useKeak({ debug: true });
|
|
632
|
+
|
|
633
|
+
return (
|
|
634
|
+
<div className="App">
|
|
635
|
+
{/* Your app content */}
|
|
636
|
+
<KeakToolbar position="bottom-right" />
|
|
637
|
+
</div>
|
|
638
|
+
);
|
|
639
|
+
}`,
|
|
546
640
|
'vite-react': `
|
|
547
|
-
// src/
|
|
548
|
-
import {
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
641
|
+
// src/App.tsx
|
|
642
|
+
import { useKeak, KeakToolbar } from '@keak/sdk';
|
|
643
|
+
|
|
644
|
+
function App() {
|
|
645
|
+
useKeak({ debug: true });
|
|
646
|
+
|
|
647
|
+
return (
|
|
648
|
+
<>
|
|
649
|
+
{/* Your app content */}
|
|
650
|
+
<KeakToolbar position="bottom-right" />
|
|
651
|
+
</>
|
|
652
|
+
);
|
|
653
|
+
}`
|
|
555
654
|
};
|
|
556
|
-
|
|
655
|
+
|
|
557
656
|
return examples[this.framework] || examples['create-react-app'];
|
|
558
657
|
}
|
|
559
658
|
|
|
@@ -567,6 +666,7 @@ Start your development server and look for the ${colors.bright}Keak toolbar${col
|
|
|
567
666
|
in the ${colors.bright}bottom-right corner${colors.reset} of your app.
|
|
568
667
|
|
|
569
668
|
${colors.blue}Quick Tips:${colors.reset}
|
|
669
|
+
• The toolbar auto-shows in development mode only
|
|
570
670
|
• Click the toolbar to expand it
|
|
571
671
|
• Use "Select" to pick elements for testing
|
|
572
672
|
• Try the AI prompts for optimization ideas
|
|
@@ -574,8 +674,7 @@ ${colors.blue}Quick Tips:${colors.reset}
|
|
|
574
674
|
|
|
575
675
|
${colors.blue}🔧 Important for Next.js users:${colors.reset}
|
|
576
676
|
• Keak requires Babel for source mapping
|
|
577
|
-
• We've
|
|
578
|
-
• Use ${colors.bright}npm run dev:turbo${colors.reset} if you need Turbopack
|
|
677
|
+
• We've configured your build tools automatically
|
|
579
678
|
• ${colors.yellow}Restart your dev server${colors.reset} for changes to take effect
|
|
580
679
|
|
|
581
680
|
${colors.blue}🎯 Want conversion tracking?${colors.reset}
|
|
@@ -607,7 +706,7 @@ const installer = new KeakInstaller();
|
|
|
607
706
|
installer.run().catch((error) => {
|
|
608
707
|
// Only show error in verbose mode or when run explicitly (not postinstall)
|
|
609
708
|
const isPostInstall = process.env.npm_lifecycle_event === 'postinstall';
|
|
610
|
-
|
|
709
|
+
|
|
611
710
|
if (!isPostInstall) {
|
|
612
711
|
console.error(`${colors.red}Installation failed: ${error.message}${colors.reset}`);
|
|
613
712
|
process.exit(1);
|
|
@@ -615,4 +714,4 @@ installer.run().catch((error) => {
|
|
|
615
714
|
// Silent fail for postinstall - don't block package installation
|
|
616
715
|
console.log(`${colors.yellow}ℹ️ Keak auto-setup skipped. Run 'npx keak-setup' manually when ready.${colors.reset}`);
|
|
617
716
|
}
|
|
618
|
-
});
|
|
717
|
+
});
|