tokimeki-image-editor 0.2.1 → 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 +53 -90
- 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
|
|
@@ -491,15 +492,8 @@ let currentAnnotationCanvas = $derived.by(() => {
|
|
|
491
492
|
</div>
|
|
492
493
|
|
|
493
494
|
<!-- Control panel -->
|
|
494
|
-
<
|
|
495
|
-
|
|
496
|
-
<h3>{$_('editor.annotate')}</h3>
|
|
497
|
-
<button class="close-btn" onclick={onClose} title={$_('editor.close')}>
|
|
498
|
-
<X size={20} />
|
|
499
|
-
</button>
|
|
500
|
-
</div>
|
|
501
|
-
|
|
502
|
-
<div class="panel-content">
|
|
495
|
+
<ToolPanel title={$_('editor.annotate')} {onClose}>
|
|
496
|
+
{#snippet children()}
|
|
503
497
|
<!-- Tool selection -->
|
|
504
498
|
<div class="tool-group">
|
|
505
499
|
<span class="group-label">{$_('annotate.tool')}</span>
|
|
@@ -593,15 +587,14 @@ let currentAnnotationCanvas = $derived.by(() => {
|
|
|
593
587
|
</button>
|
|
594
588
|
</label>
|
|
595
589
|
</div>
|
|
590
|
+
{/snippet}
|
|
596
591
|
|
|
597
|
-
|
|
598
|
-
<
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
</div>
|
|
604
|
-
</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>
|
|
605
598
|
|
|
606
599
|
<style>
|
|
607
600
|
.annotation-tool-overlay {
|
|
@@ -628,84 +621,37 @@ let currentAnnotationCanvas = $derived.by(() => {
|
|
|
628
621
|
pointer-events: none;
|
|
629
622
|
}
|
|
630
623
|
|
|
631
|
-
.
|
|
632
|
-
|
|
633
|
-
top: 1rem;
|
|
634
|
-
right: 1rem;
|
|
635
|
-
background: rgba(30, 30, 30, 0.95);
|
|
636
|
-
border: 1px solid #444;
|
|
637
|
-
border-radius: 8px;
|
|
638
|
-
padding: 1rem;
|
|
639
|
-
min-width: 250px;
|
|
640
|
-
backdrop-filter: blur(10px);
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
@media (max-width: 767px) {
|
|
644
|
-
|
|
645
|
-
.annotation-tool-panel {
|
|
646
|
-
position: absolute;
|
|
647
|
-
left: 0;
|
|
648
|
-
right: 0;
|
|
649
|
-
top: auto;
|
|
650
|
-
bottom: 0;
|
|
651
|
-
width: auto;
|
|
652
|
-
min-width: auto;
|
|
653
|
-
max-height: 50vh;
|
|
654
|
-
border-radius: 16px 16px 0 0;
|
|
655
|
-
z-index: 1001;
|
|
656
|
-
overflow-y: auto
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
.panel-header {
|
|
661
|
-
display: flex;
|
|
662
|
-
justify-content: space-between;
|
|
663
|
-
align-items: center;
|
|
664
|
-
margin-bottom: 1rem;
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
.panel-header h3 {
|
|
668
|
-
margin: 0;
|
|
669
|
-
font-size: 1.1rem;
|
|
670
|
-
color: #fff;
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
.close-btn {
|
|
674
|
-
display: flex;
|
|
675
|
-
align-items: center;
|
|
676
|
-
justify-content: center;
|
|
677
|
-
padding: 0.25rem;
|
|
678
|
-
background: transparent;
|
|
679
|
-
border: none;
|
|
680
|
-
color: #999;
|
|
681
|
-
cursor: pointer;
|
|
682
|
-
border-radius: 4px;
|
|
683
|
-
transition: all 0.2s;
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
.close-btn:hover {
|
|
687
|
-
background: #444;
|
|
688
|
-
color: #fff;
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
.panel-content {
|
|
624
|
+
.tool-group,
|
|
625
|
+
.control-group {
|
|
692
626
|
display: flex;
|
|
693
627
|
flex-direction: column;
|
|
694
|
-
gap:
|
|
628
|
+
gap: 0.5rem;
|
|
695
629
|
}
|
|
696
630
|
|
|
631
|
+
@media (max-width: 767px) {
|
|
632
|
+
|
|
697
633
|
.tool-group,
|
|
698
634
|
.control-group {
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
635
|
+
flex-direction: row;
|
|
636
|
+
align-items: center;
|
|
637
|
+
gap: 0.75rem
|
|
702
638
|
}
|
|
639
|
+
}
|
|
703
640
|
|
|
704
641
|
.group-label {
|
|
705
642
|
font-size: 0.9rem;
|
|
706
643
|
color: #ccc;
|
|
707
644
|
}
|
|
708
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
|
+
|
|
709
655
|
.tool-buttons {
|
|
710
656
|
display: flex;
|
|
711
657
|
gap: 0.5rem;
|
|
@@ -788,6 +734,14 @@ let currentAnnotationCanvas = $derived.by(() => {
|
|
|
788
734
|
color: #ccc;
|
|
789
735
|
}
|
|
790
736
|
|
|
737
|
+
@media (max-width: 767px) {
|
|
738
|
+
|
|
739
|
+
.control-group label {
|
|
740
|
+
font-size: 0.75rem;
|
|
741
|
+
gap: 0.5rem
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
791
745
|
.control-group .value {
|
|
792
746
|
font-weight: 600;
|
|
793
747
|
}
|
|
@@ -801,6 +755,14 @@ let currentAnnotationCanvas = $derived.by(() => {
|
|
|
801
755
|
cursor: pointer;
|
|
802
756
|
}
|
|
803
757
|
|
|
758
|
+
@media (max-width: 767px) {
|
|
759
|
+
|
|
760
|
+
.control-group input[type='range'] {
|
|
761
|
+
width: 80px;
|
|
762
|
+
flex-shrink: 0
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
|
|
804
766
|
.control-group input[type='range']::-webkit-slider-thumb {
|
|
805
767
|
appearance: none;
|
|
806
768
|
width: 16px;
|
|
@@ -825,13 +787,6 @@ let currentAnnotationCanvas = $derived.by(() => {
|
|
|
825
787
|
transition: all 0.2s;
|
|
826
788
|
}
|
|
827
789
|
|
|
828
|
-
.panel-actions {
|
|
829
|
-
display: flex;
|
|
830
|
-
justify-content: flex-end;
|
|
831
|
-
gap: 0.5rem;
|
|
832
|
-
margin-top: 0.5rem;
|
|
833
|
-
}
|
|
834
|
-
|
|
835
790
|
.btn {
|
|
836
791
|
padding: 0.5rem 1rem;
|
|
837
792
|
border: none;
|
|
@@ -841,6 +796,14 @@ let currentAnnotationCanvas = $derived.by(() => {
|
|
|
841
796
|
transition: all 0.2s;
|
|
842
797
|
}
|
|
843
798
|
|
|
799
|
+
@media (max-width: 767px) {
|
|
800
|
+
|
|
801
|
+
.btn {
|
|
802
|
+
padding: 0.4rem 0.75rem;
|
|
803
|
+
font-size: 0.75rem
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
844
807
|
.btn:disabled {
|
|
845
808
|
opacity: 0.5;
|
|
846
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;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">import { _ } from 'svelte-i18n';
|
|
2
2
|
import { Download } from 'lucide-svelte';
|
|
3
|
+
import ToolPanel from './ToolPanel.svelte';
|
|
3
4
|
let { options, onChange, onExport, onClose } = $props();
|
|
4
5
|
function handleFormatChange(format) {
|
|
5
6
|
onChange({ format });
|
|
@@ -15,54 +16,53 @@ function handleWheel(e) {
|
|
|
15
16
|
</script>
|
|
16
17
|
|
|
17
18
|
<div class="export-tool" onwheel={handleWheel}>
|
|
18
|
-
<
|
|
19
|
-
|
|
20
|
-
<button class="close-btn" onclick={onClose}>✕</button>
|
|
21
|
-
</div>
|
|
22
|
-
|
|
23
|
-
<div class="tool-content">
|
|
24
|
-
<div class="tool-group">
|
|
25
|
-
<label>{$_('editor.format')}</label>
|
|
26
|
-
<div class="format-buttons">
|
|
27
|
-
<button
|
|
28
|
-
class="format-btn"
|
|
29
|
-
class:active={options.format === 'png'}
|
|
30
|
-
onclick={() => handleFormatChange('png')}
|
|
31
|
-
>
|
|
32
|
-
PNG
|
|
33
|
-
</button>
|
|
34
|
-
<button
|
|
35
|
-
class="format-btn"
|
|
36
|
-
class:active={options.format === 'jpeg'}
|
|
37
|
-
onclick={() => handleFormatChange('jpeg')}
|
|
38
|
-
>
|
|
39
|
-
JPEG
|
|
40
|
-
</button>
|
|
41
|
-
</div>
|
|
42
|
-
</div>
|
|
43
|
-
|
|
44
|
-
{#if options.format === 'jpeg'}
|
|
19
|
+
<ToolPanel title={$_('editor.export')} {onClose}>
|
|
20
|
+
{#snippet children()}
|
|
45
21
|
<div class="tool-group">
|
|
46
|
-
<label>
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
22
|
+
<label>{$_('editor.format')}</label>
|
|
23
|
+
<div class="format-buttons">
|
|
24
|
+
<button
|
|
25
|
+
class="format-btn"
|
|
26
|
+
class:active={options.format === 'png'}
|
|
27
|
+
onclick={() => handleFormatChange('png')}
|
|
28
|
+
>
|
|
29
|
+
PNG
|
|
30
|
+
</button>
|
|
31
|
+
<button
|
|
32
|
+
class="format-btn"
|
|
33
|
+
class:active={options.format === 'jpeg'}
|
|
34
|
+
onclick={() => handleFormatChange('jpeg')}
|
|
35
|
+
>
|
|
36
|
+
JPEG
|
|
37
|
+
</button>
|
|
38
|
+
</div>
|
|
58
39
|
</div>
|
|
59
|
-
{/if}
|
|
60
40
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
41
|
+
{#if options.format === 'jpeg'}
|
|
42
|
+
<div class="tool-group">
|
|
43
|
+
<label>
|
|
44
|
+
{$_('editor.quality')}: {Math.round(options.quality * 100)}%
|
|
45
|
+
</label>
|
|
46
|
+
<input
|
|
47
|
+
type="range"
|
|
48
|
+
min="0.1"
|
|
49
|
+
max="1"
|
|
50
|
+
step="0.05"
|
|
51
|
+
value={options.quality}
|
|
52
|
+
oninput={handleQualityChange}
|
|
53
|
+
class="quality-slider"
|
|
54
|
+
/>
|
|
55
|
+
</div>
|
|
56
|
+
{/if}
|
|
57
|
+
{/snippet}
|
|
58
|
+
|
|
59
|
+
{#snippet actions()}
|
|
60
|
+
<button class="export-btn" onclick={onExport}>
|
|
61
|
+
<Download size={20} />
|
|
62
|
+
<span>{$_('editor.download')}</span>
|
|
63
|
+
</button>
|
|
64
|
+
{/snippet}
|
|
65
|
+
</ToolPanel>
|
|
66
66
|
</div>
|
|
67
67
|
|
|
68
68
|
<style>
|
|
@@ -72,43 +72,6 @@ function handleWheel(e) {
|
|
|
72
72
|
gap: 1rem;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
.tool-header {
|
|
76
|
-
display: flex;
|
|
77
|
-
justify-content: space-between;
|
|
78
|
-
align-items: center;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
.tool-header h3 {
|
|
82
|
-
margin: 0;
|
|
83
|
-
font-size: 1.1rem;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
.close-btn {
|
|
87
|
-
background: none;
|
|
88
|
-
border: none;
|
|
89
|
-
color: #fff;
|
|
90
|
-
font-size: 1.5rem;
|
|
91
|
-
cursor: pointer;
|
|
92
|
-
padding: 0;
|
|
93
|
-
width: 30px;
|
|
94
|
-
height: 30px;
|
|
95
|
-
display: flex;
|
|
96
|
-
align-items: center;
|
|
97
|
-
justify-content: center;
|
|
98
|
-
border-radius: 4px;
|
|
99
|
-
transition: background 0.2s;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
.close-btn:hover {
|
|
103
|
-
background: #444;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
.tool-content {
|
|
107
|
-
display: flex;
|
|
108
|
-
flex-direction: column;
|
|
109
|
-
gap: 1.5rem;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
75
|
.tool-group {
|
|
113
76
|
display: flex;
|
|
114
77
|
flex-direction: column;
|