@keak/sdk 2.0.0 → 2.0.2

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.js CHANGED
@@ -33,301 +33,88 @@ var css_248z$1 = "@import \"./toolbar/styles/toolbar.css\";.keak-toolbar,.keak-t
33
33
  styleInject(css_248z$1);
34
34
 
35
35
  /**
36
- * Try to inject source locations using jsx-tool (primary method)
37
- * jsx-tool may inject source locations directly into elements or via WebSocket
36
+ * Find the internal React fiber node from a DOM element
38
37
  */
39
- function injectSourceLocationsViaJsxTool() {
40
- if (typeof window === 'undefined')
41
- return 0;
42
- let injectedCount = 0;
43
- // Check for jsx-tool injected attributes directly on elements
44
- // jsx-tool may inject data-jsx-source or similar attributes
45
- const allElements = document.querySelectorAll('*');
46
- // Check for jsx-tool WebSocket URL (primary indicator that jsx-tool is set up)
47
- // According to jsx-tool docs, this is set by the Vite plugin or manual script tag
48
- const jsxToolWsUrl = window.__JSX_TOOL_DEV_SERVER_WS_URL__;
49
- // Also check for other jsx-tool indicators
50
- const hasJsxToolGlobal = !!jsxToolWsUrl || !!window.__JSX_TOOL__ || !!window.__JSX_TOOL_SOURCE_MAP__ || !!window.__JSX_TOOL_WS__;
51
- if (jsxToolWsUrl) {
52
- console.log('[Keak] 🔍 jsx-tool WebSocket URL detected:', jsxToolWsUrl);
53
- }
54
- else if (hasJsxToolGlobal) {
55
- console.log('[Keak] 🔍 jsx-tool globals detected (but no WS URL):', {
56
- __JSX_TOOL__: !!window.__JSX_TOOL__,
57
- __JSX_TOOL_SOURCE_MAP__: !!window.__JSX_TOOL_SOURCE_MAP__,
58
- __JSX_TOOL_WS__: !!window.__JSX_TOOL_WS__
59
- });
38
+ function getFiberFromNode(node) {
39
+ try {
40
+ // React stores fiber on DOM nodes with keys starting with __react
41
+ const key = Object.keys(node).find(k => k.startsWith('__reactFiber') ||
42
+ k.startsWith('__reactInternalInstance'));
43
+ if (key) {
44
+ const fiber = node[key];
45
+ return fiber;
46
+ }
47
+ // Fallback: search through all properties for React-like objects
48
+ const allValues = Object.values(node);
49
+ const fiberCandidate = allValues.find((v) => v &&
50
+ typeof v === 'object' &&
51
+ v.hasOwnProperty('memoizedProps') &&
52
+ v.hasOwnProperty('return') &&
53
+ (v.hasOwnProperty('type') || v.hasOwnProperty('elementType')));
54
+ return fiberCandidate;
60
55
  }
61
- allElements.forEach((element) => {
62
- if (!(element instanceof HTMLElement))
63
- return;
64
- // Skip if already has our source location
65
- if (element.hasAttribute('data-keak-src'))
66
- return;
67
- // Skip Keak's own elements
68
- if (element.classList.contains('keak-toolbar') ||
69
- element.closest('.keak-toolbar') ||
70
- element.id === 'keak-toolbar-root') {
71
- return;
72
- }
73
- // Check for jsx-tool injected source attributes
74
- // jsx-tool may use various attribute names - check common patterns
75
- const jsxSource = element.getAttribute('data-jsx-source') ||
76
- element.getAttribute('data-jsx-tool-source') ||
77
- element.getAttribute('data-source') ||
78
- element.getAttribute('data-jsx-file') ||
79
- element.getAttribute('data-jsx-line');
80
- if (jsxSource) {
81
- // Copy jsx-tool source location to our attribute
82
- element.setAttribute('data-keak-src', jsxSource);
83
- injectedCount++;
84
- }
85
- else {
86
- // Also check for jsx-tool ID attributes that we can use to look up in source map
87
- const jsxToolId = element.getAttribute('data-jsx-tool-id') ||
88
- element.getAttribute('data-jsx-id') ||
89
- element.getAttribute('data-reactid');
90
- if (jsxToolId && window.__JSX_TOOL_SOURCE_MAP__) {
91
- const sourceMap = window.__JSX_TOOL_SOURCE_MAP__;
92
- if (sourceMap[jsxToolId]) {
93
- const mapping = sourceMap[jsxToolId];
94
- if (mapping.file && mapping.line) {
95
- const sourceLocation = `${mapping.file}:${mapping.line}:${mapping.column || 1}`;
96
- element.setAttribute('data-keak-src', sourceLocation);
97
- injectedCount++;
98
- }
99
- }
100
- }
101
- }
102
- });
103
- if (injectedCount > 0) {
104
- console.log(`[Keak] ✅ Injected ${injectedCount} source locations via jsx-tool (direct attributes)`);
105
- return injectedCount;
106
- }
107
- else if (hasJsxToolGlobal) {
108
- console.log('[Keak] 🔍 jsx-tool globals found but no source locations injected yet (may need to wait for jsx-tool to initialize)');
109
- }
110
- // Check for jsx-tool WebSocket connection or source map global
111
- const jsxTool = window.__JSX_TOOL__;
112
- const jsxToolWS = window.__JSX_TOOL_WS__;
113
- const jsxToolSourceMap = window.__JSX_TOOL_SOURCE_MAP__;
114
- // Debug logging for jsx-tool detection
115
- if (jsxTool || jsxToolWS || jsxToolSourceMap) {
116
- console.log('[Keak] 🔍 jsx-tool detected:', {
117
- __JSX_TOOL__: !!jsxTool,
118
- __JSX_TOOL_WS__: !!jsxToolWS,
119
- __JSX_TOOL_SOURCE_MAP__: !!jsxToolSourceMap,
120
- sourceMapKeys: jsxToolSourceMap ? Object.keys(jsxToolSourceMap).length : 0
121
- });
56
+ catch (error) {
57
+ console.warn('[Keak Source] Error accessing fiber node:', error);
58
+ return null;
122
59
  }
123
- // If jsx-tool source map is available globally, use it
124
- if (jsxToolSourceMap && typeof jsxToolSourceMap === 'object') {
125
- allElements.forEach((element) => {
126
- if (!(element instanceof HTMLElement))
127
- return;
128
- if (element.hasAttribute('data-keak-src'))
129
- return;
130
- if (element.classList.contains('keak-toolbar') ||
131
- element.closest('.keak-toolbar') ||
132
- element.id === 'keak-toolbar-root') {
133
- return;
134
- }
135
- // Try to find source mapping for this element
136
- const elementId = element.getAttribute('data-jsx-tool-id') ||
137
- element.getAttribute('data-reactid') ||
138
- element.id;
139
- if (elementId && jsxToolSourceMap[elementId]) {
140
- const mapping = jsxToolSourceMap[elementId];
141
- if (mapping.file && mapping.line) {
142
- const sourceLocation = `${mapping.file}:${mapping.line}:${mapping.column || 1}`;
143
- element.setAttribute('data-keak-src', sourceLocation);
144
- if (mapping.component) {
145
- element.setAttribute('data-keak-component', mapping.component);
146
- }
147
- injectedCount++;
148
- }
149
- }
150
- });
151
- if (injectedCount > 0) {
152
- console.log(`[Keak] ✅ Injected ${injectedCount} source locations via jsx-tool (global source map)`);
153
- return injectedCount;
154
- }
155
- }
156
- // Try to connect to jsx-tool WebSocket server (non-blocking)
157
- // Only try once per page load to avoid repeated connection attempts
158
- // Use the WebSocket URL from jsx-tool's configuration
159
- if (!jsxTool && !jsxToolWS && !window.__KEAK_JSX_TOOL_CONNECTION_ATTEMPTED__) {
160
- // Check for jsx-tool WebSocket URL (set by Vite plugin or manual script)
161
- const jsxToolWsUrl = window.__JSX_TOOL_DEV_SERVER_WS_URL__;
162
- // Also check for other indicators
163
- const hasJsxToolScript = Array.from(document.scripts).some(script => script.src.includes('jsx-tool') || script.textContent?.includes('jsx-tool') || script.textContent?.includes('__JSX_TOOL_DEV_SERVER_WS_URL__'));
164
- // Check for any elements with jsx-tool attributes (indicates jsx-tool is active)
165
- const hasJsxToolAttributes = document.querySelector('[data-jsx-source], [data-jsx-tool-source], [data-jsx-tool-id]') !== null;
166
- // Use jsx-tool's WebSocket URL if available, otherwise try default port
167
- let wsUrl = null;
168
- if (jsxToolWsUrl) {
169
- // Use the URL provided by jsx-tool
170
- wsUrl = jsxToolWsUrl;
171
- console.log('[Keak] 🔍 Using jsx-tool WebSocket URL:', wsUrl);
172
- }
173
- else if (hasJsxToolScript || hasJsxToolAttributes || window.__JSX_TOOL_WS_PORT__) {
174
- // Fallback to default port if indicators suggest jsx-tool might be running
175
- const wsPort = window.__JSX_TOOL_WS_PORT__ || 12021;
176
- const wsHost = window.__JSX_TOOL_WS_HOST__ || 'localhost';
177
- const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
178
- wsUrl = `${wsProtocol}//${wsHost}:${wsPort}`;
179
- console.log('[Keak] 🔍 jsx-tool indicators found, attempting WebSocket connection to:', wsUrl);
180
- }
181
- if (wsUrl) {
182
- window.__KEAK_JSX_TOOL_CONNECTION_ATTEMPTED__ = true;
183
- try {
184
- // Try to establish connection (non-blocking, async)
185
- // Use a very short timeout to fail fast if server isn't running
186
- const testWs = new WebSocket(wsUrl);
187
- // Set a short timeout to close connection if it doesn't establish quickly
188
- const connectionTimeout = setTimeout(() => {
189
- if (testWs.readyState === WebSocket.CONNECTING) {
190
- testWs.close();
191
- }
192
- }, 1000); // Reduced to 1 second to fail faster
193
- testWs.onopen = () => {
194
- clearTimeout(connectionTimeout);
195
- console.log('[Keak] ✅ Connected to jsx-tool WebSocket server');
196
- window.__JSX_TOOL_WS__ = testWs;
197
- // Request source mappings for all elements
198
- testWs.send(JSON.stringify({ type: 'getSourceMappings' }));
199
- };
200
- testWs.onmessage = (event) => {
201
- try {
202
- const data = JSON.parse(event.data);
203
- if (data.type === 'sourceMappings' && data.mappings) {
204
- let wsInjectedCount = 0;
205
- // Apply source mappings to elements
206
- Object.entries(data.mappings).forEach(([elementId, mapping]) => {
207
- const element = document.querySelector(`[data-jsx-tool-id="${elementId}"]`);
208
- if (element && mapping.file && mapping.line && !element.hasAttribute('data-keak-src')) {
209
- const sourceLocation = `${mapping.file}:${mapping.line}:${mapping.column || 1}`;
210
- element.setAttribute('data-keak-src', sourceLocation);
211
- if (mapping.component) {
212
- element.setAttribute('data-keak-component', mapping.component);
213
- }
214
- wsInjectedCount++;
215
- }
216
- });
217
- if (wsInjectedCount > 0) {
218
- console.log(`[Keak] ✅ Injected ${wsInjectedCount} source locations via jsx-tool WebSocket`);
219
- // Re-run injection to update counts
220
- setTimeout(() => injectSourceLocations(), 100);
221
- }
222
- }
223
- }
224
- catch (error) {
225
- console.warn('[Keak] Error parsing jsx-tool WebSocket message:', error);
226
- }
227
- };
228
- testWs.onerror = () => {
229
- clearTimeout(connectionTimeout);
230
- // WebSocket connection failed - this is expected if jsx-tool isn't running
231
- // Silently fall back to React Fiber - no error logging needed
232
- };
233
- testWs.onclose = () => {
234
- clearTimeout(connectionTimeout);
235
- // Connection closed - expected if jsx-tool server isn't running
236
- };
237
- }
238
- catch (error) {
239
- // WebSocket not available - will fall back silently
240
- }
60
+ }
61
+ /**
62
+ * Extract source information from a React fiber node
63
+ */
64
+ function getSourceFromFiber(fiber) {
65
+ if (!fiber)
66
+ return null;
67
+ let pointer = fiber;
68
+ let steps = 0;
69
+ const maxSteps = 20; // Prevent infinite loops
70
+ while (pointer && steps < maxSteps) {
71
+ // Check _debugSource (primary source of truth)
72
+ if (pointer._debugSource) {
73
+ const src = pointer._debugSource;
74
+ return {
75
+ fileName: src.fileName,
76
+ lineNumber: src.lineNumber,
77
+ columnNumber: src.columnNumber
78
+ };
241
79
  }
242
- else {
243
- // No indicators that jsx-tool is set up - skip connection attempt
244
- window.__KEAK_JSX_TOOL_CONNECTION_ATTEMPTED__ = true;
80
+ // Check owner fiber if available
81
+ if (pointer._owner && pointer._owner._debugSource) {
82
+ const src = pointer._owner._debugSource;
83
+ return {
84
+ fileName: src.fileName,
85
+ lineNumber: src.lineNumber,
86
+ columnNumber: src.columnNumber
87
+ };
245
88
  }
89
+ // Check props.__source as fallback (Babel plugin output)
90
+ const props = pointer.memoizedProps || pointer.pendingProps;
91
+ if (props && props.__source) {
92
+ const src = props.__source;
93
+ return {
94
+ fileName: src.fileName,
95
+ lineNumber: src.lineNumber,
96
+ columnNumber: src.columnNumber
97
+ };
98
+ }
99
+ // Try both return and _owner for traversal
100
+ pointer = pointer.return || pointer._owner;
101
+ steps++;
246
102
  }
247
- return injectedCount;
103
+ return null;
248
104
  }
105
+ /**
106
+ * Inject source attributes into all DOM elements using React Fiber data
107
+ */
249
108
  function injectSourceLocations() {
250
- if (typeof window === 'undefined')
251
- return;
252
- // Step 1: Try jsx-tool first (primary method)
253
- const jsxToolCount = injectSourceLocationsViaJsxTool();
254
- if (jsxToolCount > 0) {
255
- console.log(`[Keak] ✅ Used jsx-tool for source mapping (${jsxToolCount} elements)`);
256
- return;
257
- }
258
- // Step 1.5: If jsx-tool is detected but no injections yet, schedule a retry
259
- // Check for the WebSocket URL (primary indicator) or other globals
260
- const jsxToolWsUrl = window.__JSX_TOOL_DEV_SERVER_WS_URL__;
261
- const hasJsxToolGlobals = !!jsxToolWsUrl || !!window.__JSX_TOOL__ || !!window.__JSX_TOOL_SOURCE_MAP__ || !!window.__JSX_TOOL_WS__;
262
- const retryCount = (window.__KEAK_JSX_TOOL_RETRY_COUNT__ || 0);
263
- if (hasJsxToolGlobals && retryCount < 3) {
264
- window.__KEAK_JSX_TOOL_RETRY_COUNT__ = retryCount + 1;
265
- console.log(`[Keak] 🔄 jsx-tool detected (WS URL: ${jsxToolWsUrl || 'not set'}) but not ready yet, will retry in 1s (attempt ${retryCount + 1}/3)...`);
266
- setTimeout(() => {
267
- injectSourceLocations();
268
- }, 1000);
269
- return; // Don't fall back yet, wait for retry
270
- }
271
- // Step 2: Fall back to React Fiber
272
- // Only log if we haven't already tried and failed multiple times
273
- const attemptCount = (window.__KEAK_INJECTION_ATTEMPTS__ || 0) + 1;
274
- window.__KEAK_INJECTION_ATTEMPTS__ = attemptCount;
275
- if (attemptCount <= 3) {
276
- // Only log on first few attempts to avoid console spam
277
- if (attemptCount === 1) {
278
- if (!hasJsxToolGlobals) {
279
- console.log('[Keak] jsx-tool not detected, trying React Fiber fallback...');
280
- console.log('[Keak] 💡 To use jsx-tool for better source mapping:');
281
- console.log('[Keak] 1. Install: npm install @jsx-tool/jsx-tool');
282
- console.log('[Keak] 2. For Vite: Add jsxToolDevServer() to vite.config.ts plugins');
283
- console.log('[Keak] 3. For non-Vite: Add script tag with window.__JSX_TOOL_DEV_SERVER_WS_URL__');
284
- console.log('[Keak] 4. Restart your dev server');
285
- }
286
- else {
287
- console.log('[Keak] jsx-tool detected but no source locations found, falling back to React Fiber...');
288
- console.log('[Keak] 💡 Make sure jsx-tool dev server is running (check for WebSocket connection)');
289
- }
290
- }
291
- }
292
- // Check if React DevTools is available (development mode)
293
- const reactDevTools = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
294
- if (!reactDevTools) {
295
- console.warn('[Keak] React DevTools not detected. Source locations will not be available.');
296
- console.warn('[Keak] Make sure you are running in development mode.');
297
- return;
298
- }
299
- // Find React Fiber roots
300
- const reactRoots = [];
301
- if (reactDevTools.renderers) {
302
- reactDevTools.renderers.forEach((renderer) => {
303
- try {
304
- // Get the fiber root from the renderer
305
- const fiberRoot = renderer.findFiberByHostInstance || renderer.getFiberRoots;
306
- if (fiberRoot) {
307
- reactRoots.push(renderer);
308
- }
309
- }
310
- catch (error) {
311
- console.warn('[Keak] Error accessing renderer:', error);
312
- }
313
- });
314
- }
315
- if (reactRoots.length === 0) {
316
- // Logging disabled - using Babel plugin for source injection instead
317
- // console.warn('[Keak] No React roots found. Trying alternative method...');
318
- injectSourceLocationsViaFiber();
319
- return;
320
- }
321
- // Logging disabled - using Babel plugin for source injection instead
322
- // console.log(`[Keak] Found ${reactRoots.length} React renderer(s)`);
323
- // Process all DOM elements
109
+ if (typeof window === 'undefined' || typeof document === 'undefined')
110
+ return 0;
324
111
  let injectedCount = 0;
325
112
  const allElements = document.querySelectorAll('*');
326
113
  allElements.forEach((element) => {
327
114
  if (!(element instanceof HTMLElement))
328
115
  return;
329
- // Skip if already has source location
330
- if (element.hasAttribute('data-keak-src'))
116
+ // Skip if already has source attributes
117
+ if (element.hasAttribute('data-source-file'))
331
118
  return;
332
119
  // Skip Keak's own elements
333
120
  if (element.classList.contains('keak-toolbar') ||
@@ -335,171 +122,68 @@ function injectSourceLocations() {
335
122
  element.id === 'keak-toolbar-root') {
336
123
  return;
337
124
  }
338
- // Try to find React Fiber for this element
339
- const fiber = findFiberForElement(element);
340
- if (fiber && fiber._debugSource) {
341
- const { fileName, lineNumber, columnNumber } = fiber._debugSource;
342
- // Clean up the file name (remove webpack prefixes, make relative)
343
- let cleanFileName = fileName;
344
- // Remove webpack internal paths
345
- cleanFileName = cleanFileName.replace(/^webpack:\/\/\/_next\//, '');
346
- cleanFileName = cleanFileName.replace(/^webpack:\/\/\/\.\//, '');
347
- cleanFileName = cleanFileName.replace(/^\.\/_next\//, '');
348
- // Remove node_modules
349
- if (cleanFileName.includes('node_modules')) {
350
- return; // Skip node_modules files
351
- }
352
- // Make path relative to project root
353
- cleanFileName = cleanFileName.replace(/^.*\/(app|src|pages|components)\//, '$1/');
354
- const sourceLocation = `${cleanFileName}:${lineNumber}:${columnNumber || 1}`;
355
- element.setAttribute('data-keak-src', sourceLocation);
356
- // Add component name if available
357
- if (fiber.type && fiber.type.name) {
358
- element.setAttribute('data-keak-component', fiber.type.name);
359
- }
125
+ // Get React fiber for this element
126
+ const fiber = getFiberFromNode(element);
127
+ const source = getSourceFromFiber(fiber);
128
+ if (source) {
129
+ // Clean the file path for better display
130
+ let cleanPath = source.fileName;
131
+ // Extract from src/ onwards
132
+ if (cleanPath.includes('/src/')) {
133
+ const parts = cleanPath.split('/src/');
134
+ cleanPath = 'src/' + parts[parts.length - 1];
135
+ }
136
+ else if (cleanPath.includes('/app/')) {
137
+ const parts = cleanPath.split('/app/');
138
+ cleanPath = 'app/' + parts[parts.length - 1];
139
+ }
140
+ else if (cleanPath.includes('/components/')) {
141
+ const parts = cleanPath.split('/components/');
142
+ cleanPath = 'components/' + parts[parts.length - 1];
143
+ }
144
+ // Inject source attributes (same format as react-source-lens)
145
+ element.setAttribute('data-source-file', cleanPath);
146
+ element.setAttribute('data-source-line', String(source.lineNumber));
147
+ // Also create combined data-keak-src for backward compatibility
148
+ element.setAttribute('data-keak-src', `${cleanPath}:${source.lineNumber}:${source.columnNumber || 1}`);
360
149
  injectedCount++;
361
150
  }
362
151
  });
363
- console.log(`[Keak] Injected source locations into ${injectedCount} elements (React Fiber fallback)`);
364
- if (injectedCount === 0) {
365
- console.warn('[Keak] No source locations injected. Make sure you are running in development mode with source maps enabled.');
366
- }
367
- }
368
- // Find React Fiber node for a DOM element
369
- function findFiberForElement(element) {
370
- // Look for React Fiber keys on the element
371
- const allKeys = Object.keys(element);
372
- const fiberKey = allKeys.find(key => key.startsWith('__reactFiber') ||
373
- key.startsWith('__reactInternalInstance') ||
374
- key.startsWith('__reactProps'));
375
- if (fiberKey) {
376
- const fiber = element[fiberKey];
377
- // Debug disabled - using Babel plugin instead of runtime injection
378
- // if (!element.hasAttribute('data-keak-debug-logged')) {
379
- // element.setAttribute('data-keak-debug-logged', 'true');
380
- // console.log('[Keak Debug] Sample Fiber node:', {
381
- // hasFiber: !!fiber,
382
- // hasDebugSource: !!fiber?._debugSource,
383
- // debugSource: fiber?._debugSource,
384
- // type: fiber?.type,
385
- // elementType: fiber?.elementType,
386
- // keys: allKeys.filter(k => k.startsWith('__react'))
387
- // });
388
- // }
389
- return fiber;
152
+ if (injectedCount > 0) {
153
+ console.log(`[Keak Source] ✅ Injected ${injectedCount} source locations from React Fiber`);
390
154
  }
391
- return null;
392
- }
393
- // Alternative method: Walk the Fiber tree and inject source locations
394
- function injectSourceLocationsViaFiber() {
395
- if (typeof window === 'undefined')
396
- return;
397
- // Find all elements with React Fiber data
398
- const allElements = document.querySelectorAll('*');
399
- allElements.forEach((element) => {
400
- if (!(element instanceof HTMLElement))
401
- return;
402
- // Skip if already has source location
403
- if (element.hasAttribute('data-keak-src')) {
404
- return;
405
- }
406
- // Skip Keak's own elements
407
- if (element.classList.contains('keak-toolbar') ||
408
- element.closest('.keak-toolbar') ||
409
- element.id === 'keak-toolbar-root' ||
410
- element.id?.startsWith('keak-')) {
411
- return;
412
- }
413
- // Find React Fiber
414
- const fiber = findFiberForElement(element);
415
- if (!fiber) {
416
- return;
417
- }
418
- if (!fiber._debugSource) {
419
- return;
420
- }
421
- const { fileName, lineNumber, columnNumber } = fiber._debugSource;
422
- // Skip node_modules BEFORE cleaning
423
- if (fileName.includes('node_modules')) {
424
- return;
425
- }
426
- // Clean file name
427
- let cleanFileName = fileName;
428
- // Remove webpack prefixes
429
- cleanFileName = cleanFileName.replace(/^webpack:\/\/\/_next\//, '');
430
- cleanFileName = cleanFileName.replace(/^webpack:\/\/\/\.\//, '');
431
- cleanFileName = cleanFileName.replace(/^\.\/_next\//, '');
432
- cleanFileName = cleanFileName.replace(/^webpack-internal:\/\/\//, '');
433
- cleanFileName = cleanFileName.replace(/^\(app-pages-browser\)\//, '');
434
- // Make relative to project root - find common patterns
435
- const patterns = [
436
- /^.*?\/(app|src|pages|components|lib|utils)\//,
437
- /^.*\/\.next\/server\/(app|src|pages)\//,
438
- ];
439
- for (const pattern of patterns) {
440
- if (pattern.test(cleanFileName)) {
441
- cleanFileName = cleanFileName.replace(pattern, '$1/');
442
- break;
443
- }
444
- }
445
- const sourceLocation = `${cleanFileName}:${lineNumber}:${columnNumber || 1}`;
446
- element.setAttribute('data-keak-src', sourceLocation);
447
- // Add component name
448
- if (fiber.type) {
449
- const componentName = fiber.type.name || fiber.type.displayName;
450
- if (componentName && typeof componentName === 'string') {
451
- element.setAttribute('data-keak-component', componentName);
452
- }
453
- }
454
- });
455
- // Logging disabled - using Babel plugin for source injection instead
456
- // console.log(`[Keak] Source injection summary:`);
457
- // console.log(` ✅ Injected: ${injectedCount} elements`);
458
- // console.log(` ⏭️ Skipped (no fiber): ${skippedNoFiber}`);
459
- // console.log(` ⏭️ Skipped (no source): ${skippedNoSource}`);
460
- // console.log(` ⏭️ Skipped (node_modules): ${skippedNodeModules}`);
461
- // // If no elements injected, log a warning with tips
462
- // if (injectedCount === 0) {
463
- // console.warn('[Keak] ⚠️ No source locations injected!');
464
- // console.warn('[Keak] Make sure:');
465
- // console.warn('[Keak] 1. You are running in development mode (npm run dev)');
466
- // console.warn('[Keak] 2. React DevTools is available');
467
- // console.warn('[Keak] 3. Next.js is generating source maps (default in dev)');
468
- // }
155
+ return injectedCount;
469
156
  }
470
- // Auto-inject source locations when page loads
157
+ /**
158
+ * Initialize source injection on DOM ready and watch for changes
159
+ */
471
160
  if (typeof window !== 'undefined') {
472
- // Wait longer for React to fully render (especially Next.js App Router)
473
- const runInjection = () => {
474
- // Try multiple times with delays
475
- setTimeout(() => injectSourceLocations(), 1000);
476
- setTimeout(() => injectSourceLocations(), 2000);
477
- setTimeout(() => injectSourceLocations(), 3000);
478
- // Also watch for DOM changes
479
- const observer = new MutationObserver(() => {
480
- // Debounced injection on DOM changes
481
- clearTimeout(window.__keakInjectionTimeout);
482
- window.__keakInjectionTimeout = setTimeout(() => {
483
- injectSourceLocations();
484
- }, 500);
485
- });
486
- observer.observe(document.body, {
487
- childList: true,
488
- subtree: true
489
- });
490
- };
161
+ // Wait for DOM to be ready
491
162
  if (document.readyState === 'loading') {
492
- document.addEventListener('DOMContentLoaded', runInjection);
163
+ document.addEventListener('DOMContentLoaded', () => {
164
+ // Initial injection with delays to catch dynamically rendered content
165
+ setTimeout(() => injectSourceLocations(), 500);
166
+ setTimeout(() => injectSourceLocations(), 1500);
167
+ setTimeout(() => injectSourceLocations(), 3000);
168
+ });
493
169
  }
494
170
  else {
495
- runInjection();
171
+ // DOM already ready
172
+ setTimeout(() => injectSourceLocations(), 500);
173
+ setTimeout(() => injectSourceLocations(), 1500);
174
+ setTimeout(() => injectSourceLocations(), 3000);
496
175
  }
497
- // Re-inject on navigation (for SPAs)
498
- if (typeof window !== 'undefined' && 'navigation' in window) {
499
- window.navigation?.addEventListener('navigate', () => {
500
- setTimeout(() => injectSourceLocations(), 1000);
176
+ // Watch for DOM changes and re-inject
177
+ const observer = new MutationObserver(() => {
178
+ injectSourceLocations();
179
+ });
180
+ // Start observing after a brief delay
181
+ setTimeout(() => {
182
+ observer.observe(document.body, {
183
+ childList: true,
184
+ subtree: true
501
185
  });
502
- }
186
+ }, 1000);
503
187
  }
504
188
 
505
189
  /**
@@ -1828,14 +1512,33 @@ const KeakToolbar = ({ position = 'bottom-right', theme = 'auto' }) => {
1828
1512
  sourceMappingTelemetry.fiberSuccess++;
1829
1513
  }
1830
1514
  else {
1831
- // Fallback to data-keak-src attribute from Babel plugin
1832
- const keakSrc = element.getAttribute('data-keak-src');
1515
+ // Fallback to various data-* attributes from different source mapping tools
1516
+ // Priority: data-keak-src > react-source-lens > webpack loader
1517
+ let keakSrc = element.getAttribute('data-keak-src');
1833
1518
  const keakComponent = element.getAttribute('data-keak-component');
1834
1519
  const keakIdx = element.getAttribute('data-keak-idx');
1835
1520
  console.log('[Keak Toolbar] No Fiber source, checking data-keak-* attributes:');
1836
1521
  console.log('[Keak Toolbar] data-keak-src:', keakSrc);
1837
1522
  console.log('[Keak Toolbar] data-keak-component:', keakComponent);
1838
1523
  console.log('[Keak Toolbar] data-keak-idx:', keakIdx);
1524
+ // Fallback 1: Check for react-source-lens attributes if data-keak-src not found
1525
+ if (!keakSrc) {
1526
+ const sourceLensFile = element.getAttribute('data-source-file');
1527
+ const sourceLensLine = element.getAttribute('data-source-line');
1528
+ if (sourceLensFile && sourceLensLine) {
1529
+ keakSrc = `${sourceLensFile}:${sourceLensLine}:1`;
1530
+ console.log('[Keak Toolbar] react-source-lens detected:', keakSrc);
1531
+ }
1532
+ }
1533
+ // Fallback 2: Check for webpack loader attributes if still not found
1534
+ if (!keakSrc) {
1535
+ const keakFile = element.getAttribute('data-keak-file');
1536
+ const keakLine = element.getAttribute('data-keak-line');
1537
+ if (keakFile && keakLine) {
1538
+ keakSrc = `${keakFile}:${keakLine}:1`;
1539
+ console.log('[Keak Toolbar] webpack loader attributes detected:', keakSrc);
1540
+ }
1541
+ }
1839
1542
  if (keakSrc) {
1840
1543
  // Format is "file:line:column"
1841
1544
  const parts = keakSrc.split(':');
@@ -1844,7 +1547,7 @@ const KeakToolbar = ({ position = 'bottom-right', theme = 'auto' }) => {
1844
1547
  const columnNumber = parts[parts.length - 1];
1845
1548
  const parsedLineNumber = parseInt(lineNumber, 10);
1846
1549
  const parsedColumnNumber = parseInt(columnNumber, 10);
1847
- console.log('[Keak Toolbar] ✅ Using Babel plugin source:', { fileName, lineNumber: parsedLineNumber, columnNumber: parsedColumnNumber });
1550
+ console.log('[Keak Toolbar] ✅ Using build-time source attributes:', { fileName, lineNumber: parsedLineNumber, columnNumber: parsedColumnNumber });
1848
1551
  elementData.sourceMapping = {
1849
1552
  method: 'babel-plugin',
1850
1553
  fileName: fileName,
@@ -1858,7 +1561,7 @@ const KeakToolbar = ({ position = 'bottom-right', theme = 'auto' }) => {
1858
1561
  sourceMappingTelemetry.babelUsed++;
1859
1562
  }
1860
1563
  else {
1861
- console.log('[Keak Toolbar] ❌ No source mapping found - no data-keak-src attribute in DOM');
1564
+ console.log('[Keak Toolbar] ❌ No source mapping found - no data-keak-src, react-source-lens, or webpack loader attributes in DOM');
1862
1565
  // Track complete failure (Fiber failed, no Babel either)
1863
1566
  sourceMappingTelemetry.fiberFailed++;
1864
1567
  sourceMappingTelemetry.noMapping++;
@@ -3006,7 +2709,6 @@ class KeakSDK {
3006
2709
  }
3007
2710
  /**
3008
2711
  * Main Keak hook - initializes the SDK with minimal configuration
3009
- * Similar to react-source-lens's useReactSourceLens() hook
3010
2712
  *
3011
2713
  * @example
3012
2714
  * // Minimal usage (no config needed)