@gradio/multimodaltextbox 0.11.3 → 0.11.5
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 +37 -0
- package/Example.svelte +14 -10
- package/Index.svelte +17 -19
- package/MultimodalTextbox.stories.svelte +96 -59
- package/MultimodalTextboxExample.stories.svelte +17 -13
- package/dist/Example.svelte +14 -10
- package/dist/Example.svelte.d.ts +4 -18
- package/dist/Index.svelte +17 -19
- package/dist/shared/MultimodalTextbox.svelte +259 -194
- package/dist/shared/MultimodalTextbox.svelte.d.ts +19 -37
- package/dist/shared/types.d.ts +1 -1
- package/dist/types.d.ts +0 -2
- package/package.json +12 -12
- package/shared/MultimodalTextbox.svelte +259 -194
- package/shared/types.ts +1 -1
- package/types.ts +0 -2
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
onMount,
|
|
4
|
-
beforeUpdate,
|
|
5
|
-
afterUpdate,
|
|
6
|
-
createEventDispatcher,
|
|
7
|
-
tick
|
|
8
|
-
} from "svelte";
|
|
2
|
+
import { tick } from "svelte";
|
|
9
3
|
import { text_area_resize, resize } from "../shared/utils";
|
|
10
|
-
import { BlockTitle } from "@gradio/atoms";
|
|
4
|
+
import { BlockTitle, ScrollFade } from "@gradio/atoms";
|
|
11
5
|
import { Upload } from "@gradio/upload";
|
|
12
6
|
import { Image } from "@gradio/image/shared";
|
|
13
7
|
import type { I18nFormatter } from "js/core/src/gradio_helper";
|
|
@@ -24,112 +18,170 @@
|
|
|
24
18
|
Microphone,
|
|
25
19
|
Check
|
|
26
20
|
} from "@gradio/icons";
|
|
27
|
-
import type
|
|
28
|
-
import { BaseInteractiveAudio as InteractiveAudio } from "@gradio/audio";
|
|
21
|
+
import { should_show_scroll_fade, type SelectData } from "@gradio/utils";
|
|
29
22
|
import {
|
|
30
23
|
MinimalAudioPlayer,
|
|
31
24
|
MinimalAudioRecorder
|
|
32
25
|
} from "@gradio/audio/shared";
|
|
33
26
|
import type { InputHTMLAttributes } from "./types";
|
|
34
27
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
28
|
+
let {
|
|
29
|
+
value = $bindable(),
|
|
30
|
+
value_is_output = false,
|
|
31
|
+
lines = 1,
|
|
32
|
+
i18n: _i18n,
|
|
33
|
+
placeholder = "",
|
|
34
|
+
disabled = false,
|
|
35
|
+
label,
|
|
36
|
+
info = undefined,
|
|
37
|
+
show_label = true,
|
|
38
|
+
max_lines,
|
|
39
|
+
submit_btn = null,
|
|
40
|
+
stop_btn = null,
|
|
41
|
+
rtl = false,
|
|
42
|
+
autofocus = false,
|
|
43
|
+
text_align = undefined,
|
|
44
|
+
autoscroll = true,
|
|
45
|
+
root,
|
|
46
|
+
file_types_string = null,
|
|
47
|
+
max_file_size = null,
|
|
48
|
+
upload,
|
|
49
|
+
stream_handler,
|
|
50
|
+
file_count = "multiple",
|
|
51
|
+
max_plain_text_length = 1000,
|
|
52
|
+
waveform_settings,
|
|
53
|
+
waveform_options: _waveform_options = { show_recording_waveform: true },
|
|
54
|
+
sources_string = "upload",
|
|
55
|
+
active_source = $bindable<"microphone" | null>(),
|
|
56
|
+
html_attributes = null,
|
|
57
|
+
upload_promise = $bindable<Promise<any> | null>(),
|
|
58
|
+
dragging = $bindable<boolean>(),
|
|
59
|
+
onchange,
|
|
60
|
+
onsubmit,
|
|
61
|
+
onstop,
|
|
62
|
+
onblur,
|
|
63
|
+
onselect,
|
|
64
|
+
oninput,
|
|
65
|
+
onfocus,
|
|
66
|
+
ondrag,
|
|
67
|
+
onupload,
|
|
68
|
+
onclear,
|
|
69
|
+
onload: _onload,
|
|
70
|
+
onerror,
|
|
71
|
+
onstop_recording
|
|
72
|
+
}: {
|
|
73
|
+
value?: { text: string; files: FileData[] };
|
|
74
|
+
value_is_output?: boolean;
|
|
75
|
+
lines?: number;
|
|
76
|
+
i18n: I18nFormatter;
|
|
77
|
+
placeholder?: string;
|
|
78
|
+
disabled?: boolean;
|
|
79
|
+
label: string;
|
|
80
|
+
info?: string | undefined;
|
|
81
|
+
show_label?: boolean;
|
|
82
|
+
max_lines: number;
|
|
83
|
+
submit_btn?: string | boolean | null;
|
|
84
|
+
stop_btn?: string | boolean | null;
|
|
85
|
+
rtl?: boolean;
|
|
86
|
+
autofocus?: boolean;
|
|
87
|
+
text_align?: "left" | "right" | undefined;
|
|
88
|
+
autoscroll?: boolean;
|
|
89
|
+
root: string;
|
|
90
|
+
file_types_string?: string | null;
|
|
91
|
+
max_file_size?: number | null;
|
|
92
|
+
upload: Client["upload"];
|
|
93
|
+
stream_handler: Client["stream"];
|
|
94
|
+
file_count?: "single" | "multiple" | "directory";
|
|
95
|
+
max_plain_text_length?: number;
|
|
96
|
+
waveform_settings: Record<string, any>;
|
|
97
|
+
waveform_options?: WaveformOptions;
|
|
98
|
+
sources_string?:
|
|
99
|
+
| "upload"
|
|
100
|
+
| "upload,microphone"
|
|
101
|
+
| "microphone"
|
|
102
|
+
| "microphone,upload";
|
|
103
|
+
active_source?: "microphone" | null;
|
|
104
|
+
html_attributes?: InputHTMLAttributes | null;
|
|
105
|
+
upload_promise?: Promise<any> | null;
|
|
106
|
+
dragging?: boolean;
|
|
107
|
+
onchange?: (value: { text: string; files: FileData[] }) => void;
|
|
108
|
+
onsubmit?: () => void;
|
|
109
|
+
onstop?: () => void;
|
|
110
|
+
onblur?: () => void;
|
|
111
|
+
onselect?: (data: SelectData) => void;
|
|
112
|
+
oninput?: () => void;
|
|
113
|
+
onfocus?: () => void;
|
|
114
|
+
ondrag?: (dragging: boolean) => void;
|
|
115
|
+
onupload?: (files: FileData[] | FileData) => void;
|
|
116
|
+
onclear?: () => void;
|
|
117
|
+
onload?: (files: FileData[] | FileData) => void;
|
|
118
|
+
onerror?: (error: string) => void;
|
|
119
|
+
onstop_recording?: () => void;
|
|
120
|
+
} = $props();
|
|
74
121
|
|
|
75
122
|
let upload_component: Upload;
|
|
76
123
|
let el: HTMLTextAreaElement | HTMLInputElement;
|
|
77
|
-
let can_scroll
|
|
78
|
-
let previous_scroll_top = 0;
|
|
79
|
-
let user_has_scrolled_up = false;
|
|
80
|
-
|
|
81
|
-
let uploading = false;
|
|
82
|
-
|
|
83
|
-
let
|
|
84
|
-
let
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
124
|
+
let can_scroll = $state(false);
|
|
125
|
+
let previous_scroll_top = $state(0);
|
|
126
|
+
let user_has_scrolled_up = $state(false);
|
|
127
|
+
let show_fade = $state(false);
|
|
128
|
+
let uploading = $state(false);
|
|
129
|
+
let oldValue = $state(value?.text ?? "");
|
|
130
|
+
let recording = $state(false);
|
|
131
|
+
let mic_audio = $state<FileData | null>(null);
|
|
132
|
+
let full_container: HTMLDivElement;
|
|
133
|
+
|
|
134
|
+
let sources = $derived(
|
|
135
|
+
sources_string
|
|
136
|
+
.split(",")
|
|
137
|
+
.map((s) => s.trim())
|
|
138
|
+
.filter((s) => s === "upload" || s === "microphone") as (
|
|
139
|
+
| "upload"
|
|
140
|
+
| "microphone"
|
|
141
|
+
)[]
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
let file_types = $derived(
|
|
145
|
+
file_types_string ? file_types_string.split(",").map((s) => s.trim()) : null
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
let show_upload = $derived(
|
|
98
149
|
sources &&
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
150
|
+
sources.includes("upload") &&
|
|
151
|
+
!(file_count === "single" && value?.files?.length > 0)
|
|
152
|
+
);
|
|
102
153
|
|
|
103
|
-
|
|
154
|
+
function update_fade(): void {
|
|
155
|
+
show_fade = should_show_scroll_fade(el);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
$effect(() => {
|
|
159
|
+
if (el && value?.text !== undefined) {
|
|
160
|
+
tick().then(update_fade);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
$effect(() => {
|
|
165
|
+
ondrag?.(dragging);
|
|
166
|
+
});
|
|
104
167
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
upload: FileData[] | FileData;
|
|
123
|
-
clear: undefined;
|
|
124
|
-
load: FileData[] | FileData;
|
|
125
|
-
error: string;
|
|
126
|
-
start_recording: undefined;
|
|
127
|
-
pause_recording: undefined;
|
|
128
|
-
stop_recording: undefined;
|
|
129
|
-
}>();
|
|
130
|
-
|
|
131
|
-
beforeUpdate(() => {
|
|
132
|
-
can_scroll = el && el.offsetHeight + el.scrollTop > el.scrollHeight - 100;
|
|
168
|
+
$effect(() => {
|
|
169
|
+
if (value && oldValue !== value.text) {
|
|
170
|
+
onchange?.(value);
|
|
171
|
+
oldValue = value.text;
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
$effect(() => {
|
|
176
|
+
if (el && lines !== max_lines) {
|
|
177
|
+
resize(el, lines, max_lines);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
$effect.pre(() => {
|
|
182
|
+
if (el && el.offsetHeight + el.scrollTop > el.scrollHeight - 100) {
|
|
183
|
+
can_scroll = true;
|
|
184
|
+
}
|
|
133
185
|
});
|
|
134
186
|
|
|
135
187
|
const scroll = (): void => {
|
|
@@ -139,28 +191,24 @@
|
|
|
139
191
|
};
|
|
140
192
|
|
|
141
193
|
async function handle_change(): Promise<void> {
|
|
142
|
-
|
|
194
|
+
onchange?.(value);
|
|
143
195
|
if (!value_is_output) {
|
|
144
|
-
|
|
196
|
+
oninput?.();
|
|
145
197
|
}
|
|
146
198
|
}
|
|
147
199
|
|
|
148
|
-
|
|
149
|
-
|
|
200
|
+
$effect(() => {
|
|
201
|
+
value;
|
|
202
|
+
if (autofocus && el) {
|
|
150
203
|
el.focus();
|
|
151
204
|
}
|
|
152
205
|
});
|
|
153
206
|
|
|
154
|
-
|
|
207
|
+
$effect(() => {
|
|
155
208
|
if (can_scroll && autoscroll) {
|
|
156
209
|
scroll();
|
|
157
210
|
}
|
|
158
|
-
|
|
159
|
-
el.focus();
|
|
160
|
-
}
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
afterUpdate(after_update);
|
|
211
|
+
});
|
|
164
212
|
|
|
165
213
|
function handle_select(event: Event): void {
|
|
166
214
|
const target: HTMLTextAreaElement | HTMLInputElement = event.target as
|
|
@@ -171,14 +219,14 @@
|
|
|
171
219
|
target.selectionStart as number,
|
|
172
220
|
target.selectionEnd as number
|
|
173
221
|
];
|
|
174
|
-
|
|
222
|
+
onselect?.({ value: text.substring(...index), index: index });
|
|
175
223
|
}
|
|
176
224
|
|
|
177
225
|
async function handle_keypress(e: KeyboardEvent): Promise<void> {
|
|
178
226
|
if (e.key === "Enter" && e.shiftKey && lines > 1) {
|
|
179
227
|
e.preventDefault();
|
|
180
228
|
await tick();
|
|
181
|
-
|
|
229
|
+
onsubmit?.();
|
|
182
230
|
} else if (
|
|
183
231
|
e.key === "Enter" &&
|
|
184
232
|
!e.shiftKey &&
|
|
@@ -189,7 +237,7 @@
|
|
|
189
237
|
add_mic_audio_to_files();
|
|
190
238
|
active_source = null;
|
|
191
239
|
await tick();
|
|
192
|
-
|
|
240
|
+
onsubmit?.();
|
|
193
241
|
}
|
|
194
242
|
}
|
|
195
243
|
|
|
@@ -206,11 +254,10 @@
|
|
|
206
254
|
if (user_has_scrolled_to_bottom) {
|
|
207
255
|
user_has_scrolled_up = false;
|
|
208
256
|
}
|
|
257
|
+
update_fade();
|
|
209
258
|
}
|
|
210
259
|
|
|
211
|
-
async function handle_upload({
|
|
212
|
-
detail
|
|
213
|
-
}: CustomEvent<FileData>): Promise<void> {
|
|
260
|
+
async function handle_upload(detail: FileData | FileData[]): Promise<void> {
|
|
214
261
|
handle_change();
|
|
215
262
|
if (Array.isArray(detail)) {
|
|
216
263
|
for (let file of detail) {
|
|
@@ -222,8 +269,8 @@
|
|
|
222
269
|
value = value;
|
|
223
270
|
}
|
|
224
271
|
await tick();
|
|
225
|
-
|
|
226
|
-
|
|
272
|
+
onchange?.(value);
|
|
273
|
+
onupload?.(detail);
|
|
227
274
|
}
|
|
228
275
|
|
|
229
276
|
function remove_thumbnail(event: MouseEvent, index: number): void {
|
|
@@ -238,7 +285,7 @@
|
|
|
238
285
|
}
|
|
239
286
|
|
|
240
287
|
function handle_stop(): void {
|
|
241
|
-
|
|
288
|
+
onstop?.();
|
|
242
289
|
}
|
|
243
290
|
|
|
244
291
|
function add_mic_audio_to_files(): void {
|
|
@@ -246,14 +293,14 @@
|
|
|
246
293
|
value.files.push(mic_audio);
|
|
247
294
|
value = value;
|
|
248
295
|
mic_audio = null;
|
|
249
|
-
|
|
296
|
+
onchange?.(value);
|
|
250
297
|
}
|
|
251
298
|
}
|
|
252
299
|
|
|
253
300
|
function handle_submit(): void {
|
|
254
301
|
add_mic_audio_to_files();
|
|
255
302
|
active_source = null;
|
|
256
|
-
|
|
303
|
+
onsubmit?.();
|
|
257
304
|
}
|
|
258
305
|
|
|
259
306
|
async function handle_paste(event: ClipboardEvent): Promise<void> {
|
|
@@ -319,8 +366,7 @@
|
|
|
319
366
|
|
|
320
367
|
const invalid_files = files.length - valid_files.length;
|
|
321
368
|
if (invalid_files > 0) {
|
|
322
|
-
|
|
323
|
-
"error",
|
|
369
|
+
onerror?.(
|
|
324
370
|
`${invalid_files} file(s) were rejected. Accepted formats: ${file_types.join(", ")}`
|
|
325
371
|
);
|
|
326
372
|
}
|
|
@@ -339,10 +385,10 @@
|
|
|
339
385
|
class="full-container"
|
|
340
386
|
class:dragging
|
|
341
387
|
bind:this={full_container}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
388
|
+
ondragenter={handle_dragenter}
|
|
389
|
+
ondragleave={handle_dragleave}
|
|
390
|
+
ondragover={(e) => e.preventDefault()}
|
|
391
|
+
ondrop={handle_drop}
|
|
346
392
|
role="group"
|
|
347
393
|
aria-label="Multimedia input field"
|
|
348
394
|
>
|
|
@@ -360,22 +406,18 @@
|
|
|
360
406
|
{root}
|
|
361
407
|
{max_file_size}
|
|
362
408
|
bind:upload_promise
|
|
363
|
-
|
|
364
|
-
mic_audio =
|
|
409
|
+
onchange={(audio_value) => {
|
|
410
|
+
mic_audio = audio_value;
|
|
365
411
|
}}
|
|
366
|
-
|
|
412
|
+
onstoprecording={() => {
|
|
367
413
|
recording = false;
|
|
368
|
-
|
|
414
|
+
onstop_recording?.();
|
|
369
415
|
}}
|
|
370
|
-
|
|
371
|
-
active_source = null;
|
|
372
|
-
recording = false;
|
|
373
|
-
mic_audio = null;
|
|
374
|
-
}}
|
|
375
|
-
on:error={({ detail }) => {
|
|
416
|
+
onclear={() => {
|
|
376
417
|
active_source = null;
|
|
377
418
|
recording = false;
|
|
378
419
|
mic_audio = null;
|
|
420
|
+
onclear?.();
|
|
379
421
|
}}
|
|
380
422
|
/>
|
|
381
423
|
</div>
|
|
@@ -389,7 +431,7 @@
|
|
|
389
431
|
<div class="action-buttons">
|
|
390
432
|
<button
|
|
391
433
|
class="confirm-button"
|
|
392
|
-
|
|
434
|
+
onclick={() => {
|
|
393
435
|
add_mic_audio_to_files();
|
|
394
436
|
active_source = null;
|
|
395
437
|
recording = false;
|
|
@@ -400,7 +442,7 @@
|
|
|
400
442
|
</button>
|
|
401
443
|
<button
|
|
402
444
|
class="cancel-button"
|
|
403
|
-
|
|
445
|
+
onclick={() => {
|
|
404
446
|
active_source = null;
|
|
405
447
|
recording = false;
|
|
406
448
|
mic_audio = null;
|
|
@@ -418,7 +460,7 @@
|
|
|
418
460
|
<Upload
|
|
419
461
|
bind:upload_promise
|
|
420
462
|
bind:this={upload_component}
|
|
421
|
-
|
|
463
|
+
onload={handle_upload}
|
|
422
464
|
{file_count}
|
|
423
465
|
filetype={file_types}
|
|
424
466
|
{root}
|
|
@@ -427,7 +469,7 @@
|
|
|
427
469
|
bind:uploading
|
|
428
470
|
show_progress={false}
|
|
429
471
|
disable_click={true}
|
|
430
|
-
|
|
472
|
+
{onerror}
|
|
431
473
|
hidden={true}
|
|
432
474
|
{upload}
|
|
433
475
|
{stream_handler}
|
|
@@ -436,9 +478,9 @@
|
|
|
436
478
|
|
|
437
479
|
<div
|
|
438
480
|
class="input-wrapper"
|
|
439
|
-
class:has-files={value
|
|
481
|
+
class:has-files={(value?.files?.length ?? 0) > 0 || uploading}
|
|
440
482
|
>
|
|
441
|
-
{#if value
|
|
483
|
+
{#if (value?.files?.length ?? 0) > 0 || uploading}
|
|
442
484
|
<div
|
|
443
485
|
class="thumbnails"
|
|
444
486
|
aria-label="Uploaded files"
|
|
@@ -449,13 +491,13 @@
|
|
|
449
491
|
data-testid="upload-button"
|
|
450
492
|
class="upload-button thumbnail-add"
|
|
451
493
|
{disabled}
|
|
452
|
-
|
|
494
|
+
onclick={handle_upload_click}
|
|
453
495
|
aria-label="Upload a file"
|
|
454
496
|
>
|
|
455
497
|
<Paperclip />
|
|
456
498
|
</button>
|
|
457
499
|
{/if}
|
|
458
|
-
{#each value
|
|
500
|
+
{#each value?.files ?? [] as file, index}
|
|
459
501
|
<span
|
|
460
502
|
class="thumbnail-wrapper"
|
|
461
503
|
role="listitem"
|
|
@@ -481,7 +523,7 @@
|
|
|
481
523
|
{/if}
|
|
482
524
|
<button
|
|
483
525
|
class="delete-button"
|
|
484
|
-
|
|
526
|
+
onclick={(event) => remove_thumbnail(event, index)}
|
|
485
527
|
aria-label="Remove file"
|
|
486
528
|
>
|
|
487
529
|
<Clear />
|
|
@@ -496,35 +538,49 @@
|
|
|
496
538
|
{/if}
|
|
497
539
|
|
|
498
540
|
<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
|
-
|
|
541
|
+
{#if show_upload && (value?.files?.length ?? 0) === 0 && !uploading}
|
|
542
|
+
<button
|
|
543
|
+
data-testid="upload-button"
|
|
544
|
+
class="upload-button icon-button"
|
|
545
|
+
{disabled}
|
|
546
|
+
onclick={handle_upload_click}
|
|
547
|
+
aria-label="Upload a file"
|
|
548
|
+
>
|
|
549
|
+
<Paperclip />
|
|
550
|
+
</button>
|
|
551
|
+
{/if}
|
|
552
|
+
<div class="textarea-wrapper">
|
|
553
|
+
<textarea
|
|
554
|
+
data-testid="textbox"
|
|
555
|
+
use:text_area_resize={{
|
|
556
|
+
text: value.text,
|
|
557
|
+
lines: lines,
|
|
558
|
+
max_lines: max_lines
|
|
559
|
+
}}
|
|
560
|
+
class:no-label={!show_label}
|
|
561
|
+
dir={rtl ? "rtl" : "ltr"}
|
|
562
|
+
bind:value={value.text}
|
|
563
|
+
bind:this={el}
|
|
564
|
+
{placeholder}
|
|
565
|
+
rows={lines}
|
|
566
|
+
{disabled}
|
|
567
|
+
onkeypress={handle_keypress}
|
|
568
|
+
onblur={() => onblur?.()}
|
|
569
|
+
onselect={handle_select}
|
|
570
|
+
onfocus={() => onfocus?.()}
|
|
571
|
+
onscroll={handle_scroll}
|
|
572
|
+
onpaste={handle_paste}
|
|
573
|
+
style={text_align ? "text-align: " + text_align : ""}
|
|
574
|
+
autocapitalize={html_attributes?.autocapitalize}
|
|
575
|
+
autocorrect={html_attributes?.autocorrect}
|
|
576
|
+
spellcheck={html_attributes?.spellcheck}
|
|
577
|
+
autocomplete={html_attributes?.autocomplete}
|
|
578
|
+
tabindex={html_attributes?.tabindex}
|
|
579
|
+
enterkeyhint={html_attributes?.enterkeyhint}
|
|
580
|
+
lang={html_attributes?.lang}
|
|
581
|
+
/>
|
|
582
|
+
<ScrollFade visible={show_fade} position="absolute" />
|
|
583
|
+
</div>
|
|
528
584
|
|
|
529
585
|
{#if sources && sources.includes("microphone")}
|
|
530
586
|
<button
|
|
@@ -532,7 +588,7 @@
|
|
|
532
588
|
class="microphone-button"
|
|
533
589
|
class:recording
|
|
534
590
|
{disabled}
|
|
535
|
-
|
|
591
|
+
onclick={async () => {
|
|
536
592
|
if (active_source !== "microphone") {
|
|
537
593
|
active_source = "microphone";
|
|
538
594
|
await tick();
|
|
@@ -554,7 +610,7 @@
|
|
|
554
610
|
data-testid="submit-button"
|
|
555
611
|
class:padded-button={submit_btn !== true}
|
|
556
612
|
{disabled}
|
|
557
|
-
|
|
613
|
+
onclick={handle_submit}
|
|
558
614
|
aria-label="Submit"
|
|
559
615
|
>
|
|
560
616
|
{#if submit_btn === true}
|
|
@@ -568,7 +624,7 @@
|
|
|
568
624
|
<button
|
|
569
625
|
class="stop-button"
|
|
570
626
|
class:padded-button={stop_btn !== true}
|
|
571
|
-
|
|
627
|
+
onclick={handle_stop}
|
|
572
628
|
aria-label="Stop"
|
|
573
629
|
>
|
|
574
630
|
{#if stop_btn === true}
|
|
@@ -617,7 +673,7 @@
|
|
|
617
673
|
gap: 0;
|
|
618
674
|
background: var(--block-background-fill);
|
|
619
675
|
border-radius: var(--radius-xl);
|
|
620
|
-
padding: var(--spacing-sm);
|
|
676
|
+
padding: var(--spacing-sm) var(--spacing-sm) var(--spacing-sm) 0;
|
|
621
677
|
align-items: flex-start;
|
|
622
678
|
min-height: auto;
|
|
623
679
|
}
|
|
@@ -628,7 +684,7 @@
|
|
|
628
684
|
|
|
629
685
|
.input-row {
|
|
630
686
|
display: flex;
|
|
631
|
-
align-items: flex-
|
|
687
|
+
align-items: flex-start;
|
|
632
688
|
gap: var(--spacing-sm);
|
|
633
689
|
width: 100%;
|
|
634
690
|
}
|
|
@@ -724,7 +780,7 @@
|
|
|
724
780
|
flex-grow: 1;
|
|
725
781
|
outline: none !important;
|
|
726
782
|
background: transparent;
|
|
727
|
-
padding: 0;
|
|
783
|
+
padding: var(--spacing-sm) 0;
|
|
728
784
|
color: var(--body-text-color);
|
|
729
785
|
font-weight: var(--input-text-weight);
|
|
730
786
|
font-size: var(--input-text-size);
|
|
@@ -734,7 +790,6 @@
|
|
|
734
790
|
position: relative;
|
|
735
791
|
z-index: var(--layer-1);
|
|
736
792
|
text-align: left;
|
|
737
|
-
min-height: var(--size-9);
|
|
738
793
|
}
|
|
739
794
|
|
|
740
795
|
textarea:disabled {
|
|
@@ -750,15 +805,12 @@
|
|
|
750
805
|
text-align: right;
|
|
751
806
|
}
|
|
752
807
|
|
|
753
|
-
textarea[dir="rtl"] ~ .submit-button {
|
|
754
|
-
order: -1;
|
|
755
|
-
}
|
|
756
|
-
|
|
757
808
|
textarea[dir="rtl"] ~ .submit-button :global(svg) {
|
|
758
809
|
transform: scaleX(-1);
|
|
759
810
|
}
|
|
760
811
|
|
|
761
|
-
.microphone-button
|
|
812
|
+
.microphone-button,
|
|
813
|
+
.icon-button {
|
|
762
814
|
color: var(--body-text-color);
|
|
763
815
|
cursor: pointer;
|
|
764
816
|
padding: var(--spacing-sm);
|
|
@@ -796,21 +848,25 @@
|
|
|
796
848
|
height: var(--size-5);
|
|
797
849
|
}
|
|
798
850
|
|
|
799
|
-
.microphone-button
|
|
851
|
+
.microphone-button,
|
|
852
|
+
.icon-button {
|
|
800
853
|
width: var(--size-9);
|
|
801
854
|
height: var(--size-9);
|
|
802
855
|
}
|
|
803
856
|
|
|
804
|
-
.microphone-button:hover:not(:disabled)
|
|
857
|
+
.microphone-button:hover:not(:disabled),
|
|
858
|
+
.icon-button:hover:not(:disabled) {
|
|
805
859
|
background: var(--button-secondary-background-fill);
|
|
806
860
|
}
|
|
807
861
|
|
|
808
|
-
.microphone-button:disabled
|
|
862
|
+
.microphone-button:disabled,
|
|
863
|
+
.icon-button:disabled {
|
|
809
864
|
opacity: 0.5;
|
|
810
865
|
cursor: not-allowed;
|
|
811
866
|
}
|
|
812
867
|
|
|
813
|
-
.microphone-button :global(svg)
|
|
868
|
+
.microphone-button :global(svg),
|
|
869
|
+
.icon-button :global(svg) {
|
|
814
870
|
width: var(--size-5);
|
|
815
871
|
height: var(--size-5);
|
|
816
872
|
}
|
|
@@ -1002,4 +1058,13 @@
|
|
|
1002
1058
|
opacity: 1;
|
|
1003
1059
|
}
|
|
1004
1060
|
}
|
|
1061
|
+
|
|
1062
|
+
.textarea-wrapper {
|
|
1063
|
+
position: relative;
|
|
1064
|
+
flex-grow: 1;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
.textarea-wrapper textarea {
|
|
1068
|
+
width: 100%;
|
|
1069
|
+
}
|
|
1005
1070
|
</style>
|