2
0
mirror of https://github.com/gradio-app/gradio.git synced 2025-03-19 12:00:39 +08:00

Support large files try 2 ()

* changes

* changes

* changes

* changes

* changes

* changes

* changes

* changes

* changes

* changes

* changes

* changes

* changes

* chagnes

* changes

* changes

* changes

* changes

* changes

* changes

* chnages

* changes

* changes

* merge

* changes

* changes

* changes

* changes

* changes

* changes

---------

Co-authored-by: Abubakar Abid <abubakar@huggingface.co>
This commit is contained in:
aliabid94 2023-02-17 15:31:02 -08:00 committed by GitHub
parent 9b2119f297
commit f34120c077
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 265 additions and 204 deletions

@ -70,6 +70,7 @@ By [@dawoodkhan82](https://github.com/dawoodkhan82) in [PR 3165](https://github.
* Fixes `gr.utils.delete_none` to only remove props whose values are `None` from the config by [@abidlabs](https://github.com/abidlabs) in [PR 3188](https://github.com/gradio-app/gradio/pull/3188)
* Fix bug where embedded demos were not loading files properly by [@freddyaboulton](https://github.com/freddyaboulton) in [PR 3177](https://github.com/gradio-app/gradio/pull/3177)
* The `change` event is now triggered when users click the 'Clear All' button of the multiselect DropDown component by [@freddyaboulton](https://github.com/freddyaboulton) in [PR 3195](https://github.com/gradio-app/gradio/pull/3195)
* Stops File component from freezing when a large file is uploaded by [@aliabid94](https://github.com/aliabid94) in [PR 3191](https://github.com/gradio-app/gradio/pull/3191)
* Support Chinese pinyin in Dataframe by [@aliabid94](https://github.com/aliabid94) in [PR 3206](https://github.com/gradio-app/gradio/pull/3206)
* The `clear` event is now triggered when images are cleared by [@freddyaboulton](https://github.com/freddyaboulton) in [PR 3218](https://github.com/gradio-app/gradio/pull/3218)
* Fix bug where auth cookies where not sent when connecting to an app via http by [@freddyaboulton](https://github.com/freddyaboulton) in [PR 3223](https://github.com/gradio-app/gradio/pull/3223)

1
demo/zip_files/.gitignore vendored Normal file

@ -0,0 +1 @@
tmp.zip

@ -1 +1 @@
{"cells": [{"cell_type": "markdown", "id": 302934307671667531413257853548643485645, "metadata": {}, "source": ["# Gradio Demo: zip_files"]}, {"cell_type": "code", "execution_count": null, "id": 272996653310673477252411125948039410165, "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": 288918539441861185822528903084949547379, "metadata": {}, "outputs": [], "source": ["# Downloading files from the demo repo\n", "import os\n", "os.mkdir('files')\n", "!wget -q -O files/titanic.csv https://github.com/gradio-app/gradio/raw/main/demo/zip_files/files/titanic.csv\n", "!wget -q https://github.com/gradio-app/gradio/raw/main/demo/zip_files/tmp.zip"]}, {"cell_type": "code", "execution_count": null, "id": 44380577570523278879349135829904343037, "metadata": {}, "outputs": [], "source": ["import os\n", "from zipfile import ZipFile\n", "\n", "import gradio as gr\n", "\n", "\n", "def zip_files(files):\n", " with ZipFile(\"tmp.zip\", \"w\") as zipObj:\n", " for idx, file in enumerate(files):\n", " zipObj.write(file.name, \"file\" + str(idx))\n", " return \"tmp.zip\"\n", "\n", "demo = gr.Interface(\n", " zip_files,\n", " gr.File(file_count=\"multiple\", file_types=[\"text\", \".json\", \".csv\"]),\n", " \"file\",\n", " examples=[[[os.path.join(os.path.abspath(''),\"files/titanic.csv\"), \n", " os.path.join(os.path.abspath(''),\"files/titanic.csv\"), \n", " os.path.join(os.path.abspath(''),\"files/titanic.csv\")]]], \n", " cache_examples=True\n", ")\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
{"cells": [{"cell_type": "markdown", "id": 302934307671667531413257853548643485645, "metadata": {}, "source": ["# Gradio Demo: zip_files"]}, {"cell_type": "code", "execution_count": null, "id": 272996653310673477252411125948039410165, "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": 288918539441861185822528903084949547379, "metadata": {}, "outputs": [], "source": ["# Downloading files from the demo repo\n", "import os\n", "os.mkdir('files')\n", "!wget -q -O files/titanic.csv https://github.com/gradio-app/gradio/raw/main/demo/zip_files/files/titanic.csv"]}, {"cell_type": "code", "execution_count": null, "id": 44380577570523278879349135829904343037, "metadata": {}, "outputs": [], "source": ["import os\n", "from zipfile import ZipFile\n", "\n", "import gradio as gr\n", "\n", "\n", "def zip_files(files):\n", " with ZipFile(\"tmp.zip\", \"w\") as zipObj:\n", " for idx, file in enumerate(files):\n", " zipObj.write(file.name, file.name.split(\"/\")[-1])\n", " return \"tmp.zip\"\n", "\n", "demo = gr.Interface(\n", " zip_files,\n", " gr.File(file_count=\"multiple\", file_types=[\"text\", \".json\", \".csv\"]),\n", " \"file\",\n", " examples=[[[os.path.join(os.path.abspath(''),\"files/titanic.csv\"), \n", " os.path.join(os.path.abspath(''),\"files/titanic.csv\"), \n", " os.path.join(os.path.abspath(''),\"files/titanic.csv\")]]], \n", " cache_examples=True\n", ")\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}

@ -7,7 +7,7 @@ import gradio as gr
def zip_files(files):
with ZipFile("tmp.zip", "w") as zipObj:
for idx, file in enumerate(files):
zipObj.write(file.name, "file" + str(idx))
zipObj.write(file.name, file.name.split("/")[-1])
return "tmp.zip"
demo = gr.Interface(

Binary file not shown.

@ -15,8 +15,10 @@ from io import BytesIO
from pathlib import Path
from typing import Dict, Set, Tuple
import aiofiles
import numpy as np
import requests
from fastapi import UploadFile
from ffmpy import FFmpeg, FFprobe, FFRuntimeError
from PIL import Image, ImageOps, PngImagePlugin
@ -403,6 +405,19 @@ class TempFileManager:
self.temp_files.add(full_temp_file_path)
return full_temp_file_path
async def save_uploaded_file(self, file: UploadFile, upload_dir: str) -> str:
prefix, extension = self.get_prefix_and_extension(file.filename)
output_file_obj = tempfile.NamedTemporaryFile(
delete=False, dir=upload_dir, suffix=f"{extension}", prefix=f"{prefix}_"
)
async with aiofiles.open(output_file_obj.name, "wb") as output_file:
while True:
content = await file.read(100 * 1024 * 1024)
if not content:
break
await output_file.write(content)
return str(utils.abspath(output_file_obj.name))
def download_temp_copy_if_needed(self, url: str) -> str:
"""Downloads a file and makes a temporary file path for a copy if does not already
exist. Otherwise returns the path to the existing temp file."""

@ -10,6 +10,7 @@ import mimetypes
import os
import posixpath
import secrets
import tempfile
import traceback
from collections import defaultdict
from copy import deepcopy
@ -21,7 +22,7 @@ import httpx
import markupsafe
import orjson
import pkg_resources
from fastapi import Depends, FastAPI, HTTPException, WebSocket, status
from fastapi import Depends, FastAPI, File, HTTPException, UploadFile, WebSocket, status
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import (
FileResponse,
@ -43,6 +44,7 @@ from gradio.context import Context
from gradio.data_classes import PredictBody, ResetBody
from gradio.documentation import document, set_documentation_group
from gradio.exceptions import Error
from gradio.processing_utils import TempFileManager
from gradio.queueing import Estimation, Event
from gradio.utils import cancel_tasks, run_coro_in_background, set_task_name
@ -109,6 +111,7 @@ class App(FastAPI):
self.lock = asyncio.Lock()
self.queue_token = secrets.token_urlsafe(32)
self.startup_events_triggered = False
self.uploaded_file_dir = str(utils.abspath(tempfile.mkdtemp()))
super().__init__(**kwargs)
def configure_app(self, blocks: gradio.Blocks) -> None:
@ -297,17 +300,19 @@ class App(FastAPI):
)
abs_path = str(utils.abspath(path_or_url))
in_app_dir = utils.abspath(app.cwd) in utils.abspath(path_or_url).parents
created_by_app = str(utils.abspath(path_or_url)) in set().union(
*blocks.temp_file_sets
)
created_by_app = abs_path in set().union(*blocks.temp_file_sets)
in_file_dir = any(
(
utils.abspath(dir) in utils.abspath(path_or_url).parents
for dir in blocks.file_directories
)
)
was_uploaded = (
utils.abspath(app.uploaded_file_dir)
in utils.abspath(path_or_url).parents
)
if in_app_dir or created_by_app or in_file_dir:
if in_app_dir or created_by_app or in_file_dir or was_uploaded:
range_val = request.headers.get("Range", "").strip()
if range_val.startswith("bytes=") and "-" in range_val:
range_val = range_val[6:]
@ -514,6 +519,20 @@ class App(FastAPI):
async def get_queue_status():
return app.get_blocks()._queue.get_estimation()
@app.post("/upload", dependencies=[Depends(login_check)])
async def upload_file(
files: List[UploadFile] = File(...),
):
output_files = []
file_manager = TempFileManager()
for input_file in files:
output_files.append(
await file_manager.save_uploaded_file(
input_file, app.uploaded_file_dir
)
)
return output_files
@app.on_event("startup")
@app.get("/startup-events")
async def startup_events():

@ -0,0 +1 @@
abcdefghijklmnopqrstuvwxyz

@ -54,6 +54,17 @@ class TestRoutes:
response = test_client.get("/config/")
assert response.status_code == 200
def test_upload_route(self, test_client):
response = test_client.post(
"/upload", files={"files": open("test/test_files/alphabet.txt", "r")}
)
assert response.status_code == 200
file = response.json()[0]
assert "alphabet" in file
assert file.endswith(".txt")
with open(file) as saved_file:
assert saved_file.read() == "abcdefghijklmnopqrstuvwxyz"
def test_predict_route(self, test_client):
response = test_client.post(
"/api/predict/", json={"data": ["test"], "fn_index": 0}

@ -36,6 +36,10 @@ interface PostResponse {
error?: string;
[x: string]: any;
}
export interface UploadResponse {
error?: string;
files?: Array<string>;
}
const QUEUE_FULL_MSG = "This application is too busy. Keep trying!";
const BROKEN_CONNECTION_MSG = "Connection errored out.";
@ -55,6 +59,27 @@ export async function post_data(
const output: PostResponse = await response.json();
return [output, response.status];
}
export async function upload_files(
root: string,
files: Array<File>
): Promise<UploadResponse> {
const formData = new FormData();
files.forEach((file) => {
formData.append("files", file);
});
try {
var response = await fetch(`${root}upload`, {
method: "POST",
body: formData
});
} catch (e) {
return { error: BROKEN_CONNECTION_MSG };
}
const output: UploadResponse["files"] = await response.json();
return { files: output };
}
interface UpdateOutput {
__type__: string;
[key: string]: unknown;

@ -1,5 +1,5 @@
<script lang="ts">
import type { FileData } from "../../File/types";
import type { FileData } from "@gradio/upload";
export let value: FileData;
export let type: "gallery" | "table";

@ -1,10 +1,11 @@
<script lang="ts">
import { createEventDispatcher } from "svelte";
import { File, FileUpload } from "@gradio/file";
import type { FileData } from "@gradio/upload";
import { File as FileComponent, FileUpload } from "@gradio/file";
import { blobToBase64, FileData } from "@gradio/upload";
import { normalise_file } from "@gradio/upload";
import { Block } from "@gradio/atoms";
import UploadText from "../UploadText.svelte";
import { upload_files } from "../../api";
import StatusTracker from "../StatusTracker/StatusTracker.svelte";
import type { LoadingStatus } from "../StatusTracker/types";
@ -26,20 +27,56 @@
export let loading_status: LoadingStatus;
let _value: null | FileData | Array<FileData>;
$: _value = normalise_file(value, root, root_url);
let dragging = false;
let pending_upload = false;
const dispatch = createEventDispatcher<{
change: undefined;
error: string;
}>();
$: {
if (value !== old_value) {
old_value = value;
if (_value !== old_value) {
old_value = _value;
if (_value === null) {
dispatch("change");
pending_upload = false;
} else if (mode === "dynamic") {
let files = (Array.isArray(_value) ? _value : [_value]).map(
(file_data) => file_data.blob!
);
let upload_value = _value;
pending_upload = true;
upload_files(root, files).then((response) => {
if (upload_value !== _value) {
// value has changed since upload started
return;
}
dispatch("change");
pending_upload = false;
if (response.error) {
(Array.isArray(_value) ? _value : [_value]).forEach(
async (file_data, i) => {
file_data.data = await blobToBase64(file_data.blob!);
}
);
} else {
(Array.isArray(_value) ? _value : [_value]).forEach(
(file_data, i) => {
if (response.files) {
file_data.orig_name = file_data.name;
file_data.name = response.files[i];
file_data.is_file = true;
}
}
);
_value = normalise_file(value, root, root_url);
}
dispatch("change");
});
}
}
}
</script>
@ -51,7 +88,12 @@
padding={false}
{elem_id}
>
<StatusTracker {...loading_status} />
<StatusTracker
{...loading_status}
status={pending_upload
? "generating"
: loading_status?.status || "complete"}
/>
{#if mode === "dynamic"}
<FileUpload
@ -68,6 +110,6 @@
<UploadText type="file" />
</FileUpload>
{:else}
<File value={_value} {label} {show_label} />
<FileComponent value={_value} {label} {show_label} />
{/if}
</Block>

@ -1,6 +0,0 @@
export interface FileData {
name: string;
size: number;
data: string;
is_example: false;
}

@ -3,21 +3,34 @@
import type { Styles } from "@gradio/utils";
import type { FileData } from "@gradio/upload";
import { UploadButton } from "@gradio/upload-button";
import { upload_files } from "../../api";
import { blobToBase64 } from "@gradio/upload";
import { _ } from "svelte-i18n";
export let style: Styles = {};
export let elem_id: string = "";
export let visible: boolean = true;
export let label: string;
export let value: null | FileData | Array<FileData>;
export let value: null | FileData;
export let file_count: string;
export let file_types: Array<string> = ["file"];
export let root: string;
async function handle_upload({ detail }: CustomEvent<FileData>) {
value = detail;
console.log(detail);
await tick();
dispatch("change", value);
dispatch("upload", detail);
upload_files(root, [detail.blob!]).then(async (response) => {
if (response.error) {
detail.data = await blobToBase64(detail.blob!);
} else {
detail.orig_name = detail.name;
detail.name = response.files![0];
detail.is_file = true;
}
dispatch("change", value);
dispatch("upload", detail);
});
}
const dispatch = createEventDispatcher<{

@ -21,6 +21,6 @@
<style>
.file-preview {
overflow-x: scroll;
overflow-x: auto;
}
</style>

@ -2,11 +2,7 @@
import type { FileData } from "@gradio/upload";
import { Download } from "@gradio/icons";
import { IconButton } from "@gradio/atoms";
import {
display_file_name,
download_files,
display_file_size
} from "./utils";
import { display_file_name, display_file_size } from "./utils";
export let value: FileData | FileData[];
</script>
@ -24,13 +20,19 @@
</td>
<td class="download">
<a
href={download_files(file)}
target={window.__is_colab__ ? "_blank" : null}
download={window.__is_colab__ ? null : file.orig_name || file.name}
>
Download
</a>
{#if file.data}
<a
href={file.data}
target="_blank"
download={window.__is_colab__
? null
: file.orig_name || file.name}
>
Download
</a>
{:else}
Uploading...
{/if}
</td>
</tr>
{/each}
@ -52,7 +54,7 @@
margin-bottom: var(--size-7);
width: var(--size-full);
max-height: var(--size-60);
overflow-y: scroll;
overflow-y: auto;
color: var(--color-text-body);
}
.file {

@ -14,7 +14,9 @@
export let file_count: string = "single";
export let file_types: string[] | null = null;
async function handle_upload({ detail }: CustomEvent<FileData>) {
async function handle_upload({
detail
}: CustomEvent<FileData | Array<FileData>>) {
value = detail;
await tick();
dispatch("change", value);
@ -28,10 +30,10 @@
}
const dispatch = createEventDispatcher<{
change: FileData | null;
change: Array<FileData> | FileData | null;
clear: undefined;
drag: boolean;
upload: FileData;
upload: Array<FileData> | FileData;
error: string;
}>();
@ -55,16 +57,17 @@
<BlockLabel {show_label} Icon={File} label={label || "File"} />
{#if value === null}
{#if value}
<ModifyUpload on:clear={handle_clear} absolute />
<FilePreview {value} />
{:else}
<Upload
on:load={handle_upload}
filetype={accept_file_types}
parse_to_data_url={false}
{file_count}
bind:dragging
>
<slot />
</Upload>
{:else}
<ModifyUpload on:clear={handle_clear} absolute />
<FilePreview {value} />
{/if}

@ -19,10 +19,6 @@ export const display_file_name = (value: FileData): string => {
} else return str;
};
export const download_files = (value: FileData): string => {
return value.data;
};
export const display_file_size = (
value: FileData | Array<FileData>
): string => {

@ -2,7 +2,6 @@
import type { FileData } from "@gradio/upload";
import { BlockLabel, IconButton } from "@gradio/atoms";
import { File, Download } from "@gradio/icons";
import { download_files } from "./utils";
export let value: FileData | null;
export let clearColor: Array<number> = [0, 0, 0, 0];
@ -81,7 +80,7 @@
<div class="model3D">
<div class="download">
<a
href={download_files(value)}
href={value.data}
target={window.__is_colab__ ? "_blank" : null}
download={window.__is_colab__ ? null : value.orig_name || value.name}
>

@ -19,10 +19,6 @@ export const display_file_name = (value: FileData): string => {
} else return str;
};
export const download_files = (value: FileData): string => {
return value.data;
};
export const display_file_size = (
value: FileData | Array<FileData>
): string => {

@ -9,6 +9,7 @@
"private": true,
"dependencies": {
"@gradio/button": "workspace:^0.0.1",
"@gradio/utils": "workspace:^0.0.1"
"@gradio/utils": "workspace:^0.0.1",
"@gradio/upload": "workspace:^0.0.1"
}
}

@ -2,7 +2,7 @@
import { Button } from "@gradio/button";
import type { Styles } from "@gradio/utils";
import { createEventDispatcher } from "svelte";
import type { FileData } from "./types";
import type { FileData } from "@gradio/upload";
import { type } from "@testing-library/user-event/dist/type";
export let style: Styles = {};
@ -35,33 +35,30 @@
const loadFiles = (files: FileList) => {
let _files: Array<File> = Array.from(files);
if (!files.length || !window.FileReader) {
if (!files.length) {
return;
}
if (file_count === "single") {
_files = [files[0]];
}
var all_file_data: Array<FileData | string> = [];
var all_file_data: Array<FileData | File> = [];
_files.forEach((f, i) => {
let ReaderObj = new FileReader();
ReaderObj.readAsDataURL(f);
ReaderObj.onloadend = function () {
all_file_data[i] = include_file_metadata
? {
name: f.name,
size: f.size,
data: this.result as string
}
: (this.result as string);
if (
all_file_data.filter((x) => x !== undefined).length === files.length
) {
dispatch(
"load",
file_count == "single" ? all_file_data[0] : all_file_data
);
}
};
all_file_data[i] = include_file_metadata
? {
name: f.name,
size: f.size,
data: "",
blob: f
}
: f;
if (
all_file_data.filter((x) => x !== undefined).length === files.length
) {
dispatch(
"load",
file_count == "single" ? all_file_data[0] : all_file_data
);
}
});
};

@ -1,7 +0,0 @@
export interface FileData {
name: string;
orig_name?: string;
size?: number;
data: string;
is_file?: boolean;
}

@ -1,6 +1,7 @@
<script lang="ts">
import { createEventDispatcher } from "svelte";
import type { FileData } from "./types";
import { blobToBase64 } from "./utils";
export let filetype: string | null = null;
export let include_file_metadata = true;
@ -10,6 +11,7 @@
export let flex: boolean = true;
export let file_count: string = "single";
export let disable_click = false;
export let parse_to_data_url = true;
let hidden_upload: HTMLInputElement;
@ -25,7 +27,7 @@
hidden_upload.click();
};
const loadFiles = (files: FileList) => {
const loadFiles = async (files: FileList) => {
let _files: Array<File> = Array.from(files);
if (!files.length || !window.FileReader) {
return;
@ -33,41 +35,55 @@
if (file_count === "single") {
_files = [files[0]];
}
var all_file_data: Array<FileData | string> = [];
_files.forEach((f, i) => {
let ReaderObj = new FileReader();
ReaderObj.readAsDataURL(f);
ReaderObj.onloadend = function () {
all_file_data[i] = include_file_metadata
? {
name: f.name,
size: f.size,
data: this.result as string
}
: (this.result as string);
if (
all_file_data.filter((x) => x !== undefined).length === files.length
) {
dispatch(
"load",
file_count == "single" ? all_file_data[0] : all_file_data
);
}
};
});
if (include_file_metadata) {
var file_metadata: Array<{ name: string; size: number }> = _files.map(
(f) => ({
name: f.name,
size: f.size
})
);
}
var load_file_data = [];
var file_data: Array<string> | Array<File> = [];
if (parse_to_data_url) {
file_data = await Promise.all(_files.map((f) => blobToBase64(f)));
} else {
file_data = _files;
}
if (include_file_metadata) {
if (parse_to_data_url) {
load_file_data = file_data.map((data, i) => ({
data,
...file_metadata[i]
}));
} else {
load_file_data = file_data.map((data, i) => ({
data: "",
blob: data,
...file_metadata[i]
}));
}
} else {
load_file_data = file_data;
}
dispatch(
"load",
file_count === "single" ? load_file_data[0] : load_file_data
);
};
const loadFilesFromUpload = (e: Event) => {
const loadFilesFromUpload = async (e: Event) => {
const target = e.target as HTMLInputElement;
if (!target.files) return;
loadFiles(target.files);
await loadFiles(target.files);
};
const loadFilesFromDrop = (e: DragEvent) => {
const loadFilesFromDrop = async (e: DragEvent) => {
dragging = false;
if (!e.dataTransfer?.files) return;
loadFiles(e.dataTransfer.files);
await loadFiles(e.dataTransfer.files);
};
</script>

@ -3,5 +3,6 @@ export interface FileData {
orig_name?: string;
size?: number;
data: string;
blob?: File;
is_file?: boolean;
}

@ -35,3 +35,13 @@ export function normalise_file(
}
return file;
}
export const blobToBase64 = (blob: File): Promise<string> => {
const reader = new FileReader();
reader.readAsDataURL(blob);
return new Promise((resolve) => {
reader.onloadend = () => {
resolve(reader.result as string);
};
});
};

107
ui/pnpm-lock.yaml generated

@ -1,4 +1,4 @@
lockfileVersion: 5.4
lockfileVersion: 5.3
importers:
@ -48,7 +48,7 @@ importers:
'@tailwindcss/forms': 0.5.0_tailwindcss@3.1.6
'@testing-library/dom': 8.11.3
'@testing-library/svelte': 3.1.0_svelte@3.49.0
'@testing-library/user-event': 13.5.0_gzufz4q333be4gqfrvipwvqt6a
'@testing-library/user-event': 13.5.0_@testing-library+dom@8.11.3
autoprefixer: 10.4.4_postcss@8.4.6
babylonjs: 5.18.0
babylonjs-loaders: 5.18.0
@ -65,15 +65,15 @@ importers:
postcss-nested: 5.0.6_postcss@8.4.6
postcss-prefix-selector: 1.16.0_postcss@8.4.6
prettier: 2.6.2
prettier-plugin-css-order: 1.3.0_ob5okuz2s5mlecytbeo2erc43a
prettier-plugin-svelte: 2.7.0_3cyj5wbackxvw67rnaarcmbw7y
prettier-plugin-css-order: 1.3.0_postcss@8.4.6+prettier@2.6.2
prettier-plugin-svelte: 2.7.0_prettier@2.6.2+svelte@3.49.0
sirv: 2.0.2
sirv-cli: 2.0.2
svelte: 3.49.0
svelte-check: 2.8.0_mgmdnb6x5rpawk37gozc2sbtta
svelte-check: 2.8.0_postcss@8.4.6+svelte@3.49.0
svelte-i18n: 3.3.13_svelte@3.49.0
svelte-preprocess: 4.10.6_mlkquajfpxs65rn6bdfntu7nmy
tailwindcss: 3.1.6_postcss@8.4.6
svelte-preprocess: 4.10.6_62d50a01257de5eec5be08cad9d3ed66
tailwindcss: 3.1.6
tinyspy: 0.3.0
typescript: 4.7.4
vite: 2.9.9
@ -373,9 +373,11 @@ importers:
packages/upload-button:
specifiers:
'@gradio/button': workspace:^0.0.1
'@gradio/upload': workspace:^0.0.1
'@gradio/utils': workspace:^0.0.1
dependencies:
'@gradio/button': link:../button
'@gradio/upload': link:../upload
'@gradio/utils': link:../utils
packages/utils:
@ -725,7 +727,7 @@ packages:
tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1'
dependencies:
mini-svg-data-uri: 1.4.4
tailwindcss: 3.1.6_postcss@8.4.6
tailwindcss: 3.1.6
dev: false
/@testing-library/dom/7.31.2:
@ -766,7 +768,7 @@ packages:
svelte: 3.49.0
dev: false
/@testing-library/user-event/13.5.0_gzufz4q333be4gqfrvipwvqt6a:
/@testing-library/user-event/13.5.0_@testing-library+dom@8.11.3:
resolution: {integrity: sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==}
engines: {node: '>=10', npm: '>=6'}
peerDependencies:
@ -2981,19 +2983,6 @@ packages:
postcss-value-parser: 4.2.0
read-cache: 1.0.0
resolve: 1.22.1
dev: true
/postcss-import/14.1.0_postcss@8.4.6:
resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==}
engines: {node: '>=10.0.0'}
peerDependencies:
postcss: ^8.0.0
dependencies:
postcss: 8.4.6
postcss-value-parser: 4.2.0
read-cache: 1.0.0
resolve: 1.22.1
dev: false
/postcss-js/4.0.0_postcss@8.4.21:
resolution: {integrity: sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==}
@ -3003,17 +2992,6 @@ packages:
dependencies:
camelcase-css: 2.0.1
postcss: 8.4.21
dev: true
/postcss-js/4.0.0_postcss@8.4.6:
resolution: {integrity: sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==}
engines: {node: ^12 || ^14 || >= 16}
peerDependencies:
postcss: ^8.3.3
dependencies:
camelcase-css: 2.0.1
postcss: 8.4.6
dev: false
/postcss-less/6.0.0_postcss@8.4.6:
resolution: {integrity: sha512-FPX16mQLyEjLzEuuJtxA8X3ejDLNGGEG503d2YGZR5Ask1SpDN8KmZUMpzCvyalWRywAn1n1VOA5dcqfCLo5rg==}
@ -3052,24 +3030,6 @@ packages:
lilconfig: 2.0.6
postcss: 8.4.21
yaml: 1.10.2
dev: true
/postcss-load-config/3.1.4_postcss@8.4.6:
resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==}
engines: {node: '>= 10'}
peerDependencies:
postcss: '>=8.0.9'
ts-node: '>=9.0.0'
peerDependenciesMeta:
postcss:
optional: true
ts-node:
optional: true
dependencies:
lilconfig: 2.0.6
postcss: 8.4.6
yaml: 1.10.2
dev: false
/postcss-nested/5.0.6_postcss@8.4.21:
resolution: {integrity: sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==}
@ -3079,7 +3039,6 @@ packages:
dependencies:
postcss: 8.4.21
postcss-selector-parser: 6.0.9
dev: true
/postcss-nested/5.0.6_postcss@8.4.6:
resolution: {integrity: sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==}
@ -3157,7 +3116,7 @@ packages:
picocolors: 1.0.0
source-map-js: 1.0.2
/prettier-plugin-css-order/1.3.0_ob5okuz2s5mlecytbeo2erc43a:
/prettier-plugin-css-order/1.3.0_postcss@8.4.6+prettier@2.6.2:
resolution: {integrity: sha512-wOS4qlbUARCoiiuOG0TiB/j751soC3+gUnMMva5HVWKvHJdLNYqh+jXK3MvvixR6xkJVPxHSF7rIIhkHIuHTFg==}
engines: {node: '>=14'}
peerDependencies:
@ -3172,7 +3131,7 @@ packages:
- postcss
dev: false
/prettier-plugin-svelte/2.7.0_3cyj5wbackxvw67rnaarcmbw7y:
/prettier-plugin-svelte/2.7.0_prettier@2.6.2+svelte@3.49.0:
resolution: {integrity: sha512-fQhhZICprZot2IqEyoiUYLTRdumULGRvw0o4dzl5jt0jfzVWdGqeYW27QTWAeXhoupEZJULmNoH3ueJwUWFLIA==}
peerDependencies:
prettier: ^1.16.4 || ^2.0.0
@ -3659,7 +3618,7 @@ packages:
- sugarss
dev: true
/svelte-check/2.8.0_mgmdnb6x5rpawk37gozc2sbtta:
/svelte-check/2.8.0_postcss@8.4.6+svelte@3.49.0:
resolution: {integrity: sha512-HRL66BxffMAZusqe5I5k26mRWQ+BobGd9Rxm3onh7ZVu0nTk8YTKJ9vu3LVPjUGLU9IX7zS+jmwPVhJYdXJ8vg==}
hasBin: true
peerDependencies:
@ -3672,7 +3631,7 @@ packages:
picocolors: 1.0.0
sade: 1.8.1
svelte: 3.49.0
svelte-preprocess: 4.10.6_mlkquajfpxs65rn6bdfntu7nmy
svelte-preprocess: 4.10.6_62d50a01257de5eec5be08cad9d3ed66
typescript: 4.7.4
transitivePeerDependencies:
- '@babel/core'
@ -3763,7 +3722,7 @@ packages:
typescript: 4.5.5
dev: true
/svelte-preprocess/4.10.6_mlkquajfpxs65rn6bdfntu7nmy:
/svelte-preprocess/4.10.6_62d50a01257de5eec5be08cad9d3ed66:
resolution: {integrity: sha512-I2SV1w/AveMvgIQlUF/ZOO3PYVnhxfcpNyGt8pxpUVhPfyfL/CZBkkw/KPfuFix5FJ9TnnNYMhACK3DtSaYVVQ==}
engines: {node: '>= 9.11.2'}
requiresBuild: true
@ -3883,40 +3842,6 @@ packages:
resolve: 1.22.1
transitivePeerDependencies:
- ts-node
dev: true
/tailwindcss/3.1.6_postcss@8.4.6:
resolution: {integrity: sha512-7skAOY56erZAFQssT1xkpk+kWt2NrO45kORlxFPXUt3CiGsVPhH1smuH5XoDH6sGPXLyBv+zgCKA2HWBsgCytg==}
engines: {node: '>=12.13.0'}
hasBin: true
peerDependencies:
postcss: ^8.0.9
dependencies:
arg: 5.0.2
chokidar: 3.5.3
color-name: 1.1.4
detective: 5.2.1
didyoumean: 1.2.2
dlv: 1.1.3
fast-glob: 3.2.11
glob-parent: 6.0.2
is-glob: 4.0.3
lilconfig: 2.0.6
normalize-path: 3.0.0
object-hash: 3.0.0
picocolors: 1.0.0
postcss: 8.4.6
postcss-import: 14.1.0_postcss@8.4.6
postcss-js: 4.0.0_postcss@8.4.6
postcss-load-config: 3.1.4_postcss@8.4.6
postcss-nested: 5.0.6_postcss@8.4.6
postcss-selector-parser: 6.0.10
postcss-value-parser: 4.2.0
quick-lru: 5.1.1
resolve: 1.22.1
transitivePeerDependencies:
- ts-node
dev: false
/then-request/6.0.2:
resolution: {integrity: sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==}