@keak/sdk 2.0.1 → 2.0.3

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