@gradio/multimodaltextbox 0.11.3 → 0.11.4
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/CHANGELOG.md +21 -0
- package/MultimodalTextbox.stories.svelte +96 -59
- package/MultimodalTextboxExample.stories.svelte +17 -13
- package/dist/shared/MultimodalTextbox.svelte +85 -59
- package/dist/shared/MultimodalTextbox.svelte.d.ts +2 -2
- package/package.json +12 -12
- package/shared/MultimodalTextbox.svelte +85 -59
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# @gradio/multimodaltextbox
|
|
2
2
|
|
|
3
|
+
## 0.11.4
|
|
4
|
+
|
|
5
|
+
### Fixes
|
|
6
|
+
|
|
7
|
+
- [#12800](https://github.com/gradio-app/gradio/pull/12800) [`7a1c321`](https://github.com/gradio-app/gradio/commit/7a1c321b6546ba05a353488f5133e8262c4a8a39) - Bump svelte/kit for security reasons. Thanks @freddyaboulton!
|
|
8
|
+
- [#12779](https://github.com/gradio-app/gradio/pull/12779) [`ea2d3e9`](https://github.com/gradio-app/gradio/commit/ea2d3e985a8b42d188e551f517c5825c00790628) - Migrate Audio + Upload + Atoms to Svelte 5. Thanks @dawoodkhan82!
|
|
9
|
+
- [#12795](https://github.com/gradio-app/gradio/pull/12795) [`184968a`](https://github.com/gradio-app/gradio/commit/184968aa13636b5afe78f8ae443cd7fc29635bfa) - Add fade effect to overflowing text. Thanks @hannahblair!
|
|
10
|
+
- [#12594](https://github.com/gradio-app/gradio/pull/12594) [`c2f0f19`](https://github.com/gradio-app/gradio/commit/c2f0f1949d453bc850fa65e5b140067008a83e22) - add conditional rendering for the upload button in multimodal textbox. Thanks @hannahblair!
|
|
11
|
+
|
|
12
|
+
### Dependency updates
|
|
13
|
+
|
|
14
|
+
- @gradio/statustracker@0.12.2
|
|
15
|
+
- @gradio/atoms@0.20.1
|
|
16
|
+
- @gradio/utils@0.11.2
|
|
17
|
+
- @gradio/icons@0.15.1
|
|
18
|
+
- @gradio/upload@0.17.4
|
|
19
|
+
- @gradio/client@2.0.3
|
|
20
|
+
- @gradio/image@0.25.1
|
|
21
|
+
- @gradio/video@0.20.1
|
|
22
|
+
- @gradio/audio@0.22.1
|
|
23
|
+
|
|
3
24
|
## 0.11.3
|
|
4
25
|
|
|
5
26
|
### Fixes
|
|
@@ -1,60 +1,63 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
import {
|
|
1
|
+
<script module>
|
|
2
|
+
import { defineMeta } from "@storybook/addon-svelte-csf";
|
|
3
3
|
import MultimodalTextbox from "./Index.svelte";
|
|
4
|
-
|
|
4
|
+
import { wrapProps } from "../storybook/wrapProps";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
6
|
+
const cheetah = "/cheetah.jpg";
|
|
7
|
+
|
|
8
|
+
const { Story } = defineMeta({
|
|
9
|
+
title: "Components/MultimodalTextbox",
|
|
10
|
+
component: MultimodalTextbox,
|
|
11
|
+
argTypes: {
|
|
12
|
+
label: {
|
|
13
|
+
control: "text",
|
|
14
|
+
description: "The textbox label",
|
|
15
|
+
name: "label"
|
|
16
|
+
},
|
|
17
|
+
show_label: {
|
|
18
|
+
options: [true, false],
|
|
19
|
+
description: "Whether to show the label",
|
|
20
|
+
control: { type: "boolean" },
|
|
21
|
+
defaultValue: true
|
|
22
|
+
},
|
|
23
|
+
text_align: {
|
|
24
|
+
options: ["left", "right"],
|
|
25
|
+
description: "Whether to align the text left or right",
|
|
26
|
+
control: { type: "select" },
|
|
27
|
+
defaultValue: "left"
|
|
28
|
+
},
|
|
29
|
+
lines: {
|
|
30
|
+
options: [1, 5, 10, 20],
|
|
31
|
+
description: "The number of lines to display in the textbox",
|
|
32
|
+
control: { type: "select" },
|
|
33
|
+
defaultValue: 1
|
|
34
|
+
},
|
|
35
|
+
max_lines: {
|
|
36
|
+
options: [1, 5, 10, 20],
|
|
37
|
+
description:
|
|
38
|
+
"The maximum number of lines to allow users to type in the textbox",
|
|
39
|
+
control: { type: "select" },
|
|
40
|
+
defaultValue: 1
|
|
41
|
+
},
|
|
42
|
+
rtl: {
|
|
43
|
+
options: [true, false],
|
|
44
|
+
description: "Whether to render right-to-left",
|
|
45
|
+
control: { type: "boolean" },
|
|
46
|
+
defaultValue: false
|
|
47
|
+
},
|
|
48
|
+
sources: {
|
|
49
|
+
options: ["upload", "microphone"],
|
|
50
|
+
description: "The sources to enable",
|
|
51
|
+
control: { type: "select" },
|
|
52
|
+
defaultValue: ["upload", "microphone"]
|
|
53
|
+
}
|
|
51
54
|
}
|
|
52
|
-
}
|
|
53
|
-
|
|
55
|
+
});
|
|
56
|
+
</script>
|
|
54
57
|
|
|
55
|
-
|
|
56
|
-
<MultimodalTextbox {...args} />
|
|
57
|
-
|
|
58
|
+
{#snippet template(args)}
|
|
59
|
+
<MultimodalTextbox {...wrapProps(args)} />
|
|
60
|
+
{/snippet}
|
|
58
61
|
|
|
59
62
|
<Story
|
|
60
63
|
name="MultimodalTextbox with file and label"
|
|
@@ -63,43 +66,77 @@
|
|
|
63
66
|
text: "sample text",
|
|
64
67
|
files: [
|
|
65
68
|
{
|
|
66
|
-
path:
|
|
67
|
-
url:
|
|
69
|
+
path: cheetah,
|
|
70
|
+
url: cheetah,
|
|
68
71
|
orig_name: "cheetah.jpg"
|
|
69
72
|
}
|
|
70
73
|
]
|
|
71
74
|
},
|
|
72
75
|
label: "My simple label",
|
|
73
|
-
show_label: true
|
|
76
|
+
show_label: true,
|
|
77
|
+
sources: ["upload", "microphone"],
|
|
78
|
+
file_types: [],
|
|
79
|
+
lines: 1,
|
|
80
|
+
max_lines: 5
|
|
74
81
|
}}
|
|
82
|
+
{template}
|
|
75
83
|
/>
|
|
76
84
|
<Story
|
|
77
85
|
name="MultimodalTextbox with 5 lines and max 5 lines"
|
|
78
|
-
args={{
|
|
86
|
+
args={{
|
|
87
|
+
lines: 5,
|
|
88
|
+
max_lines: 5,
|
|
89
|
+
sources: ["upload", "microphone"],
|
|
90
|
+
file_types: [],
|
|
91
|
+
value: { text: "", files: [] }
|
|
92
|
+
}}
|
|
93
|
+
{template}
|
|
94
|
+
/>
|
|
95
|
+
<Story
|
|
96
|
+
name="Right aligned textbox"
|
|
97
|
+
args={{
|
|
98
|
+
text_align: "right",
|
|
99
|
+
sources: ["upload", "microphone"],
|
|
100
|
+
file_types: [],
|
|
101
|
+
lines: 1,
|
|
102
|
+
max_lines: 5,
|
|
103
|
+
value: { text: "", files: [] }
|
|
104
|
+
}}
|
|
105
|
+
{template}
|
|
79
106
|
/>
|
|
80
|
-
<Story name="Right aligned textbox" args={{ text_align: "right" }} />
|
|
81
107
|
<Story
|
|
82
108
|
name="Single file upload"
|
|
83
109
|
args={{
|
|
84
110
|
file_count: "single",
|
|
85
111
|
submit_btn: true,
|
|
112
|
+
sources: ["upload"],
|
|
113
|
+
file_types: [],
|
|
114
|
+
lines: 1,
|
|
115
|
+
max_lines: 5,
|
|
86
116
|
value: {
|
|
87
117
|
text: "sample text",
|
|
88
118
|
files: [
|
|
89
119
|
{
|
|
90
|
-
path:
|
|
120
|
+
path: cheetah,
|
|
121
|
+
url: cheetah,
|
|
122
|
+
orig_name: "cheetah.jpg"
|
|
91
123
|
}
|
|
92
124
|
]
|
|
93
125
|
}
|
|
94
126
|
}}
|
|
127
|
+
{template}
|
|
95
128
|
/>
|
|
96
129
|
<Story
|
|
97
130
|
name="MultimodalTextbox with microphone input and right to left text"
|
|
98
131
|
args={{
|
|
99
132
|
label: "مرحبًا",
|
|
100
133
|
sources: ["microphone"],
|
|
134
|
+
file_types: [],
|
|
101
135
|
rtl: true,
|
|
102
136
|
submit_btn: true,
|
|
137
|
+
lines: 1,
|
|
138
|
+
max_lines: 5,
|
|
103
139
|
value: { text: "مرحبًا", files: [] }
|
|
104
140
|
}}
|
|
141
|
+
{template}
|
|
105
142
|
/>
|
|
@@ -1,27 +1,31 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
import {
|
|
1
|
+
<script module>
|
|
2
|
+
import { defineMeta } from "@storybook/addon-svelte-csf";
|
|
3
3
|
import MultimodalTextbox from "./Example.svelte";
|
|
4
|
-
</script>
|
|
5
|
-
|
|
6
|
-
<Meta
|
|
7
|
-
title="Components/MultimodalTextbox/Example"
|
|
8
|
-
component={MultimodalTextbox}
|
|
9
|
-
/>
|
|
10
4
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
5
|
+
const { Story } = defineMeta({
|
|
6
|
+
title: "Components/MultimodalTextbox/Example",
|
|
7
|
+
component: MultimodalTextbox
|
|
8
|
+
});
|
|
9
|
+
</script>
|
|
14
10
|
|
|
15
11
|
<Story
|
|
16
12
|
name="Text value"
|
|
17
13
|
args={{
|
|
18
14
|
value: { text: "the quick brown fox", files: [] }
|
|
19
15
|
}}
|
|
20
|
-
|
|
16
|
+
>
|
|
17
|
+
{#snippet template(args)}
|
|
18
|
+
<MultimodalTextbox {...args} />
|
|
19
|
+
{/snippet}
|
|
20
|
+
</Story>
|
|
21
21
|
|
|
22
22
|
<Story
|
|
23
23
|
name="Empty Value"
|
|
24
24
|
args={{
|
|
25
25
|
value: { text: "", files: [] }
|
|
26
26
|
}}
|
|
27
|
-
|
|
27
|
+
>
|
|
28
|
+
{#snippet template(args)}
|
|
29
|
+
<MultimodalTextbox {...args} />
|
|
30
|
+
{/snippet}
|
|
31
|
+
</Story>
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
tick
|
|
8
8
|
} from "svelte";
|
|
9
9
|
import { text_area_resize, resize } from "../shared/utils";
|
|
10
|
-
import { BlockTitle } from "@gradio/atoms";
|
|
10
|
+
import { BlockTitle, ScrollFade } from "@gradio/atoms";
|
|
11
11
|
import { Upload } from "@gradio/upload";
|
|
12
12
|
import { Image } from "@gradio/image/shared";
|
|
13
13
|
import type { I18nFormatter } from "js/core/src/gradio_helper";
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
Microphone,
|
|
25
25
|
Check
|
|
26
26
|
} from "@gradio/icons";
|
|
27
|
-
import type
|
|
27
|
+
import { should_show_scroll_fade, type SelectData } from "@gradio/utils";
|
|
28
28
|
import { BaseInteractiveAudio as InteractiveAudio } from "@gradio/audio";
|
|
29
29
|
import {
|
|
30
30
|
MinimalAudioPlayer,
|
|
@@ -77,6 +77,7 @@
|
|
|
77
77
|
let can_scroll: boolean;
|
|
78
78
|
let previous_scroll_top = 0;
|
|
79
79
|
let user_has_scrolled_up = false;
|
|
80
|
+
let show_fade = false;
|
|
80
81
|
export let dragging = false;
|
|
81
82
|
let uploading = false;
|
|
82
83
|
// value can be null in multimodalchatinterface when loading a deep link
|
|
@@ -110,6 +111,14 @@
|
|
|
110
111
|
$: if (value === null) value = { text: "", files: [] };
|
|
111
112
|
$: (value, el && lines !== max_lines && resize(el, lines, max_lines));
|
|
112
113
|
|
|
114
|
+
function update_fade(): void {
|
|
115
|
+
show_fade = should_show_scroll_fade(el);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
$: if (el && value.text !== undefined) {
|
|
119
|
+
tick().then(update_fade);
|
|
120
|
+
}
|
|
121
|
+
|
|
113
122
|
const dispatch = createEventDispatcher<{
|
|
114
123
|
change: typeof value;
|
|
115
124
|
submit: undefined;
|
|
@@ -206,11 +215,10 @@
|
|
|
206
215
|
if (user_has_scrolled_to_bottom) {
|
|
207
216
|
user_has_scrolled_up = false;
|
|
208
217
|
}
|
|
218
|
+
update_fade();
|
|
209
219
|
}
|
|
210
220
|
|
|
211
|
-
async function handle_upload({
|
|
212
|
-
detail
|
|
213
|
-
}: CustomEvent<FileData>): Promise<void> {
|
|
221
|
+
async function handle_upload(detail: FileData | FileData[]): Promise<void> {
|
|
214
222
|
handle_change();
|
|
215
223
|
if (Array.isArray(detail)) {
|
|
216
224
|
for (let file of detail) {
|
|
@@ -360,19 +368,14 @@
|
|
|
360
368
|
{root}
|
|
361
369
|
{max_file_size}
|
|
362
370
|
bind:upload_promise
|
|
363
|
-
|
|
364
|
-
mic_audio =
|
|
371
|
+
onchange={(value) => {
|
|
372
|
+
mic_audio = value;
|
|
365
373
|
}}
|
|
366
|
-
|
|
374
|
+
onstoprecording={() => {
|
|
367
375
|
recording = false;
|
|
368
376
|
dispatch("stop_recording");
|
|
369
377
|
}}
|
|
370
|
-
|
|
371
|
-
active_source = null;
|
|
372
|
-
recording = false;
|
|
373
|
-
mic_audio = null;
|
|
374
|
-
}}
|
|
375
|
-
on:error={({ detail }) => {
|
|
378
|
+
onclear={() => {
|
|
376
379
|
active_source = null;
|
|
377
380
|
recording = false;
|
|
378
381
|
mic_audio = null;
|
|
@@ -418,7 +421,7 @@
|
|
|
418
421
|
<Upload
|
|
419
422
|
bind:upload_promise
|
|
420
423
|
bind:this={upload_component}
|
|
421
|
-
|
|
424
|
+
onload={handle_upload}
|
|
422
425
|
{file_count}
|
|
423
426
|
filetype={file_types}
|
|
424
427
|
{root}
|
|
@@ -427,7 +430,7 @@
|
|
|
427
430
|
bind:uploading
|
|
428
431
|
show_progress={false}
|
|
429
432
|
disable_click={true}
|
|
430
|
-
|
|
433
|
+
onerror={(e) => dispatch("error", e)}
|
|
431
434
|
hidden={true}
|
|
432
435
|
{upload}
|
|
433
436
|
{stream_handler}
|
|
@@ -438,7 +441,7 @@
|
|
|
438
441
|
class="input-wrapper"
|
|
439
442
|
class:has-files={value.files.length > 0 || uploading}
|
|
440
443
|
>
|
|
441
|
-
{#if value.files.length > 0 || uploading
|
|
444
|
+
{#if value.files.length > 0 || uploading}
|
|
442
445
|
<div
|
|
443
446
|
class="thumbnails"
|
|
444
447
|
aria-label="Uploaded files"
|
|
@@ -496,35 +499,49 @@
|
|
|
496
499
|
{/if}
|
|
497
500
|
|
|
498
501
|
<div class="input-row">
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
502
|
+
{#if show_upload && value.files.length === 0 && !uploading}
|
|
503
|
+
<button
|
|
504
|
+
data-testid="upload-button"
|
|
505
|
+
class="upload-button icon-button"
|
|
506
|
+
{disabled}
|
|
507
|
+
on:click={handle_upload_click}
|
|
508
|
+
aria-label="Upload a file"
|
|
509
|
+
>
|
|
510
|
+
<Paperclip />
|
|
511
|
+
</button>
|
|
512
|
+
{/if}
|
|
513
|
+
<div class="textarea-wrapper">
|
|
514
|
+
<textarea
|
|
515
|
+
data-testid="textbox"
|
|
516
|
+
use:text_area_resize={{
|
|
517
|
+
text: value.text,
|
|
518
|
+
lines: lines,
|
|
519
|
+
max_lines: max_lines
|
|
520
|
+
}}
|
|
521
|
+
class:no-label={!show_label}
|
|
522
|
+
dir={rtl ? "rtl" : "ltr"}
|
|
523
|
+
bind:value={value.text}
|
|
524
|
+
bind:this={el}
|
|
525
|
+
{placeholder}
|
|
526
|
+
rows={lines}
|
|
527
|
+
{disabled}
|
|
528
|
+
on:keypress={handle_keypress}
|
|
529
|
+
on:blur
|
|
530
|
+
on:select={handle_select}
|
|
531
|
+
on:focus
|
|
532
|
+
on:scroll={handle_scroll}
|
|
533
|
+
on:paste={handle_paste}
|
|
534
|
+
style={text_align ? "text-align: " + text_align : ""}
|
|
535
|
+
autocapitalize={html_attributes?.autocapitalize}
|
|
536
|
+
autocorrect={html_attributes?.autocorrect}
|
|
537
|
+
spellcheck={html_attributes?.spellcheck}
|
|
538
|
+
autocomplete={html_attributes?.autocomplete}
|
|
539
|
+
tabindex={html_attributes?.tabindex}
|
|
540
|
+
enterkeyhint={html_attributes?.enterkeyhint}
|
|
541
|
+
lang={html_attributes?.lang}
|
|
542
|
+
/>
|
|
543
|
+
<ScrollFade visible={show_fade} position="absolute" />
|
|
544
|
+
</div>
|
|
528
545
|
|
|
529
546
|
{#if sources && sources.includes("microphone")}
|
|
530
547
|
<button
|
|
@@ -617,7 +634,7 @@
|
|
|
617
634
|
gap: 0;
|
|
618
635
|
background: var(--block-background-fill);
|
|
619
636
|
border-radius: var(--radius-xl);
|
|
620
|
-
padding: var(--spacing-sm);
|
|
637
|
+
padding: var(--spacing-sm) var(--spacing-sm) var(--spacing-sm) 0;
|
|
621
638
|
align-items: flex-start;
|
|
622
639
|
min-height: auto;
|
|
623
640
|
}
|
|
@@ -628,7 +645,7 @@
|
|
|
628
645
|
|
|
629
646
|
.input-row {
|
|
630
647
|
display: flex;
|
|
631
|
-
align-items: flex-
|
|
648
|
+
align-items: flex-start;
|
|
632
649
|
gap: var(--spacing-sm);
|
|
633
650
|
width: 100%;
|
|
634
651
|
}
|
|
@@ -724,7 +741,7 @@
|
|
|
724
741
|
flex-grow: 1;
|
|
725
742
|
outline: none !important;
|
|
726
743
|
background: transparent;
|
|
727
|
-
padding: 0;
|
|
744
|
+
padding: var(--spacing-sm) 0;
|
|
728
745
|
color: var(--body-text-color);
|
|
729
746
|
font-weight: var(--input-text-weight);
|
|
730
747
|
font-size: var(--input-text-size);
|
|
@@ -734,7 +751,6 @@
|
|
|
734
751
|
position: relative;
|
|
735
752
|
z-index: var(--layer-1);
|
|
736
753
|
text-align: left;
|
|
737
|
-
min-height: var(--size-9);
|
|
738
754
|
}
|
|
739
755
|
|
|
740
756
|
textarea:disabled {
|
|
@@ -750,15 +766,12 @@
|
|
|
750
766
|
text-align: right;
|
|
751
767
|
}
|
|
752
768
|
|
|
753
|
-
textarea[dir="rtl"] ~ .submit-button {
|
|
754
|
-
order: -1;
|
|
755
|
-
}
|
|
756
|
-
|
|
757
769
|
textarea[dir="rtl"] ~ .submit-button :global(svg) {
|
|
758
770
|
transform: scaleX(-1);
|
|
759
771
|
}
|
|
760
772
|
|
|
761
|
-
.microphone-button
|
|
773
|
+
.microphone-button,
|
|
774
|
+
.icon-button {
|
|
762
775
|
color: var(--body-text-color);
|
|
763
776
|
cursor: pointer;
|
|
764
777
|
padding: var(--spacing-sm);
|
|
@@ -796,21 +809,25 @@
|
|
|
796
809
|
height: var(--size-5);
|
|
797
810
|
}
|
|
798
811
|
|
|
799
|
-
.microphone-button
|
|
812
|
+
.microphone-button,
|
|
813
|
+
.icon-button {
|
|
800
814
|
width: var(--size-9);
|
|
801
815
|
height: var(--size-9);
|
|
802
816
|
}
|
|
803
817
|
|
|
804
|
-
.microphone-button:hover:not(:disabled)
|
|
818
|
+
.microphone-button:hover:not(:disabled),
|
|
819
|
+
.icon-button:hover:not(:disabled) {
|
|
805
820
|
background: var(--button-secondary-background-fill);
|
|
806
821
|
}
|
|
807
822
|
|
|
808
|
-
.microphone-button:disabled
|
|
823
|
+
.microphone-button:disabled,
|
|
824
|
+
.icon-button:disabled {
|
|
809
825
|
opacity: 0.5;
|
|
810
826
|
cursor: not-allowed;
|
|
811
827
|
}
|
|
812
828
|
|
|
813
|
-
.microphone-button :global(svg)
|
|
829
|
+
.microphone-button :global(svg),
|
|
830
|
+
.icon-button :global(svg) {
|
|
814
831
|
width: var(--size-5);
|
|
815
832
|
height: var(--size-5);
|
|
816
833
|
}
|
|
@@ -1002,4 +1019,13 @@
|
|
|
1002
1019
|
opacity: 1;
|
|
1003
1020
|
}
|
|
1004
1021
|
}
|
|
1022
|
+
|
|
1023
|
+
.textarea-wrapper {
|
|
1024
|
+
position: relative;
|
|
1025
|
+
flex-grow: 1;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
.textarea-wrapper textarea {
|
|
1029
|
+
width: 100%;
|
|
1030
|
+
}
|
|
1005
1031
|
</style>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { I18nFormatter } from "js/core/src/gradio_helper";
|
|
2
2
|
import type { FileData, Client } from "@gradio/client";
|
|
3
3
|
import type { WaveformOptions } from "@gradio/audio";
|
|
4
|
-
import type
|
|
4
|
+
import { type SelectData } from "@gradio/utils";
|
|
5
5
|
import type { InputHTMLAttributes } from "./types";
|
|
6
6
|
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
7
7
|
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
@@ -52,7 +52,6 @@ declare const MultimodalTextbox: $$__sveltets_2_IsomorphicComponent<{
|
|
|
52
52
|
dragging?: boolean;
|
|
53
53
|
}, {
|
|
54
54
|
dragover: DragEvent;
|
|
55
|
-
error: CustomEvent<any>;
|
|
56
55
|
blur: CustomEvent<any>;
|
|
57
56
|
focus: CustomEvent<any>;
|
|
58
57
|
change: CustomEvent<{
|
|
@@ -67,6 +66,7 @@ declare const MultimodalTextbox: $$__sveltets_2_IsomorphicComponent<{
|
|
|
67
66
|
upload: CustomEvent<FileData | FileData[]>;
|
|
68
67
|
clear: CustomEvent<undefined>;
|
|
69
68
|
load: CustomEvent<FileData | FileData[]>;
|
|
69
|
+
error: CustomEvent<string>;
|
|
70
70
|
start_recording: CustomEvent<undefined>;
|
|
71
71
|
pause_recording: CustomEvent<undefined>;
|
|
72
72
|
stop_recording: CustomEvent<undefined>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gradio/multimodaltextbox",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.4",
|
|
4
4
|
"description": "Gradio UI packages",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "",
|
|
@@ -22,21 +22,21 @@
|
|
|
22
22
|
"./package.json": "./package.json"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@gradio/icons": "^0.15.
|
|
26
|
-
"@gradio/
|
|
27
|
-
"@gradio/
|
|
28
|
-
"@gradio/
|
|
29
|
-
"@gradio/upload": "^0.17.
|
|
30
|
-
"@gradio/image": "^0.25.
|
|
31
|
-
"@gradio/
|
|
32
|
-
"@gradio/
|
|
33
|
-
"@gradio/audio": "^0.22.
|
|
25
|
+
"@gradio/icons": "^0.15.1",
|
|
26
|
+
"@gradio/statustracker": "^0.12.2",
|
|
27
|
+
"@gradio/utils": "^0.11.2",
|
|
28
|
+
"@gradio/atoms": "^0.20.1",
|
|
29
|
+
"@gradio/upload": "^0.17.4",
|
|
30
|
+
"@gradio/image": "^0.25.1",
|
|
31
|
+
"@gradio/video": "^0.20.1",
|
|
32
|
+
"@gradio/client": "^2.0.3",
|
|
33
|
+
"@gradio/audio": "^0.22.1"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@gradio/preview": "^0.15.
|
|
36
|
+
"@gradio/preview": "^0.15.2"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
|
-
"svelte": "^5.
|
|
39
|
+
"svelte": "^5.48.0"
|
|
40
40
|
},
|
|
41
41
|
"repository": {
|
|
42
42
|
"type": "git",
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
tick
|
|
8
8
|
} from "svelte";
|
|
9
9
|
import { text_area_resize, resize } from "../shared/utils";
|
|
10
|
-
import { BlockTitle } from "@gradio/atoms";
|
|
10
|
+
import { BlockTitle, ScrollFade } from "@gradio/atoms";
|
|
11
11
|
import { Upload } from "@gradio/upload";
|
|
12
12
|
import { Image } from "@gradio/image/shared";
|
|
13
13
|
import type { I18nFormatter } from "js/core/src/gradio_helper";
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
Microphone,
|
|
25
25
|
Check
|
|
26
26
|
} from "@gradio/icons";
|
|
27
|
-
import type
|
|
27
|
+
import { should_show_scroll_fade, type SelectData } from "@gradio/utils";
|
|
28
28
|
import { BaseInteractiveAudio as InteractiveAudio } from "@gradio/audio";
|
|
29
29
|
import {
|
|
30
30
|
MinimalAudioPlayer,
|
|
@@ -77,6 +77,7 @@
|
|
|
77
77
|
let can_scroll: boolean;
|
|
78
78
|
let previous_scroll_top = 0;
|
|
79
79
|
let user_has_scrolled_up = false;
|
|
80
|
+
let show_fade = false;
|
|
80
81
|
export let dragging = false;
|
|
81
82
|
let uploading = false;
|
|
82
83
|
// value can be null in multimodalchatinterface when loading a deep link
|
|
@@ -110,6 +111,14 @@
|
|
|
110
111
|
$: if (value === null) value = { text: "", files: [] };
|
|
111
112
|
$: (value, el && lines !== max_lines && resize(el, lines, max_lines));
|
|
112
113
|
|
|
114
|
+
function update_fade(): void {
|
|
115
|
+
show_fade = should_show_scroll_fade(el);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
$: if (el && value.text !== undefined) {
|
|
119
|
+
tick().then(update_fade);
|
|
120
|
+
}
|
|
121
|
+
|
|
113
122
|
const dispatch = createEventDispatcher<{
|
|
114
123
|
change: typeof value;
|
|
115
124
|
submit: undefined;
|
|
@@ -206,11 +215,10 @@
|
|
|
206
215
|
if (user_has_scrolled_to_bottom) {
|
|
207
216
|
user_has_scrolled_up = false;
|
|
208
217
|
}
|
|
218
|
+
update_fade();
|
|
209
219
|
}
|
|
210
220
|
|
|
211
|
-
async function handle_upload({
|
|
212
|
-
detail
|
|
213
|
-
}: CustomEvent<FileData>): Promise<void> {
|
|
221
|
+
async function handle_upload(detail: FileData | FileData[]): Promise<void> {
|
|
214
222
|
handle_change();
|
|
215
223
|
if (Array.isArray(detail)) {
|
|
216
224
|
for (let file of detail) {
|
|
@@ -360,19 +368,14 @@
|
|
|
360
368
|
{root}
|
|
361
369
|
{max_file_size}
|
|
362
370
|
bind:upload_promise
|
|
363
|
-
|
|
364
|
-
mic_audio =
|
|
371
|
+
onchange={(value) => {
|
|
372
|
+
mic_audio = value;
|
|
365
373
|
}}
|
|
366
|
-
|
|
374
|
+
onstoprecording={() => {
|
|
367
375
|
recording = false;
|
|
368
376
|
dispatch("stop_recording");
|
|
369
377
|
}}
|
|
370
|
-
|
|
371
|
-
active_source = null;
|
|
372
|
-
recording = false;
|
|
373
|
-
mic_audio = null;
|
|
374
|
-
}}
|
|
375
|
-
on:error={({ detail }) => {
|
|
378
|
+
onclear={() => {
|
|
376
379
|
active_source = null;
|
|
377
380
|
recording = false;
|
|
378
381
|
mic_audio = null;
|
|
@@ -418,7 +421,7 @@
|
|
|
418
421
|
<Upload
|
|
419
422
|
bind:upload_promise
|
|
420
423
|
bind:this={upload_component}
|
|
421
|
-
|
|
424
|
+
onload={handle_upload}
|
|
422
425
|
{file_count}
|
|
423
426
|
filetype={file_types}
|
|
424
427
|
{root}
|
|
@@ -427,7 +430,7 @@
|
|
|
427
430
|
bind:uploading
|
|
428
431
|
show_progress={false}
|
|
429
432
|
disable_click={true}
|
|
430
|
-
|
|
433
|
+
onerror={(e) => dispatch("error", e)}
|
|
431
434
|
hidden={true}
|
|
432
435
|
{upload}
|
|
433
436
|
{stream_handler}
|
|
@@ -438,7 +441,7 @@
|
|
|
438
441
|
class="input-wrapper"
|
|
439
442
|
class:has-files={value.files.length > 0 || uploading}
|
|
440
443
|
>
|
|
441
|
-
{#if value.files.length > 0 || uploading
|
|
444
|
+
{#if value.files.length > 0 || uploading}
|
|
442
445
|
<div
|
|
443
446
|
class="thumbnails"
|
|
444
447
|
aria-label="Uploaded files"
|
|
@@ -496,35 +499,49 @@
|
|
|
496
499
|
{/if}
|
|
497
500
|
|
|
498
501
|
<div class="input-row">
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
502
|
+
{#if show_upload && value.files.length === 0 && !uploading}
|
|
503
|
+
<button
|
|
504
|
+
data-testid="upload-button"
|
|
505
|
+
class="upload-button icon-button"
|
|
506
|
+
{disabled}
|
|
507
|
+
on:click={handle_upload_click}
|
|
508
|
+
aria-label="Upload a file"
|
|
509
|
+
>
|
|
510
|
+
<Paperclip />
|
|
511
|
+
</button>
|
|
512
|
+
{/if}
|
|
513
|
+
<div class="textarea-wrapper">
|
|
514
|
+
<textarea
|
|
515
|
+
data-testid="textbox"
|
|
516
|
+
use:text_area_resize={{
|
|
517
|
+
text: value.text,
|
|
518
|
+
lines: lines,
|
|
519
|
+
max_lines: max_lines
|
|
520
|
+
}}
|
|
521
|
+
class:no-label={!show_label}
|
|
522
|
+
dir={rtl ? "rtl" : "ltr"}
|
|
523
|
+
bind:value={value.text}
|
|
524
|
+
bind:this={el}
|
|
525
|
+
{placeholder}
|
|
526
|
+
rows={lines}
|
|
527
|
+
{disabled}
|
|
528
|
+
on:keypress={handle_keypress}
|
|
529
|
+
on:blur
|
|
530
|
+
on:select={handle_select}
|
|
531
|
+
on:focus
|
|
532
|
+
on:scroll={handle_scroll}
|
|
533
|
+
on:paste={handle_paste}
|
|
534
|
+
style={text_align ? "text-align: " + text_align : ""}
|
|
535
|
+
autocapitalize={html_attributes?.autocapitalize}
|
|
536
|
+
autocorrect={html_attributes?.autocorrect}
|
|
537
|
+
spellcheck={html_attributes?.spellcheck}
|
|
538
|
+
autocomplete={html_attributes?.autocomplete}
|
|
539
|
+
tabindex={html_attributes?.tabindex}
|
|
540
|
+
enterkeyhint={html_attributes?.enterkeyhint}
|
|
541
|
+
lang={html_attributes?.lang}
|
|
542
|
+
/>
|
|
543
|
+
<ScrollFade visible={show_fade} position="absolute" />
|
|
544
|
+
</div>
|
|
528
545
|
|
|
529
546
|
{#if sources && sources.includes("microphone")}
|
|
530
547
|
<button
|
|
@@ -617,7 +634,7 @@
|
|
|
617
634
|
gap: 0;
|
|
618
635
|
background: var(--block-background-fill);
|
|
619
636
|
border-radius: var(--radius-xl);
|
|
620
|
-
padding: var(--spacing-sm);
|
|
637
|
+
padding: var(--spacing-sm) var(--spacing-sm) var(--spacing-sm) 0;
|
|
621
638
|
align-items: flex-start;
|
|
622
639
|
min-height: auto;
|
|
623
640
|
}
|
|
@@ -628,7 +645,7 @@
|
|
|
628
645
|
|
|
629
646
|
.input-row {
|
|
630
647
|
display: flex;
|
|
631
|
-
align-items: flex-
|
|
648
|
+
align-items: flex-start;
|
|
632
649
|
gap: var(--spacing-sm);
|
|
633
650
|
width: 100%;
|
|
634
651
|
}
|
|
@@ -724,7 +741,7 @@
|
|
|
724
741
|
flex-grow: 1;
|
|
725
742
|
outline: none !important;
|
|
726
743
|
background: transparent;
|
|
727
|
-
padding: 0;
|
|
744
|
+
padding: var(--spacing-sm) 0;
|
|
728
745
|
color: var(--body-text-color);
|
|
729
746
|
font-weight: var(--input-text-weight);
|
|
730
747
|
font-size: var(--input-text-size);
|
|
@@ -734,7 +751,6 @@
|
|
|
734
751
|
position: relative;
|
|
735
752
|
z-index: var(--layer-1);
|
|
736
753
|
text-align: left;
|
|
737
|
-
min-height: var(--size-9);
|
|
738
754
|
}
|
|
739
755
|
|
|
740
756
|
textarea:disabled {
|
|
@@ -750,15 +766,12 @@
|
|
|
750
766
|
text-align: right;
|
|
751
767
|
}
|
|
752
768
|
|
|
753
|
-
textarea[dir="rtl"] ~ .submit-button {
|
|
754
|
-
order: -1;
|
|
755
|
-
}
|
|
756
|
-
|
|
757
769
|
textarea[dir="rtl"] ~ .submit-button :global(svg) {
|
|
758
770
|
transform: scaleX(-1);
|
|
759
771
|
}
|
|
760
772
|
|
|
761
|
-
.microphone-button
|
|
773
|
+
.microphone-button,
|
|
774
|
+
.icon-button {
|
|
762
775
|
color: var(--body-text-color);
|
|
763
776
|
cursor: pointer;
|
|
764
777
|
padding: var(--spacing-sm);
|
|
@@ -796,21 +809,25 @@
|
|
|
796
809
|
height: var(--size-5);
|
|
797
810
|
}
|
|
798
811
|
|
|
799
|
-
.microphone-button
|
|
812
|
+
.microphone-button,
|
|
813
|
+
.icon-button {
|
|
800
814
|
width: var(--size-9);
|
|
801
815
|
height: var(--size-9);
|
|
802
816
|
}
|
|
803
817
|
|
|
804
|
-
.microphone-button:hover:not(:disabled)
|
|
818
|
+
.microphone-button:hover:not(:disabled),
|
|
819
|
+
.icon-button:hover:not(:disabled) {
|
|
805
820
|
background: var(--button-secondary-background-fill);
|
|
806
821
|
}
|
|
807
822
|
|
|
808
|
-
.microphone-button:disabled
|
|
823
|
+
.microphone-button:disabled,
|
|
824
|
+
.icon-button:disabled {
|
|
809
825
|
opacity: 0.5;
|
|
810
826
|
cursor: not-allowed;
|
|
811
827
|
}
|
|
812
828
|
|
|
813
|
-
.microphone-button :global(svg)
|
|
829
|
+
.microphone-button :global(svg),
|
|
830
|
+
.icon-button :global(svg) {
|
|
814
831
|
width: var(--size-5);
|
|
815
832
|
height: var(--size-5);
|
|
816
833
|
}
|
|
@@ -1002,4 +1019,13 @@
|
|
|
1002
1019
|
opacity: 1;
|
|
1003
1020
|
}
|
|
1004
1021
|
}
|
|
1022
|
+
|
|
1023
|
+
.textarea-wrapper {
|
|
1024
|
+
position: relative;
|
|
1025
|
+
flex-grow: 1;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
.textarea-wrapper textarea {
|
|
1029
|
+
width: 100%;
|
|
1030
|
+
}
|
|
1005
1031
|
</style>
|