@gradio/multimodaltextbox 0.10.22-dev.0 → 0.11.0-dev.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.
@@ -12,19 +12,19 @@
12
12
  import { Image } from "@gradio/image/shared";
13
13
  import type { I18nFormatter } from "js/core/src/gradio_helper";
14
14
  import type { FileData, Client } from "@gradio/client";
15
- import type { WaveformOptions } from "../../audio/shared/types";
15
+ import type { WaveformOptions } from "@gradio/audio";
16
16
  import {
17
17
  Clear,
18
18
  File,
19
19
  Music,
20
20
  Paperclip,
21
21
  Video,
22
- Send,
22
+ ArrowUp,
23
23
  Square,
24
24
  Microphone
25
25
  } from "@gradio/icons";
26
26
  import type { SelectData } from "@gradio/utils";
27
- import InteractiveAudio from "../../audio/interactive/InteractiveAudio.svelte";
27
+ import { BaseInteractiveAudio as InteractiveAudio } from "@gradio/audio";
28
28
  import type { InputHTMLAttributes } from "./types";
29
29
 
30
30
  export let value: { text: string; files: FileData[] } = {
@@ -65,6 +65,7 @@
65
65
  | "microphone,upload" = "upload";
66
66
  export let active_source: "microphone" | null = null;
67
67
  export let html_attributes: InputHTMLAttributes | null = null;
68
+ export let upload_promise: Promise<any> | null = null;
68
69
 
69
70
  let upload_component: Upload;
70
71
  let el: HTMLTextAreaElement | HTMLInputElement;
@@ -88,6 +89,10 @@
88
89
  ? file_types_string.split(",").map((s) => s.trim())
89
90
  : null;
90
91
  $: dispatch("drag", dragging);
92
+ $: show_upload =
93
+ sources &&
94
+ sources.includes("upload") &&
95
+ !(file_count === "single" && value.files.length > 0);
91
96
  let mic_audio: FileData | null = null;
92
97
 
93
98
  let full_container: HTMLDivElement;
@@ -98,7 +103,7 @@
98
103
  }
99
104
 
100
105
  $: if (value === null) value = { text: "", files: [] };
101
- $: value, el && lines !== max_lines && resize(el, lines, max_lines);
106
+ $: (value, el && lines !== max_lines && resize(el, lines, max_lines));
102
107
 
103
108
  const dispatch = createEventDispatcher<{
104
109
  change: typeof value;
@@ -165,9 +170,9 @@
165
170
  }
166
171
 
167
172
  async function handle_keypress(e: KeyboardEvent): Promise<void> {
168
- await tick();
169
173
  if (e.key === "Enter" && e.shiftKey && lines > 1) {
170
174
  e.preventDefault();
175
+ await tick();
171
176
  dispatch("submit");
172
177
  } else if (
173
178
  e.key === "Enter" &&
@@ -176,13 +181,10 @@
176
181
  max_lines >= 1
177
182
  ) {
178
183
  e.preventDefault();
184
+ await tick();
179
185
  dispatch("submit");
180
186
  active_source = null;
181
- if (mic_audio) {
182
- value.files.push(mic_audio);
183
- value = value;
184
- mic_audio = null;
185
- }
187
+ add_mic_audio_to_files();
186
188
  }
187
189
  }
188
190
 
@@ -234,9 +236,7 @@
234
236
  dispatch("stop");
235
237
  }
236
238
 
237
- function handle_submit(): void {
238
- dispatch("submit");
239
- active_source = null;
239
+ function add_mic_audio_to_files(): void {
240
240
  if (mic_audio) {
241
241
  value.files.push(mic_audio);
242
242
  value = value;
@@ -244,6 +244,12 @@
244
244
  }
245
245
  }
246
246
 
247
+ function handle_submit(): void {
248
+ dispatch("submit");
249
+ active_source = null;
250
+ add_mic_audio_to_files();
251
+ }
252
+
247
253
  async function handle_paste(event: ClipboardEvent): Promise<void> {
248
254
  if (!event.clipboardData) return;
249
255
  const items = event.clipboardData.items;
@@ -335,80 +341,42 @@
335
341
  aria-label="Multimedia input field"
336
342
  >
337
343
  <BlockTitle {show_label} {info} {rtl}>{label}</BlockTitle>
338
- {#if value.files.length > 0 || uploading}
339
- <div
340
- class="thumbnails scroll-hide"
341
- aria-label="Uploaded files"
342
- data-testid="container_el"
343
- style="display: {value.files.length > 0 || uploading ? 'flex' : 'none'};"
344
- >
345
- {#each value.files as file, index}
346
- <span role="listitem" aria-label="File thumbnail">
347
- <button class="thumbnail-item thumbnail-small">
348
- <button
349
- class:disabled
350
- class="delete-button"
351
- on:click={(event) => remove_thumbnail(event, index)}
352
- ><Clear /></button
353
- >
354
- {#if file.mime_type && file.mime_type.includes("image")}
355
- <Image
356
- src={file.url}
357
- title={null}
358
- alt=""
359
- loading="lazy"
360
- class={"thumbnail-image"}
361
- />
362
- {:else if file.mime_type && file.mime_type.includes("audio")}
363
- <Music />
364
- {:else if file.mime_type && file.mime_type.includes("video")}
365
- <Video />
366
- {:else}
367
- <File />
368
- {/if}
369
- </button>
370
- </span>
371
- {/each}
372
- {#if uploading}
373
- <div class="loader" role="status" aria-label="Uploading"></div>
374
- {/if}
375
- </div>
376
- {/if}
377
- {#if sources && sources.includes("microphone") && active_source === "microphone"}
378
- <InteractiveAudio
379
- on:change={({ detail }) => {
380
- if (detail !== null) {
381
- mic_audio = detail;
382
- }
383
- }}
384
- on:clear={() => {
385
- active_source = null;
386
- }}
387
- on:start_recording={() => dispatch("start_recording")}
388
- on:pause_recording={() => dispatch("pause_recording")}
389
- on:stop_recording={() => dispatch("stop_recording")}
390
- sources={["microphone"]}
391
- class_name="compact-audio"
392
- {recording}
393
- {waveform_settings}
394
- {waveform_options}
395
- {i18n}
396
- {active_source}
397
- {upload}
398
- {stream_handler}
399
- stream_every={1}
400
- editable={true}
401
- {label}
402
- {root}
403
- loop={false}
404
- show_label={false}
405
- buttons={[]}
406
- dragging={false}
407
- />
408
- {/if}
409
344
  <div class="input-container">
410
- {#if sources && sources.includes("upload") && !(file_count === "single" && value.files.length > 0)}
345
+ {#if sources && sources.includes("microphone") && active_source === "microphone"}
346
+ <InteractiveAudio
347
+ on:change={({ detail }) => {
348
+ if (detail !== null) {
349
+ mic_audio = detail;
350
+ }
351
+ }}
352
+ on:clear={() => {
353
+ active_source = null;
354
+ }}
355
+ on:start_recording={() => dispatch("start_recording")}
356
+ on:pause_recording={() => dispatch("pause_recording")}
357
+ on:stop_recording={() => dispatch("stop_recording")}
358
+ sources={["microphone"]}
359
+ class_name="compact-audio"
360
+ {recording}
361
+ {waveform_settings}
362
+ {waveform_options}
363
+ {i18n}
364
+ {active_source}
365
+ {upload}
366
+ {stream_handler}
367
+ stream_every={1}
368
+ editable={true}
369
+ {label}
370
+ {root}
371
+ loop={false}
372
+ show_label={false}
373
+ buttons={[]}
374
+ dragging={false}
375
+ />
376
+ {/if}
377
+ {#if show_upload}
411
378
  <Upload
379
+ bind:upload_promise
412
380
  bind:this={upload_component}
413
381
  on:load={handle_upload}
414
382
  {file_count}
@@ -424,87 +392,147 @@
424
392
  {upload}
425
393
  {stream_handler}
426
394
  />
427
- <button
428
- data-testid="upload-button"
429
- class="upload-button"
430
- {disabled}
431
- on:click={disabled ? undefined : handle_upload_click}
432
- ><Paperclip /></button
433
- >
434
395
  {/if}
435
- {#if sources && sources.includes("microphone")}
436
- <button
437
- data-testid="microphone-button"
438
- class="microphone-button"
439
- class:recording
440
- {disabled}
441
- on:click={disabled
442
- ? undefined
443
- : () => {
396
+
397
+ <div
398
+ class="input-wrapper"
399
+ class:has-files={value.files.length > 0 || uploading}
400
+ >
401
+ {#if value.files.length > 0 || uploading || show_upload}
402
+ <div
403
+ class="thumbnails"
404
+ aria-label="Uploaded files"
405
+ data-testid="container_el"
406
+ >
407
+ {#if show_upload}
408
+ <button
409
+ data-testid="upload-button"
410
+ class="upload-button mobile-thumbnail-add"
411
+ {disabled}
412
+ on:click={handle_upload_click}
413
+ aria-label="Upload a file"
414
+ >
415
+ <Paperclip />
416
+ </button>
417
+ {/if}
418
+ {#each value.files as file, index}
419
+ <span
420
+ class="thumbnail-wrapper"
421
+ role="listitem"
422
+ aria-label="File thumbnail"
423
+ >
424
+ <div class="thumbnail-item thumbnail-small">
425
+ {#if file.mime_type && file.mime_type.includes("image")}
426
+ <Image
427
+ src={file.url}
428
+ restProps={{
429
+ title: null,
430
+ alt: "",
431
+ loading: "lazy",
432
+ class: "thumbnail-image"
433
+ }}
434
+ />
435
+ {:else if file.mime_type && file.mime_type.includes("audio")}
436
+ <Music />
437
+ {:else if file.mime_type && file.mime_type.includes("video")}
438
+ <Video />
439
+ {:else}
440
+ <File />
441
+ {/if}
442
+ <button
443
+ class="delete-button"
444
+ on:click={(event) => remove_thumbnail(event, index)}
445
+ aria-label="Remove file"
446
+ >
447
+ <Clear />
448
+ </button>
449
+ </div>
450
+ </span>
451
+ {/each}
452
+ {#if uploading}
453
+ <div class="loader" role="status" aria-label="Uploading"></div>
454
+ {/if}
455
+ </div>
456
+ {/if}
457
+
458
+ <div class="input-row">
459
+ <textarea
460
+ data-testid="textbox"
461
+ use:text_area_resize={{
462
+ text: value.text,
463
+ lines: lines,
464
+ max_lines: max_lines
465
+ }}
466
+ class:no-label={!show_label}
467
+ dir={rtl ? "rtl" : "ltr"}
468
+ bind:value={value.text}
469
+ bind:this={el}
470
+ {placeholder}
471
+ rows={lines}
472
+ {disabled}
473
+ on:keypress={handle_keypress}
474
+ on:blur
475
+ on:select={handle_select}
476
+ on:focus
477
+ on:scroll={handle_scroll}
478
+ on:paste={handle_paste}
479
+ style={text_align ? "text-align: " + text_align : ""}
480
+ autocapitalize={html_attributes?.autocapitalize}
481
+ autocorrect={html_attributes?.autocorrect}
482
+ spellcheck={html_attributes?.spellcheck}
483
+ autocomplete={html_attributes?.autocomplete}
484
+ tabindex={html_attributes?.tabindex}
485
+ enterkeyhint={html_attributes?.enterkeyhint}
486
+ lang={html_attributes?.lang}
487
+ />
488
+
489
+ {#if sources && sources.includes("microphone")}
490
+ <button
491
+ data-testid="microphone-button"
492
+ class="microphone-button"
493
+ class:recording
494
+ {disabled}
495
+ on:click={() => {
444
496
  active_source =
445
497
  active_source !== "microphone" ? "microphone" : null;
446
498
  }}
447
- >
448
- <Microphone />
449
- </button>
450
- {/if}
451
- <textarea
452
- data-testid="textbox"
453
- use:text_area_resize={{
454
- text: value.text,
455
- lines: lines,
456
- max_lines: max_lines
457
- }}
458
- class="scroll-hide"
459
- class:no-label={!show_label}
460
- dir={rtl ? "rtl" : "ltr"}
461
- bind:value={value.text}
462
- bind:this={el}
463
- {placeholder}
464
- rows={lines}
465
- {disabled}
466
- on:keypress={handle_keypress}
467
- on:blur
468
- on:select={handle_select}
469
- on:focus
470
- on:scroll={handle_scroll}
471
- on:paste={handle_paste}
472
- style={text_align ? "text-align: " + text_align : ""}
473
- autocapitalize={html_attributes?.autocapitalize}
474
- autocorrect={html_attributes?.autocorrect}
475
- spellcheck={html_attributes?.spellcheck}
476
- autocomplete={html_attributes?.autocomplete}
477
- tabindex={html_attributes?.tabindex}
478
- enterkeyhint={html_attributes?.enterkeyhint}
479
- lang={html_attributes?.lang}
480
- />
481
- {#if submit_btn}
482
- <button
483
- class="submit-button"
484
- class:padded-button={submit_btn !== true}
485
- {disabled}
486
- on:click={disabled ? undefined : handle_submit}
487
- >
488
- {#if submit_btn === true}
489
- <Send />
490
- {:else}
491
- {submit_btn}
499
+ aria-label="Record audio"
500
+ >
501
+ <Microphone />
502
+ </button>
492
503
  {/if}
493
- </button>
494
- {/if}
495
- {#if stop_btn}
496
- <button
497
- class="stop-button"
498
- class:padded-button={stop_btn !== true}
499
- on:click={handle_stop}
500
- >
501
- {#if stop_btn === true}
502
- <Square fill={"none"} stroke_width={2.5} />
503
- {:else}
504
- {stop_btn}
504
+
505
+ {#if submit_btn}
506
+ <button
507
+ class="submit-button"
508
+ class:padded-button={submit_btn !== true}
509
+ {disabled}
510
+ on:click={handle_submit}
511
+ aria-label="Submit"
512
+ >
513
+ {#if submit_btn === true}
514
+ <ArrowUp />
515
+ {:else}
516
+ {submit_btn}
517
+ {/if}
518
+ </button>
505
519
  {/if}
506
- </button>
507
- {/if}
520
+ {#if stop_btn}
521
+ <button
522
+ class="stop-button"
523
+ class:padded-button={stop_btn !== true}
524
+ on:click={handle_stop}
525
+ aria-label="Stop"
526
+ >
527
+ {#if stop_btn === true}
528
+ <Square fill={"none"} stroke_width={2.5} />
529
+ {:else}
530
+ {stop_btn}
531
+ {/if}
532
+ </button>
533
+ {/if}
534
+ </div>
535
+ </div>
508
536
  </div>
509
537
  </div>
510
538
 
@@ -517,237 +545,299 @@
517
545
  }
518
546
 
519
547
  .full-container.dragging {
520
- border: 1px solid var(--color-accent);
521
- border-radius: calc(var(--radius-sm) - 1px);
548
+ border-color: var(--color-accent);
549
+ border-radius: calc(var(--radius-sm) - var(--size-px));
522
550
  }
523
551
 
524
552
  .full-container.dragging::after {
525
553
  content: "";
526
554
  position: absolute;
527
- top: 0;
528
- left: 0;
529
- right: 0;
530
- bottom: 0;
555
+ inset: 0;
531
556
  pointer-events: none;
532
557
  }
533
558
 
534
559
  .input-container {
535
560
  display: flex;
536
561
  position: relative;
562
+ flex-direction: column;
563
+ gap: 0;
564
+ }
565
+
566
+ .input-wrapper {
567
+ display: flex;
568
+ position: relative;
569
+ flex-direction: column;
570
+ gap: 0;
571
+ background: var(--block-background-fill);
572
+ border-radius: var(--radius-xl);
573
+ padding: var(--spacing-sm);
574
+ align-items: flex-start;
575
+ min-height: auto;
576
+ }
577
+
578
+ .input-wrapper.has-files {
579
+ padding-top: var(--spacing-xs);
580
+ }
581
+
582
+ .input-row {
583
+ display: flex;
537
584
  align-items: flex-end;
585
+ gap: var(--spacing-sm);
586
+ width: 100%;
587
+ }
588
+
589
+ .thumbnails {
590
+ display: flex;
591
+ align-items: center;
592
+ gap: var(--spacing-sm);
593
+ padding: var(--spacing-xs) 0;
594
+ margin-bottom: var(--spacing-xs);
595
+ overflow-x: auto;
596
+ overflow-y: hidden;
597
+ scrollbar-width: none;
598
+ -ms-overflow-style: none;
599
+ flex-wrap: nowrap;
600
+ -webkit-overflow-scrolling: touch;
601
+ scroll-behavior: smooth;
602
+ overflow-y: scroll;
603
+ width: 100%;
604
+ }
605
+
606
+ .thumbnails::-webkit-scrollbar,
607
+ .thumbnails::-webkit-scrollbar-track,
608
+ .thumbnails::-webkit-scrollbar-thumb {
609
+ display: none;
610
+ }
611
+
612
+ .thumbnails :global(img) {
613
+ width: var(--size-full);
614
+ height: var(--size-full);
615
+ object-fit: cover;
616
+ border-radius: var(--radius-md);
617
+ }
618
+
619
+ .thumbnail-wrapper {
620
+ position: relative;
621
+ flex-shrink: 0;
622
+ }
623
+
624
+ .thumbnail-item {
625
+ position: relative;
626
+ display: flex;
627
+ justify-content: center;
628
+ align-items: center;
629
+ border: var(--size-px) solid var(--border-color-primary);
630
+ border-radius: var(--radius-md);
631
+ background: var(--background-fill-secondary);
632
+ width: var(--size-full);
633
+ height: var(--size-full);
634
+ cursor: default;
635
+ padding: 0;
636
+ }
637
+
638
+ .thumbnail-small {
639
+ width: var(--size-10);
640
+ height: var(--size-10);
641
+ }
642
+
643
+ .thumbnail-item :global(svg) {
644
+ width: var(--size-5);
645
+ height: var(--size-5);
646
+ }
647
+
648
+ .delete-button {
649
+ position: absolute;
650
+ inset: 0;
651
+ display: flex;
652
+ justify-content: center;
653
+ align-items: center;
654
+ color: var(--button-primary-text-color);
655
+ background: rgba(0, 0, 0, 0.6);
656
+ backdrop-filter: var(--blur-xs);
657
+ border-radius: var(--radius-md);
658
+ padding: 0;
659
+ z-index: var(--layer-1);
660
+ opacity: 0;
661
+ transition: opacity 0.1s var(--easing-standard);
662
+ }
663
+
664
+ .delete-button:hover {
665
+ background: rgba(0, 0, 0, 0.8);
666
+ }
667
+
668
+ .delete-button :global(svg) {
669
+ width: var(--size-5);
670
+ height: var(--size-5);
671
+ }
672
+
673
+ .thumbnail-item:hover .delete-button {
674
+ opacity: 1;
538
675
  }
539
676
 
540
677
  textarea {
541
678
  flex-grow: 1;
542
679
  outline: none !important;
543
- background: var(--block-background-fill);
544
- padding: var(--input-padding);
680
+ background: transparent;
681
+ padding: 0;
545
682
  color: var(--body-text-color);
546
683
  font-weight: var(--input-text-weight);
547
684
  font-size: var(--input-text-size);
548
- line-height: var(--line-sm);
549
685
  border: none;
550
- margin-top: 0px;
551
- margin-bottom: 0px;
686
+ margin: 0;
552
687
  resize: none;
553
688
  position: relative;
554
- z-index: 1;
689
+ z-index: var(--layer-1);
555
690
  text-align: left;
691
+ min-height: var(--size-9);
692
+ }
693
+
694
+ textarea:disabled {
695
+ -webkit-opacity: 1;
696
+ opacity: 1;
556
697
  }
698
+
699
+ textarea::placeholder {
700
+ color: var(--input-placeholder-color);
701
+ }
702
+
557
703
  textarea[dir="rtl"] {
558
704
  text-align: right;
559
705
  }
560
706
 
561
707
  textarea[dir="rtl"] ~ .submit-button {
562
708
  order: -1;
563
- margin-left: 0;
564
- margin-right: var(--spacing-sm);
565
709
  }
566
710
 
567
711
  textarea[dir="rtl"] ~ .submit-button :global(svg) {
568
712
  transform: scaleX(-1);
569
713
  }
570
714
 
571
- textarea.no-label {
572
- padding-top: 5px;
573
- padding-bottom: 5px;
715
+ .mobile-thumbnail-add,
716
+ .microphone-button {
717
+ color: var(--body-text-color);
718
+ cursor: pointer;
719
+ padding: var(--spacing-sm);
720
+ display: flex;
721
+ justify-content: center;
722
+ align-items: center;
723
+ flex-shrink: 0;
724
+ border-radius: var(--radius-md);
725
+ transition:
726
+ background 0.2s var(--easing-standard),
727
+ border-color 0.2s var(--easing-standard);
574
728
  }
575
729
 
576
- textarea:disabled {
577
- -webkit-opacity: 1;
578
- opacity: 1;
730
+ .mobile-thumbnail-add {
731
+ width: var(--size-10);
732
+ height: var(--size-10);
579
733
  }
580
734
 
581
- textarea::placeholder {
582
- color: var(--input-placeholder-color);
735
+ .microphone-button {
736
+ width: var(--size-9);
737
+ height: var(--size-9);
738
+ }
739
+
740
+ .mobile-thumbnail-add:hover:not(:disabled),
741
+ .microphone-button:hover:not(:disabled) {
742
+ background: var(--button-secondary-background-fill-hover);
743
+ border-color: var(--border-color-primary);
744
+ }
745
+
746
+ .mobile-thumbnail-add:disabled,
747
+ .microphone-button:disabled {
748
+ opacity: 0.5;
749
+ cursor: not-allowed;
750
+ }
751
+
752
+ .mobile-thumbnail-add :global(svg),
753
+ .microphone-button :global(svg) {
754
+ width: var(--size-5);
755
+ height: var(--size-5);
583
756
  }
584
757
 
585
- .microphone-button,
586
- .upload-button,
587
758
  .submit-button,
588
759
  .stop-button {
589
- border: none;
590
- text-align: center;
591
- text-decoration: none;
592
- font-size: 14px;
760
+ border: var(--size-px) solid var(--border-color-primary);
761
+ background: var(--button-secondary-background-fill);
593
762
  cursor: pointer;
594
- border-radius: 15px;
595
- min-width: 30px;
596
- height: 30px;
597
- flex-shrink: 0;
598
763
  display: flex;
599
764
  justify-content: center;
600
765
  align-items: center;
766
+ flex-shrink: 0;
767
+ width: var(--size-9);
768
+ height: var(--size-9);
769
+ border-radius: var(--radius-md);
601
770
  z-index: var(--layer-1);
602
- margin-left: var(--spacing-sm);
603
- }
604
- .padded-button {
605
- padding: 0 10px;
771
+ transition:
772
+ background 0.2s var(--easing-standard),
773
+ border-color 0.2s var(--easing-standard);
606
774
  }
607
775
 
608
- .microphone-button,
609
- .stop-button,
610
- .upload-button,
611
- .submit-button {
612
- background: var(--button-secondary-background-fill);
613
- }
614
-
615
- .microphone-button:hover:not(:disabled),
616
- .stop-button:hover:not(:disabled),
617
- .upload-button:hover:not(:disabled),
618
- .submit-button:hover:not(:disabled) {
776
+ .submit-button:hover:not(:disabled),
777
+ .stop-button:hover:not(:disabled) {
619
778
  background: var(--button-secondary-background-fill-hover);
620
779
  }
621
780
 
622
- .microphone-button:disabled,
623
- .stop-button:disabled,
624
- .upload-button:disabled,
625
- .submit-button:disabled {
626
- background: var(--button-secondary-background-fill);
627
- cursor: not-allowed;
628
- }
629
- .microphone-button:active,
630
- .stop-button:active,
631
- .upload-button:active,
632
- .submit-button:active {
781
+ .submit-button:active:not(:disabled),
782
+ .stop-button:active:not(:disabled) {
633
783
  box-shadow: var(--button-shadow-active);
634
784
  }
635
785
 
636
- .submit-button :global(svg) {
637
- height: 22px;
638
- width: 22px;
639
- }
640
- .microphone-button :global(svg),
641
- .upload-button :global(svg) {
642
- height: 17px;
643
- width: 17px;
786
+ .submit-button:disabled,
787
+ .stop-button:disabled {
788
+ cursor: not-allowed;
644
789
  }
645
790
 
791
+ .submit-button :global(svg),
646
792
  .stop-button :global(svg) {
647
- height: 16px;
648
- width: 16px;
793
+ width: var(--size-5);
794
+ height: var(--size-5);
795
+ }
796
+
797
+ .padded-button {
798
+ padding: 0 var(--spacing-lg);
799
+ width: auto;
800
+ border-radius: var(--radius-xl);
649
801
  }
650
802
 
651
803
  .loader {
652
804
  display: flex;
653
805
  justify-content: center;
654
806
  align-items: center;
655
- --ring-color: transparent;
656
807
  position: relative;
657
- border: 5px solid #f3f3f3;
658
- border-top: 5px solid var(--color-accent);
659
- border-radius: 50%;
660
- width: 25px;
661
- height: 25px;
662
- animation: spin 2s linear infinite;
808
+ border: var(--size-1) solid var(--border-color-primary);
809
+ border-top-color: var(--color-accent);
810
+ border-radius: var(--radius-100);
811
+ width: var(--size-5);
812
+ height: var(--size-5);
813
+ animation: spin 1s linear infinite;
814
+ flex-shrink: 0;
663
815
  }
664
816
 
665
817
  @keyframes spin {
666
- 0% {
667
- transform: rotate(0deg);
668
- }
669
- 100% {
818
+ to {
670
819
  transform: rotate(360deg);
671
820
  }
672
821
  }
673
822
 
674
- .thumbnails :global(img) {
675
- width: var(--size-full);
676
- height: var(--size-full);
677
- object-fit: cover;
678
- border-radius: var(--radius-lg);
679
- }
680
-
681
- .thumbnails {
682
- display: flex;
683
- align-items: center;
684
- gap: var(--spacing-lg);
685
- overflow-x: scroll;
686
- padding-top: var(--spacing-sm);
687
- margin-bottom: 6px;
688
- }
689
-
690
- .thumbnail-item {
691
- display: flex;
692
- justify-content: center;
693
- align-items: center;
694
- --ring-color: transparent;
695
- position: relative;
696
- box-shadow:
697
- 0 0 0 2px var(--ring-color),
698
- var(--shadow-drop);
699
- border: 1px solid var(--border-color-primary);
700
- border-radius: var(--radius-lg);
701
- background: var(--background-fill-secondary);
702
- aspect-ratio: var(--ratio-square);
703
- width: var(--size-full);
704
- height: var(--size-full);
705
- cursor: default;
706
- }
707
-
708
- .thumbnail-small {
709
- flex: none;
710
- transform: scale(0.9);
711
- transition: 0.075s;
712
- width: var(--size-12);
713
- height: var(--size-12);
714
- }
715
-
716
- .thumbnail-item :global(svg) {
717
- width: 30px;
718
- height: 30px;
719
- }
720
-
721
- .delete-button {
722
- display: flex;
723
- justify-content: center;
724
- align-items: center;
725
- position: absolute;
726
- right: -7px;
727
- top: -7px;
728
- color: var(--button-secondary-text-color);
729
- background: var(--button-secondary-background-fill);
730
- border: none;
731
- text-align: center;
732
- text-decoration: none;
733
- font-size: 10px;
734
- cursor: pointer;
735
- border-radius: 50%;
736
- width: 20px;
737
- height: 20px;
738
- }
823
+ @media (max-width: 768px) {
824
+ .input-wrapper {
825
+ padding: var(--spacing-xs);
826
+ }
739
827
 
740
- .disabled {
741
- display: none;
742
- }
828
+ .thumbnails {
829
+ padding: var(--spacing-xs) 0;
830
+ margin-bottom: var(--spacing-md);
831
+ }
743
832
 
744
- .delete-button :global(svg) {
745
- width: 12px;
746
- height: 12px;
747
- }
833
+ .thumbnail-small,
834
+ .mobile-thumbnail-add {
835
+ width: var(--size-9);
836
+ height: var(--size-9);
837
+ }
748
838
 
749
- .delete-button:hover {
750
- filter: brightness(1.2);
751
- border: 0.8px solid var(--color-grey-500);
839
+ .thumbnail-item:active .delete-button {
840
+ opacity: 1;
841
+ }
752
842
  }
753
843
  </style>