@gradio/multimodaltextbox 0.10.22-dev.0 → 0.11.0

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,24 @@
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
- Microphone
24
+ Microphone,
25
+ Check
25
26
  } from "@gradio/icons";
26
27
  import type { SelectData } from "@gradio/utils";
27
- import InteractiveAudio from "../../audio/interactive/InteractiveAudio.svelte";
28
+ import { BaseInteractiveAudio as InteractiveAudio } from "@gradio/audio";
29
+ import {
30
+ MinimalAudioPlayer,
31
+ MinimalAudioRecorder
32
+ } from "@gradio/audio/shared";
28
33
  import type { InputHTMLAttributes } from "./types";
29
34
 
30
35
  export let value: { text: string; files: FileData[] } = {
@@ -35,7 +40,7 @@
35
40
  export let value_is_output = false;
36
41
  export let lines = 1;
37
42
  export let i18n: I18nFormatter;
38
- export let placeholder = "Type here...";
43
+ export let placeholder = "";
39
44
  export let disabled = false;
40
45
  export let label: string;
41
46
  export let info: string | undefined = undefined;
@@ -65,6 +70,7 @@
65
70
  | "microphone,upload" = "upload";
66
71
  export let active_source: "microphone" | null = null;
67
72
  export let html_attributes: InputHTMLAttributes | null = null;
73
+ export let upload_promise: Promise<any> | null = null;
68
74
 
69
75
  let upload_component: Upload;
70
76
  let el: HTMLTextAreaElement | HTMLInputElement;
@@ -88,6 +94,10 @@
88
94
  ? file_types_string.split(",").map((s) => s.trim())
89
95
  : null;
90
96
  $: dispatch("drag", dragging);
97
+ $: show_upload =
98
+ sources &&
99
+ sources.includes("upload") &&
100
+ !(file_count === "single" && value.files.length > 0);
91
101
  let mic_audio: FileData | null = null;
92
102
 
93
103
  let full_container: HTMLDivElement;
@@ -98,7 +108,7 @@
98
108
  }
99
109
 
100
110
  $: if (value === null) value = { text: "", files: [] };
101
- $: value, el && lines !== max_lines && resize(el, lines, max_lines);
111
+ $: (value, el && lines !== max_lines && resize(el, lines, max_lines));
102
112
 
103
113
  const dispatch = createEventDispatcher<{
104
114
  change: typeof value;
@@ -165,9 +175,9 @@
165
175
  }
166
176
 
167
177
  async function handle_keypress(e: KeyboardEvent): Promise<void> {
168
- await tick();
169
178
  if (e.key === "Enter" && e.shiftKey && lines > 1) {
170
179
  e.preventDefault();
180
+ await tick();
171
181
  dispatch("submit");
172
182
  } else if (
173
183
  e.key === "Enter" &&
@@ -176,13 +186,10 @@
176
186
  max_lines >= 1
177
187
  ) {
178
188
  e.preventDefault();
179
- dispatch("submit");
189
+ add_mic_audio_to_files();
180
190
  active_source = null;
181
- if (mic_audio) {
182
- value.files.push(mic_audio);
183
- value = value;
184
- mic_audio = null;
185
- }
191
+ await tick();
192
+ dispatch("submit");
186
193
  }
187
194
  }
188
195
 
@@ -234,16 +241,21 @@
234
241
  dispatch("stop");
235
242
  }
236
243
 
237
- function handle_submit(): void {
238
- dispatch("submit");
239
- active_source = null;
244
+ function add_mic_audio_to_files(): void {
240
245
  if (mic_audio) {
241
246
  value.files.push(mic_audio);
242
247
  value = value;
243
248
  mic_audio = null;
249
+ dispatch("change", value);
244
250
  }
245
251
  }
246
252
 
253
+ function handle_submit(): void {
254
+ add_mic_audio_to_files();
255
+ active_source = null;
256
+ dispatch("submit");
257
+ }
258
+
247
259
  async function handle_paste(event: ClipboardEvent): Promise<void> {
248
260
  if (!event.clipboardData) return;
249
261
  const items = event.clipboardData.items;
@@ -335,80 +347,77 @@
335
347
  aria-label="Multimedia input field"
336
348
  >
337
349
  <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
350
  <div class="input-container">
410
- {#if sources && sources.includes("upload") && !(file_count === "single" && value.files.length > 0)}
351
+ {#if sources && sources.includes("microphone") && active_source === "microphone"}
352
+ <div class="recording-overlay" class:has-audio={mic_audio !== null}>
353
+ {#if !mic_audio}
354
+ <div class="recording-content">
355
+ <MinimalAudioRecorder
356
+ label={label || "Audio"}
357
+ {waveform_settings}
358
+ {recording}
359
+ {upload}
360
+ {root}
361
+ {max_file_size}
362
+ bind:upload_promise
363
+ on:change={({ detail }) => {
364
+ mic_audio = detail;
365
+ }}
366
+ on:stop_recording={() => {
367
+ recording = false;
368
+ dispatch("stop_recording");
369
+ }}
370
+ on:clear={() => {
371
+ active_source = null;
372
+ recording = false;
373
+ mic_audio = null;
374
+ }}
375
+ on:error={({ detail }) => {
376
+ console.log("Failed to record:", detail);
377
+ active_source = null;
378
+ recording = false;
379
+ mic_audio = null;
380
+ }}
381
+ />
382
+ </div>
383
+ {:else}
384
+ <div class="recording-content">
385
+ <MinimalAudioPlayer
386
+ value={mic_audio}
387
+ label={label || "Audio"}
388
+ loop={false}
389
+ />
390
+ <div class="action-buttons">
391
+ <button
392
+ class="confirm-button"
393
+ on:click={() => {
394
+ add_mic_audio_to_files();
395
+ active_source = null;
396
+ recording = false;
397
+ }}
398
+ aria-label="Attach audio"
399
+ >
400
+ <Check />
401
+ </button>
402
+ <button
403
+ class="cancel-button"
404
+ on:click={() => {
405
+ active_source = null;
406
+ recording = false;
407
+ mic_audio = null;
408
+ }}
409
+ aria-label="Clear audio"
410
+ >
411
+ <Clear />
412
+ </button>
413
+ </div>
414
+ </div>
415
+ {/if}
416
+ </div>
417
+ {/if}
418
+ {#if show_upload}
411
419
  <Upload
420
+ bind:upload_promise
412
421
  bind:this={upload_component}
413
422
  on:load={handle_upload}
414
423
  {file_count}
@@ -424,87 +433,154 @@
424
433
  {upload}
425
434
  {stream_handler}
426
435
  />
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
436
  {/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
- : () => {
444
- active_source =
445
- active_source !== "microphone" ? "microphone" : null;
437
+
438
+ <div
439
+ class="input-wrapper"
440
+ class:has-files={value.files.length > 0 || uploading}
441
+ >
442
+ {#if value.files.length > 0 || uploading || show_upload}
443
+ <div
444
+ class="thumbnails"
445
+ aria-label="Uploaded files"
446
+ data-testid="container_el"
447
+ >
448
+ {#if show_upload}
449
+ <button
450
+ data-testid="upload-button"
451
+ class="upload-button thumbnail-add"
452
+ {disabled}
453
+ on:click={handle_upload_click}
454
+ aria-label="Upload a file"
455
+ >
456
+ <Paperclip />
457
+ </button>
458
+ {/if}
459
+ {#each value.files as file, index}
460
+ <span
461
+ class="thumbnail-wrapper"
462
+ role="listitem"
463
+ aria-label="File thumbnail"
464
+ >
465
+ <div class="thumbnail-item thumbnail-small">
466
+ {#if file.mime_type && file.mime_type.includes("image")}
467
+ <Image
468
+ src={file.url}
469
+ restProps={{
470
+ title: null,
471
+ alt: "",
472
+ loading: "lazy",
473
+ class: "thumbnail-image"
474
+ }}
475
+ />
476
+ {:else if file.mime_type && file.mime_type.includes("audio")}
477
+ <Music />
478
+ {:else if file.mime_type && file.mime_type.includes("video")}
479
+ <Video />
480
+ {:else}
481
+ <File />
482
+ {/if}
483
+ <button
484
+ class="delete-button"
485
+ on:click={(event) => remove_thumbnail(event, index)}
486
+ aria-label="Remove file"
487
+ >
488
+ <Clear />
489
+ </button>
490
+ </div>
491
+ </span>
492
+ {/each}
493
+ {#if uploading}
494
+ <div class="loader" role="status" aria-label="Uploading"></div>
495
+ {/if}
496
+ </div>
497
+ {/if}
498
+
499
+ <div class="input-row">
500
+ <textarea
501
+ data-testid="textbox"
502
+ use:text_area_resize={{
503
+ text: value.text,
504
+ lines: lines,
505
+ max_lines: max_lines
506
+ }}
507
+ class:no-label={!show_label}
508
+ dir={rtl ? "rtl" : "ltr"}
509
+ bind:value={value.text}
510
+ bind:this={el}
511
+ {placeholder}
512
+ rows={lines}
513
+ {disabled}
514
+ on:keypress={handle_keypress}
515
+ on:blur
516
+ on:select={handle_select}
517
+ on:focus
518
+ on:scroll={handle_scroll}
519
+ on:paste={handle_paste}
520
+ style={text_align ? "text-align: " + text_align : ""}
521
+ autocapitalize={html_attributes?.autocapitalize}
522
+ autocorrect={html_attributes?.autocorrect}
523
+ spellcheck={html_attributes?.spellcheck}
524
+ autocomplete={html_attributes?.autocomplete}
525
+ tabindex={html_attributes?.tabindex}
526
+ enterkeyhint={html_attributes?.enterkeyhint}
527
+ lang={html_attributes?.lang}
528
+ />
529
+
530
+ {#if sources && sources.includes("microphone")}
531
+ <button
532
+ data-testid="microphone-button"
533
+ class="microphone-button"
534
+ class:recording
535
+ {disabled}
536
+ on:click={async () => {
537
+ if (active_source !== "microphone") {
538
+ active_source = "microphone";
539
+ await tick();
540
+ recording = true;
541
+ } else {
542
+ active_source = null;
543
+ recording = false;
544
+ }
446
545
  }}
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}
546
+ aria-label="Record audio"
547
+ >
548
+ <Microphone />
549
+ </button>
492
550
  {/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}
551
+
552
+ {#if submit_btn}
553
+ <button
554
+ class="submit-button"
555
+ data-testid="submit-button"
556
+ class:padded-button={submit_btn !== true}
557
+ {disabled}
558
+ on:click={handle_submit}
559
+ aria-label="Submit"
560
+ >
561
+ {#if submit_btn === true}
562
+ <ArrowUp />
563
+ {:else}
564
+ {submit_btn}
565
+ {/if}
566
+ </button>
505
567
  {/if}
506
- </button>
507
- {/if}
568
+ {#if stop_btn}
569
+ <button
570
+ class="stop-button"
571
+ class:padded-button={stop_btn !== true}
572
+ on:click={handle_stop}
573
+ aria-label="Stop"
574
+ >
575
+ {#if stop_btn === true}
576
+ <Square fill={"none"} stroke_width={2.5} />
577
+ {:else}
578
+ {stop_btn}
579
+ {/if}
580
+ </button>
581
+ {/if}
582
+ </div>
583
+ </div>
508
584
  </div>
509
585
  </div>
510
586
 
@@ -517,237 +593,414 @@
517
593
  }
518
594
 
519
595
  .full-container.dragging {
520
- border: 1px solid var(--color-accent);
521
- border-radius: calc(var(--radius-sm) - 1px);
596
+ border-color: var(--color-accent);
597
+ border-radius: calc(var(--radius-sm) - var(--size-px));
522
598
  }
523
599
 
524
600
  .full-container.dragging::after {
525
601
  content: "";
526
602
  position: absolute;
527
- top: 0;
528
- left: 0;
529
- right: 0;
530
- bottom: 0;
603
+ inset: 0;
531
604
  pointer-events: none;
532
605
  }
533
606
 
534
607
  .input-container {
535
608
  display: flex;
536
609
  position: relative;
610
+ flex-direction: column;
611
+ gap: 0;
612
+ }
613
+
614
+ .input-wrapper {
615
+ display: flex;
616
+ position: relative;
617
+ flex-direction: column;
618
+ gap: 0;
619
+ background: var(--block-background-fill);
620
+ border-radius: var(--radius-xl);
621
+ padding: var(--spacing-sm);
622
+ align-items: flex-start;
623
+ min-height: auto;
624
+ }
625
+
626
+ .input-wrapper.has-files {
627
+ padding-top: var(--spacing-xs);
628
+ }
629
+
630
+ .input-row {
631
+ display: flex;
537
632
  align-items: flex-end;
633
+ gap: var(--spacing-sm);
634
+ width: 100%;
635
+ }
636
+
637
+ .thumbnails {
638
+ display: flex;
639
+ align-items: center;
640
+ gap: var(--spacing-sm);
641
+ padding: var(--spacing-xs) 0;
642
+ margin-bottom: var(--spacing-xs);
643
+ overflow-x: auto;
644
+ overflow-y: hidden;
645
+ scrollbar-width: none;
646
+ -ms-overflow-style: none;
647
+ flex-wrap: nowrap;
648
+ -webkit-overflow-scrolling: touch;
649
+ scroll-behavior: smooth;
650
+ overflow-y: scroll;
651
+ width: 100%;
652
+ }
653
+
654
+ .thumbnails::-webkit-scrollbar,
655
+ .thumbnails::-webkit-scrollbar-track,
656
+ .thumbnails::-webkit-scrollbar-thumb {
657
+ display: none;
658
+ }
659
+
660
+ .thumbnails :global(img) {
661
+ width: var(--size-full);
662
+ height: var(--size-full);
663
+ object-fit: cover;
664
+ border-radius: var(--radius-md);
665
+ }
666
+
667
+ .thumbnail-wrapper {
668
+ position: relative;
669
+ flex-shrink: 0;
670
+ }
671
+
672
+ .thumbnail-item {
673
+ position: relative;
674
+ display: flex;
675
+ justify-content: center;
676
+ align-items: center;
677
+ border-radius: var(--radius-md);
678
+ background: var(--background-fill-secondary);
679
+ width: var(--size-full);
680
+ height: var(--size-full);
681
+ cursor: default;
682
+ padding: 0;
683
+ }
684
+
685
+ .thumbnail-small {
686
+ width: var(--size-10);
687
+ height: var(--size-10);
688
+ }
689
+
690
+ .thumbnail-item :global(svg) {
691
+ width: var(--size-5);
692
+ height: var(--size-5);
693
+ }
694
+
695
+ .delete-button {
696
+ position: absolute;
697
+ inset: 0;
698
+ display: flex;
699
+ justify-content: center;
700
+ align-items: center;
701
+ color: var(--button-primary-text-color);
702
+ background: rgba(0, 0, 0, 0.6);
703
+ backdrop-filter: var(--blur-xs);
704
+ border-radius: var(--radius-md);
705
+ padding: 0;
706
+ z-index: var(--layer-1);
707
+ opacity: 0;
708
+ transition: opacity 0.1s var(--easing-standard);
709
+ }
710
+
711
+ .delete-button:hover {
712
+ background: rgba(0, 0, 0, 0.8);
713
+ }
714
+
715
+ .delete-button :global(svg) {
716
+ width: var(--size-5);
717
+ height: var(--size-5);
718
+ }
719
+
720
+ .thumbnail-item:hover .delete-button {
721
+ opacity: 1;
538
722
  }
539
723
 
540
724
  textarea {
541
725
  flex-grow: 1;
542
726
  outline: none !important;
543
- background: var(--block-background-fill);
544
- padding: var(--input-padding);
727
+ background: transparent;
728
+ padding: 0;
545
729
  color: var(--body-text-color);
546
730
  font-weight: var(--input-text-weight);
547
731
  font-size: var(--input-text-size);
548
- line-height: var(--line-sm);
549
732
  border: none;
550
- margin-top: 0px;
551
- margin-bottom: 0px;
733
+ margin: 0;
552
734
  resize: none;
553
735
  position: relative;
554
- z-index: 1;
736
+ z-index: var(--layer-1);
555
737
  text-align: left;
738
+ min-height: var(--size-9);
739
+ }
740
+
741
+ textarea:disabled {
742
+ -webkit-opacity: 1;
743
+ opacity: 1;
744
+ }
745
+
746
+ textarea::placeholder {
747
+ color: var(--input-placeholder-color);
556
748
  }
749
+
557
750
  textarea[dir="rtl"] {
558
751
  text-align: right;
559
752
  }
560
753
 
561
754
  textarea[dir="rtl"] ~ .submit-button {
562
755
  order: -1;
563
- margin-left: 0;
564
- margin-right: var(--spacing-sm);
565
756
  }
566
757
 
567
758
  textarea[dir="rtl"] ~ .submit-button :global(svg) {
568
759
  transform: scaleX(-1);
569
760
  }
570
761
 
571
- textarea.no-label {
572
- padding-top: 5px;
573
- padding-bottom: 5px;
762
+ .microphone-button {
763
+ color: var(--body-text-color);
764
+ cursor: pointer;
765
+ padding: var(--spacing-sm);
766
+ display: flex;
767
+ justify-content: center;
768
+ align-items: center;
769
+ flex-shrink: 0;
770
+ border-radius: var(--radius-md);
574
771
  }
575
772
 
576
- textarea:disabled {
577
- -webkit-opacity: 1;
578
- opacity: 1;
773
+ .thumbnail-add {
774
+ background: var(--button-secondary-background-fill);
775
+ cursor: pointer;
776
+ display: flex;
777
+ justify-content: center;
778
+ align-items: center;
779
+ flex-shrink: 0;
780
+ width: var(--size-10);
781
+ height: var(--size-10);
782
+ border-radius: var(--radius-md);
783
+ z-index: var(--layer-1);
579
784
  }
580
785
 
581
- textarea::placeholder {
582
- color: var(--input-placeholder-color);
786
+ .thumbnail-add:hover:not(:disabled) {
787
+ background: var(--button-secondary-background-fill-hover);
788
+ }
789
+
790
+ .thumbnail-add:disabled {
791
+ opacity: 0.5;
792
+ cursor: not-allowed;
793
+ }
794
+
795
+ .thumbnail-add :global(svg) {
796
+ width: var(--size-5);
797
+ height: var(--size-5);
798
+ }
799
+
800
+ .microphone-button {
801
+ width: var(--size-9);
802
+ height: var(--size-9);
803
+ }
804
+
805
+ .microphone-button:hover:not(:disabled) {
806
+ background: var(--button-secondary-background-fill);
807
+ }
808
+
809
+ .microphone-button:disabled {
810
+ opacity: 0.5;
811
+ cursor: not-allowed;
812
+ }
813
+
814
+ .microphone-button :global(svg) {
815
+ width: var(--size-5);
816
+ height: var(--size-5);
583
817
  }
584
818
 
585
- .microphone-button,
586
- .upload-button,
587
819
  .submit-button,
588
820
  .stop-button {
589
- border: none;
590
- text-align: center;
591
- text-decoration: none;
592
- font-size: 14px;
821
+ background: var(--button-secondary-background-fill);
593
822
  cursor: pointer;
594
- border-radius: 15px;
595
- min-width: 30px;
596
- height: 30px;
597
- flex-shrink: 0;
598
823
  display: flex;
599
824
  justify-content: center;
600
825
  align-items: center;
826
+ flex-shrink: 0;
827
+ width: var(--size-9);
828
+ height: var(--size-9);
829
+ border-radius: var(--radius-md);
601
830
  z-index: var(--layer-1);
602
- margin-left: var(--spacing-sm);
603
- }
604
- .padded-button {
605
- padding: 0 10px;
606
- }
607
-
608
- .microphone-button,
609
- .stop-button,
610
- .upload-button,
611
- .submit-button {
612
- background: var(--button-secondary-background-fill);
613
831
  }
614
832
 
615
- .microphone-button:hover:not(:disabled),
616
- .stop-button:hover:not(:disabled),
617
- .upload-button:hover:not(:disabled),
618
- .submit-button:hover:not(:disabled) {
833
+ .submit-button:hover:not(:disabled),
834
+ .stop-button:hover:not(:disabled) {
619
835
  background: var(--button-secondary-background-fill-hover);
620
836
  }
621
837
 
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 {
838
+ .submit-button:active:not(:disabled),
839
+ .stop-button:active:not(:disabled) {
633
840
  box-shadow: var(--button-shadow-active);
634
841
  }
635
842
 
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;
843
+ .submit-button:disabled,
844
+ .stop-button:disabled {
845
+ cursor: not-allowed;
644
846
  }
645
847
 
848
+ .submit-button :global(svg),
646
849
  .stop-button :global(svg) {
647
- height: 16px;
648
- width: 16px;
850
+ width: var(--size-5);
851
+ height: var(--size-5);
852
+ }
853
+
854
+ .padded-button {
855
+ padding: 0 var(--spacing-lg);
856
+ width: auto;
857
+ border-radius: var(--radius-xl);
649
858
  }
650
859
 
651
860
  .loader {
652
861
  display: flex;
653
862
  justify-content: center;
654
863
  align-items: center;
655
- --ring-color: transparent;
656
864
  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;
865
+ border: var(--size-1) solid var(--border-color-primary);
866
+ border-top-color: var(--color-accent);
867
+ border-radius: var(--radius-100);
868
+ width: var(--size-5);
869
+ height: var(--size-5);
870
+ animation: spin 1s linear infinite;
871
+ flex-shrink: 0;
663
872
  }
664
873
 
665
874
  @keyframes spin {
666
- 0% {
667
- transform: rotate(0deg);
668
- }
669
- 100% {
875
+ to {
670
876
  transform: rotate(360deg);
671
877
  }
672
878
  }
673
879
 
674
- .thumbnails :global(img) {
675
- width: var(--size-full);
676
- height: var(--size-full);
677
- object-fit: cover;
678
- border-radius: var(--radius-lg);
880
+ .recording-overlay {
881
+ position: absolute;
882
+ top: 0;
883
+ left: 0;
884
+ right: 0;
885
+ bottom: 0;
886
+ background: var(--block-background-fill);
887
+ border-radius: var(--radius-xl);
888
+ display: flex;
889
+ align-items: center;
890
+ justify-content: center;
891
+ z-index: var(--layer-5);
892
+ padding: var(--spacing-lg);
893
+ backdrop-filter: blur(8px);
894
+ animation: fadeIn 0.2s var(--easing-standard);
679
895
  }
680
896
 
681
- .thumbnails {
897
+ @keyframes fadeIn {
898
+ from {
899
+ opacity: 0;
900
+ }
901
+ to {
902
+ opacity: 1;
903
+ }
904
+ }
905
+
906
+ .recording-content {
682
907
  display: flex;
683
908
  align-items: center;
684
909
  gap: var(--spacing-lg);
685
- overflow-x: scroll;
686
- padding-top: var(--spacing-sm);
687
- margin-bottom: 6px;
910
+ width: 100%;
911
+ max-width: 700px;
688
912
  }
689
913
 
690
- .thumbnail-item {
914
+ .recording-content :global(.minimal-audio-recorder),
915
+ .recording-content :global(.minimal-audio-player) {
916
+ flex: 1;
917
+ }
918
+
919
+ .action-buttons {
691
920
  display: flex;
692
- justify-content: center;
693
921
  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;
922
+ gap: var(--spacing-sm);
923
+ flex-shrink: 0;
706
924
  }
707
925
 
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);
926
+ .stop-button,
927
+ .confirm-button,
928
+ .cancel-button {
929
+ display: flex;
930
+ align-items: center;
931
+ justify-content: center;
932
+ width: var(--size-9);
933
+ height: var(--size-9);
934
+ padding: 0;
935
+ border-radius: var(--radius-md);
936
+ cursor: pointer;
937
+ flex-shrink: 0;
714
938
  }
715
939
 
716
- .thumbnail-item :global(svg) {
717
- width: 30px;
718
- height: 30px;
940
+ .stop-button {
941
+ background: var(--button-secondary-background-fill);
942
+ border-color: var(--border-color-primary);
943
+ color: var(--error-500);
719
944
  }
720
945
 
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);
946
+ .stop-button:hover {
947
+ background: var(--button-secondary-background-fill-hover);
948
+ color: var(--error-600);
949
+ }
950
+
951
+ .stop-button:active {
952
+ transform: scale(0.95);
953
+ }
954
+
955
+ .confirm-button {
956
+ background: var(--button-primary-background-fill);
957
+ border-color: var(--button-primary-border-color);
958
+ color: white;
959
+ }
960
+
961
+ .confirm-button:hover {
962
+ background: var(--button-primary-background-fill-hover);
963
+ color: var(--button-primary-text-color-hover);
964
+ }
965
+
966
+ .confirm-button:active {
967
+ transform: scale(0.95);
968
+ }
969
+
970
+ .cancel-button {
729
971
  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;
972
+ color: var(--body-text-color);
738
973
  }
739
974
 
740
- .disabled {
741
- display: none;
975
+ .cancel-button:hover {
976
+ background: var(--button-secondary-background-fill-hover);
742
977
  }
743
978
 
744
- .delete-button :global(svg) {
745
- width: 12px;
746
- height: 12px;
979
+ .stop-button :global(svg),
980
+ .confirm-button :global(svg),
981
+ .cancel-button :global(svg) {
982
+ width: var(--size-5);
983
+ height: var(--size-5);
747
984
  }
748
985
 
749
- .delete-button:hover {
750
- filter: brightness(1.2);
751
- border: 0.8px solid var(--color-grey-500);
986
+ @media (max-width: 768px) {
987
+ .input-wrapper {
988
+ padding: var(--spacing-xs);
989
+ }
990
+
991
+ .thumbnails {
992
+ padding: var(--spacing-xs) 0;
993
+ margin-bottom: var(--spacing-md);
994
+ }
995
+
996
+ .thumbnail-small,
997
+ .thumbnail-add {
998
+ width: var(--size-9);
999
+ height: var(--size-9);
1000
+ }
1001
+
1002
+ .thumbnail-item:active .delete-button {
1003
+ opacity: 1;
1004
+ }
752
1005
  }
753
1006
  </style>