@gradio/multimodaltextbox 0.5.5 → 0.6.0-beta.1

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.
@@ -0,0 +1,543 @@
1
+ <script>import {
2
+ beforeUpdate,
3
+ afterUpdate,
4
+ createEventDispatcher,
5
+ tick
6
+ } from "svelte";
7
+ import { text_area_resize, resize } from "../shared/utils";
8
+ import { BlockTitle } from "@gradio/atoms";
9
+ import { Upload } from "@gradio/upload";
10
+ import { Image } from "@gradio/image/shared";
11
+ import {
12
+ Clear,
13
+ File,
14
+ Music,
15
+ Paperclip,
16
+ Video,
17
+ Send,
18
+ Square
19
+ } from "@gradio/icons";
20
+ export let value = {
21
+ text: "",
22
+ files: []
23
+ };
24
+ export let value_is_output = false;
25
+ export let lines = 1;
26
+ export let placeholder = "Type here...";
27
+ export let disabled = false;
28
+ export let label;
29
+ export let info = void 0;
30
+ export let show_label = true;
31
+ export let container = true;
32
+ export let max_lines;
33
+ export let submit_btn = null;
34
+ export let stop_btn = null;
35
+ export let rtl = false;
36
+ export let autofocus = false;
37
+ export let text_align = void 0;
38
+ export let autoscroll = true;
39
+ export let root;
40
+ export let file_types = null;
41
+ export let max_file_size = null;
42
+ export let upload;
43
+ export let stream_handler;
44
+ export let file_count = "multiple";
45
+ let upload_component;
46
+ let hidden_upload;
47
+ let el;
48
+ let can_scroll;
49
+ let previous_scroll_top = 0;
50
+ let user_has_scrolled_up = false;
51
+ export let dragging = false;
52
+ let uploading = false;
53
+ let oldValue = value.text;
54
+ $:
55
+ dispatch("drag", dragging);
56
+ let full_container;
57
+ $:
58
+ if (oldValue !== value.text) {
59
+ dispatch("change", value);
60
+ oldValue = value.text;
61
+ }
62
+ let accept_file_types;
63
+ if (file_types == null) {
64
+ accept_file_types = null;
65
+ } else {
66
+ file_types = file_types.map((x) => {
67
+ if (x.startsWith(".")) {
68
+ return x;
69
+ }
70
+ return x + "/*";
71
+ });
72
+ accept_file_types = file_types.join(", ");
73
+ }
74
+ $:
75
+ if (value === null)
76
+ value = { text: "", files: [] };
77
+ $:
78
+ value, el && lines !== max_lines && resize(el, lines, max_lines);
79
+ const dispatch = createEventDispatcher();
80
+ beforeUpdate(() => {
81
+ can_scroll = el && el.offsetHeight + el.scrollTop > el.scrollHeight - 100;
82
+ });
83
+ const scroll = () => {
84
+ if (can_scroll && autoscroll && !user_has_scrolled_up) {
85
+ el.scrollTo(0, el.scrollHeight);
86
+ }
87
+ };
88
+ async function handle_change() {
89
+ dispatch("change", value);
90
+ if (!value_is_output) {
91
+ dispatch("input");
92
+ }
93
+ }
94
+ afterUpdate(() => {
95
+ if (autofocus && el !== null) {
96
+ el.focus();
97
+ }
98
+ if (can_scroll && autoscroll) {
99
+ scroll();
100
+ }
101
+ value_is_output = false;
102
+ });
103
+ function handle_select(event) {
104
+ const target = event.target;
105
+ const text = target.value;
106
+ const index = [
107
+ target.selectionStart,
108
+ target.selectionEnd
109
+ ];
110
+ dispatch("select", { value: text.substring(...index), index });
111
+ }
112
+ async function handle_keypress(e) {
113
+ await tick();
114
+ if (e.key === "Enter" && e.shiftKey && lines > 1) {
115
+ e.preventDefault();
116
+ dispatch("submit");
117
+ } else if (e.key === "Enter" && !e.shiftKey && lines === 1 && max_lines >= 1) {
118
+ e.preventDefault();
119
+ dispatch("submit");
120
+ }
121
+ }
122
+ function handle_scroll(event) {
123
+ const target = event.target;
124
+ const current_scroll_top = target.scrollTop;
125
+ if (current_scroll_top < previous_scroll_top) {
126
+ user_has_scrolled_up = true;
127
+ }
128
+ previous_scroll_top = current_scroll_top;
129
+ const max_scroll_top = target.scrollHeight - target.clientHeight;
130
+ const user_has_scrolled_to_bottom = current_scroll_top >= max_scroll_top;
131
+ if (user_has_scrolled_to_bottom) {
132
+ user_has_scrolled_up = false;
133
+ }
134
+ }
135
+ async function handle_upload({
136
+ detail
137
+ }) {
138
+ handle_change();
139
+ if (Array.isArray(detail)) {
140
+ for (let file of detail) {
141
+ value.files.push(file);
142
+ }
143
+ value = value;
144
+ } else {
145
+ value.files.push(detail);
146
+ value = value;
147
+ }
148
+ await tick();
149
+ dispatch("change", value);
150
+ dispatch("upload", detail);
151
+ }
152
+ function remove_thumbnail(event, index) {
153
+ handle_change();
154
+ event.stopPropagation();
155
+ value.files.splice(index, 1);
156
+ value = value;
157
+ }
158
+ function handle_upload_click() {
159
+ if (hidden_upload) {
160
+ hidden_upload.value = "";
161
+ hidden_upload.click();
162
+ }
163
+ }
164
+ function handle_stop() {
165
+ dispatch("stop");
166
+ }
167
+ function handle_submit() {
168
+ dispatch("submit");
169
+ }
170
+ function handle_paste(event) {
171
+ if (!event.clipboardData)
172
+ return;
173
+ const items = event.clipboardData.items;
174
+ for (let index in items) {
175
+ const item = items[index];
176
+ if (item.kind === "file" && item.type.includes("image")) {
177
+ const blob = item.getAsFile();
178
+ if (blob)
179
+ upload_component.load_files([blob]);
180
+ }
181
+ }
182
+ }
183
+ function handle_dragenter(event) {
184
+ event.preventDefault();
185
+ dragging = true;
186
+ }
187
+ function handle_dragleave(event) {
188
+ event.preventDefault();
189
+ const rect = full_container.getBoundingClientRect();
190
+ const { clientX, clientY } = event;
191
+ if (clientX <= rect.left || clientX >= rect.right || clientY <= rect.top || clientY >= rect.bottom) {
192
+ dragging = false;
193
+ }
194
+ }
195
+ function handle_drop(event) {
196
+ event.preventDefault();
197
+ dragging = false;
198
+ if (event.dataTransfer && event.dataTransfer.files) {
199
+ upload_component.load_files(Array.from(event.dataTransfer.files));
200
+ }
201
+ }
202
+ </script>
203
+
204
+ <div
205
+ class="full-container"
206
+ class:dragging
207
+ bind:this={full_container}
208
+ on:dragenter={handle_dragenter}
209
+ on:dragleave={handle_dragleave}
210
+ on:dragover|preventDefault
211
+ on:drop={handle_drop}
212
+ role="group"
213
+ aria-label="Multimedia input field"
214
+ >
215
+ <!-- svelte-ignore a11y-autofocus -->
216
+ <label class:container>
217
+ <BlockTitle {show_label} {info}>{label}</BlockTitle>
218
+ {#if value.files.length > 0 || uploading}
219
+ <div
220
+ class="thumbnails scroll-hide"
221
+ aria-label="Uploaded files"
222
+ data-testid="container_el"
223
+ style="display: {value.files.length > 0 || uploading
224
+ ? 'flex'
225
+ : 'none'};"
226
+ >
227
+ {#each value.files as file, index}
228
+ <span role="listitem" aria-label="File thumbnail">
229
+ <button class="thumbnail-item thumbnail-small">
230
+ <button
231
+ class:disabled
232
+ class="delete-button"
233
+ on:click={(event) => remove_thumbnail(event, index)}
234
+ ><Clear /></button
235
+ >
236
+ {#if file.mime_type && file.mime_type.includes("image")}
237
+ <Image
238
+ src={file.url}
239
+ title={null}
240
+ alt=""
241
+ loading="lazy"
242
+ class={"thumbnail-image"}
243
+ />
244
+ {:else if file.mime_type && file.mime_type.includes("audio")}
245
+ <Music />
246
+ {:else if file.mime_type && file.mime_type.includes("video")}
247
+ <Video />
248
+ {:else}
249
+ <File />
250
+ {/if}
251
+ </button>
252
+ </span>
253
+ {/each}
254
+ {#if uploading}
255
+ <div class="loader" role="status" aria-label="Uploading"></div>
256
+ {/if}
257
+ </div>
258
+ {/if}
259
+ <div class="input-container">
260
+ <Upload
261
+ bind:this={upload_component}
262
+ on:load={handle_upload}
263
+ {file_count}
264
+ {root}
265
+ {max_file_size}
266
+ bind:dragging
267
+ bind:uploading
268
+ show_progress={false}
269
+ disable_click={true}
270
+ bind:hidden_upload
271
+ on:error
272
+ hidden={true}
273
+ {upload}
274
+ {stream_handler}
275
+ ></Upload>
276
+ <button
277
+ data-testid="upload-button"
278
+ class="upload-button"
279
+ on:click={handle_upload_click}><Paperclip /></button
280
+ >
281
+ <textarea
282
+ data-testid="textbox"
283
+ use:text_area_resize={{
284
+ text: value.text,
285
+ lines: lines,
286
+ max_lines: max_lines
287
+ }}
288
+ class="scroll-hide"
289
+ dir={rtl ? "rtl" : "ltr"}
290
+ bind:value={value.text}
291
+ bind:this={el}
292
+ {placeholder}
293
+ rows={lines}
294
+ {disabled}
295
+ {autofocus}
296
+ on:keypress={handle_keypress}
297
+ on:blur
298
+ on:select={handle_select}
299
+ on:focus
300
+ on:scroll={handle_scroll}
301
+ on:paste={handle_paste}
302
+ style={text_align ? "text-align: " + text_align : ""}
303
+ />
304
+ {#if submit_btn}
305
+ <button
306
+ class="submit-button"
307
+ class:padded-button={submit_btn !== true}
308
+ on:click={handle_submit}
309
+ >
310
+ {#if submit_btn === true}
311
+ <Send />
312
+ {:else}
313
+ {submit_btn}
314
+ {/if}
315
+ </button>
316
+ {/if}
317
+ {#if stop_btn}
318
+ <button
319
+ class="stop-button"
320
+ class:padded-button={stop_btn !== true}
321
+ on:click={handle_stop}
322
+ >
323
+ {#if stop_btn === true}
324
+ <Square />
325
+ {:else}
326
+ {stop_btn}
327
+ {/if}
328
+ </button>
329
+ {/if}
330
+ </div>
331
+ </label>
332
+ </div>
333
+
334
+ <style>
335
+ .full-container {
336
+ width: 100%;
337
+ position: relative;
338
+ }
339
+
340
+ .full-container.dragging::after {
341
+ content: "";
342
+ position: absolute;
343
+ top: 0;
344
+ left: 0;
345
+ right: 0;
346
+ bottom: 0;
347
+ pointer-events: none;
348
+ }
349
+
350
+ .input-container {
351
+ display: flex;
352
+ position: relative;
353
+ align-items: flex-end;
354
+ }
355
+
356
+ textarea {
357
+ flex-grow: 1;
358
+ outline: none !important;
359
+ background: var(--input-background-fill);
360
+ padding: var(--input-padding);
361
+ color: var(--body-text-color);
362
+ font-weight: var(--input-text-weight);
363
+ font-size: var(--input-text-size);
364
+ line-height: var(--line-sm);
365
+ border: none;
366
+ margin-top: 0px;
367
+ margin-bottom: 0px;
368
+ resize: none;
369
+ position: relative;
370
+ z-index: 1;
371
+ }
372
+
373
+ textarea:disabled {
374
+ -webkit-opacity: 1;
375
+ opacity: 1;
376
+ }
377
+
378
+ textarea::placeholder {
379
+ color: var(--input-placeholder-color);
380
+ }
381
+
382
+ .upload-button,
383
+ .submit-button,
384
+ .stop-button {
385
+ border: none;
386
+ text-align: center;
387
+ text-decoration: none;
388
+ font-size: 14px;
389
+ cursor: pointer;
390
+ border-radius: 15px;
391
+ min-width: 30px;
392
+ height: 30px;
393
+ flex-shrink: 0;
394
+ display: flex;
395
+ justify-content: center;
396
+ align-items: center;
397
+ margin-bottom: 5px;
398
+ z-index: var(--layer-1);
399
+ }
400
+ .padded-button {
401
+ padding: 0 10px;
402
+ }
403
+
404
+ .upload-button,
405
+ .submit-button {
406
+ background: var(--button-secondary-background-fill);
407
+ color: var(--button-secondary-text-color);
408
+ }
409
+
410
+ .upload-button:hover,
411
+ .submit-button:hover {
412
+ background: var(--button-secondary-background-fill-hover);
413
+ }
414
+
415
+ .upload-button:active,
416
+ .submit-button:active {
417
+ box-shadow: var(--button-shadow-active);
418
+ }
419
+
420
+ .submit-button :global(svg) {
421
+ height: 22px;
422
+ width: 22px;
423
+ }
424
+ .upload-button :global(svg) {
425
+ height: 17px;
426
+ width: 17px;
427
+ }
428
+
429
+ .stop-button {
430
+ background: var(--button-cancel-background-fill);
431
+ color: var(--button-cancel-text-color);
432
+ }
433
+ .stop-button:hover {
434
+ background: var(--button-cancel-background-fill-hover);
435
+ color: var(--button-cancel-text-color-hover);
436
+ }
437
+ .stop-button :global(svg) {
438
+ height: 18px;
439
+ width: 18px;
440
+ }
441
+
442
+ .loader {
443
+ display: flex;
444
+ justify-content: center;
445
+ align-items: center;
446
+ --ring-color: transparent;
447
+ position: relative;
448
+ border: 5px solid #f3f3f3;
449
+ border-top: 5px solid var(--color-accent);
450
+ border-radius: 50%;
451
+ width: 25px;
452
+ height: 25px;
453
+ animation: spin 2s linear infinite;
454
+ }
455
+
456
+ @keyframes spin {
457
+ 0% {
458
+ transform: rotate(0deg);
459
+ }
460
+ 100% {
461
+ transform: rotate(360deg);
462
+ }
463
+ }
464
+
465
+ .thumbnails :global(img) {
466
+ width: var(--size-full);
467
+ height: var(--size-full);
468
+ object-fit: cover;
469
+ border-radius: var(--radius-lg);
470
+ }
471
+
472
+ .thumbnails {
473
+ display: flex;
474
+ align-items: center;
475
+ gap: var(--spacing-lg);
476
+ overflow-x: scroll;
477
+ padding-top: var(--spacing-sm);
478
+ }
479
+
480
+ .thumbnail-item {
481
+ display: flex;
482
+ justify-content: center;
483
+ align-items: center;
484
+ --ring-color: transparent;
485
+ position: relative;
486
+ box-shadow:
487
+ 0 0 0 2px var(--ring-color),
488
+ var(--shadow-drop);
489
+ border: 1px solid var(--border-color-primary);
490
+ border-radius: var(--radius-lg);
491
+ background: var(--background-fill-secondary);
492
+ aspect-ratio: var(--ratio-square);
493
+ width: var(--size-full);
494
+ height: var(--size-full);
495
+ cursor: default;
496
+ }
497
+
498
+ .thumbnail-small {
499
+ flex: none;
500
+ transform: scale(0.9);
501
+ transition: 0.075s;
502
+ width: var(--size-12);
503
+ height: var(--size-12);
504
+ }
505
+
506
+ .thumbnail-item :global(svg) {
507
+ width: 30px;
508
+ height: 30px;
509
+ }
510
+
511
+ .delete-button {
512
+ display: flex;
513
+ justify-content: center;
514
+ align-items: center;
515
+ position: absolute;
516
+ right: -7px;
517
+ top: -7px;
518
+ color: var(--button-secondary-text-color);
519
+ background: var(--button-secondary-background-fill);
520
+ border: none;
521
+ text-align: center;
522
+ text-decoration: none;
523
+ font-size: 10px;
524
+ cursor: pointer;
525
+ border-radius: 50%;
526
+ width: 20px;
527
+ height: 20px;
528
+ }
529
+
530
+ .disabled {
531
+ display: none;
532
+ }
533
+
534
+ .delete-button :global(svg) {
535
+ width: 12px;
536
+ height: 12px;
537
+ }
538
+
539
+ .delete-button:hover {
540
+ filter: brightness(1.2);
541
+ border: 0.8px solid var(--color-grey-500);
542
+ }
543
+ </style>
@@ -0,0 +1,60 @@
1
+ import { SvelteComponent } from "svelte";
2
+ import type { FileData, Client } from "@gradio/client";
3
+ import type { SelectData } from "@gradio/utils";
4
+ declare const __propDef: {
5
+ props: {
6
+ value?: {
7
+ text: string;
8
+ files: FileData[];
9
+ } | undefined;
10
+ value_is_output?: boolean | undefined;
11
+ lines?: number | undefined;
12
+ placeholder?: string | undefined;
13
+ disabled?: boolean | undefined;
14
+ label: string;
15
+ info?: string | undefined;
16
+ show_label?: boolean | undefined;
17
+ container?: boolean | undefined;
18
+ max_lines: number;
19
+ submit_btn?: (string | boolean | null) | undefined;
20
+ stop_btn?: (string | boolean | null) | undefined;
21
+ rtl?: boolean | undefined;
22
+ autofocus?: boolean | undefined;
23
+ text_align?: "left" | "right" | undefined;
24
+ autoscroll?: boolean | undefined;
25
+ root: string;
26
+ file_types?: (string[] | null) | undefined;
27
+ max_file_size?: (number | null) | undefined;
28
+ upload: Client["upload"];
29
+ stream_handler: Client["stream"];
30
+ file_count?: ("single" | "multiple" | "directory") | undefined;
31
+ dragging?: boolean | undefined;
32
+ };
33
+ events: {
34
+ dragover: DragEvent;
35
+ error: CustomEvent<any>;
36
+ blur: CustomEvent<any>;
37
+ focus: CustomEvent<any>;
38
+ change: CustomEvent<{
39
+ text: string;
40
+ files: FileData[];
41
+ }>;
42
+ submit: CustomEvent<undefined>;
43
+ stop: CustomEvent<undefined>;
44
+ select: CustomEvent<SelectData>;
45
+ input: CustomEvent<undefined>;
46
+ drag: CustomEvent<boolean>;
47
+ upload: CustomEvent<FileData | FileData[]>;
48
+ clear: CustomEvent<undefined>;
49
+ load: CustomEvent<FileData | FileData[]>;
50
+ } & {
51
+ [evt: string]: CustomEvent<any>;
52
+ };
53
+ slots: {};
54
+ };
55
+ export type MultimodalTextboxProps = typeof __propDef.props;
56
+ export type MultimodalTextboxEvents = typeof __propDef.events;
57
+ export type MultimodalTextboxSlots = typeof __propDef.slots;
58
+ export default class MultimodalTextbox extends SvelteComponent<MultimodalTextboxProps, MultimodalTextboxEvents, MultimodalTextboxSlots> {
59
+ }
60
+ export {};
@@ -0,0 +1,8 @@
1
+ interface Value {
2
+ lines: number;
3
+ max_lines: number;
4
+ text: string;
5
+ }
6
+ export declare function resize(target: HTMLTextAreaElement | HTMLInputElement, lines: number, max_lines: number): Promise<void>;
7
+ export declare function text_area_resize(_el: HTMLTextAreaElement, _value: Value): any | undefined;
8
+ export {};
@@ -0,0 +1,39 @@
1
+ import { tick } from "svelte";
2
+ export async function resize(target, lines, max_lines) {
3
+ await tick();
4
+ if (lines === max_lines)
5
+ return;
6
+ let max = max_lines === undefined
7
+ ? false
8
+ : max_lines === undefined // default
9
+ ? 21 * 11
10
+ : 21 * (max_lines + 1);
11
+ let min = 21 * (lines + 1);
12
+ target.style.height = "1px";
13
+ let scroll_height;
14
+ if (max && target.scrollHeight > max) {
15
+ scroll_height = max;
16
+ }
17
+ else if (target.scrollHeight < min) {
18
+ scroll_height = min;
19
+ }
20
+ else {
21
+ scroll_height = target.scrollHeight;
22
+ }
23
+ target.style.height = `${scroll_height}px`;
24
+ }
25
+ export function text_area_resize(_el, _value) {
26
+ if (_value.lines === _value.max_lines)
27
+ return;
28
+ _el.style.overflowY = "scroll";
29
+ function handle_input(event) {
30
+ resize(event.target, _value.lines, _value.max_lines);
31
+ }
32
+ _el.addEventListener("input", handle_input);
33
+ if (!_value.text.trim())
34
+ return;
35
+ resize(_el, _value.lines, _value.max_lines);
36
+ return {
37
+ destroy: () => _el.removeEventListener("input", handle_input)
38
+ };
39
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gradio/multimodaltextbox",
3
- "version": "0.5.5",
3
+ "version": "0.6.0-beta.1",
4
4
  "description": "Gradio UI packages",
5
5
  "type": "module",
6
6
  "author": "",
@@ -9,21 +9,37 @@
9
9
  "main_changeset": true,
10
10
  "main": "Index.svelte",
11
11
  "exports": {
12
- ".": "./Index.svelte",
13
- "./example": "./Example.svelte",
12
+ ".": {
13
+ "gradio": "./Index.svelte",
14
+ "svelte": "./dist/Index.svelte",
15
+ "types": "./dist/Index.svelte.d.ts"
16
+ },
17
+ "./example": {
18
+ "gradio": "./Example.svelte",
19
+ "svelte": "./dist/Example.svelte",
20
+ "types": "./dist/Example.svelte.d.ts"
21
+ },
14
22
  "./package.json": "./package.json"
15
23
  },
16
24
  "dependencies": {
17
- "@gradio/icons": "^0.7.0",
18
- "@gradio/statustracker": "^0.7.4",
19
- "@gradio/upload": "^0.12.2",
20
- "@gradio/image": "^0.14.0",
21
- "@gradio/video": "^0.10.2",
22
- "@gradio/client": "^1.5.0",
23
- "@gradio/utils": "^0.5.2",
24
- "@gradio/atoms": "^0.7.9"
25
+ "@gradio/atoms": "^0.8.1-beta.1",
26
+ "@gradio/icons": "^0.8.0-beta.1",
27
+ "@gradio/statustracker": "^0.8.0-beta.1",
28
+ "@gradio/utils": "^0.7.0-beta.1",
29
+ "@gradio/upload": "^0.12.4-beta.1",
30
+ "@gradio/image": "^0.16.0-beta.1",
31
+ "@gradio/video": "^0.11.0-beta.1",
32
+ "@gradio/client": "^1.6.0-beta.1"
25
33
  },
26
34
  "devDependencies": {
27
- "@gradio/preview": "^0.10.2"
35
+ "@gradio/preview": "^0.11.1-beta.0"
36
+ },
37
+ "peerDependencies": {
38
+ "svelte": "^4.0.0"
39
+ },
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/gradio-app/gradio.git",
43
+ "directory": "js/multimodaltextbox"
28
44
  }
29
45
  }