@littlecarlito/blorktools 0.50.3 → 0.51.0

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.
Files changed (114) hide show
  1. package/bin/cli.js +69 -0
  2. package/package.json +13 -7
  3. package/src/asset_debugger/axis-indicator/axis-indicator.css +6 -0
  4. package/src/asset_debugger/axis-indicator/axis-indicator.html +20 -0
  5. package/src/asset_debugger/axis-indicator/axis-indicator.js +822 -0
  6. package/src/asset_debugger/debugger-scene/debugger-scene.css +142 -0
  7. package/src/asset_debugger/debugger-scene/debugger-scene.html +80 -0
  8. package/src/asset_debugger/debugger-scene/debugger-scene.js +791 -0
  9. package/src/asset_debugger/header/header.css +73 -0
  10. package/src/asset_debugger/header/header.html +24 -0
  11. package/src/asset_debugger/header/header.js +224 -0
  12. package/src/asset_debugger/index.html +76 -0
  13. package/src/asset_debugger/landing-page/landing-page.css +396 -0
  14. package/src/asset_debugger/landing-page/landing-page.html +81 -0
  15. package/src/asset_debugger/landing-page/landing-page.js +611 -0
  16. package/src/asset_debugger/loading-splash/loading-splash.css +195 -0
  17. package/src/asset_debugger/loading-splash/loading-splash.html +22 -0
  18. package/src/asset_debugger/loading-splash/loading-splash.js +59 -0
  19. package/src/asset_debugger/loading-splash/preview-loading-splash.js +66 -0
  20. package/src/asset_debugger/main.css +14 -0
  21. package/src/asset_debugger/modals/examples-modal/examples-modal.css +41 -0
  22. package/src/asset_debugger/modals/examples-modal/examples-modal.html +18 -0
  23. package/src/asset_debugger/modals/examples-modal/examples-modal.js +111 -0
  24. package/src/asset_debugger/modals/examples-modal/examples.js +125 -0
  25. package/src/asset_debugger/modals/html-editor-modal/html-editor-modal.css +452 -0
  26. package/src/asset_debugger/modals/html-editor-modal/html-editor-modal.html +87 -0
  27. package/src/asset_debugger/modals/html-editor-modal/html-editor-modal.js +675 -0
  28. package/src/asset_debugger/modals/mesh-info-modal/mesh-info-modal.css +219 -0
  29. package/src/asset_debugger/modals/mesh-info-modal/mesh-info-modal.html +20 -0
  30. package/src/asset_debugger/modals/mesh-info-modal/mesh-info-modal.js +548 -0
  31. package/src/asset_debugger/modals/settings-modal/settings-modal.css +103 -0
  32. package/src/asset_debugger/modals/settings-modal/settings-modal.html +158 -0
  33. package/src/asset_debugger/modals/settings-modal/settings-modal.js +475 -0
  34. package/src/asset_debugger/panels/asset-panel/asset-panel.css +263 -0
  35. package/src/asset_debugger/panels/asset-panel/asset-panel.html +123 -0
  36. package/src/asset_debugger/panels/asset-panel/asset-panel.js +136 -0
  37. package/src/asset_debugger/panels/asset-panel/atlas-heading/atlas-heading.css +94 -0
  38. package/src/asset_debugger/panels/asset-panel/atlas-heading/atlas-heading.js +312 -0
  39. package/src/asset_debugger/panels/asset-panel/mesh-heading/mesh-heading.css +129 -0
  40. package/src/asset_debugger/panels/asset-panel/mesh-heading/mesh-heading.js +486 -0
  41. package/src/asset_debugger/panels/asset-panel/rig-heading/rig-heading.css +545 -0
  42. package/src/asset_debugger/panels/asset-panel/rig-heading/rig-heading.js +538 -0
  43. package/src/asset_debugger/panels/asset-panel/uv-heading/uv-heading.css +70 -0
  44. package/src/asset_debugger/panels/asset-panel/uv-heading/uv-heading.js +586 -0
  45. package/src/asset_debugger/panels/world-panel/world-panel.css +364 -0
  46. package/src/asset_debugger/panels/world-panel/world-panel.html +173 -0
  47. package/src/asset_debugger/panels/world-panel/world-panel.js +1891 -0
  48. package/src/asset_debugger/router.js +190 -0
  49. package/src/asset_debugger/util/animation/playback/animation-playback-controller.js +150 -0
  50. package/src/asset_debugger/util/animation/playback/animation-preview-controller.js +316 -0
  51. package/src/asset_debugger/util/animation/playback/css3d-bounce-controller.js +400 -0
  52. package/src/asset_debugger/util/animation/playback/css3d-reversal-controller.js +821 -0
  53. package/src/asset_debugger/util/animation/render/css3d-prerender-controller.js +696 -0
  54. package/src/asset_debugger/util/animation/render/debug-texture-factory.js +0 -0
  55. package/src/asset_debugger/util/animation/render/iframe2texture-render-controller.js +199 -0
  56. package/src/asset_debugger/util/animation/render/image2texture-prerender-controller.js +461 -0
  57. package/src/asset_debugger/util/animation/render/pbr-material-factory.js +82 -0
  58. package/src/asset_debugger/util/common.css +280 -0
  59. package/src/asset_debugger/util/data/animation-classifier.js +323 -0
  60. package/src/asset_debugger/util/data/duplicate-handler.js +20 -0
  61. package/src/asset_debugger/util/data/glb-buffer-manager.js +407 -0
  62. package/src/asset_debugger/util/data/glb-classifier.js +290 -0
  63. package/src/asset_debugger/util/data/html-formatter.js +76 -0
  64. package/src/asset_debugger/util/data/html-linter.js +276 -0
  65. package/src/asset_debugger/util/data/localstorage-manager.js +265 -0
  66. package/src/asset_debugger/util/data/mesh-html-manager.js +295 -0
  67. package/src/asset_debugger/util/data/string-serder.js +303 -0
  68. package/src/asset_debugger/util/data/texture-classifier.js +663 -0
  69. package/src/asset_debugger/util/data/upload/background-file-handler.js +292 -0
  70. package/src/asset_debugger/util/data/upload/dropzone-preview-controller.js +396 -0
  71. package/src/asset_debugger/util/data/upload/file-upload-manager.js +495 -0
  72. package/src/asset_debugger/util/data/upload/glb-file-handler.js +36 -0
  73. package/src/asset_debugger/util/data/upload/glb-preview-controller.js +317 -0
  74. package/src/asset_debugger/util/data/upload/lighting-file-handler.js +194 -0
  75. package/src/asset_debugger/util/data/upload/model-file-manager.js +104 -0
  76. package/src/asset_debugger/util/data/upload/texture-file-handler.js +166 -0
  77. package/src/asset_debugger/util/data/upload/zip-handler.js +686 -0
  78. package/src/asset_debugger/util/loaders/html2canvas-loader.js +107 -0
  79. package/src/asset_debugger/util/rig/bone-kinematics.js +403 -0
  80. package/src/asset_debugger/util/rig/rig-constraint-manager.js +618 -0
  81. package/src/asset_debugger/util/rig/rig-controller.js +612 -0
  82. package/src/asset_debugger/util/rig/rig-factory.js +628 -0
  83. package/src/asset_debugger/util/rig/rig-handle-factory.js +46 -0
  84. package/src/asset_debugger/util/rig/rig-label-factory.js +441 -0
  85. package/src/asset_debugger/util/rig/rig-mouse-handler.js +377 -0
  86. package/src/asset_debugger/util/rig/rig-state-manager.js +175 -0
  87. package/src/asset_debugger/util/rig/rig-tooltip-manager.js +267 -0
  88. package/src/asset_debugger/util/rig/rig-ui-factory.js +700 -0
  89. package/src/asset_debugger/util/scene/background-manager.js +284 -0
  90. package/src/asset_debugger/util/scene/camera-controller.js +243 -0
  91. package/src/asset_debugger/util/scene/css3d-debug-controller.js +406 -0
  92. package/src/asset_debugger/util/scene/css3d-frame-factory.js +113 -0
  93. package/src/asset_debugger/util/scene/css3d-scene-manager.js +529 -0
  94. package/src/asset_debugger/util/scene/glb-controller.js +208 -0
  95. package/src/asset_debugger/util/scene/lighting-manager.js +690 -0
  96. package/src/asset_debugger/util/scene/threejs-model-manager.js +437 -0
  97. package/src/asset_debugger/util/scene/threejs-preview-manager.js +207 -0
  98. package/src/asset_debugger/util/scene/threejs-preview-setup.js +478 -0
  99. package/src/asset_debugger/util/scene/threejs-scene-controller.js +286 -0
  100. package/src/asset_debugger/util/scene/ui-manager.js +107 -0
  101. package/src/asset_debugger/util/state/animation-state.js +128 -0
  102. package/src/asset_debugger/util/state/css3d-state.js +83 -0
  103. package/src/asset_debugger/util/state/glb-preview-state.js +31 -0
  104. package/src/asset_debugger/util/state/log-util.js +197 -0
  105. package/src/asset_debugger/util/state/scene-state.js +452 -0
  106. package/src/asset_debugger/util/state/threejs-state.js +54 -0
  107. package/src/asset_debugger/util/workers/lighting-worker.js +61 -0
  108. package/src/asset_debugger/util/workers/model-worker.js +109 -0
  109. package/src/asset_debugger/util/workers/texture-worker.js +54 -0
  110. package/src/asset_debugger/util/workers/worker-manager.js +212 -0
  111. package/src/asset_debugger/widgets/mesh-info-widget.js +280 -0
  112. package/src/index.html +261 -0
  113. package/src/index.js +8 -0
  114. package/vite.config.js +66 -0
@@ -0,0 +1,400 @@
1
+ import { classifyAnimation, classifyTransition } from "../../data/animation-classifier";
2
+ import {
3
+ animationProperties,
4
+ animationStack,
5
+ batchTimeWindow,
6
+ currentAnimationBatch,
7
+ isCapturingAnimations,
8
+ lastAnimationTime,
9
+ lastBatchTime,
10
+ pushAnimationBatch,
11
+ resetAnimationState,
12
+ resetCurrentAniamtionBatch,
13
+ setLastAnimationTime,
14
+ setLastBatchTime
15
+ } from "../../state/css3d-state";
16
+
17
+ /**
18
+ * Set up bounce animation tracking for the iframe
19
+ * @param {HTMLIFrameElement} iframe - The iframe to track animations in
20
+ */
21
+ export function setupBounceAnimationTracking(iframe) {
22
+ if (!iframe) return;
23
+
24
+ try {
25
+ resetAnimationState();
26
+
27
+ // Check if iframe and its content document are available
28
+ if (!iframe.contentDocument) {
29
+ console.debug('Iframe content document not available, skipping animation tracking setup');
30
+ return;
31
+ }
32
+
33
+ console.debug('Setting up animation tracking for bounce mode');
34
+
35
+ // Clean up any existing event listeners to prevent duplicates
36
+ if (iframe._animationStartHandler) {
37
+ iframe.contentDocument.removeEventListener('animationstart', iframe._animationStartHandler);
38
+ }
39
+ if (iframe._transitionStartHandler) {
40
+ iframe.contentDocument.removeEventListener('transitionstart', iframe._transitionStartHandler);
41
+ }
42
+
43
+ // Track all animations that start
44
+ const animationStartHandler = (event) => {
45
+ // Skip if we're in reversal mode and not capturing
46
+ if (!isCapturingAnimations) return;
47
+
48
+ // Verify iframe is still valid
49
+ if (!iframe || !iframe.contentDocument || !iframe.contentDocument.contains(event.target)) {
50
+ return;
51
+ }
52
+
53
+ const now = Date.now();
54
+
55
+ // If there was a delay since the last animation, add a delay entry to the stack
56
+ const timeSinceLastAnimation = now - lastAnimationTime;
57
+
58
+ // Check if this animation should start a new batch
59
+ if (timeSinceLastAnimation > batchTimeWindow || currentAnimationBatch.length === 0) {
60
+ // If we have items in the current batch, push it to the stack
61
+ if (currentAnimationBatch.length > 0) {
62
+ animationStack.push({
63
+ type: 'batch',
64
+ animations: [...currentAnimationBatch],
65
+ time: lastBatchTime
66
+ });
67
+ console.debug(`Added batch of ${currentAnimationBatch.length} animations to stack`);
68
+ resetCurrentAniamtionBatch();
69
+ }
70
+
71
+ // If there's a significant delay, add a delay marker
72
+ if (timeSinceLastAnimation > batchTimeWindow) {
73
+ animationStack.push({
74
+ type: 'delay',
75
+ duration: timeSinceLastAnimation,
76
+ time: now
77
+ });
78
+ console.debug(`Delay added to stack: ${timeSinceLastAnimation}ms`);
79
+ }
80
+
81
+ // Update batch time
82
+ setLastBatchTime(now);
83
+ }
84
+
85
+ // Generate a unique selector for this element
86
+ const uniqueSelector = generateUniqueSelector(event.target);
87
+
88
+ // Store animation properties
89
+ const animatedElement = event.target;
90
+ const animationName = event.animationName;
91
+ const computedStyle = window.getComputedStyle(animatedElement);
92
+
93
+ // Capture parent info for potential DOM recreation
94
+ let parentSelector = null;
95
+ if (animatedElement.parentElement) {
96
+ parentSelector = generateUniqueSelector(animatedElement.parentElement);
97
+ }
98
+
99
+ // Capture the animation properties from the computed style
100
+ const animDuration = parseFloat(computedStyle.animationDuration) || 0.3;
101
+ const animDelay = parseFloat(computedStyle.animationDelay) || 0;
102
+ const animTiming = computedStyle.animationTimingFunction || 'ease';
103
+ const animFillMode = computedStyle.animationFillMode || 'none';
104
+
105
+ // Get initial style snapshot before animation fully applies
106
+ const initialStyle = {
107
+ opacity: computedStyle.opacity,
108
+ transform: computedStyle.transform,
109
+ visibility: computedStyle.visibility,
110
+ display: computedStyle.display,
111
+ width: computedStyle.width,
112
+ height: computedStyle.height,
113
+ top: computedStyle.top,
114
+ left: computedStyle.left,
115
+ position: computedStyle.position,
116
+ parentSelector: parentSelector
117
+ };
118
+
119
+ // Capture final state after animation is complete
120
+ // We use a timeout to capture the state at the end of the animation
121
+ const durationMs = animDuration * 1000;
122
+ setTimeout(() => {
123
+ try {
124
+ // Verify iframe and element are still valid
125
+ if (animatedElement && iframe && iframe.contentDocument &&
126
+ iframe.contentDocument.contains(animatedElement)) {
127
+ const finalComputedStyle = window.getComputedStyle(animatedElement);
128
+ const finalStyle = {
129
+ opacity: finalComputedStyle.opacity,
130
+ transform: finalComputedStyle.transform,
131
+ visibility: finalComputedStyle.visibility,
132
+ display: finalComputedStyle.display,
133
+ width: finalComputedStyle.width,
134
+ height: finalComputedStyle.height,
135
+ top: finalComputedStyle.top,
136
+ left: finalComputedStyle.left,
137
+ position: finalComputedStyle.position
138
+ };
139
+
140
+ // Store the final state in our properties map
141
+ const propKey = `${uniqueSelector}:${animationName}`;
142
+ if (animationProperties[propKey]) {
143
+ animationProperties[propKey].finalStyle = finalStyle;
144
+ console.debug(`Captured final state for: ${propKey}`);
145
+ }
146
+ }
147
+ } catch (err) {
148
+ console.debug('Error capturing final animation state:', err);
149
+ }
150
+ }, durationMs + 50); // Add a small buffer to ensure animation is complete
151
+
152
+ // Get the animation properties for reverse playback
153
+ const properties = {
154
+ name: animationName,
155
+ selector: uniqueSelector,
156
+ elementTagName: animatedElement.tagName,
157
+ elementId: animatedElement.id,
158
+ elementClasses: animatedElement.className,
159
+ cssText: animatedElement.style.cssText,
160
+ parentSelector: parentSelector,
161
+ type: 'animation',
162
+ // Animation specifics
163
+ duration: animDuration,
164
+ delay: animDelay,
165
+ timingFunction: animTiming,
166
+ fillMode: animFillMode,
167
+ initialStyle: initialStyle,
168
+ // Record animation type
169
+ classified: classifyAnimation(animationName, initialStyle),
170
+ time: now
171
+ };
172
+
173
+ // Save animation properties
174
+ const key = `${uniqueSelector}:${animationName}`;
175
+ animationProperties[key] = properties;
176
+
177
+ // Add to current batch instead of directly to stack
178
+ pushAnimationBatch(properties);
179
+ console.debug(`Animation added to current batch: ${animationName}, batch size: ${currentAnimationBatch.length}`);
180
+
181
+ // Update time tracking
182
+ setLastAnimationTime(now);
183
+ };
184
+
185
+ // Also track transitions
186
+ const transitionStartHandler = (event) => {
187
+ // Skip if we're in reversal mode and not capturing
188
+ if (!isCapturingAnimations) return;
189
+
190
+ // Verify iframe is still valid
191
+ if (!iframe || !iframe.contentDocument || !iframe.contentDocument.contains(event.target)) {
192
+ return;
193
+ }
194
+
195
+ const now = Date.now();
196
+
197
+ // If there was a delay since the last animation, and we have a batch to save
198
+ const timeSinceLastAnimation = now - lastAnimationTime;
199
+
200
+ // Check if this transition should start a new batch
201
+ if (timeSinceLastAnimation > batchTimeWindow || currentAnimationBatch.length === 0) {
202
+ // If we have items in the current batch, push it to the stack
203
+ if (currentAnimationBatch.length > 0) {
204
+ animationStack.push({
205
+ type: 'batch',
206
+ animations: [...currentAnimationBatch],
207
+ time: lastBatchTime
208
+ });
209
+ console.debug(`Added batch of ${currentAnimationBatch.length} animations to stack`);
210
+ resetCurrentAniamtionBatch();
211
+ }
212
+
213
+ // If there's a significant delay, add a delay marker
214
+ if (timeSinceLastAnimation > batchTimeWindow) {
215
+ animationStack.push({
216
+ type: 'delay',
217
+ duration: timeSinceLastAnimation,
218
+ time: now
219
+ });
220
+ console.debug(`Delay added to stack: ${timeSinceLastAnimation}ms`);
221
+ }
222
+
223
+ // Update batch time
224
+ setLastBatchTime(now);
225
+ }
226
+
227
+ // Only track if it's a property we care about for visual animations
228
+ const relevantProperties = ['opacity', 'transform', 'left', 'top', 'right', 'bottom', 'height', 'width'];
229
+ if (!relevantProperties.includes(event.propertyName)) return;
230
+
231
+ // Generate a unique selector for this element
232
+ const uniqueSelector = generateUniqueSelector(event.target);
233
+
234
+ // Store transition properties
235
+ const transitionElement = event.target;
236
+ const propertyName = event.propertyName;
237
+ const computedStyle = window.getComputedStyle(transitionElement);
238
+
239
+ // Capture parent info for potential DOM recreation
240
+ let parentSelector = null;
241
+ if (transitionElement.parentElement) {
242
+ parentSelector = generateUniqueSelector(transitionElement.parentElement);
243
+ }
244
+
245
+ // Capture full transition properties
246
+ const transDuration = parseFloat(computedStyle.transitionDuration) || 0.3;
247
+ const transDelay = parseFloat(computedStyle.transitionDelay) || 0;
248
+ const transTiming = computedStyle.transitionTimingFunction || 'ease';
249
+
250
+ // Get initial snapshot before transition fully applies
251
+ const initialStyle = {};
252
+ relevantProperties.forEach(prop => {
253
+ initialStyle[prop] = computedStyle[prop];
254
+ });
255
+
256
+ // Add parent selector to initial style
257
+ initialStyle.parentSelector = parentSelector;
258
+
259
+ // Remember initial value for this property
260
+ const initialValue = computedStyle[propertyName];
261
+ transitionElement.setAttribute(`data-initial-${propertyName}`, initialValue);
262
+
263
+ // Capture final state after transition is complete
264
+ const durationMs = transDuration * 1000;
265
+ setTimeout(() => {
266
+ try {
267
+ // Verify iframe and element are still valid
268
+ if (transitionElement && iframe && iframe.contentDocument &&
269
+ iframe.contentDocument.contains(transitionElement)) {
270
+ const finalComputedStyle = window.getComputedStyle(transitionElement);
271
+ const finalValue = finalComputedStyle[propertyName];
272
+
273
+ // Store the final value
274
+ const propKey = `${uniqueSelector}:${propertyName}`;
275
+ if (animationProperties[propKey]) {
276
+ animationProperties[propKey].finalValue = finalValue;
277
+ console.debug(`Captured final value for: ${propKey} = ${finalValue}`);
278
+ }
279
+ }
280
+ } catch (err) {
281
+ console.debug('Error capturing final transition state:', err);
282
+ }
283
+ }, durationMs + 50);
284
+
285
+ // Create transition info object
286
+ const properties = {
287
+ name: `transition-${propertyName}`,
288
+ selector: uniqueSelector,
289
+ elementTagName: transitionElement.tagName,
290
+ elementId: transitionElement.id,
291
+ elementClasses: transitionElement.className,
292
+ property: propertyName,
293
+ cssText: transitionElement.style.cssText,
294
+ parentSelector: parentSelector,
295
+ type: 'transition',
296
+ // Transition specifics
297
+ duration: transDuration,
298
+ delay: transDelay,
299
+ timingFunction: transTiming,
300
+ initialStyle: initialStyle,
301
+ initialValue: initialValue,
302
+ // Classify what kind of transition this is
303
+ classified: classifyTransition(propertyName, initialValue, computedStyle[propertyName]),
304
+ time: now
305
+ };
306
+
307
+ // Save transition properties
308
+ const key = `${uniqueSelector}:${propertyName}`;
309
+ animationProperties[key] = properties;
310
+
311
+ // Add to current batch instead of directly to stack
312
+ pushAnimationBatch(properties);
313
+ console.debug(`Transition added to current batch: ${propertyName}, batch size: ${currentAnimationBatch.length}`);
314
+
315
+ // Update time tracking
316
+ setLastAnimationTime(now);
317
+ };
318
+
319
+ // Store handlers for cleanup
320
+ iframe._animationStartHandler = animationStartHandler;
321
+ iframe._transitionStartHandler = transitionStartHandler;
322
+
323
+ // Add event listeners
324
+ iframe.contentDocument.addEventListener('animationstart', animationStartHandler);
325
+ iframe.contentDocument.addEventListener('transitionstart', transitionStartHandler);
326
+
327
+ } catch (err) {
328
+ console.error('Error setting up animation tracking:', err);
329
+ }
330
+ }
331
+
332
+ /**
333
+ * Generate a unique CSS selector for an element that can be used to find it later
334
+ * @param {Element} element - The element to create a selector for
335
+ * @returns {string} A unique CSS selector
336
+ */
337
+ function generateUniqueSelector(element) {
338
+ if (!element) return '';
339
+
340
+ try {
341
+ // Start with the tag name
342
+ let selector = element.tagName.toLowerCase();
343
+
344
+ // Add ID if it exists (most specific)
345
+ if (element.id) {
346
+ return `${selector}#${element.id}`;
347
+ }
348
+
349
+ // Add classes
350
+ if (element.className && typeof element.className === 'string') {
351
+ const classes = element.className.trim().split(/\s+/);
352
+ if (classes.length > 0 && classes[0] !== '') {
353
+ selector += '.' + classes.join('.');
354
+ }
355
+ }
356
+
357
+ // Add position among siblings for more specificity
358
+ const parent = element.parentNode;
359
+ if (parent && parent.children.length > 1) {
360
+ const siblings = Array.from(parent.children);
361
+ const index = siblings.indexOf(element);
362
+
363
+ // Use nth-child for position
364
+ selector += `:nth-child(${index + 1})`;
365
+ }
366
+
367
+ // Add nearest parent with ID or distinctive class for context
368
+ let context = '';
369
+ let parentElement = element.parentNode;
370
+ let depth = 0;
371
+ const maxDepth = 3; // Limit how far up we go
372
+
373
+ while (parentElement && parentElement.tagName && depth < maxDepth) {
374
+ if (parentElement.id) {
375
+ context = `${parentElement.tagName.toLowerCase()}#${parentElement.id} > ${context}`;
376
+ break;
377
+ } else if (parentElement.className && typeof parentElement.className === 'string' && parentElement.className.trim()) {
378
+ const parentClass = parentElement.className.trim().split(/\s+/)[0];
379
+ context = `${parentElement.tagName.toLowerCase()}.${parentClass} > ${context}`;
380
+ break;
381
+ }
382
+
383
+ // If we couldn't find a good identifier, just add the tag
384
+ context = `${parentElement.tagName.toLowerCase()} > ${context}`;
385
+ parentElement = parentElement.parentNode;
386
+ depth++;
387
+ }
388
+
389
+ // Combine context with our selector if we have context
390
+ if (context) {
391
+ selector = context + selector;
392
+ }
393
+
394
+ return selector;
395
+ } catch (err) {
396
+ console.error('Error generating selector:', err);
397
+ // Fallback to simple tag selector
398
+ return element.tagName ? element.tagName.toLowerCase() : '*';
399
+ }
400
+ }