ali components

This commit is contained in:
Ali Abid 2022-01-10 21:22:44 +00:00
parent a3dd2e5710
commit 8a3e08defa
24 changed files with 642 additions and 4 deletions

View File

@ -74,7 +74,6 @@ iface = gr.Interface(
examples=[
["the quick brown fox", "jumps over the lazy dog", 10, 12, 4, True, ["foo", "baz"], "baz", "bar", "files/cheetah1.jpg", "files/cheetah1.jpg", "files/cheetah1.jpg", "files/cheetah1.jpg", "files/world.mp4", "files/cantina.wav", "files/cantina.wav","files/titanic.csv", [[1,2,3],[3,4,5]], "files/time.csv"]
] * 3,
theme="huggingface",
title="Kitchen Sink",
description="Try out all the components!",
article="Learn more about [Gradio](http://gradio.app)"

View File

@ -5,7 +5,7 @@ def reverse_audio(audio):
sr, data = audio
return (sr, np.flipud(data))
iface = gr.Interface(reverse_audio, "microphone", "audio", examples="audio")
iface = gr.Interface(reverse_audio, "audio", "audio", examples="audio")
if __name__ == "__main__":
iface.launch()

BIN
demo/zip_two_files/tmp.zip Normal file

Binary file not shown.

View File

@ -10,6 +10,7 @@
"dependencies": {
"@rollup/plugin-replace": "^3.0.1",
"autoprefixer": "^9.8.8",
"mime-types": "^2.1.34",
"node-sass": "^7.0.1",
"sirv-cli": "^1.0.0",
"svelte-preprocess": "^4.10.1",
@ -17,6 +18,7 @@
},
"devDependencies": {
"@rollup/plugin-commonjs": "^17.0.0",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^11.0.0",
"postcss": "^8.4.5",
"postcss-nested": "^5.0.6",
@ -158,6 +160,18 @@
"rollup": "^2.30.0"
}
},
"node_modules/@rollup/plugin-json": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-4.1.0.tgz",
"integrity": "sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw==",
"dev": true,
"dependencies": {
"@rollup/pluginutils": "^3.0.8"
},
"peerDependencies": {
"rollup": "^1.20.0 || ^2.0.0"
}
},
"node_modules/@rollup/plugin-node-resolve": {
"version": "11.2.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz",
@ -4363,6 +4377,15 @@
"resolve": "^1.17.0"
}
},
"@rollup/plugin-json": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-4.1.0.tgz",
"integrity": "sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw==",
"dev": true,
"requires": {
"@rollup/pluginutils": "^3.0.8"
}
},
"@rollup/plugin-node-resolve": {
"version": "11.2.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz",

View File

@ -9,6 +9,7 @@
},
"devDependencies": {
"@rollup/plugin-commonjs": "^17.0.0",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^11.0.0",
"postcss": "^8.4.5",
"postcss-nested": "^5.0.6",
@ -17,11 +18,13 @@
"rollup-plugin-livereload": "^2.0.0",
"rollup-plugin-svelte": "^7.0.0",
"rollup-plugin-terser": "^7.0.0",
"mime-types": "^2.1.34",
"svelte": "^3.0.0"
},
"dependencies": {
"@rollup/plugin-replace": "^3.0.1",
"autoprefixer": "^9.8.8",
"mime-types": "^2.1.34",
"node-sass": "^7.0.1",
"sirv-cli": "^1.0.0",
"svelte-preprocess": "^4.10.1",

View File

@ -6,6 +6,7 @@ import livereload from 'rollup-plugin-livereload';
import { terser } from 'rollup-plugin-terser';
import css from 'rollup-plugin-css-only';
import replace from '@rollup/plugin-replace';
import json from "@rollup/plugin-json";
const production = !process.env.ROLLUP_WATCH;
@ -39,6 +40,7 @@ export default {
file: 'public/build/bundle.js'
},
plugins: [
json(),
replace({
process: JSON.stringify({
env: {

View File

@ -19,10 +19,10 @@
class="gradio-page container mx-auto flex flex-col box-border flex-grow text-gray-700 dark:text-gray-50"
>
<div class="content pt-4 px-4 mb-4">
{#if title !== undefined}
{#if title}
<h1 class="title text-center p-4 text-4xl">{title}</h1>
{/if}
{#if description !== undefined}
{#if description}
<p class="description pb-4">{description}</p>
{/if}
<Interface {input_components} {output_components} {theme} {fn} />

View File

@ -0,0 +1,9 @@
<script>
export let value, setValue, theme;
export let choices;
</script>
<div class="dummy" {theme}>DUMMY</div>
<style lang="postcss">
</style>

View File

@ -1,11 +1,49 @@
import InputNumber from "./input/Number.svelte";
import InputRadio from "./input/Radio.svelte";
import InputAudio from "./input/Audio.svelte";
import InputFile from "./input/File.svelte";
import InputImage from "./input/Image.svelte";
import InputVideo from "./input/Video.svelte";
import InputDropdown from "./input/Dropdown.svelte";
import OutputTextbox from "./output/Textbox.svelte";
import OutputImage from "./output/Image.svelte";
import OutputVideo from "./output/Video.svelte";
import OutputAudio from "./output/Audio.svelte";
import OutputFile from "./output/File.svelte";
import OutputJson from "./output/Json.svelte";
import OutputHtml from "./output/Html.svelte";
import OutputDataframe from "./output/Dataframe.svelte";
import Dummy from "./Dummy.svelte"
export const inputComponentMap = {
"audio": InputAudio,
"checkbox": Dummy,
"checkboxgroup": Dummy,
"dataframe": Dummy,
"dropdown": InputDropdown,
"file": InputFile,
"image": InputImage,
"number": InputNumber,
"radio": InputRadio,
"slider": Dummy,
"textbox": Dummy,
"timeseries": Dummy,
"video": InputVideo,
}
export const outputComponentMap = {
"audio": OutputAudio,
"carousel": Dummy,
"dataframe": OutputDataframe,
"file": OutputFile,
"highlightedtext": Dummy,
"html": OutputHtml,
"image": OutputImage,
"json": OutputJson,
"label": Dummy,
"textbox": OutputTextbox,
"timeseries": Dummy,
"video": OutputVideo,
}

View File

@ -0,0 +1,35 @@
<script>
import Upload from "../utils/Upload.svelte";
export let value, setValue, theme;
export let source;
let recording = false;
const startRecording = () => {};
const stopRecording = () => {};
</script>
<div class="input-audio">
{#if value === null}
{#if source === "microphone"}
{#if recording}
<button class="stop" onClick={stopRecording}> Recording... </button>
{:else}
<button class="start" onClick={startRecording}> Record </button>
{/if}
{:else if source === "upload"}
<Upload filetype="audio/*" load={setValue} {theme}>
Drop Audio Here
<br />- or -<br />
Click to Upload
</Upload>
{/if}
{:else}
<audio controls>
<source src={value.data} />
</audio>
{/if}
</div>
<style lang="postcss">
</style>

View File

@ -0,0 +1,46 @@
<script>
export let value, setValue, theme;
export let choices;
</script>
<div class="input-dropdown group inline-block relative" {theme}>
<button
class="selector py-2 px-3 font-semibold rounded inline-flex items-center"
>
{value}
<svg class="caret ml-2 fill-current h-4 w-4" viewBox="0 0 20 20">
<path
d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z"
/>
</svg>
</button>
<div
class="dropdown-menu-holder absolute hidden group-hover:block pt-1 z-10 bg-none"
>
<ul class="dropdown-menu max-h-80 overflow-y-auto">
{#each choices as choice, i}
<li
class="dropdown-item first:rounded-t transition last:rounded-b py-2 px-3 block whitespace-nowrap cursor-pointer"
on:click={() => setValue(choice)}
key={i}
>
{choice}
</li>
{/each}
</ul>
</div>
</div>
<style lang="postcss">
.input-dropdown[theme="default"] {
.selector {
@apply bg-white dark:bg-gray-800 shadow transition hover:shadow-md;
}
.dropdown-menu {
@apply shadow;
}
.dropdown-item {
@apply bg-white dark:bg-gray-800 hover:bg-yellow-500 dark:hover:bg-red-600 hover:text-gray-50 hover:font-semibold;
}
}
</style>

View File

@ -0,0 +1,29 @@
<script>
import Upload from "../utils/Upload.svelte";
import { prettyBytes } from "../utils/helpers";
export let value, setValue, theme;
</script>
<div class="input-file" {theme}>
{#if value === null}
<Upload load={setValue} {theme}>
Drop File Here
<br />- or -<br />
Click to Upload
</Upload>
{:else}
<div class="file-preview w-full flex flex-col justify-center items-center relative">
<div class="file-name text-4xl p-6 break-all">{value.name}</div>
<div class="file-size text-2xl p-2">
{prettyBytes(value.size)}
</div>
</div>
{/if}
</div>
<style lang="postcss">
.input-file[theme="default"] .file-preview {
@apply h-60;
}
</style>

View File

@ -0,0 +1,30 @@
<script>
import Upload from "../utils/Upload.svelte";
export let value, setValue, theme;
export let source;
</script>
<div class="input-image">
{#if value === null}
{#if source === "upload"}
<Upload
filetype="image/x-png,image/gif,image/jpeg"
load={setValue}
include_file_metadata={false}
{theme}
>
Drop Image Here
<br />- or -<br />
Click to Upload
</Upload>
{/if}
{:else}
<div class="image-preview w-full h-60 flex justify-center items-center bg-gray-200 dark:bg-gray-600 relative">
<img class="w-full h-full object-contain" src={value} />
</div>
{/if}
</div>
<style lang="postcss">
</style>

View File

@ -0,0 +1,45 @@
<script>
import Upload from "../utils/Upload.svelte";
import { prettyBytes, playable } from "../utils/helpers";
export let value, setValue, theme;
export let source;
</script>
<div class="input-video">
{#if value === null}
{#if source === "upload"}
<Upload filetype="video/mp4,video/x-m4v,video/*" load={setValue} {theme}>
Drop Video Here
<br />- or -<br />
Click to Upload
</Upload>
{/if}
{:else}
<div
class="video-preview w-full h-60 flex justify-center items-center bg-gray-200 dark:bg-gray-600 relative"
>
{#if playable(value.name)}
<video
class="video_preview"
controls
playsInline
preload
src={value.data}
/>
{:else}
<div class="file-preview h-60 w-full flex flex-col justify-center items-center relative">
<div class="file-name text-4xl p-6 break-all">{value.name}</div>
<div class="file-size text-2xl p-2">
{prettyBytes(value.size)}
</div>
</div>
{/if}
</div>
{/if}
</div>
<style lang="postcss">
</style>

View File

@ -0,0 +1,10 @@
<script>
export let value, theme;
</script>
<audio {theme} controls>
<source src={value} />
</audio>
<style lang="postcss">
</style>

View File

@ -0,0 +1,112 @@
<script>
export let value, theme;
export let headers, max_rows, max_cols, overflow_row_behaviour;
let page = 0;
let sort_by = null;
let sort_descending = false;
let table_headers = headers || value.headers;
let row_count = value.data.length;
let col_count = row_count > 0 ? value.data[0].length : 0;
let selected_data = value.data.slice();
if (sort_by) {
let sort_fn;
if (sort_descending) {
sort_fn = (a, b) =>
a[sort_by] === b[sort_by] ? 0 : a[sort_by] < b[sort_by] ? 1 : -1;
} else {
sort_fn = (a, b) =>
a[sort_by] === b[sort_by] ? 0 : a[sort_by] < b[sort_by] ? -1 : 1;
}
selected_data.sort(sort_fn);
}
let visible_pages = null;
if (max_rows !== null && row_count > max_rows) {
if (overflow_row_behaviour === "paginate") {
selected_data = selected_data.slice(page * max_rows, page + 1 * max_rows);
let page_count = Math.ceil(row_count / max_rows);
visible_pages = [];
[0, page, page_count - 1].forEach((anchor) => {
for (let i = anchor - 2; i <= anchor + 2; i++) {
if (i >= 0 && i < page_count && !visible_pages.includes(i)) {
if (
visible_pages.length > 0 &&
i - visible_pages[visible_pages.length - 1] > 1
) {
visible_pages.push(null);
}
visible_pages.push(i);
}
}
});
} else {
selected_data = selected_data
.slice(0, Math.ceil(max_rows / 2))
.concat(
[Array(col_count).fill("...")],
selected_data.slice(row_count - Math.floor(max_rows / 2))
);
}
}
if (max_cols !== null && col_count > max_cols) {
let [hidden_col_start, hidden_col_end] = [
Math.ceil(max_cols / 2),
col_count - Math.floor(max_cols / 2) - 1,
];
table_headers =
table_headers.slice(0, hidden_col_start) +
["..."] +
table_headers.slice(hidden_col_end);
selected_data = selected_data.map(
(row) =>
row.slice(0, hidden_col_start) + ["..."] + row.slice(hidden_col_end)
);
}
const sort_table = (col_index) => {
if (sort_by === col_index) {
sort_descending = !sort_descending;
} else {
sort_by = col_index;
sort_descending = false;
}
page = 0;
};
</script>
<div class="output-dataframe" {theme}>
<table>
{#if table_headers}
<thead class="font-bold border-gray-200 border-b-2">
{#each table_headers as header, i}
<th class="px-4 transition cursor-pointer hover:bg-gray-200 dark:hover:bg-gray-600" key={i} on:click={() => sort_table(i)}>
{header}
{#if sort_by === i}
<svg
class="caret h-3 inline-block ml-1 mb-0.5 fill-current"
viewBox="0 0 20 20"
transform={sort_descending ? "scale(1, -1)" : ""}
>
<path
d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z"
/>{" "}
</svg>
{/if}
</th>
{/each}
</thead>
{/if}
<tbody>
{#each selected_data as row, i}
<tr key={i}>
{#each row as cell, j}
<td class="px-4" key={j}>{cell}</td>
{/each}
</tr>
{/each}
</tbody>
</table>
</div>
<style lang="postcss">
</style>

View File

@ -0,0 +1,23 @@
<script>
import { prettyBytes } from "../utils/helpers";
export let value, theme;
</script>
<a
class="output-file w-full h-full flex flex-col justify-center items-center relative"
href={value.data}
download={value.name}
{theme}
>
<div class="file-name text-4xl p-6 break-all">{value.name}</div>
<div class="text-2xl p-2">
{isNaN(value.size) ? "" : prettyBytes(value.size)}
</div>
</a>
<style lang="postcss">
.output-file[theme="default"] {
@apply h-60 hover:text-gray-500;
}
</style>

View File

@ -0,0 +1,10 @@
<script>
export let value, theme;
</script>
<div
class="output-html"
{theme}
>
{@html value}
</div>

View File

@ -0,0 +1,10 @@
<script>
export let value, theme;
</script>
<div class="output-image w-full h-60 flex justify-center items-center bg-gray-200 dark:bg-gray-600 relative" {theme}>
<img class="w-full h-full object-contain" src={value} />
</div>
<style lang="postcss">
</style>

View File

@ -0,0 +1,15 @@
<script>
import JsonNode from "./JsonNode.svelte";
export let value, theme;
</script>
<div class="output-json font-mono leading-relaxed" {theme}>
<JsonNode value={value} depth={0} theme={theme} />
</div>
<style lang="postcss">
.output-text[theme="default"] {
@apply shadow transition hover:shadow-md dark:bg-gray-800;
}
</style>

View File

@ -0,0 +1,78 @@
<script>
export let value, theme;
export let depth;
export let collapsed = depth > 4;
</script>
<div class="json-node inline" {theme}>
{#if value instanceof Array}
{#if collapsed}
<button
on:click={() => {
collapsed = false;
}}
>
[+{value.length} children]
</button>
{:else}
[
<div class="json-children pl-4">
{#each value as node, i}
<div class="json-item">
{i}: <svelte:self
value={node}
depth={depth + 1}
{theme}
/>,
</div>
{/each}
</div>
]
{/if}
{:else if value instanceof Object}
{#if collapsed}
<button
on:click={() => {
collapsed = false;
}}
>
&#123;+{Object.keys(value).length} children&#125;
</button>
{:else}
&#123;
<div class="json-children pl-4">
{#each Object.entries(value) as node}
<div class="json-item">
{node[0]}: <svelte:self
value={node[1]}
depth={depth + 1}
{theme}
/>,
</div>
{/each}
</div>
&#125;
{/if}
{:else if value === null}
<div class="json-item inline text-gray-500 dark:text-gray-400" item-type="null">null</div>
{:else if typeof value === "string"}
<div class="json-item inline text-green-500" item-type="string">
"{value}"
</div>
{:else if typeof value === "boolean"}
<div class="json-item inline text-red-500" item-type="boolean">
{value.toLocaleString()}
</div>
{:else if typeof value === "number"}
<div class="json-item inline text-blue-500" item-type="number">
{value}
</div>
{:else}
<div class="json-item inline" item-type="other">
{value}
</div>
{/if}
</div>
<style lang="postcss">
</style>

View File

@ -0,0 +1,30 @@
<script>
import { prettyBytes, playable } from "../utils/helpers";
export let value, theme;
</script>
<div
class="output-video w-full h-60 flex justify-center items-center bg-gray-200 dark:bg-gray-600 relative"
>
{#if playable(value.name)}
<video
class="video_preview"
controls
playsInline
preload
src={value.data}
/>
{:else}
<a
href={value.data}
download={value.name}
class="file-preview h-60 w-full flex flex-col justify-center items-center relative"
>
<div class="file-name text-4xl p-6 break-all">{value.name}</div>
</a>
{/if}
</div>
<style lang="postcss">
</style>

View File

@ -0,0 +1,71 @@
<script>
export let load, filetype, theme;
export let single_file = true;
export let include_file_metadata = true;
let hidden_upload;
const openFileUpload = () => {
hidden_upload.click();
};
const loadFiles = (files) => {
if (!files.length || !window.FileReader) {
return;
}
if (single_file) {
files = [files[0]];
}
var all_file_data = [];
files.forEach((file, i) => {
let ReaderObj = new FileReader();
ReaderObj.readAsDataURL(file);
ReaderObj.onloadend = function (e) {
all_file_data[i] = include_file_metadata
? {
name: file.name,
size: file.size,
data: this.result,
is_example: false,
}
: this.result;
if (Object.keys(all_file_data).length === files.length) {
load(single_file ? all_file_data[0] : all_file_data);
}
};
});
};
const loadFilesFromUpload = (e) => {
loadFiles(e.target.files);
};
const loadFilesFromDrop = (e) => {
loadFiles(e.dataTransfer.files);
};
</script>
<div
class="upload h-60 border-gray-300 text-gray-400 dark:text-gray-500 dark:border-gray-500 border-8 border-dashed w-full flex justify-center items-center text-3xl text-center cursor-pointer leading-10"
{theme}
on:drag|preventDefault|stopPropagation
on:dragstart|preventDefault|stopPropagation
on:dragend|preventDefault|stopPropagation
on:dragover|preventDefault|stopPropagation
on:dragenter|preventDefault|stopPropagation
on:dragleave|preventDefault|stopPropagation
on:drop|preventDefault|stopPropagation
on:click={openFileUpload}
on:drop={loadFilesFromDrop}
>
<slot />
<input
class="hidden-upload hidden"
type="file"
bind:this={hidden_upload}
on:change={loadFilesFromUpload}
accept={filetype}
/>
</div>
<style lang="postcss">
.upload[theme="default"] {
@apply transition hover:border-gray-400 hover:text-gray-500 dark:hover:border-gray-300 dark:hover:text-gray-300;
}
</style>

View File

@ -0,0 +1,20 @@
// import mime from "mime-types";
export const playable = (filename) => {
// let video_element = document.createElement("video");
// let mime_type = mime.lookup(filename);
// return video_element.canPlayType(mime_type) != "";
return true; // FIX BEFORE COMMIT - mime import causing issues
};
export const prettyBytes = (bytes) => {
let units = ["B", "KB", "MB", "GB", "PB"];
let i = 0;
while (bytes > 1024) {
bytes /= 1024;
i++;
}
let unit = units[i];
return bytes.toFixed(1) + " " + unit;
}