tokimeki-image-editor 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/AdjustTool.svelte +14 -60
- package/dist/components/AnnotationTool.svelte +112 -93
- package/dist/components/BlurTool.svelte +18 -78
- package/dist/components/ExportTool.svelte +45 -82
- package/dist/components/FilterTool.svelte +43 -86
- package/dist/components/ImageEditor.svelte +19 -53
- package/dist/components/RotateTool.svelte +47 -82
- package/dist/components/ToolPanel.svelte +216 -0
- package/dist/components/ToolPanel.svelte.d.ts +10 -0
- package/dist/components/Toolbar.svelte +97 -26
- package/dist/components/Toolbar.svelte.d.ts +5 -0
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">import { _ } from 'svelte-i18n';
|
|
2
|
-
import
|
|
2
|
+
import ToolPanel from './ToolPanel.svelte';
|
|
3
3
|
let { adjustments, onChange, onClose } = $props();
|
|
4
4
|
function handleChange(key, value) {
|
|
5
5
|
onChange({ [key]: value });
|
|
@@ -27,14 +27,9 @@ function handleWheel(e) {
|
|
|
27
27
|
</script>
|
|
28
28
|
|
|
29
29
|
<div class="adjust-tool" onwheel={handleWheel}>
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
<X size={20} />
|
|
34
|
-
</button>
|
|
35
|
-
</div>
|
|
36
|
-
|
|
37
|
-
<div class="adjustments-grid">
|
|
30
|
+
<ToolPanel title={$_('editor.adjust')} {onClose}>
|
|
31
|
+
{#snippet children()}
|
|
32
|
+
<div class="adjustments-grid">
|
|
38
33
|
<!-- Exposure -->
|
|
39
34
|
<div class="adjustment-control">
|
|
40
35
|
<label for="exposure">
|
|
@@ -194,60 +189,25 @@ function handleWheel(e) {
|
|
|
194
189
|
oninput={(e) => handleChange('grain', Number(e.currentTarget.value))}
|
|
195
190
|
/>
|
|
196
191
|
</div>
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
{
|
|
202
|
-
|
|
203
|
-
|
|
192
|
+
</div>
|
|
193
|
+
{/snippet}
|
|
194
|
+
|
|
195
|
+
{#snippet actions()}
|
|
196
|
+
<button class="btn btn-secondary" onclick={resetAll}>
|
|
197
|
+
{$_('editor.reset')}
|
|
198
|
+
</button>
|
|
199
|
+
{/snippet}
|
|
200
|
+
</ToolPanel>
|
|
204
201
|
</div>
|
|
205
202
|
|
|
206
203
|
<style>
|
|
207
|
-
.adjust-tool {
|
|
208
|
-
display: flex;
|
|
209
|
-
flex-direction: column;
|
|
210
|
-
gap: 1rem;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
.tool-header {
|
|
214
|
-
display: flex;
|
|
215
|
-
justify-content: space-between;
|
|
216
|
-
align-items: center;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
.tool-header h3 {
|
|
220
|
-
margin: 0;
|
|
221
|
-
font-size: 1.1rem;
|
|
222
|
-
color: #fff;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
.close-btn {
|
|
226
|
-
display: flex;
|
|
227
|
-
align-items: center;
|
|
228
|
-
justify-content: center;
|
|
229
|
-
padding: 0.25rem;
|
|
230
|
-
background: transparent;
|
|
231
|
-
border: none;
|
|
232
|
-
color: #999;
|
|
233
|
-
cursor: pointer;
|
|
234
|
-
border-radius: 4px;
|
|
235
|
-
transition: all 0.2s;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
.close-btn:hover {
|
|
239
|
-
background: #444;
|
|
240
|
-
color: #fff;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
204
|
.adjustments-grid {
|
|
244
205
|
display: grid;
|
|
245
206
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
246
207
|
gap: 1rem;
|
|
247
208
|
}
|
|
248
209
|
|
|
249
|
-
|
|
250
|
-
|
|
210
|
+
@media (max-width: 767px) {
|
|
251
211
|
.adjustments-grid {
|
|
252
212
|
grid-template-columns: 1fr;
|
|
253
213
|
gap: 0.75rem;
|
|
@@ -330,12 +290,6 @@ function handleWheel(e) {
|
|
|
330
290
|
transform: scale(1.1);
|
|
331
291
|
}
|
|
332
292
|
|
|
333
|
-
.tool-actions {
|
|
334
|
-
display: flex;
|
|
335
|
-
justify-content: flex-end;
|
|
336
|
-
gap: 0.5rem;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
293
|
.btn {
|
|
340
294
|
padding: 0.5rem 1rem;
|
|
341
295
|
border: none;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<script lang="ts">import { onMount } from 'svelte';
|
|
2
2
|
import { _ } from 'svelte-i18n';
|
|
3
3
|
import { screenToImageCoords } from '../utils/canvas';
|
|
4
|
-
import {
|
|
4
|
+
import { Pencil, Eraser, ArrowRight, Square } from 'lucide-svelte';
|
|
5
|
+
import ToolPanel from './ToolPanel.svelte';
|
|
5
6
|
let { canvas, image, viewport, transform, annotations, cropArea, onUpdate, onClose, onViewportChange } = $props();
|
|
6
7
|
let containerElement = $state(null);
|
|
7
8
|
// Tool settings
|
|
@@ -14,6 +15,10 @@ const colorPresets = ['#FF6B6B', '#FFA94D', '#FFD93D', '#6BCB77', '#4D96FF', '#9
|
|
|
14
15
|
// Drawing state
|
|
15
16
|
let isDrawing = $state(false);
|
|
16
17
|
let currentAnnotation = $state(null);
|
|
18
|
+
// Panning state (Space + drag)
|
|
19
|
+
let isSpaceHeld = $state(false);
|
|
20
|
+
let isPanning = $state(false);
|
|
21
|
+
let panStart = $state(null);
|
|
17
22
|
// Helper to get coordinates from mouse or touch event
|
|
18
23
|
function getEventCoords(event) {
|
|
19
24
|
if ('touches' in event && event.touches.length > 0) {
|
|
@@ -82,16 +87,41 @@ onMount(() => {
|
|
|
82
87
|
}
|
|
83
88
|
};
|
|
84
89
|
});
|
|
90
|
+
// Keyboard handlers for panning (Space + drag)
|
|
91
|
+
function handleKeyDown(event) {
|
|
92
|
+
if (event.code === 'Space' && !isSpaceHeld) {
|
|
93
|
+
isSpaceHeld = true;
|
|
94
|
+
event.preventDefault();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function handleKeyUp(event) {
|
|
98
|
+
if (event.code === 'Space') {
|
|
99
|
+
isSpaceHeld = false;
|
|
100
|
+
isPanning = false;
|
|
101
|
+
panStart = null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
85
104
|
function handleMouseDown(event) {
|
|
86
105
|
if (!canvas || !image)
|
|
87
106
|
return;
|
|
88
107
|
if ('button' in event && event.button !== 0)
|
|
89
108
|
return;
|
|
90
109
|
const coords = getEventCoords(event);
|
|
110
|
+
event.preventDefault();
|
|
111
|
+
// Start panning if space is held
|
|
112
|
+
if (isSpaceHeld) {
|
|
113
|
+
isPanning = true;
|
|
114
|
+
panStart = {
|
|
115
|
+
x: coords.clientX,
|
|
116
|
+
y: coords.clientY,
|
|
117
|
+
offsetX: viewport.offsetX,
|
|
118
|
+
offsetY: viewport.offsetY
|
|
119
|
+
};
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
91
122
|
const imagePoint = toImageCoords(coords.clientX, coords.clientY);
|
|
92
123
|
if (!imagePoint)
|
|
93
124
|
return;
|
|
94
|
-
event.preventDefault();
|
|
95
125
|
if (currentTool === 'eraser') {
|
|
96
126
|
// Find and remove annotation at click point
|
|
97
127
|
const canvasPoint = { x: coords.clientX, y: coords.clientY };
|
|
@@ -136,9 +166,20 @@ function handleMouseDown(event) {
|
|
|
136
166
|
// This absorbs micro-movements of the mouse
|
|
137
167
|
const MIN_POINT_DISTANCE = 3;
|
|
138
168
|
function handleMouseMove(event) {
|
|
169
|
+
const coords = getEventCoords(event);
|
|
170
|
+
// Handle panning
|
|
171
|
+
if (isPanning && panStart && onViewportChange) {
|
|
172
|
+
event.preventDefault();
|
|
173
|
+
const dx = coords.clientX - panStart.x;
|
|
174
|
+
const dy = coords.clientY - panStart.y;
|
|
175
|
+
onViewportChange({
|
|
176
|
+
offsetX: panStart.offsetX + dx,
|
|
177
|
+
offsetY: panStart.offsetY + dy
|
|
178
|
+
});
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
139
181
|
if (!isDrawing || !currentAnnotation || !canvas || !image)
|
|
140
182
|
return;
|
|
141
|
-
const coords = getEventCoords(event);
|
|
142
183
|
const imagePoint = toImageCoords(coords.clientX, coords.clientY);
|
|
143
184
|
if (!imagePoint)
|
|
144
185
|
return;
|
|
@@ -165,6 +206,12 @@ function handleMouseMove(event) {
|
|
|
165
206
|
}
|
|
166
207
|
}
|
|
167
208
|
function handleMouseUp(event) {
|
|
209
|
+
// Stop panning
|
|
210
|
+
if (isPanning) {
|
|
211
|
+
isPanning = false;
|
|
212
|
+
panStart = null;
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
168
215
|
if (!isDrawing || !currentAnnotation) {
|
|
169
216
|
isDrawing = false;
|
|
170
217
|
return;
|
|
@@ -295,12 +342,15 @@ let currentAnnotationCanvas = $derived.by(() => {
|
|
|
295
342
|
<svelte:window
|
|
296
343
|
onmousemove={handleMouseMove}
|
|
297
344
|
onmouseup={handleMouseUp}
|
|
345
|
+
onkeydown={handleKeyDown}
|
|
346
|
+
onkeyup={handleKeyUp}
|
|
298
347
|
/>
|
|
299
348
|
|
|
300
349
|
<!-- Overlay -->
|
|
301
350
|
<div
|
|
302
351
|
bind:this={containerElement}
|
|
303
352
|
class="annotation-tool-overlay"
|
|
353
|
+
class:panning={isSpaceHeld}
|
|
304
354
|
onmousedown={handleMouseDown}
|
|
305
355
|
role="button"
|
|
306
356
|
tabindex="-1"
|
|
@@ -442,15 +492,8 @@ let currentAnnotationCanvas = $derived.by(() => {
|
|
|
442
492
|
</div>
|
|
443
493
|
|
|
444
494
|
<!-- Control panel -->
|
|
445
|
-
<
|
|
446
|
-
|
|
447
|
-
<h3>{$_('editor.annotate')}</h3>
|
|
448
|
-
<button class="close-btn" onclick={onClose} title={$_('editor.close')}>
|
|
449
|
-
<X size={20} />
|
|
450
|
-
</button>
|
|
451
|
-
</div>
|
|
452
|
-
|
|
453
|
-
<div class="panel-content">
|
|
495
|
+
<ToolPanel title={$_('editor.annotate')} {onClose}>
|
|
496
|
+
{#snippet children()}
|
|
454
497
|
<!-- Tool selection -->
|
|
455
498
|
<div class="tool-group">
|
|
456
499
|
<span class="group-label">{$_('annotate.tool')}</span>
|
|
@@ -544,15 +587,14 @@ let currentAnnotationCanvas = $derived.by(() => {
|
|
|
544
587
|
</button>
|
|
545
588
|
</label>
|
|
546
589
|
</div>
|
|
590
|
+
{/snippet}
|
|
547
591
|
|
|
548
|
-
|
|
549
|
-
<
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
</div>
|
|
555
|
-
</div>
|
|
592
|
+
{#snippet actions()}
|
|
593
|
+
<button class="btn btn-danger" onclick={handleClearAll} disabled={annotations.length === 0}>
|
|
594
|
+
{$_('annotate.clearAll')}
|
|
595
|
+
</button>
|
|
596
|
+
{/snippet}
|
|
597
|
+
</ToolPanel>
|
|
556
598
|
|
|
557
599
|
<style>
|
|
558
600
|
.annotation-tool-overlay {
|
|
@@ -565,90 +607,51 @@ let currentAnnotationCanvas = $derived.by(() => {
|
|
|
565
607
|
user-select: none;
|
|
566
608
|
}
|
|
567
609
|
|
|
610
|
+
.annotation-tool-overlay.panning {
|
|
611
|
+
cursor: grab;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
.annotation-tool-overlay.panning:active {
|
|
615
|
+
cursor: grabbing;
|
|
616
|
+
}
|
|
617
|
+
|
|
568
618
|
.annotation-tool-svg {
|
|
569
619
|
width: 100%;
|
|
570
620
|
height: 100%;
|
|
571
621
|
pointer-events: none;
|
|
572
622
|
}
|
|
573
623
|
|
|
574
|
-
.
|
|
575
|
-
|
|
576
|
-
top: 1rem;
|
|
577
|
-
right: 1rem;
|
|
578
|
-
background: rgba(30, 30, 30, 0.95);
|
|
579
|
-
border: 1px solid #444;
|
|
580
|
-
border-radius: 8px;
|
|
581
|
-
padding: 1rem;
|
|
582
|
-
min-width: 250px;
|
|
583
|
-
backdrop-filter: blur(10px);
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
@media (max-width: 767px) {
|
|
587
|
-
|
|
588
|
-
.annotation-tool-panel {
|
|
589
|
-
position: absolute;
|
|
590
|
-
left: 0;
|
|
591
|
-
right: 0;
|
|
592
|
-
top: auto;
|
|
593
|
-
bottom: 0;
|
|
594
|
-
width: auto;
|
|
595
|
-
min-width: auto;
|
|
596
|
-
max-height: 50vh;
|
|
597
|
-
border-radius: 16px 16px 0 0;
|
|
598
|
-
z-index: 1001;
|
|
599
|
-
overflow-y: auto
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
.panel-header {
|
|
604
|
-
display: flex;
|
|
605
|
-
justify-content: space-between;
|
|
606
|
-
align-items: center;
|
|
607
|
-
margin-bottom: 1rem;
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
.panel-header h3 {
|
|
611
|
-
margin: 0;
|
|
612
|
-
font-size: 1.1rem;
|
|
613
|
-
color: #fff;
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
.close-btn {
|
|
617
|
-
display: flex;
|
|
618
|
-
align-items: center;
|
|
619
|
-
justify-content: center;
|
|
620
|
-
padding: 0.25rem;
|
|
621
|
-
background: transparent;
|
|
622
|
-
border: none;
|
|
623
|
-
color: #999;
|
|
624
|
-
cursor: pointer;
|
|
625
|
-
border-radius: 4px;
|
|
626
|
-
transition: all 0.2s;
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
.close-btn:hover {
|
|
630
|
-
background: #444;
|
|
631
|
-
color: #fff;
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
.panel-content {
|
|
624
|
+
.tool-group,
|
|
625
|
+
.control-group {
|
|
635
626
|
display: flex;
|
|
636
627
|
flex-direction: column;
|
|
637
|
-
gap:
|
|
628
|
+
gap: 0.5rem;
|
|
638
629
|
}
|
|
639
630
|
|
|
631
|
+
@media (max-width: 767px) {
|
|
632
|
+
|
|
640
633
|
.tool-group,
|
|
641
634
|
.control-group {
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
635
|
+
flex-direction: row;
|
|
636
|
+
align-items: center;
|
|
637
|
+
gap: 0.75rem
|
|
645
638
|
}
|
|
639
|
+
}
|
|
646
640
|
|
|
647
641
|
.group-label {
|
|
648
642
|
font-size: 0.9rem;
|
|
649
643
|
color: #ccc;
|
|
650
644
|
}
|
|
651
645
|
|
|
646
|
+
@media (max-width: 767px) {
|
|
647
|
+
|
|
648
|
+
.group-label {
|
|
649
|
+
font-size: 0.75rem;
|
|
650
|
+
white-space: nowrap;
|
|
651
|
+
min-width: 50px
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
652
655
|
.tool-buttons {
|
|
653
656
|
display: flex;
|
|
654
657
|
gap: 0.5rem;
|
|
@@ -731,8 +734,15 @@ let currentAnnotationCanvas = $derived.by(() => {
|
|
|
731
734
|
color: #ccc;
|
|
732
735
|
}
|
|
733
736
|
|
|
737
|
+
@media (max-width: 767px) {
|
|
738
|
+
|
|
739
|
+
.control-group label {
|
|
740
|
+
font-size: 0.75rem;
|
|
741
|
+
gap: 0.5rem
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
734
745
|
.control-group .value {
|
|
735
|
-
color: var(--primary-color, #63b97b);
|
|
736
746
|
font-weight: 600;
|
|
737
747
|
}
|
|
738
748
|
|
|
@@ -745,6 +755,14 @@ let currentAnnotationCanvas = $derived.by(() => {
|
|
|
745
755
|
cursor: pointer;
|
|
746
756
|
}
|
|
747
757
|
|
|
758
|
+
@media (max-width: 767px) {
|
|
759
|
+
|
|
760
|
+
.control-group input[type='range'] {
|
|
761
|
+
width: 80px;
|
|
762
|
+
flex-shrink: 0
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
|
|
748
766
|
.control-group input[type='range']::-webkit-slider-thumb {
|
|
749
767
|
appearance: none;
|
|
750
768
|
width: 16px;
|
|
@@ -769,13 +787,6 @@ let currentAnnotationCanvas = $derived.by(() => {
|
|
|
769
787
|
transition: all 0.2s;
|
|
770
788
|
}
|
|
771
789
|
|
|
772
|
-
.panel-actions {
|
|
773
|
-
display: flex;
|
|
774
|
-
justify-content: flex-end;
|
|
775
|
-
gap: 0.5rem;
|
|
776
|
-
margin-top: 0.5rem;
|
|
777
|
-
}
|
|
778
|
-
|
|
779
790
|
.btn {
|
|
780
791
|
padding: 0.5rem 1rem;
|
|
781
792
|
border: none;
|
|
@@ -785,6 +796,14 @@ let currentAnnotationCanvas = $derived.by(() => {
|
|
|
785
796
|
transition: all 0.2s;
|
|
786
797
|
}
|
|
787
798
|
|
|
799
|
+
@media (max-width: 767px) {
|
|
800
|
+
|
|
801
|
+
.btn {
|
|
802
|
+
padding: 0.4rem 0.75rem;
|
|
803
|
+
font-size: 0.75rem
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
788
807
|
.btn:disabled {
|
|
789
808
|
opacity: 0.5;
|
|
790
809
|
cursor: not-allowed;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts">import { onMount } from 'svelte';
|
|
2
2
|
import { _ } from 'svelte-i18n';
|
|
3
3
|
import { screenToImageCoords, imageToCanvasCoords } from '../utils/canvas';
|
|
4
|
-
import
|
|
4
|
+
import ToolPanel from './ToolPanel.svelte';
|
|
5
5
|
let { canvas, image, viewport, transform, blurAreas, cropArea, onUpdate, onClose, onViewportChange } = $props();
|
|
6
6
|
let containerElement = $state(null);
|
|
7
7
|
// Helper to get coordinates from mouse or touch event
|
|
@@ -402,16 +402,9 @@ function handleTouchEnd(event) {
|
|
|
402
402
|
</div>
|
|
403
403
|
|
|
404
404
|
<!-- Control panel -->
|
|
405
|
-
<
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
<button class="close-btn" onclick={onClose} title={$_('editor.close')}>
|
|
409
|
-
<X size={20} />
|
|
410
|
-
</button>
|
|
411
|
-
</div>
|
|
412
|
-
|
|
413
|
-
{#if selectedArea}
|
|
414
|
-
<div class="panel-content">
|
|
405
|
+
<ToolPanel title={$_('editor.blur')} {onClose}>
|
|
406
|
+
{#snippet children()}
|
|
407
|
+
{#if selectedArea}
|
|
415
408
|
<div class="control-group">
|
|
416
409
|
<label for="blur-strength">
|
|
417
410
|
<span>{$_('blur.strength')}</span>
|
|
@@ -426,19 +419,21 @@ function handleTouchEnd(event) {
|
|
|
426
419
|
oninput={(e) => handleBlurStrengthChange(Number(e.currentTarget.value))}
|
|
427
420
|
/>
|
|
428
421
|
</div>
|
|
429
|
-
|
|
430
|
-
<div class="panel-
|
|
431
|
-
<
|
|
432
|
-
{$_('editor.delete')}
|
|
433
|
-
</button>
|
|
422
|
+
{:else}
|
|
423
|
+
<div class="panel-hint">
|
|
424
|
+
<p>{$_('blur.hint')}</p>
|
|
434
425
|
</div>
|
|
435
|
-
|
|
436
|
-
{
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
426
|
+
{/if}
|
|
427
|
+
{/snippet}
|
|
428
|
+
|
|
429
|
+
{#snippet actions()}
|
|
430
|
+
{#if selectedArea}
|
|
431
|
+
<button class="btn btn-danger" onclick={handleDeleteArea}>
|
|
432
|
+
{$_('editor.delete')}
|
|
433
|
+
</button>
|
|
434
|
+
{/if}
|
|
435
|
+
{/snippet}
|
|
436
|
+
</ToolPanel>
|
|
442
437
|
|
|
443
438
|
<style>
|
|
444
439
|
.blur-tool-overlay {
|
|
@@ -461,55 +456,6 @@ function handleTouchEnd(event) {
|
|
|
461
456
|
pointer-events: all;
|
|
462
457
|
}
|
|
463
458
|
|
|
464
|
-
.blur-tool-panel {
|
|
465
|
-
position: absolute;
|
|
466
|
-
top: 1rem;
|
|
467
|
-
right: 1rem;
|
|
468
|
-
background: rgba(30, 30, 30, 0.95);
|
|
469
|
-
border: 1px solid #444;
|
|
470
|
-
border-radius: 8px;
|
|
471
|
-
padding: 1rem;
|
|
472
|
-
min-width: 250px;
|
|
473
|
-
backdrop-filter: blur(10px);
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
.panel-header {
|
|
477
|
-
display: flex;
|
|
478
|
-
justify-content: space-between;
|
|
479
|
-
align-items: center;
|
|
480
|
-
margin-bottom: 1rem;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
.panel-header h3 {
|
|
484
|
-
margin: 0;
|
|
485
|
-
font-size: 1.1rem;
|
|
486
|
-
color: #fff;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
.close-btn {
|
|
490
|
-
display: flex;
|
|
491
|
-
align-items: center;
|
|
492
|
-
justify-content: center;
|
|
493
|
-
padding: 0.25rem;
|
|
494
|
-
background: transparent;
|
|
495
|
-
border: none;
|
|
496
|
-
color: #999;
|
|
497
|
-
cursor: pointer;
|
|
498
|
-
border-radius: 4px;
|
|
499
|
-
transition: all 0.2s;
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
.close-btn:hover {
|
|
503
|
-
background: #444;
|
|
504
|
-
color: #fff;
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
.panel-content {
|
|
508
|
-
display: flex;
|
|
509
|
-
flex-direction: column;
|
|
510
|
-
gap: 1rem;
|
|
511
|
-
}
|
|
512
|
-
|
|
513
459
|
.control-group {
|
|
514
460
|
display: flex;
|
|
515
461
|
flex-direction: column;
|
|
@@ -568,12 +514,6 @@ function handleTouchEnd(event) {
|
|
|
568
514
|
transform: scale(1.1);
|
|
569
515
|
}
|
|
570
516
|
|
|
571
|
-
.panel-actions {
|
|
572
|
-
display: flex;
|
|
573
|
-
justify-content: flex-end;
|
|
574
|
-
gap: 0.5rem;
|
|
575
|
-
}
|
|
576
|
-
|
|
577
517
|
.btn {
|
|
578
518
|
padding: 0.5rem 1rem;
|
|
579
519
|
border: none;
|