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.
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">import { _ } from 'svelte-i18n';
2
- import { X } from 'lucide-svelte';
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
- <div class="tool-header">
31
- <h3>{$_('editor.adjust')}</h3>
32
- <button class="close-btn" onclick={onClose} title={$_('editor.close')}>
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
- </div>
198
-
199
- <div class="tool-actions">
200
- <button class="btn btn-secondary" onclick={resetAll}>
201
- {$_('editor.reset')}
202
- </button>
203
- </div>
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
- @media (max-width: 767px) {
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 { X, Pencil, Eraser, ArrowRight, Square } from 'lucide-svelte';
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
- <div class="annotation-tool-panel">
495
- <div class="panel-header">
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
- <!-- Actions -->
598
- <div class="panel-actions">
599
- <button class="btn btn-danger" onclick={handleClearAll} disabled={annotations.length === 0}>
600
- {$_('annotate.clearAll')}
601
- </button>
602
- </div>
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
- .annotation-tool-panel {
632
- position: absolute;
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: 1rem;
628
+ gap: 0.5rem;
695
629
  }
696
630
 
631
+ @media (max-width: 767px) {
632
+
697
633
  .tool-group,
698
634
  .control-group {
699
- display: flex;
700
- flex-direction: column;
701
- gap: 0.5rem;
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 { X } from 'lucide-svelte';
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
- <div class="blur-tool-panel">
406
- <div class="panel-header">
407
- <h3>{$_('editor.blur')}</h3>
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-actions">
431
- <button class="btn btn-danger" onclick={handleDeleteArea}>
432
- {$_('editor.delete')}
433
- </button>
422
+ {:else}
423
+ <div class="panel-hint">
424
+ <p>{$_('blur.hint')}</p>
434
425
  </div>
435
- </div>
436
- {:else}
437
- <div class="panel-hint">
438
- <p>{$_('blur.hint')}</p>
439
- </div>
440
- {/if}
441
- </div>
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
- <div class="tool-header">
19
- <h3>{$_('editor.export')}</h3>
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
- {$_('editor.quality')}: {Math.round(options.quality * 100)}%
48
- </label>
49
- <input
50
- type="range"
51
- min="0.1"
52
- max="1"
53
- step="0.05"
54
- value={options.quality}
55
- oninput={handleQualityChange}
56
- class="quality-slider"
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
- <button class="export-btn" onclick={onExport}>
62
- <Download size={20} />
63
- <span>{$_('editor.download')}</span>
64
- </button>
65
- </div>
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;