figrecipe 0.6.0__py3-none-any.whl → 0.9.0__py3-none-any.whl

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 (269) hide show
  1. figrecipe/__init__.py +161 -1030
  2. figrecipe/__main__.py +12 -0
  3. figrecipe/_api/__init__.py +48 -0
  4. figrecipe/_api/_extract.py +108 -0
  5. figrecipe/_api/_notebook.py +61 -0
  6. figrecipe/_api/_panel.py +113 -0
  7. figrecipe/_api/_save.py +287 -0
  8. figrecipe/_api/_seaborn_proxy.py +34 -0
  9. figrecipe/_api/_style_manager.py +153 -0
  10. figrecipe/_api/_subplots.py +333 -0
  11. figrecipe/_api/_validate.py +82 -0
  12. figrecipe/_cli/__init__.py +7 -0
  13. figrecipe/_cli/_compose.py +87 -0
  14. figrecipe/_cli/_convert.py +117 -0
  15. figrecipe/_cli/_crop.py +82 -0
  16. figrecipe/_cli/_edit.py +70 -0
  17. figrecipe/_cli/_extract.py +128 -0
  18. figrecipe/_cli/_fonts.py +47 -0
  19. figrecipe/_cli/_info.py +67 -0
  20. figrecipe/_cli/_main.py +58 -0
  21. figrecipe/_cli/_reproduce.py +79 -0
  22. figrecipe/_cli/_style.py +77 -0
  23. figrecipe/_cli/_validate.py +66 -0
  24. figrecipe/_cli/_version.py +50 -0
  25. figrecipe/_composition/__init__.py +32 -0
  26. figrecipe/_composition/_alignment.py +452 -0
  27. figrecipe/_composition/_compose.py +179 -0
  28. figrecipe/_composition/_import_axes.py +127 -0
  29. figrecipe/_composition/_visibility.py +125 -0
  30. figrecipe/_dev/__init__.py +4 -93
  31. figrecipe/_dev/_plotters.py +76 -0
  32. figrecipe/_dev/_run_demos.py +56 -0
  33. figrecipe/_dev/browser/__init__.py +69 -0
  34. figrecipe/_dev/browser/_audio.py +240 -0
  35. figrecipe/_dev/browser/_caption.py +356 -0
  36. figrecipe/_dev/browser/_click_effect.py +146 -0
  37. figrecipe/_dev/browser/_cursor.py +196 -0
  38. figrecipe/_dev/browser/_highlight.py +105 -0
  39. figrecipe/_dev/browser/_narration.py +237 -0
  40. figrecipe/_dev/browser/_recorder.py +446 -0
  41. figrecipe/_dev/browser/_utils.py +178 -0
  42. figrecipe/_dev/browser/_video_trim/__init__.py +152 -0
  43. figrecipe/_dev/browser/_video_trim/_detection.py +223 -0
  44. figrecipe/_dev/browser/_video_trim/_markers.py +140 -0
  45. figrecipe/_dev/demo_plotters/__init__.py +35 -166
  46. figrecipe/_dev/demo_plotters/_categories.py +81 -0
  47. figrecipe/_dev/demo_plotters/_figure_creators.py +119 -0
  48. figrecipe/_dev/demo_plotters/_helpers.py +31 -0
  49. figrecipe/_dev/demo_plotters/_registry.py +50 -0
  50. figrecipe/_dev/demo_plotters/bar_categorical/__init__.py +4 -0
  51. figrecipe/_dev/demo_plotters/contour_surface/__init__.py +4 -0
  52. figrecipe/_dev/demo_plotters/distribution/__init__.py +4 -0
  53. figrecipe/_dev/demo_plotters/image_matrix/__init__.py +4 -0
  54. figrecipe/_dev/demo_plotters/line_curve/__init__.py +4 -0
  55. figrecipe/_dev/demo_plotters/{plot_plot.py → line_curve/plot_plot.py} +3 -2
  56. figrecipe/_dev/demo_plotters/scatter_points/__init__.py +4 -0
  57. figrecipe/_dev/demo_plotters/special/__init__.py +4 -0
  58. figrecipe/_dev/demo_plotters/{plot_pie.py → special/plot_pie.py} +5 -1
  59. figrecipe/_dev/demo_plotters/spectral_signal/__init__.py +4 -0
  60. figrecipe/_dev/demo_plotters/vector_flow/__init__.py +4 -0
  61. figrecipe/_editor/__init__.py +61 -13
  62. figrecipe/_editor/_bbox/__init__.py +43 -0
  63. figrecipe/_editor/_bbox/_collections.py +177 -0
  64. figrecipe/_editor/_bbox/_elements.py +159 -0
  65. figrecipe/_editor/_bbox/_extract.py +402 -0
  66. figrecipe/_editor/_bbox/_extract_axes.py +370 -0
  67. figrecipe/_editor/_bbox/_extract_text.py +466 -0
  68. figrecipe/_editor/_bbox/_lines.py +173 -0
  69. figrecipe/_editor/_bbox/_transforms.py +146 -0
  70. figrecipe/_editor/_call_overrides.py +183 -0
  71. figrecipe/_editor/_datatable_plot_handlers.py +249 -0
  72. figrecipe/_editor/_figure_layout.py +211 -0
  73. figrecipe/_editor/_flask_app.py +200 -1030
  74. figrecipe/_editor/_helpers.py +251 -0
  75. figrecipe/_editor/_hitmap/__init__.py +76 -0
  76. figrecipe/_editor/_hitmap/_artists/__init__.py +21 -0
  77. figrecipe/_editor/_hitmap/_artists/_collections.py +345 -0
  78. figrecipe/_editor/_hitmap/_artists/_images.py +68 -0
  79. figrecipe/_editor/_hitmap/_artists/_lines.py +107 -0
  80. figrecipe/_editor/_hitmap/_artists/_patches.py +163 -0
  81. figrecipe/_editor/_hitmap/_artists/_text.py +190 -0
  82. figrecipe/_editor/_hitmap/_colors.py +181 -0
  83. figrecipe/_editor/_hitmap/_detect.py +194 -0
  84. figrecipe/_editor/_hitmap/_restore.py +154 -0
  85. figrecipe/_editor/_hitmap_main.py +182 -0
  86. figrecipe/_editor/_overrides.py +4 -1
  87. figrecipe/_editor/_plot_types_registry.py +190 -0
  88. figrecipe/_editor/_preferences.py +135 -0
  89. figrecipe/_editor/_render_overrides.py +507 -0
  90. figrecipe/_editor/_renderer.py +81 -186
  91. figrecipe/_editor/_routes_annotation.py +114 -0
  92. figrecipe/_editor/_routes_axis.py +482 -0
  93. figrecipe/_editor/_routes_captions.py +130 -0
  94. figrecipe/_editor/_routes_composition.py +270 -0
  95. figrecipe/_editor/_routes_core.py +126 -0
  96. figrecipe/_editor/_routes_datatable.py +364 -0
  97. figrecipe/_editor/_routes_element.py +335 -0
  98. figrecipe/_editor/_routes_files.py +443 -0
  99. figrecipe/_editor/_routes_image.py +200 -0
  100. figrecipe/_editor/_routes_snapshot.py +94 -0
  101. figrecipe/_editor/_routes_style.py +243 -0
  102. figrecipe/_editor/_templates/__init__.py +116 -1
  103. figrecipe/_editor/_templates/_html.py +154 -64
  104. figrecipe/_editor/_templates/_html_components/__init__.py +13 -0
  105. figrecipe/_editor/_templates/_html_components/_composition_toolbar.py +79 -0
  106. figrecipe/_editor/_templates/_html_components/_file_browser.py +41 -0
  107. figrecipe/_editor/_templates/_html_datatable.py +92 -0
  108. figrecipe/_editor/_templates/_scripts/__init__.py +178 -0
  109. figrecipe/_editor/_templates/_scripts/_accordion.py +328 -0
  110. figrecipe/_editor/_templates/_scripts/_annotation_drag.py +504 -0
  111. figrecipe/_editor/_templates/_scripts/_api.py +228 -0
  112. figrecipe/_editor/_templates/_scripts/_canvas_context_menu.py +182 -0
  113. figrecipe/_editor/_templates/_scripts/_captions.py +231 -0
  114. figrecipe/_editor/_templates/_scripts/_colors.py +485 -0
  115. figrecipe/_editor/_templates/_scripts/_composition.py +283 -0
  116. figrecipe/_editor/_templates/_scripts/_core.py +493 -0
  117. figrecipe/_editor/_templates/_scripts/_datatable/__init__.py +59 -0
  118. figrecipe/_editor/_templates/_scripts/_datatable/_cell_edit.py +97 -0
  119. figrecipe/_editor/_templates/_scripts/_datatable/_clipboard.py +164 -0
  120. figrecipe/_editor/_templates/_scripts/_datatable/_context_menu.py +221 -0
  121. figrecipe/_editor/_templates/_scripts/_datatable/_core.py +150 -0
  122. figrecipe/_editor/_templates/_scripts/_datatable/_editable.py +511 -0
  123. figrecipe/_editor/_templates/_scripts/_datatable/_import.py +161 -0
  124. figrecipe/_editor/_templates/_scripts/_datatable/_plot.py +261 -0
  125. figrecipe/_editor/_templates/_scripts/_datatable/_selection.py +438 -0
  126. figrecipe/_editor/_templates/_scripts/_datatable/_table.py +256 -0
  127. figrecipe/_editor/_templates/_scripts/_datatable/_tabs.py +354 -0
  128. figrecipe/_editor/_templates/_scripts/_debug_snapshot.py +186 -0
  129. figrecipe/_editor/_templates/_scripts/_element_editor.py +325 -0
  130. figrecipe/_editor/_templates/_scripts/_files.py +429 -0
  131. figrecipe/_editor/_templates/_scripts/_files_context_menu.py +240 -0
  132. figrecipe/_editor/_templates/_scripts/_hitmap.py +512 -0
  133. figrecipe/_editor/_templates/_scripts/_image_drop.py +428 -0
  134. figrecipe/_editor/_templates/_scripts/_inspector.py +315 -0
  135. figrecipe/_editor/_templates/_scripts/_labels.py +464 -0
  136. figrecipe/_editor/_templates/_scripts/_legend_drag.py +270 -0
  137. figrecipe/_editor/_templates/_scripts/_modals.py +226 -0
  138. figrecipe/_editor/_templates/_scripts/_multi_select.py +198 -0
  139. figrecipe/_editor/_templates/_scripts/_overlays.py +292 -0
  140. figrecipe/_editor/_templates/_scripts/_panel_drag.py +505 -0
  141. figrecipe/_editor/_templates/_scripts/_panel_drag_snapshot.py +33 -0
  142. figrecipe/_editor/_templates/_scripts/_panel_position.py +463 -0
  143. figrecipe/_editor/_templates/_scripts/_panel_resize.py +230 -0
  144. figrecipe/_editor/_templates/_scripts/_panel_snap.py +307 -0
  145. figrecipe/_editor/_templates/_scripts/_region_select.py +255 -0
  146. figrecipe/_editor/_templates/_scripts/_selection.py +244 -0
  147. figrecipe/_editor/_templates/_scripts/_sync.py +242 -0
  148. figrecipe/_editor/_templates/_scripts/_tabs.py +89 -0
  149. figrecipe/_editor/_templates/_scripts/_undo_redo.py +348 -0
  150. figrecipe/_editor/_templates/_scripts/_view_mode.py +107 -0
  151. figrecipe/_editor/_templates/_scripts/_zoom.py +212 -0
  152. figrecipe/_editor/_templates/_styles/__init__.py +78 -0
  153. figrecipe/_editor/_templates/_styles/_base.py +111 -0
  154. figrecipe/_editor/_templates/_styles/_buttons.py +327 -0
  155. figrecipe/_editor/_templates/_styles/_color_input.py +123 -0
  156. figrecipe/_editor/_templates/_styles/_composition.py +87 -0
  157. figrecipe/_editor/_templates/_styles/_controls.py +430 -0
  158. figrecipe/_editor/_templates/_styles/_datatable/__init__.py +40 -0
  159. figrecipe/_editor/_templates/_styles/_datatable/_editable.py +203 -0
  160. figrecipe/_editor/_templates/_styles/_datatable/_panel.py +268 -0
  161. figrecipe/_editor/_templates/_styles/_datatable/_table.py +479 -0
  162. figrecipe/_editor/_templates/_styles/_datatable/_toolbar.py +384 -0
  163. figrecipe/_editor/_templates/_styles/_datatable/_vars.py +123 -0
  164. figrecipe/_editor/_templates/_styles/_dynamic_props.py +144 -0
  165. figrecipe/_editor/_templates/_styles/_file_browser.py +466 -0
  166. figrecipe/_editor/_templates/_styles/_forms.py +224 -0
  167. figrecipe/_editor/_templates/_styles/_hitmap.py +191 -0
  168. figrecipe/_editor/_templates/_styles/_inspector.py +90 -0
  169. figrecipe/_editor/_templates/_styles/_labels.py +118 -0
  170. figrecipe/_editor/_templates/_styles/_modals.py +127 -0
  171. figrecipe/_editor/_templates/_styles/_overlays.py +130 -0
  172. figrecipe/_editor/_templates/_styles/_preview.py +430 -0
  173. figrecipe/_editor/_templates/_styles/_selection.py +73 -0
  174. figrecipe/_editor/_templates/_styles/_spinner.py +117 -0
  175. figrecipe/_editor/static/audio/click.mp3 +0 -0
  176. figrecipe/_editor/static/click.mp3 +0 -0
  177. figrecipe/_editor/static/icons/favicon.ico +0 -0
  178. figrecipe/_integrations/__init__.py +17 -0
  179. figrecipe/_integrations/_scitex_stats.py +298 -0
  180. figrecipe/_params/_DECORATION_METHODS.py +8 -0
  181. figrecipe/_recorder.py +63 -109
  182. figrecipe/_recorder_utils.py +124 -0
  183. figrecipe/_reproducer/__init__.py +18 -0
  184. figrecipe/_reproducer/_core.py +509 -0
  185. figrecipe/_reproducer/_custom_plots.py +279 -0
  186. figrecipe/_reproducer/_seaborn.py +100 -0
  187. figrecipe/_reproducer/_violin.py +186 -0
  188. figrecipe/_signatures/_kwargs.py +273 -0
  189. figrecipe/_signatures/_loader.py +21 -423
  190. figrecipe/_signatures/_parsing.py +147 -0
  191. figrecipe/_utils/__init__.py +3 -0
  192. figrecipe/_utils/_bundle.py +205 -0
  193. figrecipe/_wrappers/_axes.py +252 -895
  194. figrecipe/_wrappers/_axes_helpers.py +136 -0
  195. figrecipe/_wrappers/_axes_plots.py +418 -0
  196. figrecipe/_wrappers/_axes_seaborn.py +157 -0
  197. figrecipe/_wrappers/_caption_generator.py +218 -0
  198. figrecipe/_wrappers/_figure.py +188 -1
  199. figrecipe/_wrappers/_panel_labels.py +127 -0
  200. figrecipe/_wrappers/_plot_helpers.py +143 -0
  201. figrecipe/_wrappers/_stat_annotation.py +274 -0
  202. figrecipe/_wrappers/_violin_helpers.py +180 -0
  203. figrecipe/styles/__init__.py +8 -6
  204. figrecipe/styles/_dotdict.py +72 -0
  205. figrecipe/styles/_finalize.py +134 -0
  206. figrecipe/styles/_fonts.py +77 -0
  207. figrecipe/styles/_kwargs_converter.py +178 -0
  208. figrecipe/styles/_plot_styles.py +209 -0
  209. figrecipe/styles/_style_applier.py +42 -480
  210. figrecipe/styles/_style_loader.py +16 -192
  211. figrecipe/styles/_themes.py +151 -0
  212. figrecipe/styles/presets/MATPLOTLIB.yaml +2 -1
  213. figrecipe/styles/presets/SCITEX.yaml +40 -28
  214. figrecipe-0.9.0.dist-info/METADATA +427 -0
  215. figrecipe-0.9.0.dist-info/RECORD +277 -0
  216. figrecipe-0.9.0.dist-info/entry_points.txt +2 -0
  217. figrecipe/_editor/_bbox.py +0 -978
  218. figrecipe/_editor/_hitmap.py +0 -937
  219. figrecipe/_editor/_templates/_scripts.py +0 -2778
  220. figrecipe/_editor/_templates/_styles.py +0 -1326
  221. figrecipe/_reproducer.py +0 -975
  222. figrecipe-0.6.0.dist-info/METADATA +0 -394
  223. figrecipe-0.6.0.dist-info/RECORD +0 -90
  224. /figrecipe/_dev/demo_plotters/{plot_bar.py → bar_categorical/plot_bar.py} +0 -0
  225. /figrecipe/_dev/demo_plotters/{plot_barh.py → bar_categorical/plot_barh.py} +0 -0
  226. /figrecipe/_dev/demo_plotters/{plot_contour.py → contour_surface/plot_contour.py} +0 -0
  227. /figrecipe/_dev/demo_plotters/{plot_contourf.py → contour_surface/plot_contourf.py} +0 -0
  228. /figrecipe/_dev/demo_plotters/{plot_tricontour.py → contour_surface/plot_tricontour.py} +0 -0
  229. /figrecipe/_dev/demo_plotters/{plot_tricontourf.py → contour_surface/plot_tricontourf.py} +0 -0
  230. /figrecipe/_dev/demo_plotters/{plot_tripcolor.py → contour_surface/plot_tripcolor.py} +0 -0
  231. /figrecipe/_dev/demo_plotters/{plot_triplot.py → contour_surface/plot_triplot.py} +0 -0
  232. /figrecipe/_dev/demo_plotters/{plot_boxplot.py → distribution/plot_boxplot.py} +0 -0
  233. /figrecipe/_dev/demo_plotters/{plot_ecdf.py → distribution/plot_ecdf.py} +0 -0
  234. /figrecipe/_dev/demo_plotters/{plot_hist.py → distribution/plot_hist.py} +0 -0
  235. /figrecipe/_dev/demo_plotters/{plot_hist2d.py → distribution/plot_hist2d.py} +0 -0
  236. /figrecipe/_dev/demo_plotters/{plot_violinplot.py → distribution/plot_violinplot.py} +0 -0
  237. /figrecipe/_dev/demo_plotters/{plot_hexbin.py → image_matrix/plot_hexbin.py} +0 -0
  238. /figrecipe/_dev/demo_plotters/{plot_imshow.py → image_matrix/plot_imshow.py} +0 -0
  239. /figrecipe/_dev/demo_plotters/{plot_matshow.py → image_matrix/plot_matshow.py} +0 -0
  240. /figrecipe/_dev/demo_plotters/{plot_pcolor.py → image_matrix/plot_pcolor.py} +0 -0
  241. /figrecipe/_dev/demo_plotters/{plot_pcolormesh.py → image_matrix/plot_pcolormesh.py} +0 -0
  242. /figrecipe/_dev/demo_plotters/{plot_spy.py → image_matrix/plot_spy.py} +0 -0
  243. /figrecipe/_dev/demo_plotters/{plot_errorbar.py → line_curve/plot_errorbar.py} +0 -0
  244. /figrecipe/_dev/demo_plotters/{plot_fill.py → line_curve/plot_fill.py} +0 -0
  245. /figrecipe/_dev/demo_plotters/{plot_fill_between.py → line_curve/plot_fill_between.py} +0 -0
  246. /figrecipe/_dev/demo_plotters/{plot_fill_betweenx.py → line_curve/plot_fill_betweenx.py} +0 -0
  247. /figrecipe/_dev/demo_plotters/{plot_stackplot.py → line_curve/plot_stackplot.py} +0 -0
  248. /figrecipe/_dev/demo_plotters/{plot_stairs.py → line_curve/plot_stairs.py} +0 -0
  249. /figrecipe/_dev/demo_plotters/{plot_step.py → line_curve/plot_step.py} +0 -0
  250. /figrecipe/_dev/demo_plotters/{plot_scatter.py → scatter_points/plot_scatter.py} +0 -0
  251. /figrecipe/_dev/demo_plotters/{plot_eventplot.py → special/plot_eventplot.py} +0 -0
  252. /figrecipe/_dev/demo_plotters/{plot_loglog.py → special/plot_loglog.py} +0 -0
  253. /figrecipe/_dev/demo_plotters/{plot_semilogx.py → special/plot_semilogx.py} +0 -0
  254. /figrecipe/_dev/demo_plotters/{plot_semilogy.py → special/plot_semilogy.py} +0 -0
  255. /figrecipe/_dev/demo_plotters/{plot_stem.py → special/plot_stem.py} +0 -0
  256. /figrecipe/_dev/demo_plotters/{plot_acorr.py → spectral_signal/plot_acorr.py} +0 -0
  257. /figrecipe/_dev/demo_plotters/{plot_angle_spectrum.py → spectral_signal/plot_angle_spectrum.py} +0 -0
  258. /figrecipe/_dev/demo_plotters/{plot_cohere.py → spectral_signal/plot_cohere.py} +0 -0
  259. /figrecipe/_dev/demo_plotters/{plot_csd.py → spectral_signal/plot_csd.py} +0 -0
  260. /figrecipe/_dev/demo_plotters/{plot_magnitude_spectrum.py → spectral_signal/plot_magnitude_spectrum.py} +0 -0
  261. /figrecipe/_dev/demo_plotters/{plot_phase_spectrum.py → spectral_signal/plot_phase_spectrum.py} +0 -0
  262. /figrecipe/_dev/demo_plotters/{plot_psd.py → spectral_signal/plot_psd.py} +0 -0
  263. /figrecipe/_dev/demo_plotters/{plot_specgram.py → spectral_signal/plot_specgram.py} +0 -0
  264. /figrecipe/_dev/demo_plotters/{plot_xcorr.py → spectral_signal/plot_xcorr.py} +0 -0
  265. /figrecipe/_dev/demo_plotters/{plot_barbs.py → vector_flow/plot_barbs.py} +0 -0
  266. /figrecipe/_dev/demo_plotters/{plot_quiver.py → vector_flow/plot_quiver.py} +0 -0
  267. /figrecipe/_dev/demo_plotters/{plot_streamplot.py → vector_flow/plot_streamplot.py} +0 -0
  268. {figrecipe-0.6.0.dist-info → figrecipe-0.9.0.dist-info}/WHEEL +0 -0
  269. {figrecipe-0.6.0.dist-info → figrecipe-0.9.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,315 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Element inspector and debug capture JavaScript."""
4
+
5
+ SCRIPTS_INSPECTOR = """
6
+ // ==================== ELEMENT INSPECTOR ====================
7
+ // Visual debugging tool for DOM elements (Alt+I to toggle)
8
+ let elementInspectorActive = false;
9
+ let elementInspectorOverlay = null;
10
+ const inspectorColors = [
11
+ '#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7',
12
+ '#DDA0DD', '#98D8C8', '#F7DC6F', '#BB8FCE', '#85C1E9'
13
+ ];
14
+
15
+ function toggleElementInspector() {
16
+ if (elementInspectorActive) {
17
+ deactivateElementInspector();
18
+ } else {
19
+ activateElementInspector();
20
+ }
21
+ }
22
+
23
+ function activateElementInspector() {
24
+ console.log('[ElementInspector] Activating...');
25
+ elementInspectorActive = true;
26
+
27
+ // Create overlay container
28
+ elementInspectorOverlay = document.createElement('div');
29
+ elementInspectorOverlay.id = 'element-inspector-overlay';
30
+ elementInspectorOverlay.className = 'element-inspector-overlay';
31
+ document.body.appendChild(elementInspectorOverlay);
32
+
33
+ // Scan and visualize elements
34
+ scanInspectorElements();
35
+
36
+ console.log('[ElementInspector] Active - Press Alt+I to deactivate');
37
+ }
38
+
39
+ function deactivateElementInspector() {
40
+ console.log('[ElementInspector] Deactivating...');
41
+ elementInspectorActive = false;
42
+
43
+ if (elementInspectorOverlay) {
44
+ elementInspectorOverlay.remove();
45
+ elementInspectorOverlay = null;
46
+ }
47
+ }
48
+
49
+ function scanInspectorElements() {
50
+ if (!elementInspectorOverlay) return;
51
+
52
+ // Clear existing overlays
53
+ elementInspectorOverlay.innerHTML = '';
54
+
55
+ // Get all visible elements
56
+ const allElements = document.querySelectorAll('*');
57
+ let colorIndex = 0;
58
+
59
+ allElements.forEach(element => {
60
+ // Skip non-visible, overlay, and script/style elements
61
+ if (element.tagName === 'SCRIPT' || element.tagName === 'STYLE' ||
62
+ element.tagName === 'HEAD' || element.tagName === 'META' ||
63
+ element.tagName === 'LINK' || element.tagName === 'TITLE' ||
64
+ element.id === 'element-inspector-overlay' ||
65
+ element.closest('#element-inspector-overlay')) {
66
+ return;
67
+ }
68
+
69
+ const rect = element.getBoundingClientRect();
70
+
71
+ // Skip invisible or zero-size elements
72
+ if (rect.width < 5 || rect.height < 5 ||
73
+ rect.bottom < 0 || rect.right < 0 ||
74
+ rect.top > window.innerHeight || rect.left > window.innerWidth) {
75
+ return;
76
+ }
77
+
78
+ // Create element box
79
+ const box = document.createElement('div');
80
+ box.className = 'element-inspector-box';
81
+ const color = inspectorColors[colorIndex % inspectorColors.length];
82
+ box.style.cssText = `
83
+ left: ${rect.left + window.scrollX}px;
84
+ top: ${rect.top + window.scrollY}px;
85
+ width: ${rect.width}px;
86
+ height: ${rect.height}px;
87
+ border-color: ${color};
88
+ `;
89
+
90
+ // Create label
91
+ const label = document.createElement('div');
92
+ label.className = 'element-inspector-label';
93
+ label.style.backgroundColor = color;
94
+
95
+ // Build element identifier
96
+ let identifier = element.tagName.toLowerCase();
97
+ if (element.id) identifier += '#' + element.id;
98
+ if (element.className && typeof element.className === 'string') {
99
+ const classes = element.className.split(' ').filter(c => c && !c.startsWith('element-inspector')).slice(0, 2);
100
+ if (classes.length) identifier += '.' + classes.join('.');
101
+ }
102
+
103
+ label.textContent = identifier;
104
+ box.appendChild(label);
105
+
106
+ // Add click handler to show element info
107
+ box.addEventListener('click', (e) => {
108
+ e.stopPropagation();
109
+ showInspectorElementInfo(element, color);
110
+ });
111
+
112
+ // Right-click to copy element path
113
+ box.addEventListener('contextmenu', (e) => {
114
+ e.preventDefault();
115
+ e.stopPropagation();
116
+ copyInspectorElementPath(element);
117
+ });
118
+
119
+ elementInspectorOverlay.appendChild(box);
120
+ colorIndex++;
121
+ });
122
+
123
+ console.log('[ElementInspector] Scanned ' + colorIndex + ' elements');
124
+ }
125
+
126
+ function showInspectorElementInfo(element, color) {
127
+ // Log element info to console
128
+ console.group('%cElement Info', 'color: ' + color + '; font-weight: bold;');
129
+ console.log('Tag:', element.tagName);
130
+ console.log('ID:', element.id || '(none)');
131
+ console.log('Classes:', element.className || '(none)');
132
+ console.log('Size:', element.offsetWidth + 'x' + element.offsetHeight);
133
+ console.log('Position:', element.getBoundingClientRect());
134
+ console.log('Element:', element);
135
+ console.groupEnd();
136
+ }
137
+
138
+ function copyInspectorElementPath(element) {
139
+ // Build CSS selector path
140
+ const path = [];
141
+ let current = element;
142
+
143
+ while (current && current !== document.body) {
144
+ let selector = current.tagName.toLowerCase();
145
+ if (current.id) {
146
+ selector = '#' + current.id;
147
+ path.unshift(selector);
148
+ break;
149
+ } else if (current.className && typeof current.className === 'string') {
150
+ const classes = current.className.split(' ').filter(c => c).slice(0, 2);
151
+ if (classes.length) selector += '.' + classes.join('.');
152
+ }
153
+ path.unshift(selector);
154
+ current = current.parentElement;
155
+ }
156
+
157
+ const selectorPath = path.join(' > ');
158
+ navigator.clipboard.writeText(selectorPath).then(() => {
159
+ console.log('[ElementInspector] Copied selector:', selectorPath);
160
+ showToast('Copied: ' + selectorPath, 'success');
161
+ });
162
+ }
163
+
164
+ // Shutter effect for screenshot feedback
165
+ function showShutterEffect() {
166
+ const shutter = document.createElement('div');
167
+ shutter.style.cssText = `
168
+ position: fixed;
169
+ top: 0;
170
+ left: 0;
171
+ width: 100vw;
172
+ height: 100vh;
173
+ background: white;
174
+ opacity: 0.8;
175
+ z-index: 99999;
176
+ pointer-events: none;
177
+ animation: shutterFlash 0.3s ease-out forwards;
178
+ `;
179
+
180
+ // Add keyframes if not exists
181
+ if (!document.getElementById('shutter-keyframes')) {
182
+ const style = document.createElement('style');
183
+ style.id = 'shutter-keyframes';
184
+ style.textContent = `
185
+ @keyframes shutterFlash {
186
+ 0% { opacity: 0.8; }
187
+ 100% { opacity: 0; }
188
+ }
189
+ `;
190
+ document.head.appendChild(style);
191
+ }
192
+
193
+ document.body.appendChild(shutter);
194
+ setTimeout(() => shutter.remove(), 300);
195
+ }
196
+
197
+ // Capture debug info: full-page screenshot first, then console logs after delay
198
+ async function captureDebugInfo() {
199
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
200
+
201
+ // Show shutter effect
202
+ showShutterEffect();
203
+
204
+ // Step 1: Capture full page screenshot using html2canvas, fallback to figure only
205
+ try {
206
+ let blob = null;
207
+
208
+ // Try html2canvas for full page capture
209
+ if (typeof html2canvas !== 'undefined') {
210
+ try {
211
+ console.log('[DebugCapture] Attempting full-page capture with html2canvas...');
212
+ const canvas = await html2canvas(document.body, {
213
+ useCORS: true,
214
+ allowTaint: true,
215
+ logging: false,
216
+ backgroundColor: document.documentElement.getAttribute('data-theme') === 'dark' ? '#1e1e1e' : '#ffffff',
217
+ ignoreElements: (element) => {
218
+ // Skip script and noscript elements
219
+ return element.tagName === 'SCRIPT' || element.tagName === 'NOSCRIPT';
220
+ }
221
+ });
222
+ blob = await new Promise(resolve => canvas.toBlob(resolve, 'image/png'));
223
+ console.log('[DebugCapture] Full-page capture successful');
224
+ } catch (html2canvasErr) {
225
+ console.warn('[DebugCapture] html2canvas failed, falling back to figure only:', html2canvasErr.message);
226
+ }
227
+ }
228
+
229
+ // Fallback: copy just the figure image
230
+ if (!blob) {
231
+ const img = document.getElementById('preview-image');
232
+ if (img && img.src) {
233
+ const response = await fetch(img.src);
234
+ blob = await response.blob();
235
+ console.log('[DebugCapture] Fallback: copied figure image only');
236
+ }
237
+ }
238
+
239
+ if (blob) {
240
+ await navigator.clipboard.write([
241
+ new ClipboardItem({ [blob.type]: blob })
242
+ ]);
243
+ showToast('Screenshot copied! Paste now. Text in 2s...', 'success');
244
+ console.log('[DebugCapture] Image copied to clipboard');
245
+ } else {
246
+ showToast('No image captured, copying text only...', 'warning');
247
+ }
248
+ } catch (err) {
249
+ console.error('[DebugCapture] Image copy failed:', err);
250
+ showToast('Image copy failed, copying text...', 'warning');
251
+ }
252
+
253
+ // Step 2: Wait 2 seconds, then copy debug text
254
+ setTimeout(async () => {
255
+ let debugInfo = `=== Debug Capture: ${timestamp} ===\\n\\n`;
256
+
257
+ // Collect console logs
258
+ debugInfo += '=== Console Logs ===\\n';
259
+ if (window.consoleLogs && window.consoleLogs.length > 0) {
260
+ window.consoleLogs.forEach(log => {
261
+ debugInfo += `[${log.type}] ${log.message}\\n`;
262
+ });
263
+ } else {
264
+ debugInfo += '(No logs captured)\\n';
265
+ }
266
+
267
+ // Add current state info
268
+ debugInfo += '\\n=== Current State ===\\n';
269
+ debugInfo += `URL: ${window.location.href}\\n`;
270
+ debugInfo += `Selected Element: ${selectedElement ? selectedElement.key : 'None'}\\n`;
271
+ debugInfo += `Zoom Level: ${Math.round(zoomLevel * 100)}%\\n`;
272
+ debugInfo += `Theme: ${document.documentElement.getAttribute('data-theme') || 'light'}\\n`;
273
+
274
+ try {
275
+ await navigator.clipboard.writeText(debugInfo);
276
+ showToast('Debug text copied! Paste now.', 'success');
277
+ console.log('[DebugCapture] Text copied:', debugInfo);
278
+ } catch (err) {
279
+ console.error('[DebugCapture] Text copy failed:', err);
280
+ showToast('Text copy failed: ' + err.message, 'error');
281
+ }
282
+ }, 2000);
283
+ }
284
+
285
+ // Console log interceptor (captures logs for debug export)
286
+ window.consoleLogs = [];
287
+ const originalConsole = {
288
+ log: console.log,
289
+ warn: console.warn,
290
+ error: console.error
291
+ };
292
+
293
+ console.log = function(...args) {
294
+ window.consoleLogs.push({ type: 'LOG', message: args.join(' '), time: new Date() });
295
+ if (window.consoleLogs.length > 100) window.consoleLogs.shift();
296
+ originalConsole.log.apply(console, args);
297
+ };
298
+
299
+ console.warn = function(...args) {
300
+ window.consoleLogs.push({ type: 'WARN', message: args.join(' '), time: new Date() });
301
+ if (window.consoleLogs.length > 100) window.consoleLogs.shift();
302
+ originalConsole.warn.apply(console, args);
303
+ };
304
+
305
+ console.error = function(...args) {
306
+ window.consoleLogs.push({ type: 'ERROR', message: args.join(' '), time: new Date() });
307
+ if (window.consoleLogs.length > 100) window.consoleLogs.shift();
308
+ originalConsole.error.apply(console, args);
309
+ };
310
+
311
+ console.log('[ElementInspector] Loaded - Press Alt+I to toggle');
312
+ console.log('[DebugCapture] Loaded - Press Alt+Shift+I for screenshot + logs');
313
+ """
314
+
315
+ __all__ = ["SCRIPTS_INSPECTOR"]