@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,618 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { execSync } from 'child_process';
|
|
6
|
+
import { AIHelper, LocalAIHelper } from './ai-helper.js';
|
|
7
|
+
import ConversionDetector from './conversion-detector.js';
|
|
8
|
+
import { FrameworkConfigManager } from './framework-config.js';
|
|
9
|
+
|
|
10
|
+
// ANSI color codes for terminal output
|
|
11
|
+
const colors = {
|
|
12
|
+
reset: '\x1b[0m',
|
|
13
|
+
bright: '\x1b[1m',
|
|
14
|
+
green: '\x1b[32m',
|
|
15
|
+
yellow: '\x1b[33m',
|
|
16
|
+
blue: '\x1b[34m',
|
|
17
|
+
cyan: '\x1b[36m',
|
|
18
|
+
red: '\x1b[31m',
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// Check for command-line arguments
|
|
22
|
+
const args = process.argv.slice(2);
|
|
23
|
+
const command = args[0];
|
|
24
|
+
|
|
25
|
+
// Handle conversions command
|
|
26
|
+
if (command === 'conversions') {
|
|
27
|
+
console.log(`
|
|
28
|
+
${colors.cyan}${colors.bright}
|
|
29
|
+
╔═══════════════════════════════════════╗
|
|
30
|
+
║ ║
|
|
31
|
+
║ 🎯 Keak Conversion Tracking Setup ║
|
|
32
|
+
║ ║
|
|
33
|
+
╚═══════════════════════════════════════╝
|
|
34
|
+
${colors.reset}
|
|
35
|
+
`);
|
|
36
|
+
|
|
37
|
+
const detector = new ConversionDetector();
|
|
38
|
+
const targetDir = args[1] || process.cwd();
|
|
39
|
+
|
|
40
|
+
console.log(`${colors.blue}🎯 Setting up conversion tracking...${colors.reset}\n`);
|
|
41
|
+
|
|
42
|
+
const result = detector.createConversionFiles(targetDir);
|
|
43
|
+
|
|
44
|
+
if (result.success) {
|
|
45
|
+
const report = result.report;
|
|
46
|
+
|
|
47
|
+
console.log(`${colors.green}✅ Conversion tracking setup complete!${colors.reset}`);
|
|
48
|
+
console.log(`${colors.cyan}🌐 Results page: ${result.htmlPath}${colors.reset}`);
|
|
49
|
+
console.log(`
|
|
50
|
+
${colors.blue}📊 Results:${colors.reset}
|
|
51
|
+
• Files processed: ${report.stats.filesProcessed}
|
|
52
|
+
• Files modified: ${report.stats.filesModified}
|
|
53
|
+
• Elements wrapped: ${report.stats.elementsWrapped}
|
|
54
|
+
`);
|
|
55
|
+
|
|
56
|
+
if (result.errors && result.errors.length > 0) {
|
|
57
|
+
console.log(`${colors.yellow}⚠️ ${result.errors.length} files had issues - check the results page for details${colors.reset}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
console.log(`
|
|
61
|
+
${colors.yellow}🔧 What happened:${colors.reset}
|
|
62
|
+
1. All React files were scanned automatically
|
|
63
|
+
2. Clickable elements (buttons, links, etc.) were wrapped with components
|
|
64
|
+
3. Backup files (.keak-backup) were created for all changes
|
|
65
|
+
4. Your components now automatically track conversion events
|
|
66
|
+
|
|
67
|
+
${colors.green}🚀 Your conversion tracking is now active!${colors.reset}
|
|
68
|
+
`);
|
|
69
|
+
} else {
|
|
70
|
+
console.error(`${colors.red}❌ Failed to setup conversion tracking: ${result.error}${colors.reset}`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
process.exit(0);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
console.log(`
|
|
78
|
+
${colors.cyan}${colors.bright}
|
|
79
|
+
╔═══════════════════════════════════════╗
|
|
80
|
+
║ ║
|
|
81
|
+
║ 🚀 Keak SDK Auto-Setup Wizard ║
|
|
82
|
+
║ ║
|
|
83
|
+
╚═══════════════════════════════════════╝
|
|
84
|
+
${colors.reset}
|
|
85
|
+
`);
|
|
86
|
+
|
|
87
|
+
class KeakInstaller {
|
|
88
|
+
constructor() {
|
|
89
|
+
this.projectRoot = process.cwd();
|
|
90
|
+
this.framework = null;
|
|
91
|
+
this.entryFile = null;
|
|
92
|
+
this.aiApiKey = process.env.OPENAI_API_KEY || process.env.ANTHROPIC_API_KEY;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async run() {
|
|
96
|
+
try {
|
|
97
|
+
console.log(`${colors.blue}🔍 Detecting project setup...${colors.reset}\n`);
|
|
98
|
+
|
|
99
|
+
// Step 1: Detect framework
|
|
100
|
+
this.detectFramework();
|
|
101
|
+
|
|
102
|
+
// Step 2: Find entry file
|
|
103
|
+
this.findEntryFile();
|
|
104
|
+
|
|
105
|
+
// Step 3: Setup framework configuration (Babel, etc.)
|
|
106
|
+
console.log(`\n${colors.blue}🔧 Configuring build tools for optimal Keak performance...${colors.reset}\n`);
|
|
107
|
+
const configManager = new FrameworkConfigManager(this.projectRoot);
|
|
108
|
+
const configSuccess = await configManager.setupBabelConfig();
|
|
109
|
+
|
|
110
|
+
if (configSuccess) {
|
|
111
|
+
console.log(`${colors.green}✅ Build configuration complete${colors.reset}\n`);
|
|
112
|
+
} else {
|
|
113
|
+
console.log(`${colors.yellow}⚠️ Build configuration incomplete - Keak may have limited functionality${colors.reset}\n`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Step 4: Check if already installed
|
|
117
|
+
if (this.isAlreadyInstalled()) {
|
|
118
|
+
console.log(`${colors.green}✅ Keak is already installed!${colors.reset}`);
|
|
119
|
+
this.showUsageInstructions();
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Step 5: Attempt automatic installation
|
|
124
|
+
const installed = await this.autoInstall();
|
|
125
|
+
|
|
126
|
+
if (installed) {
|
|
127
|
+
console.log(`\n${colors.green}${colors.bright}✨ Success! Keak has been installed.${colors.reset}`);
|
|
128
|
+
this.showPostInstallInstructions();
|
|
129
|
+
} else {
|
|
130
|
+
// Step 6: Use AI for complex cases
|
|
131
|
+
await this.aiAssistedInstall();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
} catch (error) {
|
|
135
|
+
console.error(`${colors.red}❌ Installation failed: ${error.message}${colors.reset}`);
|
|
136
|
+
this.showManualInstructions();
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
detectFramework() {
|
|
141
|
+
const packageJsonPath = path.join(this.projectRoot, 'package.json');
|
|
142
|
+
|
|
143
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
144
|
+
throw new Error('No package.json found. Are you in a Node.js project?');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
148
|
+
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
149
|
+
|
|
150
|
+
if (deps['next']) {
|
|
151
|
+
this.framework = 'nextjs';
|
|
152
|
+
console.log(`${colors.green}✓${colors.reset} Detected: Next.js`);
|
|
153
|
+
} else if (deps['react-scripts']) {
|
|
154
|
+
this.framework = 'create-react-app';
|
|
155
|
+
console.log(`${colors.green}✓${colors.reset} Detected: Create React App`);
|
|
156
|
+
} else if (deps['vite'] && deps['react']) {
|
|
157
|
+
this.framework = 'vite-react';
|
|
158
|
+
console.log(`${colors.green}✓${colors.reset} Detected: Vite + React`);
|
|
159
|
+
} else if (deps['gatsby']) {
|
|
160
|
+
this.framework = 'gatsby';
|
|
161
|
+
console.log(`${colors.green}✓${colors.reset} Detected: Gatsby`);
|
|
162
|
+
} else if (deps['react']) {
|
|
163
|
+
this.framework = 'react';
|
|
164
|
+
console.log(`${colors.green}✓${colors.reset} Detected: React (custom setup)`);
|
|
165
|
+
} else {
|
|
166
|
+
throw new Error('Could not detect React in your project. Keak currently supports React applications.');
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
findEntryFile() {
|
|
171
|
+
const possibleEntries = {
|
|
172
|
+
'nextjs': [
|
|
173
|
+
'app/layout.tsx',
|
|
174
|
+
'app/layout.jsx',
|
|
175
|
+
'app/layout.js',
|
|
176
|
+
'pages/_app.tsx',
|
|
177
|
+
'pages/_app.jsx',
|
|
178
|
+
'pages/_app.js',
|
|
179
|
+
],
|
|
180
|
+
'create-react-app': [
|
|
181
|
+
'src/index.tsx',
|
|
182
|
+
'src/index.jsx',
|
|
183
|
+
'src/index.js',
|
|
184
|
+
'src/App.tsx',
|
|
185
|
+
'src/App.jsx',
|
|
186
|
+
'src/App.js',
|
|
187
|
+
],
|
|
188
|
+
'vite-react': [
|
|
189
|
+
'src/main.tsx',
|
|
190
|
+
'src/main.jsx',
|
|
191
|
+
'src/App.tsx',
|
|
192
|
+
'src/App.jsx',
|
|
193
|
+
],
|
|
194
|
+
'gatsby': [
|
|
195
|
+
'src/pages/_app.tsx',
|
|
196
|
+
'src/pages/_app.js',
|
|
197
|
+
'gatsby-browser.js',
|
|
198
|
+
],
|
|
199
|
+
'react': [
|
|
200
|
+
'src/index.tsx',
|
|
201
|
+
'src/index.jsx',
|
|
202
|
+
'src/index.js',
|
|
203
|
+
'index.tsx',
|
|
204
|
+
'index.jsx',
|
|
205
|
+
'index.js',
|
|
206
|
+
'src/App.tsx',
|
|
207
|
+
'src/App.jsx',
|
|
208
|
+
'src/App.js',
|
|
209
|
+
'App.tsx',
|
|
210
|
+
'App.jsx',
|
|
211
|
+
'App.js',
|
|
212
|
+
]
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const entries = possibleEntries[this.framework] || possibleEntries.react;
|
|
216
|
+
|
|
217
|
+
for (const entry of entries) {
|
|
218
|
+
const fullPath = path.join(this.projectRoot, entry);
|
|
219
|
+
if (fs.existsSync(fullPath)) {
|
|
220
|
+
this.entryFile = fullPath;
|
|
221
|
+
console.log(`${colors.green}✓${colors.reset} Found entry file: ${entry}`);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// If no entry file found, try to find any file with ReactDOM.render or createRoot
|
|
227
|
+
this.entryFile = this.findReactDOMRenderFile();
|
|
228
|
+
|
|
229
|
+
if (!this.entryFile) {
|
|
230
|
+
throw new Error('Could not find React entry file. Please specify manually.');
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
findReactDOMRenderFile() {
|
|
235
|
+
const searchDirs = ['src', '.'];
|
|
236
|
+
|
|
237
|
+
for (const dir of searchDirs) {
|
|
238
|
+
const fullDir = path.join(this.projectRoot, dir);
|
|
239
|
+
if (!fs.existsSync(fullDir)) continue;
|
|
240
|
+
|
|
241
|
+
const files = this.getAllFiles(fullDir, ['.js', '.jsx', '.ts', '.tsx']);
|
|
242
|
+
|
|
243
|
+
for (const file of files) {
|
|
244
|
+
const content = fs.readFileSync(file, 'utf-8');
|
|
245
|
+
if (content.includes('ReactDOM.render') ||
|
|
246
|
+
content.includes('ReactDOM.createRoot') ||
|
|
247
|
+
content.includes('createRoot(')) {
|
|
248
|
+
console.log(`${colors.green}✓${colors.reset} Found React render in: ${path.relative(this.projectRoot, file)}`);
|
|
249
|
+
return file;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
getAllFiles(dir, extensions) {
|
|
258
|
+
const files = [];
|
|
259
|
+
|
|
260
|
+
const items = fs.readdirSync(dir);
|
|
261
|
+
for (const item of items) {
|
|
262
|
+
const fullPath = path.join(dir, item);
|
|
263
|
+
|
|
264
|
+
// Skip node_modules and other common directories
|
|
265
|
+
if (item === 'node_modules' || item === '.git' || item === 'dist' || item === 'build') {
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const stat = fs.statSync(fullPath);
|
|
270
|
+
if (stat.isDirectory()) {
|
|
271
|
+
files.push(...this.getAllFiles(fullPath, extensions));
|
|
272
|
+
} else if (extensions.some(ext =item.endsWith(ext))) {
|
|
273
|
+
files.push(fullPath);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return files;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
isAlreadyInstalled() {
|
|
281
|
+
if (!this.entryFile) return false;
|
|
282
|
+
|
|
283
|
+
const content = fs.readFileSync(this.entryFile, 'utf-8');
|
|
284
|
+
return content.includes('KeakProvider') || content.includes('keak-sdk');
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
async autoInstall() {
|
|
288
|
+
console.log(`\n${colors.blue}🔧 Attempting automatic installation...${colors.reset}\n`);
|
|
289
|
+
|
|
290
|
+
const content = fs.readFileSync(this.entryFile, 'utf-8');
|
|
291
|
+
let modified = content;
|
|
292
|
+
|
|
293
|
+
try {
|
|
294
|
+
if (this.framework === 'nextjs') {
|
|
295
|
+
modified = this.wrapNextJs(content);
|
|
296
|
+
} else if (this.framework === 'create-react-app' || this.framework === 'vite-react') {
|
|
297
|
+
modified = this.wrapCRA(content);
|
|
298
|
+
} else {
|
|
299
|
+
modified = this.wrapGenericReact(content);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (modified !== content) {
|
|
303
|
+
// Create backup
|
|
304
|
+
const backupPath = `${this.entryFile}.backup`;
|
|
305
|
+
fs.writeFileSync(backupPath, content);
|
|
306
|
+
console.log(`${colors.cyan}📋 Created backup: ${path.basename(backupPath)}${colors.reset}`);
|
|
307
|
+
|
|
308
|
+
// Write modified file
|
|
309
|
+
fs.writeFileSync(this.entryFile, modified);
|
|
310
|
+
console.log(`${colors.green}✓${colors.reset} Modified: ${path.relative(this.projectRoot, this.entryFile)}`);
|
|
311
|
+
|
|
312
|
+
return true;
|
|
313
|
+
}
|
|
314
|
+
} catch (error) {
|
|
315
|
+
console.log(`${colors.yellow}⚠️ Auto-installation needs help with your setup${colors.reset}`);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
wrapNextJs(content) {
|
|
322
|
+
const isAppDir = this.entryFile.includes('app/layout');
|
|
323
|
+
|
|
324
|
+
if (isAppDir) {
|
|
325
|
+
// Next.js 13+ App Directory
|
|
326
|
+
if (!content.includes('KeakProvider')) {
|
|
327
|
+
// Add import - include both KeakProvider and KeakToolbar
|
|
328
|
+
const importLine = "import { KeakProvider, KeakToolbar } from 'keak-sdk';\n";
|
|
329
|
+
let modified = content;
|
|
330
|
+
|
|
331
|
+
// Add import after existing imports
|
|
332
|
+
const lastImportIndex = modified.lastIndexOf('import ');
|
|
333
|
+
if (lastImportIndex !== -1) {
|
|
334
|
+
const endOfLine = modified.indexOf('\n', lastImportIndex);
|
|
335
|
+
modified = modified.slice(0, endOfLine + 1) + importLine + modified.slice(endOfLine + 1);
|
|
336
|
+
} else {
|
|
337
|
+
modified = importLine + modified;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Wrap children in body with KeakProvider and add KeakToolbar
|
|
341
|
+
const bodyMatch = modified.match(/(<body[^>]*>)([\s\S]*?)(<\/body>)/);
|
|
342
|
+
if (bodyMatch) {
|
|
343
|
+
const bodyContent = bodyMatch[2].trim();
|
|
344
|
+
const wrapped = `${bodyMatch[1]}
|
|
345
|
+
<KeakProvider config={{ apiKey: 'demo', debug: true }}>
|
|
346
|
+
${bodyContent}
|
|
347
|
+
<KeakToolbar />
|
|
348
|
+
</KeakProvider>
|
|
349
|
+
${bodyMatch[3]}`;
|
|
350
|
+
modified = modified.replace(bodyMatch[0], wrapped);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return modified;
|
|
354
|
+
}
|
|
355
|
+
} else {
|
|
356
|
+
// Next.js Pages Directory
|
|
357
|
+
if (!content.includes('KeakProvider')) {
|
|
358
|
+
// Add import - include both KeakProvider and KeakToolbar
|
|
359
|
+
const importLine = "import { KeakProvider, KeakToolbar } from 'keak-sdk';\n";
|
|
360
|
+
let modified = content;
|
|
361
|
+
|
|
362
|
+
const lastImportIndex = modified.lastIndexOf('import ');
|
|
363
|
+
if (lastImportIndex !== -1) {
|
|
364
|
+
const endOfLine = modified.indexOf('\n', lastImportIndex);
|
|
365
|
+
modified = modified.slice(0, endOfLine + 1) + importLine + modified.slice(endOfLine + 1);
|
|
366
|
+
} else {
|
|
367
|
+
modified = importLine + modified;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Wrap Component with KeakProvider and add KeakToolbar
|
|
371
|
+
const componentMatch = modified.match(/(return\s*\(?\s*)([\s\S]*?)(\s*\)?;?\s*})/);
|
|
372
|
+
if (componentMatch) {
|
|
373
|
+
const wrapped = `${componentMatch[1]}
|
|
374
|
+
<KeakProvider config={{ apiKey: 'demo', debug: true }}>
|
|
375
|
+
${componentMatch[2]}
|
|
376
|
+
<KeakToolbar />
|
|
377
|
+
</KeakProvider>
|
|
378
|
+
${componentMatch[3]}`;
|
|
379
|
+
modified = modified.replace(componentMatch[0], wrapped);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return modified;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return content;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
wrapCRA(content) {
|
|
390
|
+
let modified = content;
|
|
391
|
+
|
|
392
|
+
// Add import - include both KeakProvider and KeakToolbar
|
|
393
|
+
if (!content.includes('KeakProvider')) {
|
|
394
|
+
const importLine = "import { KeakProvider, KeakToolbar } from 'keak-sdk';\n";
|
|
395
|
+
|
|
396
|
+
const lastImportIndex = modified.lastIndexOf('import ');
|
|
397
|
+
if (lastImportIndex !== -1) {
|
|
398
|
+
const endOfLine = modified.indexOf('\n', lastImportIndex);
|
|
399
|
+
modified = modified.slice(0, endOfLine + 1) + importLine + modified.slice(endOfLine + 1);
|
|
400
|
+
} else {
|
|
401
|
+
modified = importLine + modified;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Find ReactDOM.render or createRoot
|
|
406
|
+
if (content.includes('ReactDOM.render')) {
|
|
407
|
+
// Old React 17 style
|
|
408
|
+
const renderMatch = modified.match(/(ReactDOM\.render\s*\(\s*)([\s\S]*?)(\s*,\s*document\.getElementById)/);
|
|
409
|
+
if (renderMatch) {
|
|
410
|
+
const wrapped = `${renderMatch[1]}
|
|
411
|
+
<KeakProvider config={{ apiKey: 'demo', debug: true }}>
|
|
412
|
+
${renderMatch[2]}
|
|
413
|
+
<KeakToolbar />
|
|
414
|
+
</KeakProvider>
|
|
415
|
+
${renderMatch[3]}`;
|
|
416
|
+
modified = modified.replace(renderMatch[0], wrapped);
|
|
417
|
+
}
|
|
418
|
+
} else if (content.includes('createRoot')) {
|
|
419
|
+
// React 18 style
|
|
420
|
+
const renderMatch = modified.match(/(root\.render\s*\(\s*)([\s\S]*?)(\s*\))/);
|
|
421
|
+
if (renderMatch) {
|
|
422
|
+
const wrapped = `${renderMatch[1]}
|
|
423
|
+
<KeakProvider config={{ apiKey: 'demo', debug: true }}>
|
|
424
|
+
${renderMatch[2]}
|
|
425
|
+
<KeakToolbar />
|
|
426
|
+
</KeakProvider>
|
|
427
|
+
${renderMatch[3]}`;
|
|
428
|
+
modified = modified.replace(renderMatch[0], wrapped);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
return modified;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
wrapGenericReact(content) {
|
|
436
|
+
// Generic React app wrapper
|
|
437
|
+
return this.wrapCRA(content);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
async aiAssistedInstall() {
|
|
441
|
+
console.log(`\n${colors.cyan}🤖 Using AI to help with installation...${colors.reset}\n`);
|
|
442
|
+
|
|
443
|
+
const content = fs.readFileSync(this.entryFile, 'utf-8');
|
|
444
|
+
const aiHelper = new AIHelper();
|
|
445
|
+
const localHelper = new LocalAIHelper();
|
|
446
|
+
|
|
447
|
+
try {
|
|
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
|
|
474
|
+
try {
|
|
475
|
+
const isTypeScript = this.entryFile.endsWith('.ts') || this.entryFile.endsWith('.tsx');
|
|
476
|
+
const modifiedContent = localHelper.addKeakProvider(content, isTypeScript);
|
|
477
|
+
|
|
478
|
+
if (modifiedContent && modifiedContent !== content) {
|
|
479
|
+
// Create backup
|
|
480
|
+
const backupPath = `${this.entryFile}.backup`;
|
|
481
|
+
fs.writeFileSync(backupPath, content);
|
|
482
|
+
console.log(`${colors.cyan}📋 Created backup: ${path.basename(backupPath)}${colors.reset}`);
|
|
483
|
+
|
|
484
|
+
// Write modified file
|
|
485
|
+
fs.writeFileSync(this.entryFile, modifiedContent);
|
|
486
|
+
console.log(`${colors.green}✓${colors.reset} Successfully updated: ${path.relative(this.projectRoot, this.entryFile)}`);
|
|
487
|
+
|
|
488
|
+
console.log(`\n${colors.green}${colors.bright}✨ Success! Keak has been installed.${colors.reset}`);
|
|
489
|
+
this.showPostInstallInstructions();
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
} catch (error) {
|
|
493
|
+
console.log(`${colors.yellow}Automated installation needs manual help.${colors.reset}`);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// If all else fails, show manual instructions
|
|
497
|
+
this.showManualInstructions();
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
showManualInstructions() {
|
|
501
|
+
const relativeEntry = this.entryFile
|
|
502
|
+
? path.relative(this.projectRoot, this.entryFile)
|
|
503
|
+
: 'your entry file (e.g., src/index.tsx or src/App.tsx)';
|
|
504
|
+
|
|
505
|
+
console.log(`
|
|
506
|
+
${colors.yellow}📝 Manual Installation Required${colors.reset}
|
|
507
|
+
|
|
508
|
+
Please add Keak to your app manually:
|
|
509
|
+
|
|
510
|
+
1. Open ${colors.cyan}${relativeEntry}${colors.reset}
|
|
511
|
+
|
|
512
|
+
2. Add this import at the top:
|
|
513
|
+
${colors.green}import { KeakProvider } from 'keak-sdk';${colors.reset}
|
|
514
|
+
|
|
515
|
+
3. Wrap your main component:
|
|
516
|
+
${colors.green}<KeakProvider config={{ apiKey: 'demo', debug: true }}><YourApp /></KeakProvider>${colors.reset}
|
|
517
|
+
|
|
518
|
+
Example for ${this.framework}:
|
|
519
|
+
${this.getFrameworkExample()}
|
|
520
|
+
`);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
getFrameworkExample() {
|
|
524
|
+
const examples = {
|
|
525
|
+
'nextjs': `
|
|
526
|
+
// app/layout.tsx
|
|
527
|
+
import { KeakProvider } from 'keak-sdk';
|
|
528
|
+
|
|
529
|
+
export default function RootLayout({ children }) {
|
|
530
|
+
return (
|
|
531
|
+
<html><body>
|
|
532
|
+
<KeakProvider config={{ apiKey: 'demo', debug: true }}>
|
|
533
|
+
{children}
|
|
534
|
+
</KeakProvider>
|
|
535
|
+
</body></html>);
|
|
536
|
+
}`,
|
|
537
|
+
'create-react-app': `
|
|
538
|
+
// src/index.js
|
|
539
|
+
import { KeakProvider } from 'keak-sdk';
|
|
540
|
+
|
|
541
|
+
root.render(
|
|
542
|
+
<KeakProvider config={{ apiKey: 'demo', debug: true }}>
|
|
543
|
+
<App />
|
|
544
|
+
</KeakProvider>
|
|
545
|
+
);`,
|
|
546
|
+
'vite-react': `
|
|
547
|
+
// src/main.tsx
|
|
548
|
+
import { KeakProvider } from 'keak-sdk';
|
|
549
|
+
|
|
550
|
+
ReactDOM.createRoot(document.getElementById('root')).render(
|
|
551
|
+
<KeakProvider config={{ apiKey: 'demo', debug: true }}>
|
|
552
|
+
<App />
|
|
553
|
+
</KeakProvider>
|
|
554
|
+
);`
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
return examples[this.framework] || examples['create-react-app'];
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
showPostInstallInstructions() {
|
|
561
|
+
console.log(`
|
|
562
|
+
${colors.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${colors.reset}
|
|
563
|
+
|
|
564
|
+
${colors.green}🎉 Keak is ready to use!${colors.reset}
|
|
565
|
+
|
|
566
|
+
Start your development server and look for the ${colors.bright}Keak toolbar${colors.reset}
|
|
567
|
+
in the ${colors.bright}bottom-right corner${colors.reset} of your app.
|
|
568
|
+
|
|
569
|
+
${colors.blue}Quick Tips:${colors.reset}
|
|
570
|
+
• Click the toolbar to expand it
|
|
571
|
+
• Use "Select" to pick elements for testing
|
|
572
|
+
• Try the AI prompts for optimization ideas
|
|
573
|
+
• Press ${colors.bright}Cmd+Shift+K${colors.reset} to toggle the toolbar
|
|
574
|
+
|
|
575
|
+
${colors.blue}🔧 Important for Next.js users:${colors.reset}
|
|
576
|
+
• Keak requires Babel for source mapping
|
|
577
|
+
• We've disabled Turbopack in your dev script
|
|
578
|
+
• Use ${colors.bright}npm run dev:turbo${colors.reset} if you need Turbopack
|
|
579
|
+
• ${colors.yellow}Restart your dev server${colors.reset} for changes to take effect
|
|
580
|
+
|
|
581
|
+
${colors.blue}🎯 Want conversion tracking?${colors.reset}
|
|
582
|
+
Run: ${colors.bright}npx keak-setup conversions${colors.reset}
|
|
583
|
+
|
|
584
|
+
${colors.yellow}Need help?${colors.reset} Visit https://docs.keak.dev
|
|
585
|
+
|
|
586
|
+
${colors.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${colors.reset}
|
|
587
|
+
`);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
showUsageInstructions() {
|
|
591
|
+
console.log(`
|
|
592
|
+
The Keak toolbar should appear in your app when running in development mode.
|
|
593
|
+
|
|
594
|
+
If you don't see it, make sure:
|
|
595
|
+
1. Your app is running in development mode
|
|
596
|
+
2. You've restarted your dev server after installation
|
|
597
|
+
|
|
598
|
+
Press ${colors.bright}Cmd+Shift+K${colors.reset} in your app to toggle the toolbar.
|
|
599
|
+
`);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// Run the installer
|
|
604
|
+
const installer = new KeakInstaller();
|
|
605
|
+
|
|
606
|
+
// Gracefully handle errors during postinstall (don't fail the install)
|
|
607
|
+
installer.run().catch((error) => {
|
|
608
|
+
// Only show error in verbose mode or when run explicitly (not postinstall)
|
|
609
|
+
const isPostInstall = process.env.npm_lifecycle_event === 'postinstall';
|
|
610
|
+
|
|
611
|
+
if (!isPostInstall) {
|
|
612
|
+
console.error(`${colors.red}Installation failed: ${error.message}${colors.reset}`);
|
|
613
|
+
process.exit(1);
|
|
614
|
+
} else {
|
|
615
|
+
// Silent fail for postinstall - don't block package installation
|
|
616
|
+
console.log(`${colors.yellow}ℹ️ Keak auto-setup skipped. Run 'npx keak-setup' manually when ready.${colors.reset}`);
|
|
617
|
+
}
|
|
618
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Main entry point for keak-setup commands
|
|
4
|
+
const args = process.argv.slice(2);
|
|
5
|
+
const command = args[0];
|
|
6
|
+
|
|
7
|
+
if (!command) {
|
|
8
|
+
console.log(`
|
|
9
|
+
🚀 Keak Setup Commands
|
|
10
|
+
|
|
11
|
+
Usage: npx keak-setup <command>
|
|
12
|
+
|
|
13
|
+
Commands:
|
|
14
|
+
install Set up Keak SDK in your React app
|
|
15
|
+
conversions Set up automatic conversion tracking
|
|
16
|
+
revert-conversions Undo conversion tracking changes
|
|
17
|
+
|
|
18
|
+
Examples:
|
|
19
|
+
npx keak-setup install
|
|
20
|
+
npx keak-setup conversions
|
|
21
|
+
npx keak-setup revert-conversions
|
|
22
|
+
|
|
23
|
+
For more help: https://docs.keak.dev
|
|
24
|
+
`);
|
|
25
|
+
process.exit(0);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Route to appropriate script
|
|
29
|
+
(async () => {
|
|
30
|
+
if (command === 'install') {
|
|
31
|
+
await import('./install.js');
|
|
32
|
+
} else if (command === 'conversions') {
|
|
33
|
+
// Run conversions setup directly
|
|
34
|
+
await import('./install.js');
|
|
35
|
+
} else if (command === 'revert-conversions') {
|
|
36
|
+
// Run revert command
|
|
37
|
+
await import('./revert-conversions.js');
|
|
38
|
+
} else {
|
|
39
|
+
console.error(`Unknown command: ${command}`);
|
|
40
|
+
console.log('Available commands: install, conversions, revert-conversions');
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
})();
|