figrecipe 0.5.0__py3-none-any.whl → 0.6.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 (90) hide show
  1. figrecipe/__init__.py +361 -93
  2. figrecipe/_dev/__init__.py +120 -0
  3. figrecipe/_dev/demo_plotters/__init__.py +195 -0
  4. figrecipe/_dev/demo_plotters/plot_acorr.py +24 -0
  5. figrecipe/_dev/demo_plotters/plot_angle_spectrum.py +28 -0
  6. figrecipe/_dev/demo_plotters/plot_bar.py +25 -0
  7. figrecipe/_dev/demo_plotters/plot_barbs.py +30 -0
  8. figrecipe/_dev/demo_plotters/plot_barh.py +25 -0
  9. figrecipe/_dev/demo_plotters/plot_boxplot.py +24 -0
  10. figrecipe/_dev/demo_plotters/plot_cohere.py +29 -0
  11. figrecipe/_dev/demo_plotters/plot_contour.py +30 -0
  12. figrecipe/_dev/demo_plotters/plot_contourf.py +29 -0
  13. figrecipe/_dev/demo_plotters/plot_csd.py +29 -0
  14. figrecipe/_dev/demo_plotters/plot_ecdf.py +24 -0
  15. figrecipe/_dev/demo_plotters/plot_errorbar.py +28 -0
  16. figrecipe/_dev/demo_plotters/plot_eventplot.py +25 -0
  17. figrecipe/_dev/demo_plotters/plot_fill.py +29 -0
  18. figrecipe/_dev/demo_plotters/plot_fill_between.py +30 -0
  19. figrecipe/_dev/demo_plotters/plot_fill_betweenx.py +28 -0
  20. figrecipe/_dev/demo_plotters/plot_hexbin.py +25 -0
  21. figrecipe/_dev/demo_plotters/plot_hist.py +24 -0
  22. figrecipe/_dev/demo_plotters/plot_hist2d.py +25 -0
  23. figrecipe/_dev/demo_plotters/plot_imshow.py +23 -0
  24. figrecipe/_dev/demo_plotters/plot_loglog.py +27 -0
  25. figrecipe/_dev/demo_plotters/plot_magnitude_spectrum.py +28 -0
  26. figrecipe/_dev/demo_plotters/plot_matshow.py +23 -0
  27. figrecipe/_dev/demo_plotters/plot_pcolor.py +29 -0
  28. figrecipe/_dev/demo_plotters/plot_pcolormesh.py +29 -0
  29. figrecipe/_dev/demo_plotters/plot_phase_spectrum.py +28 -0
  30. figrecipe/_dev/demo_plotters/plot_pie.py +23 -0
  31. figrecipe/_dev/demo_plotters/plot_plot.py +27 -0
  32. figrecipe/_dev/demo_plotters/plot_psd.py +29 -0
  33. figrecipe/_dev/demo_plotters/plot_quiver.py +30 -0
  34. figrecipe/_dev/demo_plotters/plot_scatter.py +24 -0
  35. figrecipe/_dev/demo_plotters/plot_semilogx.py +27 -0
  36. figrecipe/_dev/demo_plotters/plot_semilogy.py +27 -0
  37. figrecipe/_dev/demo_plotters/plot_specgram.py +30 -0
  38. figrecipe/_dev/demo_plotters/plot_spy.py +29 -0
  39. figrecipe/_dev/demo_plotters/plot_stackplot.py +29 -0
  40. figrecipe/_dev/demo_plotters/plot_stairs.py +27 -0
  41. figrecipe/_dev/demo_plotters/plot_stem.py +27 -0
  42. figrecipe/_dev/demo_plotters/plot_step.py +27 -0
  43. figrecipe/_dev/demo_plotters/plot_streamplot.py +30 -0
  44. figrecipe/_dev/demo_plotters/plot_tricontour.py +28 -0
  45. figrecipe/_dev/demo_plotters/plot_tricontourf.py +28 -0
  46. figrecipe/_dev/demo_plotters/plot_tripcolor.py +29 -0
  47. figrecipe/_dev/demo_plotters/plot_triplot.py +25 -0
  48. figrecipe/_dev/demo_plotters/plot_violinplot.py +25 -0
  49. figrecipe/_dev/demo_plotters/plot_xcorr.py +25 -0
  50. figrecipe/_editor/__init__.py +230 -0
  51. figrecipe/_editor/_bbox.py +978 -0
  52. figrecipe/_editor/_flask_app.py +1229 -0
  53. figrecipe/_editor/_hitmap.py +937 -0
  54. figrecipe/_editor/_overrides.py +318 -0
  55. figrecipe/_editor/_renderer.py +349 -0
  56. figrecipe/_editor/_templates/__init__.py +75 -0
  57. figrecipe/_editor/_templates/_html.py +406 -0
  58. figrecipe/_editor/_templates/_scripts.py +2778 -0
  59. figrecipe/_editor/_templates/_styles.py +1326 -0
  60. figrecipe/_params/_DECORATION_METHODS.py +27 -0
  61. figrecipe/_params/_PLOTTING_METHODS.py +58 -0
  62. figrecipe/_params/__init__.py +9 -0
  63. figrecipe/_recorder.py +126 -73
  64. figrecipe/_reproducer.py +658 -41
  65. figrecipe/_seaborn.py +14 -9
  66. figrecipe/_serializer.py +2 -2
  67. figrecipe/_signatures/README.md +68 -0
  68. figrecipe/_signatures/__init__.py +12 -2
  69. figrecipe/_signatures/_loader.py +515 -56
  70. figrecipe/_utils/__init__.py +6 -4
  71. figrecipe/_utils/_crop.py +10 -4
  72. figrecipe/_utils/_image_diff.py +37 -33
  73. figrecipe/_utils/_numpy_io.py +0 -1
  74. figrecipe/_utils/_units.py +11 -3
  75. figrecipe/_validator.py +12 -3
  76. figrecipe/_wrappers/_axes.py +860 -46
  77. figrecipe/_wrappers/_figure.py +115 -18
  78. figrecipe/plt.py +0 -1
  79. figrecipe/pyplot.py +2 -1
  80. figrecipe/styles/__init__.py +9 -10
  81. figrecipe/styles/_style_applier.py +332 -28
  82. figrecipe/styles/_style_loader.py +172 -44
  83. figrecipe/styles/presets/MATPLOTLIB.yaml +94 -0
  84. figrecipe/styles/presets/SCITEX.yaml +176 -0
  85. figrecipe-0.6.0.dist-info/METADATA +394 -0
  86. figrecipe-0.6.0.dist-info/RECORD +90 -0
  87. figrecipe-0.5.0.dist-info/METADATA +0 -336
  88. figrecipe-0.5.0.dist-info/RECORD +0 -26
  89. {figrecipe-0.5.0.dist-info → figrecipe-0.6.0.dist-info}/WHEEL +0 -0
  90. {figrecipe-0.5.0.dist-info → figrecipe-0.6.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,1326 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ CSS styles for figure editor.
5
+ """
6
+
7
+ STYLES = """
8
+ /* CSS Variables for theming */
9
+ :root {
10
+ --bg-primary: #ffffff;
11
+ --bg-secondary: #f5f5f5;
12
+ --bg-tertiary: #e8e8e8;
13
+ --text-primary: #1a1a1a;
14
+ --text-secondary: #666666;
15
+ --border-color: #d0d0d0;
16
+ --accent-color: #2563eb;
17
+ --accent-hover: #1d4ed8;
18
+ --success-color: #10b981;
19
+ --error-color: #ef4444;
20
+ --selection-color: rgba(37, 99, 235, 0.3);
21
+ }
22
+
23
+ [data-theme="dark"] {
24
+ --bg-primary: #1a1a1a;
25
+ --bg-secondary: #252525;
26
+ --bg-tertiary: #333333;
27
+ --text-primary: #e8e8e8;
28
+ --text-secondary: #a0a0a0;
29
+ --border-color: #404040;
30
+ --accent-color: #3b82f6;
31
+ --accent-hover: #60a5fa;
32
+ --selection-color: rgba(59, 130, 246, 0.3);
33
+ }
34
+
35
+ /* Reset and base */
36
+ * {
37
+ box-sizing: border-box;
38
+ margin: 0;
39
+ padding: 0;
40
+ }
41
+
42
+ body {
43
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
44
+ font-size: 14px;
45
+ background: var(--bg-primary);
46
+ color: var(--text-primary);
47
+ height: 100vh;
48
+ overflow: hidden;
49
+ }
50
+
51
+ /* Main container */
52
+ .editor-container {
53
+ display: flex;
54
+ height: 100vh;
55
+ }
56
+
57
+ /* Preview Panel */
58
+ .preview-panel {
59
+ flex: 1;
60
+ display: flex;
61
+ flex-direction: column;
62
+ border-right: 1px solid var(--border-color);
63
+ min-width: 400px;
64
+ }
65
+
66
+ .preview-header {
67
+ display: flex;
68
+ justify-content: space-between;
69
+ align-items: center;
70
+ padding: 12px 16px;
71
+ background: var(--bg-secondary);
72
+ border-bottom: 1px solid var(--border-color);
73
+ }
74
+
75
+ .preview-header h2 {
76
+ font-size: 16px;
77
+ font-weight: 600;
78
+ }
79
+
80
+ .preview-controls {
81
+ display: flex;
82
+ gap: 12px;
83
+ align-items: center;
84
+ }
85
+
86
+ .preview-wrapper {
87
+ flex: 1;
88
+ display: flex;
89
+ align-items: center;
90
+ justify-content: center;
91
+ padding: 20px;
92
+ background: var(--bg-tertiary);
93
+ position: relative;
94
+ overflow: auto;
95
+ /* Checkerboard pattern for transparency */
96
+ background-image:
97
+ linear-gradient(45deg, #ccc 25%, transparent 25%),
98
+ linear-gradient(-45deg, #ccc 25%, transparent 25%),
99
+ linear-gradient(45deg, transparent 75%, #ccc 75%),
100
+ linear-gradient(-45deg, transparent 75%, #ccc 75%);
101
+ background-size: 20px 20px;
102
+ background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
103
+ }
104
+
105
+ [data-theme="dark"] .preview-wrapper {
106
+ background-image:
107
+ linear-gradient(45deg, #333 25%, transparent 25%),
108
+ linear-gradient(-45deg, #333 25%, transparent 25%),
109
+ linear-gradient(45deg, transparent 75%, #333 75%),
110
+ linear-gradient(-45deg, transparent 75%, #333 75%);
111
+ }
112
+
113
+ #preview-image {
114
+ max-width: 100%;
115
+ max-height: 100%;
116
+ object-fit: contain;
117
+ cursor: crosshair;
118
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
119
+ position: relative;
120
+ z-index: 1; /* Below the hit region overlay */
121
+ }
122
+
123
+ .hitregion-overlay {
124
+ position: absolute;
125
+ top: 0;
126
+ left: 0;
127
+ width: 100%;
128
+ height: 100%;
129
+ pointer-events: auto; /* Allow clicks on empty areas to deselect */
130
+ z-index: 10; /* Above the preview image */
131
+ }
132
+
133
+ /* Always display overlay for hover detection, but control visibility via children */
134
+ .hitregion-overlay.visible .hitregion-rect,
135
+ .hitregion-overlay.visible .hitregion-polyline {
136
+ opacity: 1;
137
+ }
138
+
139
+ /* Hover-only mode: invisible until hovered */
140
+ .hitregion-overlay.hover-mode .hitregion-rect,
141
+ .hitregion-overlay.hover-mode .hitregion-polyline {
142
+ opacity: 0;
143
+ }
144
+
145
+ .hitregion-overlay.hover-mode .hitregion-rect:hover,
146
+ .hitregion-overlay.hover-mode .hitregion-polyline:hover {
147
+ opacity: 1;
148
+ }
149
+
150
+ .hitregion-rect {
151
+ --element-color: #888888; /* Default fallback */
152
+ fill: transparent;
153
+ stroke: transparent;
154
+ stroke-width: 2;
155
+ stroke-dasharray: 6, 3;
156
+ pointer-events: all;
157
+ cursor: pointer;
158
+ transition: fill 0.2s, stroke 0.2s, filter 0.2s, opacity 0.15s;
159
+ }
160
+
161
+ .hitregion-rect:hover {
162
+ fill: var(--element-color);
163
+ fill-opacity: 0.15;
164
+ stroke: var(--element-color);
165
+ stroke-opacity: 0.6;
166
+ stroke-width: 2;
167
+ filter: none;
168
+ }
169
+
170
+ .hitregion-polyline {
171
+ --element-color: #888888; /* Default fallback */
172
+ fill: none !important;
173
+ stroke: transparent;
174
+ stroke-width: 8;
175
+ stroke-linecap: round;
176
+ stroke-linejoin: round;
177
+ pointer-events: stroke;
178
+ cursor: pointer;
179
+ transition: stroke 0.15s, opacity 0.15s;
180
+ }
181
+
182
+ .hitregion-polyline:hover {
183
+ fill: none !important;
184
+ stroke: var(--element-color);
185
+ stroke-width: 8;
186
+ stroke-opacity: 0.4;
187
+ filter: none;
188
+ }
189
+
190
+ /* Scatter point circles */
191
+ .scatter-group {
192
+ --element-color: #888888; /* Default fallback */
193
+ pointer-events: all;
194
+ }
195
+
196
+ .hitregion-circle {
197
+ fill: transparent;
198
+ stroke: transparent;
199
+ stroke-width: 1;
200
+ pointer-events: all;
201
+ cursor: pointer;
202
+ opacity: 1; /* Explicit default */
203
+ transition: fill 0.15s, stroke 0.15s, opacity 0.15s;
204
+ }
205
+
206
+ .hitregion-circle:hover,
207
+ .hitregion-circle.hovered {
208
+ fill: var(--element-color);
209
+ fill-opacity: 0.2;
210
+ stroke: var(--element-color);
211
+ stroke-opacity: 0.5;
212
+ stroke-width: 1;
213
+ filter: none;
214
+ }
215
+
216
+ /* When any circle in the group is hovered, highlight ALL circles in the group */
217
+ .scatter-group:hover .hitregion-circle,
218
+ .scatter-group.hovered .hitregion-circle {
219
+ fill: var(--element-color);
220
+ fill-opacity: 0.15;
221
+ stroke: var(--element-color);
222
+ stroke-opacity: 0.4;
223
+ stroke-width: 1;
224
+ filter: none;
225
+ }
226
+
227
+ /* Scatter circles visibility modes */
228
+ .hitregion-overlay.visible .hitregion-circle,
229
+ .hitregion-overlay.visible .scatter-group {
230
+ opacity: 1;
231
+ }
232
+
233
+ .hitregion-overlay.hover-mode .hitregion-circle {
234
+ opacity: 0;
235
+ }
236
+
237
+ .hitregion-overlay.hover-mode .scatter-group:hover .hitregion-circle,
238
+ .hitregion-overlay.hover-mode .hitregion-circle:hover {
239
+ opacity: 1;
240
+ }
241
+
242
+ .hitregion-label {
243
+ font-size: 10px;
244
+ fill: var(--text-primary);
245
+ pointer-events: none;
246
+ opacity: 0;
247
+ transition: opacity 0.15s;
248
+ }
249
+
250
+ .hitregion-rect:hover + .hitregion-label,
251
+ .hitregion-group:hover .hitregion-label,
252
+ .hitregion-group.hovered .hitregion-label,
253
+ .scatter-group:hover ~ .hitregion-label,
254
+ .scatter-group.hovered ~ .hitregion-label,
255
+ .hitregion-polyline:hover + .hitregion-label {
256
+ opacity: 1;
257
+ }
258
+
259
+ #btn-show-hitmap.active {
260
+ background: var(--accent-color);
261
+ color: white;
262
+ border-color: var(--accent-color);
263
+ }
264
+
265
+ .selection-overlay {
266
+ position: absolute;
267
+ top: 0;
268
+ left: 0;
269
+ width: 100%;
270
+ height: 100%;
271
+ pointer-events: none;
272
+ }
273
+
274
+ .selection-rect {
275
+ --element-color: #2563eb; /* Default fallback to accent color */
276
+ fill: var(--element-color);
277
+ fill-opacity: 0.15;
278
+ stroke: var(--element-color);
279
+ stroke-opacity: 0.6;
280
+ stroke-width: 2;
281
+ stroke-dasharray: 5, 3;
282
+ }
283
+
284
+ /* Primary selection in a group - solid border */
285
+ .selection-rect.selection-primary {
286
+ fill-opacity: 0.2;
287
+ stroke-opacity: 0.8;
288
+ stroke-width: 3;
289
+ stroke-dasharray: none;
290
+ }
291
+
292
+ /* Selection for lines - show stroke along the path */
293
+ .selection-polyline {
294
+ --element-color: #2563eb;
295
+ fill: none !important;
296
+ stroke: var(--element-color);
297
+ stroke-width: 8;
298
+ stroke-opacity: 0.5;
299
+ stroke-linecap: round;
300
+ stroke-linejoin: round;
301
+ }
302
+
303
+ /* Selection for scatter points */
304
+ .selection-circle {
305
+ --element-color: #2563eb;
306
+ fill: var(--element-color);
307
+ fill-opacity: 0.3;
308
+ stroke: var(--element-color);
309
+ stroke-opacity: 0.7;
310
+ stroke-width: 2;
311
+ }
312
+
313
+ /* Group hover highlight - all elements in same logical group */
314
+ .group-hovered {
315
+ opacity: 1 !important;
316
+ }
317
+
318
+ .group-hovered .hitregion-polyline,
319
+ .group-hovered .hitregion-rect,
320
+ .group-hovered .hitregion-circle {
321
+ stroke: var(--accent-color) !important;
322
+ stroke-width: 3 !important;
323
+ fill: var(--selection-color) !important;
324
+ }
325
+
326
+ .hitregion-group.group-hovered .hitregion-label {
327
+ opacity: 1 !important;
328
+ font-weight: bold;
329
+ }
330
+
331
+ .selected-element-info {
332
+ padding: 12px 16px;
333
+ background: var(--bg-secondary);
334
+ border-top: 1px solid var(--border-color);
335
+ font-size: 13px;
336
+ color: var(--text-secondary);
337
+ }
338
+
339
+ /* Dynamic Call Properties (in right panel) */
340
+ .dynamic-call-properties {
341
+ padding: 12px 16px;
342
+ margin: 8px 0 16px 0;
343
+ background: var(--bg-tertiary);
344
+ border: 1px solid var(--accent-color);
345
+ border-radius: 8px;
346
+ font-size: 12px;
347
+ max-height: 400px;
348
+ overflow-y: auto;
349
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
350
+ }
351
+
352
+ .dynamic-props-header {
353
+ margin-bottom: 8px;
354
+ padding-bottom: 8px;
355
+ border-bottom: 1px solid var(--border-color);
356
+ }
357
+
358
+ .dynamic-props-header strong {
359
+ color: var(--accent-color);
360
+ }
361
+
362
+ .dynamic-props-header .call-id {
363
+ color: var(--text-secondary);
364
+ font-size: 11px;
365
+ margin-left: 8px;
366
+ }
367
+
368
+ .dynamic-props-label {
369
+ font-weight: 500;
370
+ color: var(--text-secondary);
371
+ margin-bottom: 4px;
372
+ font-size: 11px;
373
+ }
374
+
375
+ .dynamic-props-section {
376
+ margin-bottom: 8px;
377
+ }
378
+
379
+ .dynamic-field {
380
+ display: flex;
381
+ align-items: center;
382
+ gap: 8px;
383
+ padding: 4px 0;
384
+ }
385
+
386
+ .dynamic-field label {
387
+ flex: 0 0 120px;
388
+ font-size: 11px;
389
+ color: var(--text-primary);
390
+ }
391
+
392
+ .dynamic-field.unused label {
393
+ color: var(--text-secondary);
394
+ }
395
+
396
+ .dynamic-input {
397
+ flex: 1;
398
+ padding: 4px 8px;
399
+ border: 1px solid var(--border-color);
400
+ border-radius: 4px;
401
+ background: var(--bg-primary);
402
+ font-size: 11px;
403
+ }
404
+
405
+ .dynamic-input[type="checkbox"] {
406
+ flex: 0 0 auto;
407
+ width: 16px;
408
+ height: 16px;
409
+ }
410
+
411
+ .dynamic-props-available {
412
+ margin-top: 8px;
413
+ }
414
+
415
+ .dynamic-props-available summary {
416
+ cursor: pointer;
417
+ font-size: 11px;
418
+ color: var(--text-secondary);
419
+ padding: 4px 0;
420
+ }
421
+
422
+ .dynamic-props-available summary:hover {
423
+ color: var(--accent-color);
424
+ }
425
+
426
+ .more-params {
427
+ font-size: 10px;
428
+ color: var(--text-secondary);
429
+ font-style: italic;
430
+ padding: 4px 0;
431
+ }
432
+
433
+ .arg-field {
434
+ background: var(--bg-secondary);
435
+ border-radius: 4px;
436
+ padding: 4px 8px;
437
+ margin: 2px 0;
438
+ }
439
+
440
+ .arg-value {
441
+ flex: 1;
442
+ font-family: monospace;
443
+ font-size: 11px;
444
+ color: var(--text-secondary);
445
+ text-align: right;
446
+ }
447
+
448
+ .dynamic-field-container {
449
+ margin-bottom: 8px;
450
+ padding-bottom: 8px;
451
+ border-bottom: 1px solid var(--border-color);
452
+ }
453
+
454
+ .dynamic-field-container.unused {
455
+ opacity: 0.7;
456
+ }
457
+
458
+ .type-hint {
459
+ font-size: 10px;
460
+ color: var(--text-secondary);
461
+ font-family: monospace;
462
+ padding: 2px 0 0 0;
463
+ margin-left: 125px;
464
+ word-break: break-word;
465
+ line-height: 1.3;
466
+ }
467
+
468
+ /* Controls Panel */
469
+ .controls-panel {
470
+ width: 350px;
471
+ display: flex;
472
+ flex-direction: column;
473
+ background: var(--bg-primary);
474
+ overflow: hidden;
475
+ }
476
+
477
+ .controls-header {
478
+ display: flex;
479
+ justify-content: space-between;
480
+ align-items: center;
481
+ padding: 12px 16px;
482
+ background: var(--bg-secondary);
483
+ border-bottom: 1px solid var(--border-color);
484
+ }
485
+
486
+ .controls-header h2 {
487
+ font-size: 16px;
488
+ font-weight: 600;
489
+ }
490
+
491
+ .controls-actions {
492
+ display: flex;
493
+ gap: 8px;
494
+ }
495
+
496
+ .controls-sections {
497
+ flex: 1;
498
+ overflow-y: auto;
499
+ padding: 8px;
500
+ }
501
+
502
+ /* Tab Navigation */
503
+ .tab-navigation {
504
+ display: flex;
505
+ gap: 0;
506
+ background: var(--bg-secondary);
507
+ border-radius: 8px;
508
+ padding: 4px;
509
+ margin-bottom: 12px;
510
+ }
511
+
512
+ .tab-btn {
513
+ flex: 1;
514
+ padding: 10px 16px;
515
+ font-size: 13px;
516
+ font-weight: 500;
517
+ border: none;
518
+ background: transparent;
519
+ border-radius: 6px;
520
+ cursor: pointer;
521
+ transition: all 0.15s;
522
+ color: var(--text-secondary);
523
+ }
524
+
525
+ .tab-btn:hover {
526
+ background: var(--bg-tertiary);
527
+ color: var(--text-primary);
528
+ }
529
+
530
+ .tab-btn.active {
531
+ background: var(--accent-color);
532
+ color: white;
533
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
534
+ }
535
+
536
+ /* Tab Content */
537
+ .tab-content {
538
+ display: none;
539
+ }
540
+
541
+ .tab-content.active {
542
+ display: block;
543
+ }
544
+
545
+ .tab-hint {
546
+ text-align: center;
547
+ padding: 40px 20px;
548
+ color: var(--text-secondary);
549
+ background: var(--bg-secondary);
550
+ border-radius: 8px;
551
+ margin-bottom: 12px;
552
+ }
553
+
554
+ .tab-hint p {
555
+ margin: 0;
556
+ }
557
+
558
+ .tab-hint .hint-sub {
559
+ font-size: 12px;
560
+ margin-top: 8px;
561
+ opacity: 0.7;
562
+ }
563
+
564
+ /* Selected element panel in Element tab */
565
+ .selected-element-header {
566
+ display: flex;
567
+ align-items: center;
568
+ gap: 10px;
569
+ padding: 12px;
570
+ background: var(--bg-secondary);
571
+ border-radius: 8px;
572
+ margin-bottom: 12px;
573
+ }
574
+
575
+ .element-type-badge {
576
+ padding: 4px 10px;
577
+ font-size: 11px;
578
+ font-weight: 600;
579
+ text-transform: uppercase;
580
+ background: var(--accent-color);
581
+ color: white;
582
+ border-radius: 4px;
583
+ }
584
+
585
+ .element-name {
586
+ font-size: 14px;
587
+ font-weight: 500;
588
+ color: var(--text-primary);
589
+ }
590
+
591
+ /* Legacy toggle styles for backward compatibility */
592
+ .view-mode-toggle {
593
+ display: none; /* Hidden - replaced by tabs */
594
+ }
595
+
596
+ .btn-toggle {
597
+ padding: 6px 12px;
598
+ font-size: 12px;
599
+ font-weight: 500;
600
+ border: 1px solid var(--border-color);
601
+ background: var(--bg-primary);
602
+ border-radius: 4px;
603
+ cursor: pointer;
604
+ transition: all 0.15s;
605
+ }
606
+
607
+ .btn-toggle:hover {
608
+ background: var(--bg-tertiary);
609
+ }
610
+
611
+ .btn-toggle.active {
612
+ background: var(--accent-color);
613
+ border-color: var(--accent-color);
614
+ color: white;
615
+ }
616
+
617
+ .selection-hint {
618
+ margin-left: auto;
619
+ font-size: 11px;
620
+ color: var(--text-secondary);
621
+ font-style: italic;
622
+ }
623
+
624
+ /* Filtering mode: hide non-matching sections */
625
+ .controls-sections.filter-mode .section.section-hidden {
626
+ display: none;
627
+ }
628
+
629
+ .controls-sections.filter-mode .form-row.field-hidden {
630
+ display: none;
631
+ }
632
+
633
+ /* Show matching sections with highlight in filter mode */
634
+ .controls-sections.filter-mode .section.section-visible {
635
+ border-color: var(--accent-color);
636
+ }
637
+
638
+ .controls-sections.filter-mode .section.section-visible summary {
639
+ background: rgba(37, 99, 235, 0.08);
640
+ }
641
+
642
+ [data-theme="dark"] .controls-sections.filter-mode .section.section-visible summary {
643
+ background: rgba(59, 130, 246, 0.12);
644
+ }
645
+
646
+ /* Sections */
647
+ .section {
648
+ margin-bottom: 8px;
649
+ border: 1px solid var(--border-color);
650
+ border-radius: 6px;
651
+ overflow: hidden;
652
+ }
653
+
654
+ .section summary {
655
+ padding: 10px 14px;
656
+ background: var(--bg-secondary);
657
+ cursor: pointer;
658
+ font-weight: 500;
659
+ user-select: none;
660
+ list-style: none;
661
+ display: flex;
662
+ align-items: center;
663
+ }
664
+
665
+ .section summary::before {
666
+ content: '\\25B6';
667
+ font-size: 10px;
668
+ margin-right: 8px;
669
+ transition: transform 0.2s;
670
+ }
671
+
672
+ .section[open] summary::before {
673
+ transform: rotate(90deg);
674
+ }
675
+
676
+ .section-highlighted {
677
+ border-color: var(--accent-color);
678
+ box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.2);
679
+ }
680
+
681
+ .section-highlighted summary {
682
+ background: rgba(37, 99, 235, 0.1);
683
+ border-left: 3px solid var(--accent-color);
684
+ }
685
+
686
+ [data-theme="dark"] .section-highlighted {
687
+ box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.3);
688
+ }
689
+
690
+ [data-theme="dark"] .section-highlighted summary {
691
+ background: rgba(59, 130, 246, 0.15);
692
+ }
693
+
694
+ /* Field highlighting for selected element */
695
+ .field-highlighted {
696
+ background: rgba(37, 99, 235, 0.08);
697
+ border-radius: 4px;
698
+ padding: 6px 8px;
699
+ margin: -6px -8px;
700
+ margin-bottom: 8px;
701
+ border-left: 3px solid var(--accent-color);
702
+ }
703
+
704
+ .field-highlighted:last-child {
705
+ margin-bottom: 0;
706
+ }
707
+
708
+ [data-theme="dark"] .field-highlighted {
709
+ background: rgba(59, 130, 246, 0.12);
710
+ }
711
+
712
+ .section-content {
713
+ padding: 12px 14px;
714
+ background: var(--bg-primary);
715
+ }
716
+
717
+ .subsection {
718
+ margin-bottom: 12px;
719
+ }
720
+
721
+ .subsection:last-child {
722
+ margin-bottom: 0;
723
+ }
724
+
725
+ .subsection h4 {
726
+ font-size: 12px;
727
+ font-weight: 600;
728
+ color: var(--text-secondary);
729
+ margin-bottom: 8px;
730
+ text-transform: uppercase;
731
+ letter-spacing: 0.5px;
732
+ }
733
+
734
+ /* Form elements */
735
+ .form-row {
736
+ display: flex;
737
+ align-items: center;
738
+ margin-bottom: 8px;
739
+ }
740
+
741
+ .form-row:last-child {
742
+ margin-bottom: 0;
743
+ }
744
+
745
+ .form-row label {
746
+ flex: 0 0 120px;
747
+ font-size: 13px;
748
+ color: var(--text-secondary);
749
+ }
750
+
751
+ .form-row input[type="number"],
752
+ .form-row input[type="text"],
753
+ .form-row select {
754
+ flex: 1;
755
+ padding: 6px 10px;
756
+ border: 1px solid var(--border-color);
757
+ border-radius: 4px;
758
+ background: var(--bg-primary);
759
+ color: var(--text-primary);
760
+ font-size: 13px;
761
+ }
762
+
763
+ .form-row input[type="number"]:focus,
764
+ .form-row input[type="text"]:focus,
765
+ .form-row select:focus {
766
+ outline: none;
767
+ border-color: var(--accent-color);
768
+ box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.2);
769
+ }
770
+
771
+ /* Override indicator for modified values */
772
+ .form-row.value-modified input,
773
+ .form-row.value-modified select {
774
+ border-color: #f59e0b;
775
+ background: rgba(245, 158, 11, 0.05);
776
+ }
777
+
778
+ .form-row.value-modified label::after {
779
+ content: '●';
780
+ color: #f59e0b;
781
+ margin-left: 4px;
782
+ font-weight: bold;
783
+ }
784
+
785
+ [data-theme="dark"] .form-row.value-modified input,
786
+ [data-theme="dark"] .form-row.value-modified select {
787
+ border-color: #f59e0b;
788
+ background: rgba(245, 158, 11, 0.1);
789
+ }
790
+
791
+ .form-row input[type="checkbox"] {
792
+ width: 18px;
793
+ height: 18px;
794
+ cursor: pointer;
795
+ }
796
+
797
+ .form-row input[type="color"] {
798
+ width: 50px;
799
+ height: 30px;
800
+ padding: 2px;
801
+ border: 1px solid var(--border-color);
802
+ border-radius: 4px;
803
+ cursor: pointer;
804
+ }
805
+
806
+ .form-row input[type="range"] {
807
+ flex: 1;
808
+ margin-right: 8px;
809
+ }
810
+
811
+ .form-row input[type="range"] + span {
812
+ min-width: 30px;
813
+ text-align: right;
814
+ font-size: 12px;
815
+ color: var(--text-secondary);
816
+ }
817
+
818
+ .form-grid {
819
+ display: grid;
820
+ grid-template-columns: 1fr 1fr;
821
+ gap: 8px;
822
+ }
823
+
824
+ .form-grid .form-row label {
825
+ flex: 0 0 60px;
826
+ }
827
+
828
+ /* Buttons */
829
+ button {
830
+ padding: 8px 16px;
831
+ border: 1px solid var(--border-color);
832
+ border-radius: 4px;
833
+ background: var(--bg-primary);
834
+ color: var(--text-primary);
835
+ font-size: 13px;
836
+ cursor: pointer;
837
+ transition: all 0.2s;
838
+ }
839
+
840
+ button:hover {
841
+ background: var(--bg-secondary);
842
+ }
843
+
844
+ .btn-primary {
845
+ background: var(--accent-color);
846
+ border-color: var(--accent-color);
847
+ color: white;
848
+ }
849
+
850
+ .btn-primary:hover {
851
+ background: var(--accent-hover);
852
+ border-color: var(--accent-hover);
853
+ }
854
+
855
+ .btn-secondary {
856
+ background: var(--bg-secondary);
857
+ }
858
+
859
+ .btn-warning {
860
+ background: #f59e0b;
861
+ border-color: #f59e0b;
862
+ color: white;
863
+ }
864
+
865
+ .btn-warning:hover {
866
+ background: #d97706;
867
+ border-color: #d97706;
868
+ }
869
+
870
+ .style-info {
871
+ padding: 8px 16px;
872
+ background: var(--bg-secondary);
873
+ border-bottom: 1px solid var(--border-color);
874
+ font-size: 12px;
875
+ display: flex;
876
+ align-items: center;
877
+ gap: 8px;
878
+ flex-wrap: wrap;
879
+ }
880
+
881
+ .style-label {
882
+ color: var(--text-secondary);
883
+ }
884
+
885
+ .style-name {
886
+ color: var(--accent-color);
887
+ font-weight: 600;
888
+ font-family: monospace;
889
+ }
890
+
891
+ .theme-selector {
892
+ padding: 4px 8px;
893
+ border: 1px solid var(--border-color);
894
+ border-radius: 4px;
895
+ background: var(--bg-primary);
896
+ color: var(--accent-color);
897
+ font-weight: 600;
898
+ font-family: monospace;
899
+ font-size: 12px;
900
+ cursor: pointer;
901
+ }
902
+
903
+ .theme-selector:hover {
904
+ border-color: var(--accent-color);
905
+ }
906
+
907
+ .theme-selector:focus {
908
+ outline: none;
909
+ border-color: var(--accent-color);
910
+ box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.2);
911
+ }
912
+
913
+ .theme-actions {
914
+ display: flex;
915
+ gap: 4px;
916
+ margin-left: auto;
917
+ }
918
+
919
+ .btn-small {
920
+ padding: 4px 8px;
921
+ font-size: 11px;
922
+ border-radius: 3px;
923
+ }
924
+
925
+ /* Modal styles */
926
+ .modal {
927
+ position: fixed;
928
+ top: 0;
929
+ left: 0;
930
+ width: 100%;
931
+ height: 100%;
932
+ background: rgba(0, 0, 0, 0.5);
933
+ z-index: 1000;
934
+ display: flex;
935
+ align-items: center;
936
+ justify-content: center;
937
+ }
938
+
939
+ .modal-content {
940
+ background: var(--bg-primary);
941
+ border-radius: 8px;
942
+ width: 90%;
943
+ max-width: 600px;
944
+ max-height: 80vh;
945
+ display: flex;
946
+ flex-direction: column;
947
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
948
+ }
949
+
950
+ .modal-header {
951
+ display: flex;
952
+ justify-content: space-between;
953
+ align-items: center;
954
+ padding: 16px 20px;
955
+ border-bottom: 1px solid var(--border-color);
956
+ }
957
+
958
+ .modal-header h3 {
959
+ font-size: 16px;
960
+ font-weight: 600;
961
+ margin: 0;
962
+ }
963
+
964
+ .modal-close {
965
+ background: none;
966
+ border: none;
967
+ font-size: 24px;
968
+ cursor: pointer;
969
+ color: var(--text-secondary);
970
+ padding: 0;
971
+ line-height: 1;
972
+ }
973
+
974
+ .modal-close:hover {
975
+ color: var(--text-primary);
976
+ }
977
+
978
+ .theme-content-pre {
979
+ flex: 1;
980
+ overflow-y: auto;
981
+ padding: 16px 20px;
982
+ margin: 0;
983
+ font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
984
+ font-size: 12px;
985
+ line-height: 1.5;
986
+ background: var(--bg-secondary);
987
+ white-space: pre-wrap;
988
+ word-wrap: break-word;
989
+ }
990
+
991
+ .modal-footer {
992
+ display: flex;
993
+ gap: 8px;
994
+ padding: 16px 20px;
995
+ border-top: 1px solid var(--border-color);
996
+ justify-content: flex-end;
997
+ }
998
+
999
+ .override-status {
1000
+ padding: 8px 16px;
1001
+ background: rgba(245, 158, 11, 0.1);
1002
+ border-bottom: 1px solid var(--border-color);
1003
+ font-size: 12px;
1004
+ display: flex;
1005
+ justify-content: space-between;
1006
+ align-items: center;
1007
+ }
1008
+
1009
+ .override-indicator {
1010
+ color: #f59e0b;
1011
+ font-weight: 500;
1012
+ }
1013
+
1014
+ .override-timestamp {
1015
+ color: var(--text-secondary);
1016
+ font-size: 11px;
1017
+ }
1018
+
1019
+ /* Split dropdown for download button */
1020
+ .download-dropdown {
1021
+ position: relative;
1022
+ display: flex;
1023
+ }
1024
+
1025
+ .download-main {
1026
+ border-radius: 4px 0 0 4px;
1027
+ padding: 8px 16px;
1028
+ font-weight: 500;
1029
+ }
1030
+
1031
+ .download-toggle {
1032
+ border-radius: 0 4px 4px 0;
1033
+ padding: 8px 8px;
1034
+ border-left: 1px solid rgba(255, 255, 255, 0.2);
1035
+ font-size: 10px;
1036
+ }
1037
+
1038
+ .download-menu {
1039
+ display: none;
1040
+ position: absolute;
1041
+ top: 100%;
1042
+ left: 0;
1043
+ right: 0;
1044
+ background: var(--bg-primary);
1045
+ border: 1px solid var(--border-color);
1046
+ border-radius: 4px;
1047
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
1048
+ z-index: 100;
1049
+ margin-top: 4px;
1050
+ overflow: hidden;
1051
+ }
1052
+
1053
+ .download-menu.open {
1054
+ display: block;
1055
+ }
1056
+
1057
+ .download-option {
1058
+ display: block;
1059
+ width: 100%;
1060
+ padding: 10px 16px;
1061
+ text-align: left;
1062
+ border: none;
1063
+ background: transparent;
1064
+ cursor: pointer;
1065
+ font-size: 13px;
1066
+ border-radius: 0;
1067
+ }
1068
+
1069
+ .download-option:hover {
1070
+ background: var(--bg-secondary);
1071
+ }
1072
+
1073
+ .download-option.active {
1074
+ background: var(--accent-color);
1075
+ color: white;
1076
+ }
1077
+
1078
+ .download-option.active:hover {
1079
+ background: var(--accent-hover);
1080
+ }
1081
+
1082
+ /* Legacy download buttons (kept for compatibility) */
1083
+ .download-buttons {
1084
+ display: flex;
1085
+ gap: 8px;
1086
+ }
1087
+
1088
+ .download-buttons button {
1089
+ flex: 1;
1090
+ }
1091
+
1092
+ /* Theme toggle */
1093
+ .theme-toggle {
1094
+ display: flex;
1095
+ align-items: center;
1096
+ gap: 8px;
1097
+ cursor: pointer;
1098
+ font-size: 13px;
1099
+ }
1100
+
1101
+ .theme-toggle input {
1102
+ width: 18px;
1103
+ height: 18px;
1104
+ }
1105
+
1106
+ /* Loading state */
1107
+ .loading {
1108
+ opacity: 0.6;
1109
+ pointer-events: none;
1110
+ }
1111
+
1112
+ /* Scrollbar styling */
1113
+ .controls-sections::-webkit-scrollbar {
1114
+ width: 8px;
1115
+ }
1116
+
1117
+ .controls-sections::-webkit-scrollbar-track {
1118
+ background: var(--bg-secondary);
1119
+ }
1120
+
1121
+ .controls-sections::-webkit-scrollbar-thumb {
1122
+ background: var(--border-color);
1123
+ border-radius: 4px;
1124
+ }
1125
+
1126
+ .controls-sections::-webkit-scrollbar-thumb:hover {
1127
+ background: var(--text-secondary);
1128
+ }
1129
+
1130
+ /* Responsive */
1131
+ @media (max-width: 768px) {
1132
+ .editor-container {
1133
+ flex-direction: column;
1134
+ }
1135
+
1136
+ .preview-panel {
1137
+ min-width: 100%;
1138
+ border-right: none;
1139
+ border-bottom: 1px solid var(--border-color);
1140
+ }
1141
+
1142
+ .controls-panel {
1143
+ width: 100%;
1144
+ max-height: 50vh;
1145
+ }
1146
+ }
1147
+
1148
+ /* Color Input Component */
1149
+ .color-input-wrapper {
1150
+ display: flex;
1151
+ align-items: center;
1152
+ gap: 6px;
1153
+ flex: 1;
1154
+ }
1155
+
1156
+ .color-swatch {
1157
+ width: 24px;
1158
+ height: 24px;
1159
+ border: 1px solid var(--border-color);
1160
+ border-radius: 4px;
1161
+ cursor: pointer;
1162
+ flex-shrink: 0;
1163
+ }
1164
+
1165
+ .color-swatch:hover {
1166
+ border-color: var(--accent-color);
1167
+ }
1168
+
1169
+ .color-text-input {
1170
+ flex: 1;
1171
+ min-width: 80px;
1172
+ }
1173
+
1174
+ .rgb-display {
1175
+ font-size: 10px;
1176
+ color: var(--text-secondary);
1177
+ font-family: monospace;
1178
+ white-space: nowrap;
1179
+ }
1180
+
1181
+ .color-preset-select {
1182
+ padding: 4px;
1183
+ border: 1px solid var(--border-color);
1184
+ border-radius: 4px;
1185
+ background: var(--bg-primary);
1186
+ font-size: 10px;
1187
+ cursor: pointer;
1188
+ }
1189
+
1190
+ .color-picker-hidden {
1191
+ position: absolute;
1192
+ opacity: 0;
1193
+ pointer-events: none;
1194
+ width: 0;
1195
+ height: 0;
1196
+ }
1197
+
1198
+ .color-custom-input {
1199
+ padding: 4px 8px;
1200
+ border: 1px solid var(--border-color);
1201
+ border-radius: 4px;
1202
+ background: var(--bg-primary);
1203
+ font-size: 11px;
1204
+ min-width: 100px;
1205
+ }
1206
+
1207
+ .color-custom-input:focus {
1208
+ outline: none;
1209
+ border-color: var(--accent-color);
1210
+ }
1211
+
1212
+ .color-select {
1213
+ padding: 4px 6px;
1214
+ border: 1px solid var(--border-color);
1215
+ border-radius: 4px;
1216
+ background: var(--bg-primary);
1217
+ font-size: 11px;
1218
+ min-width: 70px;
1219
+ cursor: pointer;
1220
+ }
1221
+
1222
+ /* Axis Type Toggle */
1223
+ .axis-type-toggle {
1224
+ display: flex;
1225
+ gap: 0;
1226
+ border: 1px solid var(--border-color);
1227
+ border-radius: 4px;
1228
+ overflow: hidden;
1229
+ flex: 1;
1230
+ }
1231
+
1232
+ .axis-type-btn {
1233
+ flex: 1;
1234
+ padding: 6px 12px;
1235
+ font-size: 12px;
1236
+ border: none;
1237
+ background: var(--bg-primary);
1238
+ color: var(--text-secondary);
1239
+ cursor: pointer;
1240
+ transition: all 0.15s;
1241
+ border-radius: 0;
1242
+ }
1243
+
1244
+ .axis-type-btn:first-child {
1245
+ border-right: 1px solid var(--border-color);
1246
+ }
1247
+
1248
+ .axis-type-btn:hover {
1249
+ background: var(--bg-secondary);
1250
+ color: var(--text-primary);
1251
+ }
1252
+
1253
+ .axis-type-btn.active {
1254
+ background: var(--accent-color);
1255
+ color: white;
1256
+ }
1257
+
1258
+ /* Label input styling */
1259
+ .label-input {
1260
+ flex: 1;
1261
+ padding: 6px 10px;
1262
+ border: 1px solid var(--border-color);
1263
+ border-radius: 4px;
1264
+ background: var(--bg-primary);
1265
+ color: var(--text-primary);
1266
+ font-size: 13px;
1267
+ }
1268
+
1269
+ .label-input:focus {
1270
+ outline: none;
1271
+ border-color: var(--accent-color);
1272
+ box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.2);
1273
+ }
1274
+
1275
+ /* Legend custom position */
1276
+ .legend-custom-pos {
1277
+ margin-top: 8px;
1278
+ padding: 8px;
1279
+ background: var(--bg-secondary);
1280
+ border-radius: 4px;
1281
+ }
1282
+
1283
+ .form-hint {
1284
+ font-size: 11px;
1285
+ color: var(--text-secondary);
1286
+ margin-top: 4px;
1287
+ font-style: italic;
1288
+ }
1289
+
1290
+ /* Legend labels editor */
1291
+ .legend-labels-container {
1292
+ margin-top: 8px;
1293
+ }
1294
+
1295
+ .legend-label-item {
1296
+ display: flex;
1297
+ align-items: center;
1298
+ gap: 8px;
1299
+ margin-bottom: 6px;
1300
+ }
1301
+
1302
+ .legend-label-color {
1303
+ width: 16px;
1304
+ height: 16px;
1305
+ border-radius: 3px;
1306
+ border: 1px solid var(--border-color);
1307
+ flex-shrink: 0;
1308
+ }
1309
+
1310
+ .legend-label-input {
1311
+ flex: 1;
1312
+ padding: 4px 8px;
1313
+ border: 1px solid var(--border-color);
1314
+ border-radius: 4px;
1315
+ background: var(--bg-primary);
1316
+ color: var(--text-primary);
1317
+ font-size: 12px;
1318
+ }
1319
+
1320
+ .legend-label-input:focus {
1321
+ outline: none;
1322
+ border-color: var(--accent-color);
1323
+ }
1324
+ """
1325
+
1326
+ __all__ = ["STYLES"]