@gradio/multimodaltextbox 0.7.8 → 0.9.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.
- package/CHANGELOG.md +35 -0
- package/Index.svelte +62 -2
- package/MultimodalTextbox.stories.svelte +10 -0
- package/dist/Index.svelte +51 -2
- package/dist/Index.svelte.d.ts +27 -4
- package/dist/shared/MultimodalTextbox.svelte +203 -122
- package/dist/shared/MultimodalTextbox.svelte.d.ts +10 -1
- package/package.json +9 -9
- package/shared/MultimodalTextbox.svelte +212 -124
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# @gradio/multimodaltextbox
|
|
2
2
|
|
|
3
|
+
## 0.9.0
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- [#10186](https://github.com/gradio-app/gradio/pull/10186) [`9b17032`](https://github.com/gradio-app/gradio/commit/9b17032a6564c8bda4ab1749989a9797c670919c) - Add Microphone Input to MultimodalTextbox. Thanks @dawoodkhan82!
|
|
8
|
+
|
|
9
|
+
### Dependency updates
|
|
10
|
+
|
|
11
|
+
- @gradio/atoms@0.13.0
|
|
12
|
+
- @gradio/utils@0.10.0
|
|
13
|
+
- @gradio/upload@0.14.4
|
|
14
|
+
- @gradio/client@1.9.0
|
|
15
|
+
- @gradio/icons@0.9.0
|
|
16
|
+
- @gradio/statustracker@0.10.0
|
|
17
|
+
- @gradio/image@0.20.0
|
|
18
|
+
- @gradio/video@0.13.0
|
|
19
|
+
|
|
20
|
+
## 0.8.0
|
|
21
|
+
|
|
22
|
+
### Features
|
|
23
|
+
|
|
24
|
+
- [#10135](https://github.com/gradio-app/gradio/pull/10135) [`3e93740`](https://github.com/gradio-app/gradio/commit/3e93740f057712278a7c2194a1702f48915683e1) - Improve pasted text behaviour in `Multimodaltextbox`. Thanks @hannahblair!
|
|
25
|
+
|
|
26
|
+
### Fixes
|
|
27
|
+
|
|
28
|
+
- [#10168](https://github.com/gradio-app/gradio/pull/10168) [`7d70596`](https://github.com/gradio-app/gradio/commit/7d70596d0b3343dc4b357758f85420a0afdb65a2) - Multimodal autofocus fix. Thanks @dawoodkhan82!
|
|
29
|
+
|
|
30
|
+
### Dependency updates
|
|
31
|
+
|
|
32
|
+
- @gradio/video@0.12.1
|
|
33
|
+
- @gradio/statustracker@0.9.7
|
|
34
|
+
- @gradio/upload@0.14.3
|
|
35
|
+
- @gradio/atoms@0.12.0
|
|
36
|
+
- @gradio/image@0.19.0
|
|
37
|
+
|
|
3
38
|
## 0.7.8
|
|
4
39
|
|
|
5
40
|
### Dependency updates
|
package/Index.svelte
CHANGED
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
import { StatusTracker } from "@gradio/statustracker";
|
|
13
13
|
import type { LoadingStatus } from "@gradio/statustracker";
|
|
14
14
|
import type { FileData } from "@gradio/client";
|
|
15
|
+
import { onMount } from "svelte";
|
|
16
|
+
import type { WaveformOptions } from "../audio/shared/types";
|
|
15
17
|
|
|
16
18
|
export let gradio: Gradio<{
|
|
17
19
|
change: typeof value;
|
|
@@ -23,6 +25,11 @@
|
|
|
23
25
|
focus: never;
|
|
24
26
|
error: string;
|
|
25
27
|
clear_status: LoadingStatus;
|
|
28
|
+
start_recording: never;
|
|
29
|
+
pause_recording: never;
|
|
30
|
+
stop_recording: never;
|
|
31
|
+
upload: FileData[] | FileData;
|
|
32
|
+
clear: undefined;
|
|
26
33
|
}>;
|
|
27
34
|
export let elem_id = "";
|
|
28
35
|
export let elem_classes: string[] = [];
|
|
@@ -38,7 +45,6 @@
|
|
|
38
45
|
export let info: string | undefined = undefined;
|
|
39
46
|
export let show_label: boolean;
|
|
40
47
|
export let max_lines: number;
|
|
41
|
-
export let container = true;
|
|
42
48
|
export let scale: number | null = null;
|
|
43
49
|
export let min_width: number | undefined = undefined;
|
|
44
50
|
export let submit_btn: string | boolean | null = null;
|
|
@@ -52,8 +58,53 @@
|
|
|
52
58
|
export let interactive: boolean;
|
|
53
59
|
export let root: string;
|
|
54
60
|
export let file_count: "single" | "multiple" | "directory";
|
|
61
|
+
export let max_plain_text_length: number;
|
|
62
|
+
export let sources: ["microphone" | "upload"] = ["upload"];
|
|
63
|
+
export let waveform_options: WaveformOptions = {};
|
|
55
64
|
|
|
56
65
|
let dragging: boolean;
|
|
66
|
+
let active_source: "microphone" | null = null;
|
|
67
|
+
let waveform_settings: Record<string, any>;
|
|
68
|
+
let color_accent = "darkorange";
|
|
69
|
+
|
|
70
|
+
onMount(() => {
|
|
71
|
+
color_accent = getComputedStyle(document?.documentElement).getPropertyValue(
|
|
72
|
+
"--color-accent"
|
|
73
|
+
);
|
|
74
|
+
set_trim_region_colour();
|
|
75
|
+
waveform_settings.waveColor = waveform_options.waveform_color || "#9ca3af";
|
|
76
|
+
waveform_settings.progressColor =
|
|
77
|
+
waveform_options.waveform_progress_color || color_accent;
|
|
78
|
+
waveform_settings.mediaControls = waveform_options.show_controls;
|
|
79
|
+
waveform_settings.sampleRate = waveform_options.sample_rate || 44100;
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
$: waveform_settings = {
|
|
83
|
+
height: 50,
|
|
84
|
+
|
|
85
|
+
barWidth: 2,
|
|
86
|
+
barGap: 3,
|
|
87
|
+
cursorWidth: 2,
|
|
88
|
+
cursorColor: "#ddd5e9",
|
|
89
|
+
autoplay: false,
|
|
90
|
+
barRadius: 10,
|
|
91
|
+
dragToSeek: true,
|
|
92
|
+
normalize: true,
|
|
93
|
+
minPxPerSec: 20
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const trim_region_settings = {
|
|
97
|
+
color: waveform_options.trim_region_color,
|
|
98
|
+
drag: true,
|
|
99
|
+
resize: true
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
function set_trim_region_colour(): void {
|
|
103
|
+
document.documentElement.style.setProperty(
|
|
104
|
+
"--trim-region-color",
|
|
105
|
+
trim_region_settings.color || color_accent
|
|
106
|
+
);
|
|
107
|
+
}
|
|
57
108
|
</script>
|
|
58
109
|
|
|
59
110
|
<Block
|
|
@@ -79,6 +130,7 @@
|
|
|
79
130
|
bind:value
|
|
80
131
|
bind:value_is_output
|
|
81
132
|
bind:dragging
|
|
133
|
+
bind:active_source
|
|
82
134
|
{file_types}
|
|
83
135
|
{root}
|
|
84
136
|
{label}
|
|
@@ -87,14 +139,16 @@
|
|
|
87
139
|
{lines}
|
|
88
140
|
{rtl}
|
|
89
141
|
{text_align}
|
|
142
|
+
{waveform_settings}
|
|
143
|
+
i18n={gradio.i18n}
|
|
90
144
|
max_lines={!max_lines ? lines + 1 : max_lines}
|
|
91
145
|
{placeholder}
|
|
92
146
|
{submit_btn}
|
|
93
147
|
{stop_btn}
|
|
94
148
|
{autofocus}
|
|
95
|
-
{container}
|
|
96
149
|
{autoscroll}
|
|
97
150
|
{file_count}
|
|
151
|
+
{sources}
|
|
98
152
|
max_file_size={gradio.max_file_size}
|
|
99
153
|
on:change={() => gradio.dispatch("change", value)}
|
|
100
154
|
on:input={() => gradio.dispatch("input")}
|
|
@@ -106,8 +160,14 @@
|
|
|
106
160
|
on:error={({ detail }) => {
|
|
107
161
|
gradio.dispatch("error", detail);
|
|
108
162
|
}}
|
|
163
|
+
on:start_recording={() => gradio.dispatch("start_recording")}
|
|
164
|
+
on:pause_recording={() => gradio.dispatch("pause_recording")}
|
|
165
|
+
on:stop_recording={() => gradio.dispatch("stop_recording")}
|
|
166
|
+
on:upload={(e) => gradio.dispatch("upload", e.detail)}
|
|
167
|
+
on:clear={() => gradio.dispatch("clear")}
|
|
109
168
|
disabled={!interactive}
|
|
110
169
|
upload={(...args) => gradio.client.upload(...args)}
|
|
111
170
|
stream_handler={(...args) => gradio.client.stream(...args)}
|
|
171
|
+
{max_plain_text_length}
|
|
112
172
|
/>
|
|
113
173
|
</Block>
|
|
@@ -42,6 +42,12 @@
|
|
|
42
42
|
description: "Whether to render right-to-left",
|
|
43
43
|
control: { type: "boolean" },
|
|
44
44
|
defaultValue: false
|
|
45
|
+
},
|
|
46
|
+
sources: {
|
|
47
|
+
options: ["upload", "microphone"],
|
|
48
|
+
description: "The sources to enable",
|
|
49
|
+
control: { type: "select" },
|
|
50
|
+
defaultValue: ["upload", "microphone"]
|
|
45
51
|
}
|
|
46
52
|
}}
|
|
47
53
|
/>
|
|
@@ -87,3 +93,7 @@
|
|
|
87
93
|
}
|
|
88
94
|
}}
|
|
89
95
|
/>
|
|
96
|
+
<Story
|
|
97
|
+
name="MultimodalTextbox with microphone input"
|
|
98
|
+
args={{ sources: ["microphone"] }}
|
|
99
|
+
/>
|
package/dist/Index.svelte
CHANGED
|
@@ -7,6 +7,7 @@ export { default as BaseExample } from "./Example.svelte";
|
|
|
7
7
|
<script>import MultimodalTextbox from "./shared/MultimodalTextbox.svelte";
|
|
8
8
|
import { Block } from "@gradio/atoms";
|
|
9
9
|
import { StatusTracker } from "@gradio/statustracker";
|
|
10
|
+
import { onMount } from "svelte";
|
|
10
11
|
export let gradio;
|
|
11
12
|
export let elem_id = "";
|
|
12
13
|
export let elem_classes = [];
|
|
@@ -22,7 +23,6 @@ export let label = "MultimodalTextbox";
|
|
|
22
23
|
export let info = void 0;
|
|
23
24
|
export let show_label;
|
|
24
25
|
export let max_lines;
|
|
25
|
-
export let container = true;
|
|
26
26
|
export let scale = null;
|
|
27
27
|
export let min_width = void 0;
|
|
28
28
|
export let submit_btn = null;
|
|
@@ -36,7 +36,47 @@ export let autoscroll = true;
|
|
|
36
36
|
export let interactive;
|
|
37
37
|
export let root;
|
|
38
38
|
export let file_count;
|
|
39
|
+
export let max_plain_text_length;
|
|
40
|
+
export let sources = ["upload"];
|
|
41
|
+
export let waveform_options = {};
|
|
39
42
|
let dragging;
|
|
43
|
+
let active_source = null;
|
|
44
|
+
let waveform_settings;
|
|
45
|
+
let color_accent = "darkorange";
|
|
46
|
+
onMount(() => {
|
|
47
|
+
color_accent = getComputedStyle(document?.documentElement).getPropertyValue(
|
|
48
|
+
"--color-accent"
|
|
49
|
+
);
|
|
50
|
+
set_trim_region_colour();
|
|
51
|
+
waveform_settings.waveColor = waveform_options.waveform_color || "#9ca3af";
|
|
52
|
+
waveform_settings.progressColor = waveform_options.waveform_progress_color || color_accent;
|
|
53
|
+
waveform_settings.mediaControls = waveform_options.show_controls;
|
|
54
|
+
waveform_settings.sampleRate = waveform_options.sample_rate || 44100;
|
|
55
|
+
});
|
|
56
|
+
$:
|
|
57
|
+
waveform_settings = {
|
|
58
|
+
height: 50,
|
|
59
|
+
barWidth: 2,
|
|
60
|
+
barGap: 3,
|
|
61
|
+
cursorWidth: 2,
|
|
62
|
+
cursorColor: "#ddd5e9",
|
|
63
|
+
autoplay: false,
|
|
64
|
+
barRadius: 10,
|
|
65
|
+
dragToSeek: true,
|
|
66
|
+
normalize: true,
|
|
67
|
+
minPxPerSec: 20
|
|
68
|
+
};
|
|
69
|
+
const trim_region_settings = {
|
|
70
|
+
color: waveform_options.trim_region_color,
|
|
71
|
+
drag: true,
|
|
72
|
+
resize: true
|
|
73
|
+
};
|
|
74
|
+
function set_trim_region_colour() {
|
|
75
|
+
document.documentElement.style.setProperty(
|
|
76
|
+
"--trim-region-color",
|
|
77
|
+
trim_region_settings.color || color_accent
|
|
78
|
+
);
|
|
79
|
+
}
|
|
40
80
|
</script>
|
|
41
81
|
|
|
42
82
|
<Block
|
|
@@ -62,6 +102,7 @@ let dragging;
|
|
|
62
102
|
bind:value
|
|
63
103
|
bind:value_is_output
|
|
64
104
|
bind:dragging
|
|
105
|
+
bind:active_source
|
|
65
106
|
{file_types}
|
|
66
107
|
{root}
|
|
67
108
|
{label}
|
|
@@ -70,14 +111,16 @@ let dragging;
|
|
|
70
111
|
{lines}
|
|
71
112
|
{rtl}
|
|
72
113
|
{text_align}
|
|
114
|
+
{waveform_settings}
|
|
115
|
+
i18n={gradio.i18n}
|
|
73
116
|
max_lines={!max_lines ? lines + 1 : max_lines}
|
|
74
117
|
{placeholder}
|
|
75
118
|
{submit_btn}
|
|
76
119
|
{stop_btn}
|
|
77
120
|
{autofocus}
|
|
78
|
-
{container}
|
|
79
121
|
{autoscroll}
|
|
80
122
|
{file_count}
|
|
123
|
+
{sources}
|
|
81
124
|
max_file_size={gradio.max_file_size}
|
|
82
125
|
on:change={() => gradio.dispatch("change", value)}
|
|
83
126
|
on:input={() => gradio.dispatch("input")}
|
|
@@ -89,8 +132,14 @@ let dragging;
|
|
|
89
132
|
on:error={({ detail }) => {
|
|
90
133
|
gradio.dispatch("error", detail);
|
|
91
134
|
}}
|
|
135
|
+
on:start_recording={() => gradio.dispatch("start_recording")}
|
|
136
|
+
on:pause_recording={() => gradio.dispatch("pause_recording")}
|
|
137
|
+
on:stop_recording={() => gradio.dispatch("stop_recording")}
|
|
138
|
+
on:upload={(e) => gradio.dispatch("upload", e.detail)}
|
|
139
|
+
on:clear={() => gradio.dispatch("clear")}
|
|
92
140
|
disabled={!interactive}
|
|
93
141
|
upload={(...args) => gradio.client.upload(...args)}
|
|
94
142
|
stream_handler={(...args) => gradio.client.stream(...args)}
|
|
143
|
+
{max_plain_text_length}
|
|
95
144
|
/>
|
|
96
145
|
</Block>
|
package/dist/Index.svelte.d.ts
CHANGED
|
@@ -19,6 +19,11 @@ declare const __propDef: {
|
|
|
19
19
|
focus: never;
|
|
20
20
|
error: string;
|
|
21
21
|
clear_status: LoadingStatus;
|
|
22
|
+
start_recording: never;
|
|
23
|
+
pause_recording: never;
|
|
24
|
+
stop_recording: never;
|
|
25
|
+
upload: FileData[] | FileData;
|
|
26
|
+
clear: undefined;
|
|
22
27
|
}>;
|
|
23
28
|
elem_id?: string | undefined;
|
|
24
29
|
elem_classes?: string[] | undefined;
|
|
@@ -34,7 +39,6 @@ declare const __propDef: {
|
|
|
34
39
|
info?: string | undefined;
|
|
35
40
|
show_label: boolean;
|
|
36
41
|
max_lines: number;
|
|
37
|
-
container?: boolean | undefined;
|
|
38
42
|
scale?: (number | null) | undefined;
|
|
39
43
|
min_width?: number | undefined;
|
|
40
44
|
submit_btn?: (string | boolean | null) | undefined;
|
|
@@ -48,6 +52,9 @@ declare const __propDef: {
|
|
|
48
52
|
interactive: boolean;
|
|
49
53
|
root: string;
|
|
50
54
|
file_count: "single" | "multiple" | "directory";
|
|
55
|
+
max_plain_text_length: number;
|
|
56
|
+
sources?: ["microphone" | "upload"] | undefined;
|
|
57
|
+
waveform_options?: any;
|
|
51
58
|
};
|
|
52
59
|
events: {
|
|
53
60
|
[evt: string]: CustomEvent<any>;
|
|
@@ -71,6 +78,11 @@ export default class Index extends SvelteComponent<IndexProps, IndexEvents, Inde
|
|
|
71
78
|
focus: never;
|
|
72
79
|
error: string;
|
|
73
80
|
clear_status: LoadingStatus;
|
|
81
|
+
start_recording: never;
|
|
82
|
+
pause_recording: never;
|
|
83
|
+
stop_recording: never;
|
|
84
|
+
upload: FileData[] | FileData;
|
|
85
|
+
clear: undefined;
|
|
74
86
|
}>;
|
|
75
87
|
/**accessor*/
|
|
76
88
|
set gradio(_: Gradio<{
|
|
@@ -86,6 +98,11 @@ export default class Index extends SvelteComponent<IndexProps, IndexEvents, Inde
|
|
|
86
98
|
focus: never;
|
|
87
99
|
error: string;
|
|
88
100
|
clear_status: LoadingStatus;
|
|
101
|
+
start_recording: never;
|
|
102
|
+
pause_recording: never;
|
|
103
|
+
stop_recording: never;
|
|
104
|
+
upload: FileData[] | FileData;
|
|
105
|
+
clear: undefined;
|
|
89
106
|
}>);
|
|
90
107
|
get elem_id(): string | undefined;
|
|
91
108
|
/**accessor*/
|
|
@@ -126,9 +143,6 @@ export default class Index extends SvelteComponent<IndexProps, IndexEvents, Inde
|
|
|
126
143
|
get max_lines(): number;
|
|
127
144
|
/**accessor*/
|
|
128
145
|
set max_lines(_: number);
|
|
129
|
-
get container(): boolean | undefined;
|
|
130
|
-
/**accessor*/
|
|
131
|
-
set container(_: boolean | undefined);
|
|
132
146
|
get scale(): number | null | undefined;
|
|
133
147
|
/**accessor*/
|
|
134
148
|
set scale(_: number | null | undefined);
|
|
@@ -168,4 +182,13 @@ export default class Index extends SvelteComponent<IndexProps, IndexEvents, Inde
|
|
|
168
182
|
get file_count(): "single" | "multiple" | "directory";
|
|
169
183
|
/**accessor*/
|
|
170
184
|
set file_count(_: "single" | "multiple" | "directory");
|
|
185
|
+
get max_plain_text_length(): number;
|
|
186
|
+
/**accessor*/
|
|
187
|
+
set max_plain_text_length(_: number);
|
|
188
|
+
get sources(): ["upload" | "microphone"] | undefined;
|
|
189
|
+
/**accessor*/
|
|
190
|
+
set sources(_: ["upload" | "microphone"] | undefined);
|
|
191
|
+
get waveform_options(): any;
|
|
192
|
+
/**accessor*/
|
|
193
|
+
set waveform_options(_: any);
|
|
171
194
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script>import {
|
|
2
|
+
onMount,
|
|
2
3
|
beforeUpdate,
|
|
3
4
|
afterUpdate,
|
|
4
5
|
createEventDispatcher,
|
|
@@ -15,20 +16,22 @@ import {
|
|
|
15
16
|
Paperclip,
|
|
16
17
|
Video,
|
|
17
18
|
Send,
|
|
18
|
-
Square
|
|
19
|
+
Square,
|
|
20
|
+
Microphone
|
|
19
21
|
} from "@gradio/icons";
|
|
22
|
+
import InteractiveAudio from "../../audio/interactive/InteractiveAudio.svelte";
|
|
20
23
|
export let value = {
|
|
21
24
|
text: "",
|
|
22
25
|
files: []
|
|
23
26
|
};
|
|
24
27
|
export let value_is_output = false;
|
|
25
28
|
export let lines = 1;
|
|
29
|
+
export let i18n;
|
|
26
30
|
export let placeholder = "Type here...";
|
|
27
31
|
export let disabled = false;
|
|
28
32
|
export let label;
|
|
29
33
|
export let info = void 0;
|
|
30
34
|
export let show_label = true;
|
|
31
|
-
export let container = true;
|
|
32
35
|
export let max_lines;
|
|
33
36
|
export let submit_btn = null;
|
|
34
37
|
export let stop_btn = null;
|
|
@@ -42,6 +45,11 @@ export let max_file_size = null;
|
|
|
42
45
|
export let upload;
|
|
43
46
|
export let stream_handler;
|
|
44
47
|
export let file_count = "multiple";
|
|
48
|
+
export let max_plain_text_length = 1e3;
|
|
49
|
+
export let waveform_settings;
|
|
50
|
+
export let waveform_options = {};
|
|
51
|
+
export let sources = ["upload"];
|
|
52
|
+
export let active_source = null;
|
|
45
53
|
let upload_component;
|
|
46
54
|
let hidden_upload;
|
|
47
55
|
let el;
|
|
@@ -51,8 +59,10 @@ let user_has_scrolled_up = false;
|
|
|
51
59
|
export let dragging = false;
|
|
52
60
|
let uploading = false;
|
|
53
61
|
let oldValue = value.text;
|
|
62
|
+
let recording = false;
|
|
54
63
|
$:
|
|
55
64
|
dispatch("drag", dragging);
|
|
65
|
+
let mic_audio = null;
|
|
56
66
|
let full_container;
|
|
57
67
|
$:
|
|
58
68
|
if (oldValue !== value.text) {
|
|
@@ -79,10 +89,12 @@ async function handle_change() {
|
|
|
79
89
|
dispatch("input");
|
|
80
90
|
}
|
|
81
91
|
}
|
|
82
|
-
|
|
92
|
+
onMount(() => {
|
|
83
93
|
if (autofocus && el !== null) {
|
|
84
94
|
el.focus();
|
|
85
95
|
}
|
|
96
|
+
});
|
|
97
|
+
afterUpdate(() => {
|
|
86
98
|
if (can_scroll && autoscroll) {
|
|
87
99
|
scroll();
|
|
88
100
|
}
|
|
@@ -105,6 +117,11 @@ async function handle_keypress(e) {
|
|
|
105
117
|
} else if (e.key === "Enter" && !e.shiftKey && lines === 1 && max_lines >= 1) {
|
|
106
118
|
e.preventDefault();
|
|
107
119
|
dispatch("submit");
|
|
120
|
+
active_source = null;
|
|
121
|
+
if (mic_audio) {
|
|
122
|
+
value.files.push(mic_audio);
|
|
123
|
+
value = value;
|
|
124
|
+
}
|
|
108
125
|
}
|
|
109
126
|
}
|
|
110
127
|
function handle_scroll(event) {
|
|
@@ -154,11 +171,28 @@ function handle_stop() {
|
|
|
154
171
|
}
|
|
155
172
|
function handle_submit() {
|
|
156
173
|
dispatch("submit");
|
|
174
|
+
active_source = null;
|
|
175
|
+
if (mic_audio) {
|
|
176
|
+
value.files.push(mic_audio);
|
|
177
|
+
value = value;
|
|
178
|
+
}
|
|
157
179
|
}
|
|
158
|
-
function handle_paste(event) {
|
|
180
|
+
async function handle_paste(event) {
|
|
159
181
|
if (!event.clipboardData)
|
|
160
182
|
return;
|
|
161
183
|
const items = event.clipboardData.items;
|
|
184
|
+
const text = event.clipboardData.getData("text");
|
|
185
|
+
if (text && text.length > max_plain_text_length) {
|
|
186
|
+
event.preventDefault();
|
|
187
|
+
const file = new window.File([text], "pasted_text.txt", {
|
|
188
|
+
type: "text/plain",
|
|
189
|
+
lastModified: Date.now()
|
|
190
|
+
});
|
|
191
|
+
if (upload_component) {
|
|
192
|
+
upload_component.load_files([file]);
|
|
193
|
+
}
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
162
196
|
for (let index in items) {
|
|
163
197
|
const item = items[index];
|
|
164
198
|
if (item.kind === "file" && item.type.includes("image")) {
|
|
@@ -222,127 +256,167 @@ function handle_drop(event) {
|
|
|
222
256
|
role="group"
|
|
223
257
|
aria-label="Multimedia input field"
|
|
224
258
|
>
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
<
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
259
|
+
<BlockTitle {root} {show_label} {info}>{label}</BlockTitle>
|
|
260
|
+
{#if value.files.length > 0 || uploading}
|
|
261
|
+
<div
|
|
262
|
+
class="thumbnails scroll-hide"
|
|
263
|
+
aria-label="Uploaded files"
|
|
264
|
+
data-testid="container_el"
|
|
265
|
+
style="display: {value.files.length > 0 || uploading ? 'flex' : 'none'};"
|
|
266
|
+
>
|
|
267
|
+
{#each value.files as file, index}
|
|
268
|
+
<span role="listitem" aria-label="File thumbnail">
|
|
269
|
+
<button class="thumbnail-item thumbnail-small">
|
|
270
|
+
<button
|
|
271
|
+
class:disabled
|
|
272
|
+
class="delete-button"
|
|
273
|
+
on:click={(event) => remove_thumbnail(event, index)}
|
|
274
|
+
><Clear /></button
|
|
275
|
+
>
|
|
276
|
+
{#if file.mime_type && file.mime_type.includes("image")}
|
|
277
|
+
<Image
|
|
278
|
+
src={file.url}
|
|
279
|
+
title={null}
|
|
280
|
+
alt=""
|
|
281
|
+
loading="lazy"
|
|
282
|
+
class={"thumbnail-image"}
|
|
283
|
+
/>
|
|
284
|
+
{:else if file.mime_type && file.mime_type.includes("audio")}
|
|
285
|
+
<Music />
|
|
286
|
+
{:else if file.mime_type && file.mime_type.includes("video")}
|
|
287
|
+
<Video />
|
|
288
|
+
{:else}
|
|
289
|
+
<File />
|
|
290
|
+
{/if}
|
|
291
|
+
</button>
|
|
292
|
+
</span>
|
|
293
|
+
{/each}
|
|
294
|
+
{#if uploading}
|
|
295
|
+
<div class="loader" role="status" aria-label="Uploading"></div>
|
|
296
|
+
{/if}
|
|
297
|
+
</div>
|
|
298
|
+
{/if}
|
|
299
|
+
{#if sources && sources.includes("microphone") && active_source === "microphone"}
|
|
300
|
+
<InteractiveAudio
|
|
301
|
+
on:change={({ detail }) => {
|
|
302
|
+
if (detail !== null) {
|
|
303
|
+
mic_audio = detail;
|
|
304
|
+
}
|
|
305
|
+
}}
|
|
306
|
+
on:clear={() => {
|
|
307
|
+
active_source = null;
|
|
308
|
+
}}
|
|
309
|
+
on:start_recording={() => dispatch("start_recording")}
|
|
310
|
+
on:pause_recording={() => dispatch("pause_recording")}
|
|
311
|
+
on:stop_recording={() => dispatch("stop_recording")}
|
|
312
|
+
sources={["microphone"]}
|
|
313
|
+
class_name="compact-audio"
|
|
314
|
+
{recording}
|
|
315
|
+
{waveform_settings}
|
|
316
|
+
{waveform_options}
|
|
317
|
+
{i18n}
|
|
318
|
+
{active_source}
|
|
319
|
+
{upload}
|
|
320
|
+
{stream_handler}
|
|
321
|
+
stream_every={1}
|
|
322
|
+
editable={true}
|
|
323
|
+
{label}
|
|
324
|
+
{root}
|
|
325
|
+
loop={false}
|
|
326
|
+
show_label={false}
|
|
327
|
+
show_download_button={false}
|
|
328
|
+
dragging={false}
|
|
329
|
+
/>
|
|
330
|
+
{/if}
|
|
331
|
+
<div class="input-container">
|
|
332
|
+
{#if sources && sources.includes("upload") && !disabled && !(file_count === "single" && value.files.length > 0)}
|
|
333
|
+
<Upload
|
|
334
|
+
bind:this={upload_component}
|
|
335
|
+
on:load={handle_upload}
|
|
336
|
+
{file_count}
|
|
337
|
+
filetype={file_types}
|
|
338
|
+
{root}
|
|
339
|
+
{max_file_size}
|
|
340
|
+
bind:dragging
|
|
341
|
+
bind:uploading
|
|
342
|
+
show_progress={false}
|
|
343
|
+
disable_click={true}
|
|
344
|
+
bind:hidden_upload
|
|
345
|
+
on:error
|
|
346
|
+
hidden={true}
|
|
347
|
+
{upload}
|
|
348
|
+
{stream_handler}
|
|
349
|
+
/>
|
|
350
|
+
<button
|
|
351
|
+
data-testid="upload-button"
|
|
352
|
+
class="upload-button"
|
|
353
|
+
on:click={handle_upload_click}><Paperclip /></button
|
|
236
354
|
>
|
|
237
|
-
{#each value.files as file, index}
|
|
238
|
-
<span role="listitem" aria-label="File thumbnail">
|
|
239
|
-
<button class="thumbnail-item thumbnail-small">
|
|
240
|
-
<button
|
|
241
|
-
class:disabled
|
|
242
|
-
class="delete-button"
|
|
243
|
-
on:click={(event) => remove_thumbnail(event, index)}
|
|
244
|
-
><Clear /></button
|
|
245
|
-
>
|
|
246
|
-
{#if file.mime_type && file.mime_type.includes("image")}
|
|
247
|
-
<Image
|
|
248
|
-
src={file.url}
|
|
249
|
-
title={null}
|
|
250
|
-
alt=""
|
|
251
|
-
loading="lazy"
|
|
252
|
-
class={"thumbnail-image"}
|
|
253
|
-
/>
|
|
254
|
-
{:else if file.mime_type && file.mime_type.includes("audio")}
|
|
255
|
-
<Music />
|
|
256
|
-
{:else if file.mime_type && file.mime_type.includes("video")}
|
|
257
|
-
<Video />
|
|
258
|
-
{:else}
|
|
259
|
-
<File />
|
|
260
|
-
{/if}
|
|
261
|
-
</button>
|
|
262
|
-
</span>
|
|
263
|
-
{/each}
|
|
264
|
-
{#if uploading}
|
|
265
|
-
<div class="loader" role="status" aria-label="Uploading"></div>
|
|
266
|
-
{/if}
|
|
267
|
-
</div>
|
|
268
355
|
{/if}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
{root}
|
|
277
|
-
{max_file_size}
|
|
278
|
-
bind:dragging
|
|
279
|
-
bind:uploading
|
|
280
|
-
show_progress={false}
|
|
281
|
-
disable_click={true}
|
|
282
|
-
bind:hidden_upload
|
|
283
|
-
on:error
|
|
284
|
-
hidden={true}
|
|
285
|
-
{upload}
|
|
286
|
-
{stream_handler}
|
|
287
|
-
></Upload>
|
|
288
|
-
<button
|
|
289
|
-
data-testid="upload-button"
|
|
290
|
-
class="upload-button"
|
|
291
|
-
on:click={handle_upload_click}><Paperclip /></button
|
|
292
|
-
>
|
|
293
|
-
{/if}
|
|
294
|
-
<textarea
|
|
295
|
-
data-testid="textbox"
|
|
296
|
-
use:text_area_resize={{
|
|
297
|
-
text: value.text,
|
|
298
|
-
lines: lines,
|
|
299
|
-
max_lines: max_lines
|
|
356
|
+
{#if sources && sources.includes("microphone")}
|
|
357
|
+
<button
|
|
358
|
+
data-testid="microphone-button"
|
|
359
|
+
class="microphone-button"
|
|
360
|
+
class:recording
|
|
361
|
+
on:click={() => {
|
|
362
|
+
active_source = active_source !== "microphone" ? "microphone" : null;
|
|
300
363
|
}}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
{
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
{
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
364
|
+
>
|
|
365
|
+
<Microphone />
|
|
366
|
+
</button>
|
|
367
|
+
{/if}
|
|
368
|
+
<!-- svelte-ignore a11y-autofocus -->
|
|
369
|
+
<textarea
|
|
370
|
+
data-testid="textbox"
|
|
371
|
+
use:text_area_resize={{
|
|
372
|
+
text: value.text,
|
|
373
|
+
lines: lines,
|
|
374
|
+
max_lines: max_lines
|
|
375
|
+
}}
|
|
376
|
+
class="scroll-hide"
|
|
377
|
+
class:no-label={!show_label}
|
|
378
|
+
dir={rtl ? "rtl" : "ltr"}
|
|
379
|
+
bind:value={value.text}
|
|
380
|
+
bind:this={el}
|
|
381
|
+
{placeholder}
|
|
382
|
+
rows={lines}
|
|
383
|
+
{disabled}
|
|
384
|
+
{autofocus}
|
|
385
|
+
on:keypress={handle_keypress}
|
|
386
|
+
on:blur
|
|
387
|
+
on:select={handle_select}
|
|
388
|
+
on:focus
|
|
389
|
+
on:scroll={handle_scroll}
|
|
390
|
+
on:paste={handle_paste}
|
|
391
|
+
style={text_align ? "text-align: " + text_align : ""}
|
|
392
|
+
/>
|
|
393
|
+
{#if submit_btn}
|
|
394
|
+
<button
|
|
395
|
+
class="submit-button"
|
|
396
|
+
class:padded-button={submit_btn !== true}
|
|
397
|
+
on:click={handle_submit}
|
|
398
|
+
>
|
|
399
|
+
{#if submit_btn === true}
|
|
400
|
+
<Send />
|
|
401
|
+
{:else}
|
|
402
|
+
{submit_btn}
|
|
403
|
+
{/if}
|
|
404
|
+
</button>
|
|
405
|
+
{/if}
|
|
406
|
+
{#if stop_btn}
|
|
407
|
+
<button
|
|
408
|
+
class="stop-button"
|
|
409
|
+
class:padded-button={stop_btn !== true}
|
|
410
|
+
on:click={handle_stop}
|
|
411
|
+
>
|
|
412
|
+
{#if stop_btn === true}
|
|
413
|
+
<Square fill={"none"} stroke_width={2.5} />
|
|
414
|
+
{:else}
|
|
415
|
+
{stop_btn}
|
|
416
|
+
{/if}
|
|
417
|
+
</button>
|
|
418
|
+
{/if}
|
|
419
|
+
</div>
|
|
346
420
|
</div>
|
|
347
421
|
|
|
348
422
|
<style>
|
|
@@ -404,6 +478,7 @@ function handle_drop(event) {
|
|
|
404
478
|
color: var(--input-placeholder-color);
|
|
405
479
|
}
|
|
406
480
|
|
|
481
|
+
.microphone-button,
|
|
407
482
|
.upload-button,
|
|
408
483
|
.submit-button,
|
|
409
484
|
.stop-button {
|
|
@@ -420,29 +495,34 @@ function handle_drop(event) {
|
|
|
420
495
|
justify-content: center;
|
|
421
496
|
align-items: center;
|
|
422
497
|
z-index: var(--layer-1);
|
|
498
|
+
margin-left: var(--spacing-sm);
|
|
423
499
|
}
|
|
424
500
|
.padded-button {
|
|
425
501
|
padding: 0 10px;
|
|
426
502
|
}
|
|
427
503
|
|
|
504
|
+
.microphone-button,
|
|
428
505
|
.stop-button,
|
|
429
506
|
.upload-button,
|
|
430
507
|
.submit-button {
|
|
431
508
|
background: var(--button-secondary-background-fill);
|
|
432
509
|
}
|
|
433
510
|
|
|
511
|
+
.microphone-button:hover,
|
|
434
512
|
.stop-button:hover,
|
|
435
513
|
.upload-button:hover,
|
|
436
514
|
.submit-button:hover {
|
|
437
515
|
background: var(--button-secondary-background-fill-hover);
|
|
438
516
|
}
|
|
439
517
|
|
|
518
|
+
.microphone-button:disabled,
|
|
440
519
|
.stop-button:disabled,
|
|
441
520
|
.upload-button:disabled,
|
|
442
521
|
.submit-button:disabled {
|
|
443
522
|
background: var(--button-secondary-background-fill);
|
|
444
523
|
cursor: initial;
|
|
445
524
|
}
|
|
525
|
+
.microphone-button:active,
|
|
446
526
|
.stop-button:active,
|
|
447
527
|
.upload-button:active,
|
|
448
528
|
.submit-button:active {
|
|
@@ -453,6 +533,7 @@ function handle_drop(event) {
|
|
|
453
533
|
height: 22px;
|
|
454
534
|
width: 22px;
|
|
455
535
|
}
|
|
536
|
+
.microphone-button :global(svg),
|
|
456
537
|
.upload-button :global(svg) {
|
|
457
538
|
height: 17px;
|
|
458
539
|
width: 17px;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SvelteComponent } from "svelte";
|
|
2
|
+
import type { I18nFormatter } from "js/core/src/gradio_helper";
|
|
2
3
|
import type { FileData, Client } from "@gradio/client";
|
|
3
4
|
import type { SelectData } from "@gradio/utils";
|
|
4
5
|
declare const __propDef: {
|
|
@@ -9,12 +10,12 @@ declare const __propDef: {
|
|
|
9
10
|
} | undefined;
|
|
10
11
|
value_is_output?: boolean | undefined;
|
|
11
12
|
lines?: number | undefined;
|
|
13
|
+
i18n: I18nFormatter;
|
|
12
14
|
placeholder?: string | undefined;
|
|
13
15
|
disabled?: boolean | undefined;
|
|
14
16
|
label: string;
|
|
15
17
|
info?: string | undefined;
|
|
16
18
|
show_label?: boolean | undefined;
|
|
17
|
-
container?: boolean | undefined;
|
|
18
19
|
max_lines: number;
|
|
19
20
|
submit_btn?: (string | boolean | null) | undefined;
|
|
20
21
|
stop_btn?: (string | boolean | null) | undefined;
|
|
@@ -28,6 +29,11 @@ declare const __propDef: {
|
|
|
28
29
|
upload: Client["upload"];
|
|
29
30
|
stream_handler: Client["stream"];
|
|
30
31
|
file_count?: ("single" | "multiple" | "directory") | undefined;
|
|
32
|
+
max_plain_text_length?: number | undefined;
|
|
33
|
+
waveform_settings: Record<string, any>;
|
|
34
|
+
waveform_options?: any;
|
|
35
|
+
sources?: ["microphone" | "upload"] | undefined;
|
|
36
|
+
active_source?: ("microphone" | null) | undefined;
|
|
31
37
|
dragging?: boolean | undefined;
|
|
32
38
|
};
|
|
33
39
|
events: {
|
|
@@ -47,6 +53,9 @@ declare const __propDef: {
|
|
|
47
53
|
upload: CustomEvent<any>;
|
|
48
54
|
clear: CustomEvent<undefined>;
|
|
49
55
|
load: CustomEvent<any>;
|
|
56
|
+
start_recording: CustomEvent<undefined>;
|
|
57
|
+
pause_recording: CustomEvent<undefined>;
|
|
58
|
+
stop_recording: CustomEvent<undefined>;
|
|
50
59
|
} & {
|
|
51
60
|
[evt: string]: CustomEvent<any>;
|
|
52
61
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gradio/multimodaltextbox",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Gradio UI packages",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "",
|
|
@@ -22,14 +22,14 @@
|
|
|
22
22
|
"./package.json": "./package.json"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@gradio/
|
|
26
|
-
"@gradio/
|
|
27
|
-
"@gradio/statustracker": "^0.
|
|
28
|
-
"@gradio/
|
|
29
|
-
"@gradio/
|
|
30
|
-
"@gradio/image": "^0.
|
|
31
|
-
"@gradio/video": "^0.
|
|
32
|
-
"@gradio/client": "^1.
|
|
25
|
+
"@gradio/icons": "^0.9.0",
|
|
26
|
+
"@gradio/atoms": "^0.13.0",
|
|
27
|
+
"@gradio/statustracker": "^0.10.0",
|
|
28
|
+
"@gradio/upload": "^0.14.4",
|
|
29
|
+
"@gradio/utils": "^0.10.0",
|
|
30
|
+
"@gradio/image": "^0.20.0",
|
|
31
|
+
"@gradio/video": "^0.13.0",
|
|
32
|
+
"@gradio/client": "^1.9.0"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@gradio/preview": "^0.13.0"
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import {
|
|
3
|
+
onMount,
|
|
3
4
|
beforeUpdate,
|
|
4
5
|
afterUpdate,
|
|
5
6
|
createEventDispatcher,
|
|
@@ -9,7 +10,9 @@
|
|
|
9
10
|
import { BlockTitle } from "@gradio/atoms";
|
|
10
11
|
import { Upload } from "@gradio/upload";
|
|
11
12
|
import { Image } from "@gradio/image/shared";
|
|
13
|
+
import type { I18nFormatter } from "js/core/src/gradio_helper";
|
|
12
14
|
import type { FileData, Client } from "@gradio/client";
|
|
15
|
+
import type { WaveformOptions } from "../../audio/shared/types";
|
|
13
16
|
import {
|
|
14
17
|
Clear,
|
|
15
18
|
File,
|
|
@@ -17,9 +20,11 @@
|
|
|
17
20
|
Paperclip,
|
|
18
21
|
Video,
|
|
19
22
|
Send,
|
|
20
|
-
Square
|
|
23
|
+
Square,
|
|
24
|
+
Microphone
|
|
21
25
|
} from "@gradio/icons";
|
|
22
26
|
import type { SelectData } from "@gradio/utils";
|
|
27
|
+
import InteractiveAudio from "../../audio/interactive/InteractiveAudio.svelte";
|
|
23
28
|
|
|
24
29
|
export let value: { text: string; files: FileData[] } = {
|
|
25
30
|
text: "",
|
|
@@ -28,12 +33,12 @@
|
|
|
28
33
|
|
|
29
34
|
export let value_is_output = false;
|
|
30
35
|
export let lines = 1;
|
|
36
|
+
export let i18n: I18nFormatter;
|
|
31
37
|
export let placeholder = "Type here...";
|
|
32
38
|
export let disabled = false;
|
|
33
39
|
export let label: string;
|
|
34
40
|
export let info: string | undefined = undefined;
|
|
35
41
|
export let show_label = true;
|
|
36
|
-
export let container = true;
|
|
37
42
|
export let max_lines: number;
|
|
38
43
|
export let submit_btn: string | boolean | null = null;
|
|
39
44
|
export let stop_btn: string | boolean | null = null;
|
|
@@ -47,7 +52,11 @@
|
|
|
47
52
|
export let upload: Client["upload"];
|
|
48
53
|
export let stream_handler: Client["stream"];
|
|
49
54
|
export let file_count: "single" | "multiple" | "directory" = "multiple";
|
|
50
|
-
|
|
55
|
+
export let max_plain_text_length = 1000;
|
|
56
|
+
export let waveform_settings: Record<string, any>;
|
|
57
|
+
export let waveform_options: WaveformOptions = {};
|
|
58
|
+
export let sources: ["microphone" | "upload"] = ["upload"];
|
|
59
|
+
export let active_source: "microphone" | null = null;
|
|
51
60
|
let upload_component: Upload;
|
|
52
61
|
let hidden_upload: HTMLInputElement;
|
|
53
62
|
let el: HTMLTextAreaElement | HTMLInputElement;
|
|
@@ -57,7 +66,9 @@
|
|
|
57
66
|
export let dragging = false;
|
|
58
67
|
let uploading = false;
|
|
59
68
|
let oldValue = value.text;
|
|
69
|
+
let recording = false;
|
|
60
70
|
$: dispatch("drag", dragging);
|
|
71
|
+
let mic_audio: FileData | null = null;
|
|
61
72
|
|
|
62
73
|
let full_container: HTMLDivElement;
|
|
63
74
|
|
|
@@ -82,6 +93,9 @@
|
|
|
82
93
|
clear: undefined;
|
|
83
94
|
load: FileData[] | FileData;
|
|
84
95
|
error: string;
|
|
96
|
+
start_recording: undefined;
|
|
97
|
+
pause_recording: undefined;
|
|
98
|
+
stop_recording: undefined;
|
|
85
99
|
}>();
|
|
86
100
|
|
|
87
101
|
beforeUpdate(() => {
|
|
@@ -101,10 +115,13 @@
|
|
|
101
115
|
}
|
|
102
116
|
}
|
|
103
117
|
|
|
104
|
-
|
|
118
|
+
onMount(() => {
|
|
105
119
|
if (autofocus && el !== null) {
|
|
106
120
|
el.focus();
|
|
107
121
|
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
afterUpdate(() => {
|
|
108
125
|
if (can_scroll && autoscroll) {
|
|
109
126
|
scroll();
|
|
110
127
|
}
|
|
@@ -136,6 +153,11 @@
|
|
|
136
153
|
) {
|
|
137
154
|
e.preventDefault();
|
|
138
155
|
dispatch("submit");
|
|
156
|
+
active_source = null;
|
|
157
|
+
if (mic_audio) {
|
|
158
|
+
value.files.push(mic_audio);
|
|
159
|
+
value = value;
|
|
160
|
+
}
|
|
139
161
|
}
|
|
140
162
|
}
|
|
141
163
|
|
|
@@ -156,7 +178,7 @@
|
|
|
156
178
|
|
|
157
179
|
async function handle_upload({
|
|
158
180
|
detail
|
|
159
|
-
}: CustomEvent<FileData
|
|
181
|
+
}: CustomEvent<FileData>): Promise<void> {
|
|
160
182
|
handle_change();
|
|
161
183
|
if (Array.isArray(detail)) {
|
|
162
184
|
for (let file of detail) {
|
|
@@ -192,11 +214,30 @@
|
|
|
192
214
|
|
|
193
215
|
function handle_submit(): void {
|
|
194
216
|
dispatch("submit");
|
|
217
|
+
active_source = null;
|
|
218
|
+
if (mic_audio) {
|
|
219
|
+
value.files.push(mic_audio);
|
|
220
|
+
value = value;
|
|
221
|
+
}
|
|
195
222
|
}
|
|
196
223
|
|
|
197
|
-
function handle_paste(event: ClipboardEvent): void {
|
|
224
|
+
async function handle_paste(event: ClipboardEvent): Promise<void> {
|
|
198
225
|
if (!event.clipboardData) return;
|
|
199
226
|
const items = event.clipboardData.items;
|
|
227
|
+
const text = event.clipboardData.getData("text");
|
|
228
|
+
|
|
229
|
+
if (text && text.length > max_plain_text_length) {
|
|
230
|
+
event.preventDefault();
|
|
231
|
+
const file = new window.File([text], "pasted_text.txt", {
|
|
232
|
+
type: "text/plain",
|
|
233
|
+
lastModified: Date.now()
|
|
234
|
+
});
|
|
235
|
+
if (upload_component) {
|
|
236
|
+
upload_component.load_files([file]);
|
|
237
|
+
}
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
|
|
200
241
|
for (let index in items) {
|
|
201
242
|
const item = items[index];
|
|
202
243
|
if (item.kind === "file" && item.type.includes("image")) {
|
|
@@ -270,127 +311,167 @@
|
|
|
270
311
|
role="group"
|
|
271
312
|
aria-label="Multimedia input field"
|
|
272
313
|
>
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
<
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
314
|
+
<BlockTitle {root} {show_label} {info}>{label}</BlockTitle>
|
|
315
|
+
{#if value.files.length > 0 || uploading}
|
|
316
|
+
<div
|
|
317
|
+
class="thumbnails scroll-hide"
|
|
318
|
+
aria-label="Uploaded files"
|
|
319
|
+
data-testid="container_el"
|
|
320
|
+
style="display: {value.files.length > 0 || uploading ? 'flex' : 'none'};"
|
|
321
|
+
>
|
|
322
|
+
{#each value.files as file, index}
|
|
323
|
+
<span role="listitem" aria-label="File thumbnail">
|
|
324
|
+
<button class="thumbnail-item thumbnail-small">
|
|
325
|
+
<button
|
|
326
|
+
class:disabled
|
|
327
|
+
class="delete-button"
|
|
328
|
+
on:click={(event) => remove_thumbnail(event, index)}
|
|
329
|
+
><Clear /></button
|
|
330
|
+
>
|
|
331
|
+
{#if file.mime_type && file.mime_type.includes("image")}
|
|
332
|
+
<Image
|
|
333
|
+
src={file.url}
|
|
334
|
+
title={null}
|
|
335
|
+
alt=""
|
|
336
|
+
loading="lazy"
|
|
337
|
+
class={"thumbnail-image"}
|
|
338
|
+
/>
|
|
339
|
+
{:else if file.mime_type && file.mime_type.includes("audio")}
|
|
340
|
+
<Music />
|
|
341
|
+
{:else if file.mime_type && file.mime_type.includes("video")}
|
|
342
|
+
<Video />
|
|
343
|
+
{:else}
|
|
344
|
+
<File />
|
|
345
|
+
{/if}
|
|
346
|
+
</button>
|
|
347
|
+
</span>
|
|
348
|
+
{/each}
|
|
349
|
+
{#if uploading}
|
|
350
|
+
<div class="loader" role="status" aria-label="Uploading"></div>
|
|
351
|
+
{/if}
|
|
352
|
+
</div>
|
|
353
|
+
{/if}
|
|
354
|
+
{#if sources && sources.includes("microphone") && active_source === "microphone"}
|
|
355
|
+
<InteractiveAudio
|
|
356
|
+
on:change={({ detail }) => {
|
|
357
|
+
if (detail !== null) {
|
|
358
|
+
mic_audio = detail;
|
|
359
|
+
}
|
|
360
|
+
}}
|
|
361
|
+
on:clear={() => {
|
|
362
|
+
active_source = null;
|
|
363
|
+
}}
|
|
364
|
+
on:start_recording={() => dispatch("start_recording")}
|
|
365
|
+
on:pause_recording={() => dispatch("pause_recording")}
|
|
366
|
+
on:stop_recording={() => dispatch("stop_recording")}
|
|
367
|
+
sources={["microphone"]}
|
|
368
|
+
class_name="compact-audio"
|
|
369
|
+
{recording}
|
|
370
|
+
{waveform_settings}
|
|
371
|
+
{waveform_options}
|
|
372
|
+
{i18n}
|
|
373
|
+
{active_source}
|
|
374
|
+
{upload}
|
|
375
|
+
{stream_handler}
|
|
376
|
+
stream_every={1}
|
|
377
|
+
editable={true}
|
|
378
|
+
{label}
|
|
379
|
+
{root}
|
|
380
|
+
loop={false}
|
|
381
|
+
show_label={false}
|
|
382
|
+
show_download_button={false}
|
|
383
|
+
dragging={false}
|
|
384
|
+
/>
|
|
385
|
+
{/if}
|
|
386
|
+
<div class="input-container">
|
|
387
|
+
{#if sources && sources.includes("upload") && !disabled && !(file_count === "single" && value.files.length > 0)}
|
|
388
|
+
<Upload
|
|
389
|
+
bind:this={upload_component}
|
|
390
|
+
on:load={handle_upload}
|
|
391
|
+
{file_count}
|
|
392
|
+
filetype={file_types}
|
|
393
|
+
{root}
|
|
394
|
+
{max_file_size}
|
|
395
|
+
bind:dragging
|
|
396
|
+
bind:uploading
|
|
397
|
+
show_progress={false}
|
|
398
|
+
disable_click={true}
|
|
399
|
+
bind:hidden_upload
|
|
400
|
+
on:error
|
|
401
|
+
hidden={true}
|
|
402
|
+
{upload}
|
|
403
|
+
{stream_handler}
|
|
404
|
+
/>
|
|
405
|
+
<button
|
|
406
|
+
data-testid="upload-button"
|
|
407
|
+
class="upload-button"
|
|
408
|
+
on:click={handle_upload_click}><Paperclip /></button
|
|
284
409
|
>
|
|
285
|
-
{#each value.files as file, index}
|
|
286
|
-
<span role="listitem" aria-label="File thumbnail">
|
|
287
|
-
<button class="thumbnail-item thumbnail-small">
|
|
288
|
-
<button
|
|
289
|
-
class:disabled
|
|
290
|
-
class="delete-button"
|
|
291
|
-
on:click={(event) => remove_thumbnail(event, index)}
|
|
292
|
-
><Clear /></button
|
|
293
|
-
>
|
|
294
|
-
{#if file.mime_type && file.mime_type.includes("image")}
|
|
295
|
-
<Image
|
|
296
|
-
src={file.url}
|
|
297
|
-
title={null}
|
|
298
|
-
alt=""
|
|
299
|
-
loading="lazy"
|
|
300
|
-
class={"thumbnail-image"}
|
|
301
|
-
/>
|
|
302
|
-
{:else if file.mime_type && file.mime_type.includes("audio")}
|
|
303
|
-
<Music />
|
|
304
|
-
{:else if file.mime_type && file.mime_type.includes("video")}
|
|
305
|
-
<Video />
|
|
306
|
-
{:else}
|
|
307
|
-
<File />
|
|
308
|
-
{/if}
|
|
309
|
-
</button>
|
|
310
|
-
</span>
|
|
311
|
-
{/each}
|
|
312
|
-
{#if uploading}
|
|
313
|
-
<div class="loader" role="status" aria-label="Uploading"></div>
|
|
314
|
-
{/if}
|
|
315
|
-
</div>
|
|
316
410
|
{/if}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
{root}
|
|
325
|
-
{max_file_size}
|
|
326
|
-
bind:dragging
|
|
327
|
-
bind:uploading
|
|
328
|
-
show_progress={false}
|
|
329
|
-
disable_click={true}
|
|
330
|
-
bind:hidden_upload
|
|
331
|
-
on:error
|
|
332
|
-
hidden={true}
|
|
333
|
-
{upload}
|
|
334
|
-
{stream_handler}
|
|
335
|
-
></Upload>
|
|
336
|
-
<button
|
|
337
|
-
data-testid="upload-button"
|
|
338
|
-
class="upload-button"
|
|
339
|
-
on:click={handle_upload_click}><Paperclip /></button
|
|
340
|
-
>
|
|
341
|
-
{/if}
|
|
342
|
-
<textarea
|
|
343
|
-
data-testid="textbox"
|
|
344
|
-
use:text_area_resize={{
|
|
345
|
-
text: value.text,
|
|
346
|
-
lines: lines,
|
|
347
|
-
max_lines: max_lines
|
|
411
|
+
{#if sources && sources.includes("microphone")}
|
|
412
|
+
<button
|
|
413
|
+
data-testid="microphone-button"
|
|
414
|
+
class="microphone-button"
|
|
415
|
+
class:recording
|
|
416
|
+
on:click={() => {
|
|
417
|
+
active_source = active_source !== "microphone" ? "microphone" : null;
|
|
348
418
|
}}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
{
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
{
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
419
|
+
>
|
|
420
|
+
<Microphone />
|
|
421
|
+
</button>
|
|
422
|
+
{/if}
|
|
423
|
+
<!-- svelte-ignore a11y-autofocus -->
|
|
424
|
+
<textarea
|
|
425
|
+
data-testid="textbox"
|
|
426
|
+
use:text_area_resize={{
|
|
427
|
+
text: value.text,
|
|
428
|
+
lines: lines,
|
|
429
|
+
max_lines: max_lines
|
|
430
|
+
}}
|
|
431
|
+
class="scroll-hide"
|
|
432
|
+
class:no-label={!show_label}
|
|
433
|
+
dir={rtl ? "rtl" : "ltr"}
|
|
434
|
+
bind:value={value.text}
|
|
435
|
+
bind:this={el}
|
|
436
|
+
{placeholder}
|
|
437
|
+
rows={lines}
|
|
438
|
+
{disabled}
|
|
439
|
+
{autofocus}
|
|
440
|
+
on:keypress={handle_keypress}
|
|
441
|
+
on:blur
|
|
442
|
+
on:select={handle_select}
|
|
443
|
+
on:focus
|
|
444
|
+
on:scroll={handle_scroll}
|
|
445
|
+
on:paste={handle_paste}
|
|
446
|
+
style={text_align ? "text-align: " + text_align : ""}
|
|
447
|
+
/>
|
|
448
|
+
{#if submit_btn}
|
|
449
|
+
<button
|
|
450
|
+
class="submit-button"
|
|
451
|
+
class:padded-button={submit_btn !== true}
|
|
452
|
+
on:click={handle_submit}
|
|
453
|
+
>
|
|
454
|
+
{#if submit_btn === true}
|
|
455
|
+
<Send />
|
|
456
|
+
{:else}
|
|
457
|
+
{submit_btn}
|
|
458
|
+
{/if}
|
|
459
|
+
</button>
|
|
460
|
+
{/if}
|
|
461
|
+
{#if stop_btn}
|
|
462
|
+
<button
|
|
463
|
+
class="stop-button"
|
|
464
|
+
class:padded-button={stop_btn !== true}
|
|
465
|
+
on:click={handle_stop}
|
|
466
|
+
>
|
|
467
|
+
{#if stop_btn === true}
|
|
468
|
+
<Square fill={"none"} stroke_width={2.5} />
|
|
469
|
+
{:else}
|
|
470
|
+
{stop_btn}
|
|
471
|
+
{/if}
|
|
472
|
+
</button>
|
|
473
|
+
{/if}
|
|
474
|
+
</div>
|
|
394
475
|
</div>
|
|
395
476
|
|
|
396
477
|
<style>
|
|
@@ -452,6 +533,7 @@
|
|
|
452
533
|
color: var(--input-placeholder-color);
|
|
453
534
|
}
|
|
454
535
|
|
|
536
|
+
.microphone-button,
|
|
455
537
|
.upload-button,
|
|
456
538
|
.submit-button,
|
|
457
539
|
.stop-button {
|
|
@@ -468,29 +550,34 @@
|
|
|
468
550
|
justify-content: center;
|
|
469
551
|
align-items: center;
|
|
470
552
|
z-index: var(--layer-1);
|
|
553
|
+
margin-left: var(--spacing-sm);
|
|
471
554
|
}
|
|
472
555
|
.padded-button {
|
|
473
556
|
padding: 0 10px;
|
|
474
557
|
}
|
|
475
558
|
|
|
559
|
+
.microphone-button,
|
|
476
560
|
.stop-button,
|
|
477
561
|
.upload-button,
|
|
478
562
|
.submit-button {
|
|
479
563
|
background: var(--button-secondary-background-fill);
|
|
480
564
|
}
|
|
481
565
|
|
|
566
|
+
.microphone-button:hover,
|
|
482
567
|
.stop-button:hover,
|
|
483
568
|
.upload-button:hover,
|
|
484
569
|
.submit-button:hover {
|
|
485
570
|
background: var(--button-secondary-background-fill-hover);
|
|
486
571
|
}
|
|
487
572
|
|
|
573
|
+
.microphone-button:disabled,
|
|
488
574
|
.stop-button:disabled,
|
|
489
575
|
.upload-button:disabled,
|
|
490
576
|
.submit-button:disabled {
|
|
491
577
|
background: var(--button-secondary-background-fill);
|
|
492
578
|
cursor: initial;
|
|
493
579
|
}
|
|
580
|
+
.microphone-button:active,
|
|
494
581
|
.stop-button:active,
|
|
495
582
|
.upload-button:active,
|
|
496
583
|
.submit-button:active {
|
|
@@ -501,6 +588,7 @@
|
|
|
501
588
|
height: 22px;
|
|
502
589
|
width: 22px;
|
|
503
590
|
}
|
|
591
|
+
.microphone-button :global(svg),
|
|
504
592
|
.upload-button :global(svg) {
|
|
505
593
|
height: 17px;
|
|
506
594
|
width: 17px;
|