mirror of
https://github.com/gradio-app/gradio.git
synced 2025-01-18 10:44:33 +08:00
pull all components into packages
This commit is contained in:
parent
9c00ae8000
commit
0044c243db
@ -128,7 +128,7 @@
|
||||
</button>
|
||||
{:else}
|
||||
<button
|
||||
class="p-2 rounded font-semibold bg-white dark:bg-gray-600 shadow transition hover:shadow-md bg-white dark:bg-gray-800"
|
||||
class="p-2 rounded font-semibold shadow transition hover:shadow-md bg-white dark:bg-gray-800"
|
||||
on:click={record}
|
||||
>
|
||||
Record
|
||||
|
@ -66,7 +66,6 @@
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
console.log(recorder);
|
||||
if (recorder && recorder.state !== "inactive") {
|
||||
recorder.stop();
|
||||
}
|
||||
|
@ -6,8 +6,5 @@
|
||||
"main": "src/index.ts",
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@gradio/upload": "workspace:^0.0.1"
|
||||
}
|
||||
"private": true
|
||||
}
|
||||
|
17
ui/packages/carousel/README.md
Normal file
17
ui/packages/carousel/README.md
Normal file
@ -0,0 +1,17 @@
|
||||
# `@gradio/carousel`
|
||||
|
||||
```html
|
||||
<script>
|
||||
import { Carousel, CarouselItem } from "@gradio/carousel";
|
||||
</script>
|
||||
|
||||
<Carousel>
|
||||
<CarouselItem>
|
||||
<h3>hello</h3>
|
||||
</CarouselItem>
|
||||
|
||||
<CarouselItem>
|
||||
<img />
|
||||
</CarouselItem>
|
||||
</Carousel>
|
||||
```
|
10
ui/packages/carousel/package.json
Normal file
10
ui/packages/carousel/package.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "@gradio/carousel",
|
||||
"version": "0.0.1",
|
||||
"description": "Gradio UI packages",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"private": true
|
||||
}
|
78
ui/packages/carousel/src/Carousel.svelte
Normal file
78
ui/packages/carousel/src/Carousel.svelte
Normal file
@ -0,0 +1,78 @@
|
||||
<script context="module">
|
||||
export const CAROUSEL = {};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { setContext } from "svelte";
|
||||
import { writable } from "svelte/store";
|
||||
|
||||
export let theme: string = "default";
|
||||
|
||||
const items = writable<Array<number>>([]);
|
||||
const current = writable<number>();
|
||||
|
||||
let id = -1;
|
||||
|
||||
setContext(CAROUSEL, {
|
||||
register: () => {
|
||||
$items.push(++id);
|
||||
$items = $items;
|
||||
return id;
|
||||
},
|
||||
unregister: (id: number) => {
|
||||
const i = $items.findIndex((_id) => _id === id);
|
||||
$items.slice(i, 1);
|
||||
|
||||
$items = $items;
|
||||
},
|
||||
current
|
||||
});
|
||||
|
||||
let carousel_index: number = 0;
|
||||
$: $current = $items[carousel_index] || 0;
|
||||
|
||||
const next = () => {
|
||||
carousel_index = (carousel_index + 1) % $items.length;
|
||||
};
|
||||
|
||||
const prev = () => {
|
||||
carousel_index = (carousel_index - 1 + $items.length) % $items.length;
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="output-carousel flex flex-col gap-2" {theme}>
|
||||
<slot />
|
||||
|
||||
<div class="carousel-control flex gap-4 justify-center items-center my-1">
|
||||
<button on:click={prev}>
|
||||
<svg
|
||||
class="caret h-3 mt-0.5 fill-current"
|
||||
viewBox="0 0 9.1457395 15.999842"
|
||||
>
|
||||
<path
|
||||
d="M 0.32506616,7.2360106 7.1796187,0.33129769 c 0.4360247,-0.439451 1.1455702,-0.442056 1.5845974,-0.0058 0.4390612,0.435849 0.441666,1.14535901 0.00582,1.58438501 l -6.064985,6.1096644 6.10968,6.0646309 c 0.4390618,0.436026 0.4416664,1.145465 0.00582,1.584526 -0.4358485,0.439239 -1.1453586,0.441843 -1.5845975,0.0058 L 0.33088256,8.8203249 C 0.11135166,8.6022941 0.00105996,8.3161928 7.554975e-6,8.0295489 -0.00104244,7.7427633 0.10735446,7.4556467 0.32524356,7.2361162"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<div
|
||||
class="carousel_index text-xl text-center font-semibold"
|
||||
style="min-width: 60px"
|
||||
>
|
||||
{carousel_index + 1} / {$items.length}
|
||||
</div>
|
||||
<button on:click={next}>
|
||||
<svg
|
||||
class="caret h-3 mt-0.5 fill-current"
|
||||
viewBox="0 0 9.1457395 15.999842"
|
||||
transform="scale(-1, 1)"
|
||||
>
|
||||
<path
|
||||
d="M 0.32506616,7.2360106 7.1796187,0.33129769 c 0.4360247,-0.439451 1.1455702,-0.442056 1.5845974,-0.0058 0.4390612,0.435849 0.441666,1.14535901 0.00582,1.58438501 l -6.064985,6.1096644 6.10968,6.0646309 c 0.4390618,0.436026 0.4416664,1.145465 0.00582,1.584526 -0.4358485,0.439239 -1.1453586,0.441843 -1.5845975,0.0058 L 0.33088256,8.8203249 C 0.11135166,8.6022941 0.00105996,8.3161928 7.554975e-6,8.0295489 -0.00104244,7.7427633 0.10735446,7.4556467 0.32524356,7.2361162"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
</style>
|
22
ui/packages/carousel/src/CarouselItem.svelte
Normal file
22
ui/packages/carousel/src/CarouselItem.svelte
Normal file
@ -0,0 +1,22 @@
|
||||
<script lang="ts">
|
||||
import { onDestroy } from "svelte";
|
||||
import { getContext } from "svelte";
|
||||
import { CAROUSEL } from "./Carousel.svelte";
|
||||
|
||||
export let label: string | undefined = undefined;
|
||||
|
||||
const { register, unregister, current } = getContext(CAROUSEL);
|
||||
|
||||
let id = register();
|
||||
|
||||
onDestroy(() => unregister(id));
|
||||
</script>
|
||||
|
||||
{#if $current === id}
|
||||
<div class="component">
|
||||
{#if label}
|
||||
<div class="panel-header">{label}</div>
|
||||
{/if}
|
||||
<slot />
|
||||
</div>
|
||||
{/if}
|
2
ui/packages/carousel/src/index.ts
Normal file
2
ui/packages/carousel/src/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export { default as Carousel } from "./Carousel.svelte";
|
||||
export { default as CarouselItem } from "./CarouselItem.svelte";
|
1
ui/packages/chart/README.md
Normal file
1
ui/packages/chart/README.md
Normal file
@ -0,0 +1 @@
|
||||
# `@gradio/chart`
|
16
ui/packages/chart/package.json
Normal file
16
ui/packages/chart/package.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "@gradio/chart",
|
||||
"version": "0.0.1",
|
||||
"description": "Gradio UI packages",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@gradio/tooltip": "workspace:^0.0.1",
|
||||
"d3-dsv": "^3.0.1",
|
||||
"d3-scale": "^4.0.2",
|
||||
"d3-shape": "^3.1.0"
|
||||
}
|
||||
}
|
162
ui/packages/chart/src/Chart.svelte
Normal file
162
ui/packages/chart/src/Chart.svelte
Normal file
@ -0,0 +1,162 @@
|
||||
<script lang="ts">
|
||||
import { csvParse } from "d3-dsv";
|
||||
import { scaleLinear } from "d3-scale";
|
||||
import { line as _line, curveLinear } from "d3-shape";
|
||||
import { createEventDispatcher, onMount } from "svelte";
|
||||
|
||||
import { get_domains, transform_values, get_color } from "./utils";
|
||||
import { tooltip } from "@gradio/tooltip";
|
||||
|
||||
export let value: string | Array<Record<string, string>>;
|
||||
export let x: string | undefined = undefined;
|
||||
export let y: Array<string> | undefined = undefined;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
$: ({ x: _x, y: _y } =
|
||||
typeof value === "string"
|
||||
? transform_values(csvParse(value) as Array<Record<string, string>>, x, y)
|
||||
: transform_values(value, x, y));
|
||||
|
||||
$: x_domain = get_domains(_x);
|
||||
$: y_domain = get_domains(_y);
|
||||
|
||||
$: scale_x = scaleLinear(x_domain, [0, 600]).nice();
|
||||
$: scale_y = scaleLinear(y_domain, [350, 0]).nice();
|
||||
$: x_ticks = scale_x.ticks(8);
|
||||
$: y_ticks = scale_y.ticks(y_domain[1] < 10 ? y_domain[1] : 8);
|
||||
|
||||
let colors: Record<string, string>;
|
||||
$: colors = _y.reduce(
|
||||
(acc, next) => ({ ...acc, [next.name]: get_color() }),
|
||||
{}
|
||||
);
|
||||
|
||||
onMount(() => {
|
||||
dispatch("process", { x: _x, y: _y });
|
||||
});
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div class="flex justify-center align-items-center text-sm">
|
||||
{#each _y as { name }}
|
||||
<div class="mx-2">
|
||||
<span
|
||||
class="inline-block w-[10px] h-[10px]"
|
||||
style="background-color: {colors[name]}"
|
||||
/>
|
||||
{name}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
<svg class="w-full" viewBox="-70 -20 700 420">
|
||||
<g>
|
||||
{#each x_ticks as tick}
|
||||
<line
|
||||
stroke-width="0.5"
|
||||
x1={scale_x(tick)}
|
||||
x2={scale_x(tick)}
|
||||
y1={scale_y(y_ticks[0] < y_domain[0] ? y_ticks[0] : y_domain[0]) + 10}
|
||||
y2={scale_y(
|
||||
y_domain[1] > y_ticks[y_ticks.length - 1]
|
||||
? y_domain[1]
|
||||
: y_ticks[y_ticks.length - 1]
|
||||
)}
|
||||
stroke="#aaa"
|
||||
/>
|
||||
<text
|
||||
class="font-mono text-xs"
|
||||
text-anchor="middle"
|
||||
x={scale_x(tick)}
|
||||
y={scale_y(y_ticks[0]) + 30}
|
||||
>
|
||||
{tick}
|
||||
</text>
|
||||
{/each}
|
||||
|
||||
{#each y_ticks as tick}
|
||||
<line
|
||||
stroke-width="0.5"
|
||||
y1={scale_y(tick)}
|
||||
y2={scale_y(tick)}
|
||||
x1={scale_x(x_ticks[0] < x_domain[0] ? x_ticks[0] : x_domain[0]) - 10}
|
||||
x2={scale_x(
|
||||
x_domain[1] > x_ticks[x_ticks.length - 1]
|
||||
? x_domain[1]
|
||||
: x_ticks[x_ticks.length - 1]
|
||||
)}
|
||||
stroke="#aaa"
|
||||
/>
|
||||
|
||||
<text
|
||||
class="font-mono text-xs"
|
||||
text-anchor="end"
|
||||
y={scale_y(tick) + 4}
|
||||
x={scale_x(x_ticks[0]) - 20}
|
||||
>
|
||||
{tick}
|
||||
</text>
|
||||
{/each}
|
||||
|
||||
{#if y_domain[1] > y_ticks[y_ticks.length - 1]}
|
||||
<line
|
||||
stroke-width="0.5"
|
||||
y1={scale_y(y_domain[1])}
|
||||
y2={scale_y(y_domain[1])}
|
||||
x1={scale_x(x_ticks[0])}
|
||||
x2={scale_x(x_domain[1])}
|
||||
stroke="#aaa"
|
||||
/>
|
||||
<text
|
||||
class="font-mono text-xs"
|
||||
text-anchor="end"
|
||||
y={scale_y(y_domain[1]) + 4}
|
||||
x={scale_x(x_ticks[0]) - 20}
|
||||
>
|
||||
{y_domain[1]}
|
||||
</text>
|
||||
{/if}
|
||||
</g>
|
||||
|
||||
{#each _y as { name, values }}
|
||||
{@const color = colors[name]}
|
||||
{#each values as { x, y }}
|
||||
<circle
|
||||
r="3.5"
|
||||
cx={scale_x(x)}
|
||||
cy={scale_y(y)}
|
||||
stroke-width="1.5"
|
||||
stroke={color}
|
||||
fill="none"
|
||||
/>
|
||||
{/each}
|
||||
<path
|
||||
d={_line().curve(curveLinear)(
|
||||
values.map(({ x, y }) => [scale_x(x), scale_y(y)])
|
||||
)}
|
||||
fill="none"
|
||||
stroke={color}
|
||||
stroke-width="3"
|
||||
/>
|
||||
{/each}
|
||||
|
||||
{#each _y as { name, values }}
|
||||
{@const color = colors[name]}
|
||||
{#each values as { x, y }}
|
||||
<circle
|
||||
use:tooltip={{ color, text: `(${x}, ${y})` }}
|
||||
r="7"
|
||||
cx={scale_x(x)}
|
||||
cy={scale_y(y)}
|
||||
stroke="black"
|
||||
fill="black"
|
||||
style="cursor: pointer; opacity: 0"
|
||||
/>
|
||||
{/each}
|
||||
{/each}
|
||||
</svg>
|
||||
|
||||
<div class="flex justify-center align-items-center text-sm">
|
||||
{_x.name}
|
||||
</div>
|
||||
</div>
|
1
ui/packages/chart/src/index.ts
Normal file
1
ui/packages/chart/src/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as Chart } from "./Chart.svelte";
|
92
ui/packages/chart/src/utils.ts
Normal file
92
ui/packages/chart/src/utils.ts
Normal file
@ -0,0 +1,92 @@
|
||||
interface XYValue {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
interface ObjectValue {
|
||||
values: XYValue[];
|
||||
}
|
||||
|
||||
export function get_domains(
|
||||
values: ObjectValue[] | { values: number[] }
|
||||
): [number, number] {
|
||||
let _vs: number[];
|
||||
if (Array.isArray(values)) {
|
||||
_vs = values.reduce<number[]>((acc, { values }) => {
|
||||
return [...acc, ...values.map(({ x, y }) => y)];
|
||||
}, []);
|
||||
} else {
|
||||
_vs = values.values;
|
||||
}
|
||||
return [Math.min(..._vs), Math.max(..._vs)];
|
||||
}
|
||||
|
||||
interface Row {
|
||||
name: string;
|
||||
values: number[];
|
||||
}
|
||||
|
||||
interface RowPoint {
|
||||
name: string;
|
||||
values: Array<{ x: number; y: number }>;
|
||||
}
|
||||
|
||||
interface TransformedValues {
|
||||
x: Row;
|
||||
y: Array<RowPoint>;
|
||||
}
|
||||
|
||||
export function transform_values(
|
||||
values: Array<Record<string, string>>,
|
||||
x?: string,
|
||||
y?: string[]
|
||||
) {
|
||||
const transformed_values = Object.entries(
|
||||
values[0]
|
||||
).reduce<TransformedValues>(
|
||||
(acc, next, i) => {
|
||||
if ((!x && i === 0) || (x && next[0] === x)) {
|
||||
acc.x.name = next[0];
|
||||
} else if (!y || (y && y.includes(next[0]))) {
|
||||
acc.y.push({ name: next[0], values: [] });
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{ x: { name: "", values: [] }, y: [] }
|
||||
);
|
||||
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
const _a = Object.entries(values[i]);
|
||||
for (let j = 0; j < _a.length; j++) {
|
||||
let [name, x] = _a[j];
|
||||
if (name === transformed_values.x.name) {
|
||||
transformed_values.x.values.push(parseInt(x, 10));
|
||||
} else {
|
||||
transformed_values.y[j - 1].values.push({
|
||||
y: parseInt(_a[j][1], 10),
|
||||
x: parseInt(_a[0][1], 10)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return transformed_values;
|
||||
}
|
||||
|
||||
let c = 0;
|
||||
export function get_color(): string {
|
||||
let default_colors = [
|
||||
[255, 99, 132],
|
||||
[54, 162, 235],
|
||||
[240, 176, 26],
|
||||
[153, 102, 255],
|
||||
[75, 192, 192],
|
||||
[255, 159, 64]
|
||||
];
|
||||
|
||||
if (c >= default_colors.length) c = 0;
|
||||
|
||||
const [r, g, b] = default_colors[c++];
|
||||
|
||||
return `rgb(${r},${g},${b})`;
|
||||
}
|
11
ui/packages/chatbot/README.md
Normal file
11
ui/packages/chatbot/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# `@gradio/button`
|
||||
|
||||
```html
|
||||
<script>
|
||||
import { Button } from "@gradio/button";
|
||||
</script>
|
||||
|
||||
<button type="primary|secondary" href="string" on:click="{e.detail === href}">
|
||||
content
|
||||
</button>
|
||||
```
|
10
ui/packages/chatbot/package.json
Normal file
10
ui/packages/chatbot/package.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "@gradio/chatbot",
|
||||
"version": "0.0.1",
|
||||
"description": "Gradio UI packages",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"private": true
|
||||
}
|
33
ui/packages/chatbot/src/ChatBot.svelte
Normal file
33
ui/packages/chatbot/src/ChatBot.svelte
Normal file
@ -0,0 +1,33 @@
|
||||
<script lang="ts">
|
||||
import { beforeUpdate, afterUpdate } from "svelte";
|
||||
export let value: Array<[string, string]>;
|
||||
let div: HTMLDivElement;
|
||||
let autoscroll: Boolean;
|
||||
|
||||
beforeUpdate(() => {
|
||||
autoscroll =
|
||||
div && div.offsetHeight + div.scrollTop > div.scrollHeight - 20;
|
||||
});
|
||||
|
||||
afterUpdate(() => {
|
||||
if (autoscroll) div.scrollTo(0, div.scrollHeight);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="overflow-y-auto h-64 border border-b-0 rounded-t-lg leading-tight"
|
||||
bind:this={div}
|
||||
>
|
||||
<div class="flex flex-col items-end space-y-4 p-3">
|
||||
{#each value as message}
|
||||
<div class="px-3 py-2 rounded-2xl bg-yellow-500 text-white ml-7">
|
||||
{message[0]}
|
||||
</div>
|
||||
<div
|
||||
class="px-3 py-2 rounded-2xl place-self-start bg-gray-300 dark:bg-gray-850 dark:text-gray-200 mr-7"
|
||||
>
|
||||
{message[1]}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
1
ui/packages/chatbot/src/index.ts
Normal file
1
ui/packages/chatbot/src/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as ChatBot } from "./ChatBot.svelte";
|
11
ui/packages/file/README.md
Normal file
11
ui/packages/file/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# `@gradio/button`
|
||||
|
||||
```html
|
||||
<script>
|
||||
import { Button } from "@gradio/button";
|
||||
</script>
|
||||
|
||||
<button type="primary|secondary" href="string" on:click="{e.detail === href}">
|
||||
content
|
||||
</button>
|
||||
```
|
13
ui/packages/file/package.json
Normal file
13
ui/packages/file/package.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@gradio/file",
|
||||
"version": "0.0.1",
|
||||
"description": "Gradio UI packages",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@gradio/upload": "workspace:^0.0.1"
|
||||
}
|
||||
}
|
61
ui/packages/file/src/File.svelte
Normal file
61
ui/packages/file/src/File.svelte
Normal file
@ -0,0 +1,61 @@
|
||||
<script lang="ts">
|
||||
import { Upload, ModifyUpload } from "@gradio/upload";
|
||||
import type { FileData } from "@gradio/upload";
|
||||
import { prettyBytes } from "./utils";
|
||||
|
||||
export let value: null | FileData;
|
||||
|
||||
export let theme: string = "default";
|
||||
export let drop_text: string = "Drop an audio file";
|
||||
export let or_text: string = "or";
|
||||
export let upload_text: string = "click to upload";
|
||||
|
||||
function handle_upload({ detail }: CustomEvent<FileData>) {
|
||||
value = detail;
|
||||
}
|
||||
|
||||
function handle_clear({ detail }: CustomEvent<null>) {
|
||||
value = null;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="input-file" {theme}>
|
||||
{#if value === null}
|
||||
<Upload on:load={handle_upload} {theme}>
|
||||
{drop_text}
|
||||
<br />- {or_text} -<br />
|
||||
{upload_text}
|
||||
</Upload>
|
||||
{:else}
|
||||
<div
|
||||
class="file-preview w-full flex flex-row flex-wrap justify-center items-center relative"
|
||||
>
|
||||
<ModifyUpload on:clear={handle_clear} {theme} />
|
||||
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-10 w-1/5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
||||
/>
|
||||
</svg>
|
||||
<div class="file-name w-3/5 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>
|
1
ui/packages/file/src/index.ts
Normal file
1
ui/packages/file/src/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as File } from "./File.svelte";
|
10
ui/packages/file/src/utils.ts
Normal file
10
ui/packages/file/src/utils.ts
Normal file
@ -0,0 +1,10 @@
|
||||
export const prettyBytes = (bytes: number): string => {
|
||||
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;
|
||||
};
|
11
ui/packages/form/README.md
Normal file
11
ui/packages/form/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# `@gradio/button`
|
||||
|
||||
```html
|
||||
<script>
|
||||
import { Button } from "@gradio/button";
|
||||
</script>
|
||||
|
||||
<button type="primary|secondary" href="string" on:click="{e.detail === href}">
|
||||
content
|
||||
</button>
|
||||
```
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "~name~",
|
||||
"name": "@gradio/form",
|
||||
"version": "0.0.1",
|
||||
"description": "Gradio UI packages",
|
||||
"type": "module",
|
62
ui/packages/form/src/Checkbox.svelte
Normal file
62
ui/packages/form/src/Checkbox.svelte
Normal file
@ -0,0 +1,62 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from "svelte";
|
||||
export let value: boolean;
|
||||
export let theme: string = "default";
|
||||
|
||||
const dispatch = createEventDispatcher<{ change: boolean }>();
|
||||
|
||||
function handle_change() {
|
||||
dispatch("change", !value);
|
||||
value = !value;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="input-checkbox inline-block" {theme} on:click={handle_change}>
|
||||
<button
|
||||
class="checkbox-item py-2 px-3 rounded cursor-pointer"
|
||||
class:selected={value}
|
||||
>
|
||||
<div class="checkbox w-4 h-4 bg-white flex items-center justify-center">
|
||||
<svg class="check opacity-0 h-3 w-4" viewBox="-10 -10 20 20">
|
||||
<line
|
||||
x1="-7.5"
|
||||
y1="0"
|
||||
x2="-2.5"
|
||||
y2="5"
|
||||
stroke="white"
|
||||
stroke-width="4"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
<line
|
||||
x1="-2.5"
|
||||
y1="5"
|
||||
x2="7.5"
|
||||
y2="-7.5"
|
||||
stroke="white"
|
||||
stroke-width="4"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
.selected .check {
|
||||
@apply opacity-100;
|
||||
}
|
||||
.input-checkbox[theme="default"] {
|
||||
.checkbox-item {
|
||||
@apply bg-white dark:bg-gray-800 shadow transition hover:shadow-md;
|
||||
}
|
||||
.checkbox {
|
||||
@apply bg-gray-100 dark:bg-gray-400 transition;
|
||||
}
|
||||
.checkbox-item.selected {
|
||||
@apply bg-amber-500 dark:bg-red-600 text-white;
|
||||
}
|
||||
.selected .checkbox {
|
||||
@apply bg-amber-600 dark:bg-red-700;
|
||||
}
|
||||
}
|
||||
</style>
|
73
ui/packages/form/src/CheckboxGroup.svelte
Normal file
73
ui/packages/form/src/CheckboxGroup.svelte
Normal file
@ -0,0 +1,73 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from "svelte";
|
||||
export let value: Array<string>;
|
||||
export let choices: Array<string>;
|
||||
|
||||
export let theme: string = "default";
|
||||
|
||||
const dispatch = createEventDispatcher<{ change: Array<string> }>();
|
||||
|
||||
const toggleChoice = (choice: string) => {
|
||||
if (value.includes(choice)) {
|
||||
value.splice(value.indexOf(choice), 1);
|
||||
} else {
|
||||
value.push(choice);
|
||||
}
|
||||
dispatch("change", value);
|
||||
value = value;
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="input-checkbox-group flex flex-wrap gap-2" {theme}>
|
||||
{#each choices as choice, i}
|
||||
<button
|
||||
class="checkbox-item py-2 px-3 font-semibold rounded cursor-pointer flex items-center gap-2"
|
||||
class:selected={value.includes(choice)}
|
||||
on:click={() => toggleChoice(choice)}
|
||||
>
|
||||
<div class="checkbox w-4 h-4 bg-white flex items-center justify-center">
|
||||
<svg class="check opacity-0 h-3 w-4" viewBox="-10 -10 20 20">
|
||||
<line
|
||||
x1="-7.5"
|
||||
y1="0"
|
||||
x2="-2.5"
|
||||
y2="5"
|
||||
stroke="white"
|
||||
stroke-width="4"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
<line
|
||||
x1="-2.5"
|
||||
y1="5"
|
||||
x2="7.5"
|
||||
y2="-7.5"
|
||||
stroke="white"
|
||||
stroke-width="4"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
{choice}
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
.selected .check {
|
||||
@apply opacity-100;
|
||||
}
|
||||
.input-checkbox-group[theme="default"] {
|
||||
.checkbox-item {
|
||||
@apply bg-white dark:bg-gray-800 shadow transition hover:shadow-md;
|
||||
}
|
||||
.checkbox {
|
||||
@apply bg-gray-100 dark:bg-gray-400 transition;
|
||||
}
|
||||
.checkbox-item.selected {
|
||||
@apply bg-amber-500 dark:bg-red-600 text-white;
|
||||
}
|
||||
.selected .checkbox {
|
||||
@apply bg-amber-600 dark:bg-red-700;
|
||||
}
|
||||
}
|
||||
</style>
|
55
ui/packages/form/src/Dropdown.svelte
Normal file
55
ui/packages/form/src/Dropdown.svelte
Normal file
@ -0,0 +1,55 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from "svelte";
|
||||
export let label: string;
|
||||
export let value: string | undefined = undefined;
|
||||
export let theme: string = "default";
|
||||
export let choices: Array<string>;
|
||||
|
||||
const dispatch = createEventDispatcher<{ change: string }>();
|
||||
|
||||
function handle_change(choice: string) {
|
||||
dispatch("change", choice);
|
||||
value = choice;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="input-dropdown group inline-block relative" {theme}>
|
||||
<button
|
||||
class="selector py-2 px-3 font-semibold rounded inline-flex items-center"
|
||||
>
|
||||
{label}
|
||||
<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={() => handle_change(choice)}
|
||||
>
|
||||
{choice}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="postcss" global>
|
||||
.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-amber-500 dark:hover:bg-red-600 hover:text-gray-50 hover:font-semibold;
|
||||
}
|
||||
}
|
||||
</style>
|
38
ui/packages/form/src/Number.svelte
Normal file
38
ui/packages/form/src/Number.svelte
Normal file
@ -0,0 +1,38 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { debounce } from "./utils";
|
||||
|
||||
export let value: number = 0;
|
||||
export let theme: string = "default";
|
||||
|
||||
const dispatch = createEventDispatcher<{ change: number }>();
|
||||
|
||||
function handle_change(n: number) {
|
||||
dispatch("change", n);
|
||||
}
|
||||
|
||||
const debounced_handle_change = debounce(handle_change, 500);
|
||||
|
||||
$: debounced_handle_change(value);
|
||||
</script>
|
||||
|
||||
<input
|
||||
type="number"
|
||||
class="input-number w-full rounded box-border p-2 focus:outline-none appearance-none"
|
||||
bind:value
|
||||
{theme}
|
||||
/>
|
||||
|
||||
<style lang="postcss" global>
|
||||
input::-webkit-outer-spin-button,
|
||||
input::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
input[type="number"] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
.input-number[theme="default"] {
|
||||
@apply shadow transition hover:shadow-md dark:bg-gray-800;
|
||||
}
|
||||
</style>
|
47
ui/packages/form/src/Radio.svelte
Normal file
47
ui/packages/form/src/Radio.svelte
Normal file
@ -0,0 +1,47 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from "svelte";
|
||||
|
||||
export let value: string;
|
||||
export let theme: string = "default";
|
||||
export let choices: Array<string>;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
function handle_change(choice: string) {
|
||||
dispatch("change", choice);
|
||||
value = choice;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="input-radio flex flex-wrap gap-2" {theme}>
|
||||
{#each choices as choice}
|
||||
<button
|
||||
class="radio-item py-2 px-3 font-semibold rounded cursor-pointer flex items-center gap-2"
|
||||
class:selected={value === choice}
|
||||
on:click={() => handle_change(choice)}
|
||||
>
|
||||
<div class="radio-circle w-4 h-4 rounded-full box-border" />
|
||||
{choice}
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
.input-radio[theme="default"] {
|
||||
.radio-item {
|
||||
@apply bg-white dark:bg-gray-800 shadow transition hover:shadow-md;
|
||||
}
|
||||
.radio-circle {
|
||||
@apply bg-gray-50 dark:bg-gray-400 border-4 border-gray-200 dark:border-gray-600;
|
||||
}
|
||||
.radio-item.selected {
|
||||
@apply bg-amber-500 dark:bg-red-600 text-white shadow;
|
||||
}
|
||||
.radio-circle {
|
||||
@apply w-4 h-4 bg-white transition rounded-full box-border;
|
||||
}
|
||||
.selected .radio-circle {
|
||||
@apply border-amber-600 dark:border-red-700;
|
||||
}
|
||||
}
|
||||
</style>
|
50
ui/packages/form/src/Textbox.svelte
Normal file
50
ui/packages/form/src/Textbox.svelte
Normal file
@ -0,0 +1,50 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { debounce } from "./utils";
|
||||
|
||||
export let value: string = "";
|
||||
export let theme: string = "default";
|
||||
export let lines: number = 1;
|
||||
export let placeholder: string = "";
|
||||
|
||||
const dispatch = createEventDispatcher<{ change: string }>();
|
||||
|
||||
type CustomInputEvent =
|
||||
| (Event & {
|
||||
target: EventTarget & HTMLTextAreaElement;
|
||||
})
|
||||
| (Event & {
|
||||
target: EventTarget & HTMLInputElement;
|
||||
});
|
||||
|
||||
function handle_change(event: CustomInputEvent) {
|
||||
dispatch("change", event?.target?.value);
|
||||
}
|
||||
|
||||
const debounced_handle_change = debounce(handle_change, 500);
|
||||
</script>
|
||||
|
||||
{#if lines > 1}
|
||||
<textarea
|
||||
class="input-text w-full rounded box-border p-2 focus:outline-none appearance-none"
|
||||
{value}
|
||||
{placeholder}
|
||||
on:input={debounced_handle_change}
|
||||
{theme}
|
||||
/>
|
||||
{:else}
|
||||
<input
|
||||
type="text"
|
||||
class="input-text w-full rounded box-border p-2 focus:outline-none appearance-none"
|
||||
{value}
|
||||
{placeholder}
|
||||
on:input={debounced_handle_change}
|
||||
{theme}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<style lang="postcss" global>
|
||||
.input-text[theme="default"] {
|
||||
@apply shadow transition hover:shadow-md dark:bg-gray-800;
|
||||
}
|
||||
</style>
|
6
ui/packages/form/src/index.ts
Normal file
6
ui/packages/form/src/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export { default as Checkbox } from "./Checkbox.svelte";
|
||||
export { default as CheckboxGroup } from "./CheckboxGroup.svelte";
|
||||
export { default as Dropdown } from "./Dropdown.svelte";
|
||||
export { default as Number } from "./Number.svelte";
|
||||
export { default as Radio } from "./Radio.svelte";
|
||||
export { default as TextBox } from "./Textbox.svelte";
|
9
ui/packages/form/src/utils.ts
Normal file
9
ui/packages/form/src/utils.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export const debounce = (callback: Function, wait = 250) => {
|
||||
let timeout: NodeJS.Timeout | null = null;
|
||||
return (...args: Array<unknown>) => {
|
||||
const next = () => callback(...args);
|
||||
if (timeout) clearTimeout(timeout);
|
||||
|
||||
timeout = setTimeout(next, wait);
|
||||
};
|
||||
};
|
11
ui/packages/highlighted-text/README.md
Normal file
11
ui/packages/highlighted-text/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# `@gradio/button`
|
||||
|
||||
```html
|
||||
<script>
|
||||
import { Button } from "@gradio/button";
|
||||
</script>
|
||||
|
||||
<button type="primary|secondary" href="string" on:click="{e.detail === href}">
|
||||
content
|
||||
</button>
|
||||
```
|
10
ui/packages/highlighted-text/package.json
Normal file
10
ui/packages/highlighted-text/package.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "@gradio/highlighted-text",
|
||||
"version": "0.0.1",
|
||||
"description": "Gradio UI packages",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"private": true
|
||||
}
|
130
ui/packages/highlighted-text/src/HighlightedText.svelte
Normal file
130
ui/packages/highlighted-text/src/HighlightedText.svelte
Normal file
@ -0,0 +1,130 @@
|
||||
<script lang="ts">
|
||||
import { getNextColor } from "./utils";
|
||||
|
||||
export let value: Array<[string, string | number]>;
|
||||
export let theme: string = "default";
|
||||
export let show_legend: boolean = false;
|
||||
export let color_map: Record<string, string> = {};
|
||||
|
||||
let ctx: CanvasRenderingContext2D;
|
||||
|
||||
function name_to_rgba(name: string) {
|
||||
if (!ctx) {
|
||||
var canvas = document.createElement("canvas");
|
||||
ctx = canvas.getContext("2d")!;
|
||||
}
|
||||
ctx.fillStyle = name;
|
||||
ctx.fillRect(0, 0, 1, 1);
|
||||
const [r, g, b, a] = ctx.getImageData(0, 0, 1, 1).data;
|
||||
ctx.clearRect(0, 0, 1, 1);
|
||||
return `rgba(${r}, ${g}, ${b}, ${255 / a})`;
|
||||
}
|
||||
|
||||
let mode: "categories" | "scores";
|
||||
|
||||
if (color_map === null) {
|
||||
color_map = {};
|
||||
}
|
||||
if (value.length > 0) {
|
||||
for (let [_, label] of value) {
|
||||
if (label !== null) {
|
||||
if (typeof label === "string") {
|
||||
mode = "categories";
|
||||
if (!(label in color_map)) {
|
||||
let color = getNextColor(Object.keys(color_map).length);
|
||||
color_map[label] = color;
|
||||
}
|
||||
} else {
|
||||
mode = "scores";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function correct_color_map() {
|
||||
for (const col in color_map) {
|
||||
const _c = color_map[col].trim();
|
||||
if (_c.startsWith("rgba")) {
|
||||
color_map[col] = color_map[col];
|
||||
} else {
|
||||
color_map[col] = name_to_rgba(color_map[col]);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="output-highlightedtext" {theme}>
|
||||
{#if mode === "categories"}
|
||||
{#if show_legend}
|
||||
<div class="category-legend flex flex-wrap gap-1 mb-2">
|
||||
{#each Object.entries(color_map) as [category, color], i}
|
||||
<div
|
||||
class="category-label px-2 py-1 rounded text-white font-semibold"
|
||||
style={"background-color:" + color}
|
||||
>
|
||||
{category}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
<div
|
||||
class="textfield p-2 bg-white dark:bg-gray-800 rounded box-border max-w-full leading-8 break-word"
|
||||
>
|
||||
{#each value as [text, category], i}
|
||||
<span
|
||||
class="textspan p-1 mr-0.5 bg-opacity-20 dark:bg-opacity-80 rounded-sm"
|
||||
style={category === null
|
||||
? ""
|
||||
: `color: ${color_map[category]}; background-color: ${color_map[
|
||||
category
|
||||
].replace("1)", "var(--tw-bg-opacity))")}`}
|
||||
>
|
||||
<span class="text dark:text-white">{text}</span>
|
||||
{#if !show_legend && category !== null}
|
||||
<span
|
||||
class="inline-category text-xs text-white ml-0.5 px-0.5 rounded-sm"
|
||||
style={category === null
|
||||
? ""
|
||||
: `background-color: ${color_map[category]}`}
|
||||
>
|
||||
{category}
|
||||
</span>
|
||||
{/if}
|
||||
</span>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
{#if show_legend}
|
||||
<div
|
||||
class="color_legend flex px-2 py-1 justify-between rounded mb-3 font-semibold"
|
||||
style="background: -webkit-linear-gradient(to right,#8d83d6,(255,255,255,0),#eb4d4b); background: linear-gradient(to right,#8d83d6,rgba(255,255,255,0),#eb4d4b);"
|
||||
>
|
||||
<span>-1</span>
|
||||
<span>0</span>
|
||||
<span>+1</span>
|
||||
</div>
|
||||
{/if}
|
||||
<div
|
||||
class="textfield p-2 bg-white dark:bg-gray-800 rounded box-border max-w-full leading-8 break-word"
|
||||
>
|
||||
{#each value as [text, score], i}
|
||||
<span
|
||||
class="textspan p-1 mr-0.5 bg-opacity-20 dark:bg-opacity-80 rounded-sm"
|
||||
style={"background-color: rgba(" +
|
||||
(score < 0 ? "141, 131, 214," + -score : "235, 77, 75," + score) +
|
||||
")"}
|
||||
>
|
||||
<span class="text dark:text-white">{text}</span>
|
||||
</span>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
.output-highlightedtext[theme="default"] {
|
||||
.textfield {
|
||||
@apply shadow;
|
||||
}
|
||||
}
|
||||
</style>
|
1
ui/packages/highlighted-text/src/index.ts
Normal file
1
ui/packages/highlighted-text/src/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as HighlightedText } from "./HighlightedText.svelte";
|
36
ui/packages/highlighted-text/src/utils.ts
Normal file
36
ui/packages/highlighted-text/src/utils.ts
Normal file
@ -0,0 +1,36 @@
|
||||
export function randInt(min: number, max: number): number {
|
||||
return Math.floor(Math.random() * (max - min) + min);
|
||||
}
|
||||
|
||||
export const getNextColor = (index: number, alpha: number = 1): string => {
|
||||
let default_colors = [
|
||||
[255, 99, 132],
|
||||
[54, 162, 235],
|
||||
[240, 176, 26],
|
||||
[153, 102, 255],
|
||||
[75, 192, 192],
|
||||
[255, 159, 64],
|
||||
[194, 88, 74],
|
||||
[44, 102, 219],
|
||||
[44, 163, 23],
|
||||
[191, 46, 217],
|
||||
[160, 162, 162],
|
||||
[163, 151, 27]
|
||||
];
|
||||
if (index < default_colors.length) {
|
||||
var color_set = default_colors[index];
|
||||
} else {
|
||||
var color_set = [randInt(64, 196), randInt(64, 196), randInt(64, 196)];
|
||||
}
|
||||
return (
|
||||
"rgba(" +
|
||||
color_set[0] +
|
||||
", " +
|
||||
color_set[1] +
|
||||
", " +
|
||||
color_set[2] +
|
||||
", " +
|
||||
alpha +
|
||||
")"
|
||||
);
|
||||
};
|
11
ui/packages/html/README.md
Normal file
11
ui/packages/html/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# `@gradio/button`
|
||||
|
||||
```html
|
||||
<script>
|
||||
import { Button } from "@gradio/button";
|
||||
</script>
|
||||
|
||||
<button type="primary|secondary" href="string" on:click="{e.detail === href}">
|
||||
content
|
||||
</button>
|
||||
```
|
10
ui/packages/html/package.json
Normal file
10
ui/packages/html/package.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "@gradio/html",
|
||||
"version": "0.0.1",
|
||||
"description": "Gradio UI packages",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"private": true
|
||||
}
|
8
ui/packages/html/src/HTML.svelte
Normal file
8
ui/packages/html/src/HTML.svelte
Normal file
@ -0,0 +1,8 @@
|
||||
<script lang="ts">
|
||||
export let value: string;
|
||||
export let theme: string = "default";
|
||||
</script>
|
||||
|
||||
<div class="output-html" {theme}>
|
||||
{@html value}
|
||||
</div>
|
1
ui/packages/html/src/index.ts
Normal file
1
ui/packages/html/src/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as HTML } from "./HTML.svelte";
|
11
ui/packages/image/README.md
Normal file
11
ui/packages/image/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# `@gradio/button`
|
||||
|
||||
```html
|
||||
<script>
|
||||
import { Button } from "@gradio/button";
|
||||
</script>
|
||||
|
||||
<button type="primary|secondary" href="string" on:click="{e.detail === href}">
|
||||
content
|
||||
</button>
|
||||
```
|
17
ui/packages/image/package.json
Normal file
17
ui/packages/image/package.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "@gradio/image",
|
||||
"version": "0.0.1",
|
||||
"description": "Gradio UI packages",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@gradio/upload": "workspace:^0.0.1",
|
||||
"cropperjs": "^1.5.12",
|
||||
"lazy-brush": "^1.0.1",
|
||||
"resize-observer-polyfill": "^1.5.1",
|
||||
"tui-image-editor": "^3.15.2"
|
||||
}
|
||||
}
|
27
ui/packages/image/src/Cropper.svelte
Normal file
27
ui/packages/image/src/Cropper.svelte
Normal file
@ -0,0 +1,27 @@
|
||||
<script lang="ts">
|
||||
import Cropper from "cropperjs";
|
||||
import { onMount, createEventDispatcher } from "svelte";
|
||||
|
||||
export let image: string;
|
||||
let el: HTMLImageElement;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
onMount(() => {
|
||||
const cropper = new Cropper(el, {
|
||||
autoCropArea: 1,
|
||||
cropend() {
|
||||
const image_data = cropper.getCroppedCanvas().toDataURL();
|
||||
dispatch("crop", image_data);
|
||||
}
|
||||
});
|
||||
|
||||
dispatch("crop", image);
|
||||
|
||||
return () => {
|
||||
cropper.destroy();
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<img src={image} bind:this={el} alt="" />
|
103
ui/packages/image/src/Image.svelte
Normal file
103
ui/packages/image/src/Image.svelte
Normal file
@ -0,0 +1,103 @@
|
||||
<script lang="ts">
|
||||
import Cropper from "./Cropper.svelte";
|
||||
import Sketch from "./Sketch.svelte";
|
||||
import Webcam from "./Webcam.svelte";
|
||||
import ModifySketch from "./ModifySketch.svelte";
|
||||
import ImageEditor from "./ImageEditor.svelte";
|
||||
|
||||
import { Upload, ModifyUpload } from "@gradio/upload";
|
||||
|
||||
export let value: null | string;
|
||||
|
||||
export let theme: string = "default";
|
||||
export let source: "canvas" | "webcam" | "upload" = "upload";
|
||||
export let tool: "editor" | "select" = "editor";
|
||||
|
||||
export let drop_text: string = "Drop an image file";
|
||||
export let or_text: string = "or";
|
||||
export let upload_text: string = "click to upload";
|
||||
|
||||
let mode: "edit" | "view" = "view";
|
||||
let sketch: Sketch;
|
||||
|
||||
function handle_upload({ detail }: CustomEvent<string>) {
|
||||
value = detail;
|
||||
}
|
||||
|
||||
function handle_clear({ detail }: CustomEvent<null>) {
|
||||
value = null;
|
||||
}
|
||||
|
||||
function handle_save({ detail }: { detail: string }) {
|
||||
value = detail;
|
||||
mode = "view";
|
||||
}
|
||||
|
||||
$: console.log(value, source);
|
||||
</script>
|
||||
|
||||
<div class="input-image">
|
||||
<div
|
||||
class="image-preview w-full h-60 flex justify-center items-center dark:bg-gray-600 relative"
|
||||
class:bg-gray-200={value}
|
||||
class:h-60={source !== "webcam"}
|
||||
>
|
||||
{#if source === "canvas"}
|
||||
<ModifySketch
|
||||
on:undo={() => sketch.undo()}
|
||||
on:clear={() => sketch.clear()}
|
||||
/>
|
||||
<Sketch {value} bind:this={sketch} on:change={handle_save} />
|
||||
{:else if value === null}
|
||||
{#if source === "upload"}
|
||||
<Upload
|
||||
filetype="image/x-png,image/gif,image/jpeg"
|
||||
on:load={handle_upload}
|
||||
include_file_metadata={false}
|
||||
{theme}
|
||||
>
|
||||
{drop_text}
|
||||
<br />- {or_text} -<br />
|
||||
{upload_text}
|
||||
</Upload>
|
||||
{:else if source === "webcam"}
|
||||
<Webcam on:capture={handle_save} />
|
||||
{/if}
|
||||
{:else if tool === "select"}
|
||||
<Cropper image={value} on:crop={handle_save} />
|
||||
{:else if tool === "editor"}
|
||||
{#if mode === "edit"}
|
||||
<ImageEditor
|
||||
{value}
|
||||
on:cancel={() => (mode = "view")}
|
||||
on:save={handle_save}
|
||||
/>
|
||||
{/if}
|
||||
<ModifyUpload
|
||||
on:edit={() => (mode = "edit")}
|
||||
on:clear={handle_clear}
|
||||
editable
|
||||
{theme}
|
||||
/>
|
||||
|
||||
<img class="w-full h-full object-contain" src={value} alt="" />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
:global(.image_editor_buttons) {
|
||||
width: 800px;
|
||||
@apply flex justify-end gap-1;
|
||||
}
|
||||
:global(.image_editor_buttons button) {
|
||||
@apply px-2 py-1 text-xl bg-black text-white font-semibold rounded-t;
|
||||
}
|
||||
:global(.tui-image-editor-header-buttons) {
|
||||
@apply hidden;
|
||||
}
|
||||
:global(.tui-colorpicker-palette-button) {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
</style>
|
46
ui/packages/image/src/ImageEditor.svelte
Normal file
46
ui/packages/image/src/ImageEditor.svelte
Normal file
@ -0,0 +1,46 @@
|
||||
<script lang="ts">
|
||||
import ImageEditor from "tui-image-editor";
|
||||
|
||||
import { createEventDispatcher, onMount } from "svelte";
|
||||
|
||||
export let value: string;
|
||||
let el: HTMLDivElement;
|
||||
let editor: ImageEditor;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
function create_editor() {
|
||||
editor = new ImageEditor(el, {
|
||||
usageStatistics: false,
|
||||
includeUI: {
|
||||
loadImage: {
|
||||
path: value,
|
||||
name: "Edit Image"
|
||||
},
|
||||
menuBarPosition: "left",
|
||||
uiSize: {
|
||||
width: "800px",
|
||||
height: "600px"
|
||||
}
|
||||
},
|
||||
cssMaxWidth: 700,
|
||||
cssMaxHeight: 500,
|
||||
selectionStyle: {
|
||||
cornerSize: 20,
|
||||
rotatingPointOffset: 70
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onMount(create_editor);
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="fixed w-screen h-screen top-0 left-0 bg-black bg-opacity-50 z-40 flex flex-col justify-center items-center"
|
||||
>
|
||||
<div class="image_editor_buttons">
|
||||
<button on:click={() => dispatch("save", editor.toDataURL())}>Save</button>
|
||||
<button on:click={() => dispatch("cancel")}>Cancel</button>
|
||||
</div>
|
||||
<div bind:this={el} />
|
||||
</div>
|
23
ui/packages/image/src/ModifySketch.svelte
Normal file
23
ui/packages/image/src/ModifySketch.svelte
Normal file
@ -0,0 +1,23 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from "svelte";
|
||||
|
||||
import undo_solid from "./undo-solid.svg";
|
||||
import clear from "./clear.svg";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
</script>
|
||||
|
||||
<div class="z-50 top-0 right-0 justify-end m-1 flex gap-1 absolute">
|
||||
<button
|
||||
class="bg-opacity-30 hover:bg-opacity-100 transition p-1.5 bg-amber-500 dark:bg-red-600 rounded shadow w-8 h-8"
|
||||
on:click={() => dispatch("undo")}
|
||||
>
|
||||
<img alt="undo sketch" src={undo_solid} />
|
||||
</button>
|
||||
<button
|
||||
class="clear bg-opacity-30 hover:bg-opacity-100 transition p-1 bg-gray-50 dark:bg-gray-500 rounded shadow w-8 h-8"
|
||||
on:click={() => dispatch("clear")}
|
||||
>
|
||||
<img alt="clear sketch" src={clear} />
|
||||
</button>
|
||||
</div>
|
365
ui/packages/image/src/Sketch.svelte
Normal file
365
ui/packages/image/src/Sketch.svelte
Normal file
@ -0,0 +1,365 @@
|
||||
<script>
|
||||
import { onMount, onDestroy, createEventDispatcher } from "svelte";
|
||||
import { LazyBrush } from "lazy-brush/src";
|
||||
import ResizeObserver from "resize-observer-polyfill";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let value;
|
||||
|
||||
let mounted;
|
||||
|
||||
let brush_radius = 50;
|
||||
let brush_color = "#444";
|
||||
let catenary_color = "#aaa";
|
||||
let background_color = "#FFF";
|
||||
|
||||
let canvas_width = 400;
|
||||
let canvas_height = 400;
|
||||
|
||||
$: mounted && !value && clear();
|
||||
|
||||
function mid_point(p1, p2) {
|
||||
return {
|
||||
x: p1.x + (p2.x - p1.x) / 2,
|
||||
y: p1.y + (p2.y - p1.y) / 2
|
||||
};
|
||||
}
|
||||
|
||||
const canvas_types = [
|
||||
{
|
||||
name: "interface",
|
||||
zIndex: 15
|
||||
},
|
||||
{
|
||||
name: "drawing",
|
||||
zIndex: 11
|
||||
},
|
||||
{
|
||||
name: "temp",
|
||||
zIndex: 12
|
||||
}
|
||||
];
|
||||
|
||||
let canvas = {};
|
||||
let ctx = {};
|
||||
let points = [];
|
||||
let lines = [];
|
||||
let mouse_has_moved = true;
|
||||
let values_changed = true;
|
||||
let is_drawing = false;
|
||||
let is_pressing = false;
|
||||
let lazy = null;
|
||||
let chain_length = null;
|
||||
let canvas_container = null;
|
||||
let canvas_observer = null;
|
||||
let save_data = "";
|
||||
|
||||
onMount(() => {
|
||||
Object.keys(canvas).forEach((key) => {
|
||||
ctx[key] = canvas[key].getContext("2d");
|
||||
});
|
||||
lazy = new LazyBrush({
|
||||
radius: brush_radius / 1.5,
|
||||
enabled: true,
|
||||
initialPoint: {
|
||||
x: window.innerWidth / 2,
|
||||
y: window.innerHeight / 2
|
||||
}
|
||||
});
|
||||
chain_length = brush_radius;
|
||||
canvas_observer = new ResizeObserver((entries, observer) =>
|
||||
handle_canvas_resize(entries, observer)
|
||||
);
|
||||
canvas_observer.observe(canvas_container);
|
||||
loop();
|
||||
mounted = true;
|
||||
window.setTimeout(() => {
|
||||
const initX = window.innerWidth / 2;
|
||||
const initY = window.innerHeight / 2;
|
||||
lazy.update({ x: initX - chain_length / 4, y: initY }, { both: true });
|
||||
lazy.update({ x: initX + chain_length / 4, y: initY }, { both: false });
|
||||
mouse_has_moved = true;
|
||||
values_changed = true;
|
||||
clear();
|
||||
|
||||
if (save_data) {
|
||||
load_save_data(save_data);
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
mounted = false;
|
||||
canvas_observer.unobserve(canvas_container);
|
||||
});
|
||||
|
||||
export function undo() {
|
||||
const _lines = lines.slice(0, -1);
|
||||
clear();
|
||||
draw_lines({ lines: _lines });
|
||||
trigger_on_change();
|
||||
}
|
||||
|
||||
let get_save_data = () => {
|
||||
return JSON.stringify({
|
||||
lines: lines,
|
||||
width: canvas_width,
|
||||
height: canvas_height
|
||||
});
|
||||
};
|
||||
|
||||
let load_save_data = (save_data) => {
|
||||
if (typeof save_data !== "string") {
|
||||
throw new Error("save_data needs to be of type string!");
|
||||
}
|
||||
const { lines, width, height } = JSON.parse(save_data);
|
||||
if (!lines || typeof lines.push !== "function") {
|
||||
throw new Error("save_data.lines needs to be an array!");
|
||||
}
|
||||
clear();
|
||||
if (width === canvas_width && height === canvas_height) {
|
||||
draw_lines({
|
||||
lines
|
||||
});
|
||||
} else {
|
||||
const scaleX = canvas_width / width;
|
||||
const scaleY = canvas_height / height;
|
||||
draw_lines({
|
||||
lines: lines.map((line) => ({
|
||||
...line,
|
||||
points: line.points.map((p) => ({
|
||||
x: p.x * scaleX,
|
||||
y: p.y * scaleY
|
||||
})),
|
||||
brush_radius: line.brush_radius
|
||||
}))
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let draw_lines = ({ lines }) => {
|
||||
lines.forEach((line) => {
|
||||
const { points: _points, brush_color, brush_radius } = line;
|
||||
|
||||
draw_points({
|
||||
points: _points,
|
||||
brush_color,
|
||||
brush_radius
|
||||
});
|
||||
points = _points;
|
||||
saveLine({ brush_color, brush_radius });
|
||||
return;
|
||||
});
|
||||
};
|
||||
|
||||
let handle_draw_start = (e) => {
|
||||
e.preventDefault();
|
||||
is_pressing = true;
|
||||
const { x, y } = get_pointer_pos(e);
|
||||
if (e.touches && e.touches.length > 0) {
|
||||
lazy.update({ x, y }, { both: true });
|
||||
}
|
||||
handle_pointer_move(x, y);
|
||||
};
|
||||
|
||||
let handle_draw_move = (e) => {
|
||||
e.preventDefault();
|
||||
const { x, y } = get_pointer_pos(e);
|
||||
handle_pointer_move(x, y);
|
||||
};
|
||||
|
||||
let handle_draw_end = (e) => {
|
||||
e.preventDefault();
|
||||
handle_draw_move(e);
|
||||
is_drawing = false;
|
||||
is_pressing = false;
|
||||
saveLine();
|
||||
};
|
||||
|
||||
let handle_canvas_resize = (entries) => {
|
||||
const save_data = get_save_data();
|
||||
for (const entry of entries) {
|
||||
const { width, height } = entry.contentRect;
|
||||
set_canvas_size(canvas.interface, width, height);
|
||||
set_canvas_size(canvas.drawing, width, height);
|
||||
set_canvas_size(canvas.temp, width, height);
|
||||
loop({ once: true });
|
||||
}
|
||||
load_save_data(save_data, true);
|
||||
};
|
||||
|
||||
let set_canvas_size = (canvas, width, height) => {
|
||||
canvas.width = width * 3;
|
||||
canvas.height = height * 3;
|
||||
canvas.style.width = width;
|
||||
canvas.style.height = height;
|
||||
};
|
||||
|
||||
let get_pointer_pos = (e) => {
|
||||
const rect = canvas.interface.getBoundingClientRect();
|
||||
|
||||
let clientX = e.clientX;
|
||||
let clientY = e.clientY;
|
||||
if (e.changedTouches && e.changedTouches.length > 0) {
|
||||
clientX = e.changedTouches[0].clientX;
|
||||
clientY = e.changedTouches[0].clientY;
|
||||
}
|
||||
return {
|
||||
x: clientX - rect.left,
|
||||
y: clientY - rect.top
|
||||
};
|
||||
};
|
||||
|
||||
let handle_pointer_move = (x, y) => {
|
||||
lazy.update({ x: x * 3, y: y * 3 });
|
||||
const is_disabled = !lazy.isEnabled();
|
||||
if ((is_pressing && !is_drawing) || (is_disabled && is_pressing)) {
|
||||
is_drawing = true;
|
||||
points.push(lazy.brush.toObject());
|
||||
}
|
||||
if (is_drawing) {
|
||||
points.push(lazy.brush.toObject());
|
||||
|
||||
draw_points({
|
||||
points: points,
|
||||
brush_color,
|
||||
brush_radius
|
||||
});
|
||||
}
|
||||
mouse_has_moved = true;
|
||||
};
|
||||
|
||||
let draw_points = ({ points, brush_color, brush_radius }) => {
|
||||
ctx.temp.lineJoin = "round";
|
||||
ctx.temp.lineCap = "round";
|
||||
ctx.temp.strokeStyle = brush_color;
|
||||
ctx.temp.clearRect(0, 0, ctx.temp.canvas.width, ctx.temp.canvas.height);
|
||||
ctx.temp.lineWidth = brush_radius;
|
||||
let p1 = points[0];
|
||||
let p2 = points[1];
|
||||
ctx.temp.moveTo(p2.x, p2.y);
|
||||
ctx.temp.beginPath();
|
||||
for (var i = 1, len = points.length; i < len; i++) {
|
||||
var midPoint = mid_point(p1, p2);
|
||||
ctx.temp.quadraticCurveTo(p1.x, p1.y, midPoint.x, midPoint.y);
|
||||
p1 = points[i];
|
||||
p2 = points[i + 1];
|
||||
}
|
||||
|
||||
ctx.temp.lineTo(p1.x, p1.y);
|
||||
ctx.temp.stroke();
|
||||
};
|
||||
|
||||
let saveLine = () => {
|
||||
if (points.length < 2) return;
|
||||
|
||||
lines.push({
|
||||
points: [...points],
|
||||
brush_color: brush_color,
|
||||
brush_radius
|
||||
});
|
||||
points.length = 0;
|
||||
const width = canvas.temp.width;
|
||||
const height = canvas.temp.height;
|
||||
ctx.drawing.drawImage(canvas.temp, 0, 0, width, height);
|
||||
|
||||
ctx.temp.clearRect(0, 0, width, height);
|
||||
trigger_on_change();
|
||||
};
|
||||
|
||||
let trigger_on_change = () => {
|
||||
dispatch("change", get_image_data());
|
||||
};
|
||||
|
||||
export function clear() {
|
||||
lines = [];
|
||||
values_changed = true;
|
||||
ctx.drawing.clearRect(0, 0, canvas.drawing.width, canvas.drawing.height);
|
||||
ctx.temp.clearRect(0, 0, canvas.temp.width, canvas.temp.height);
|
||||
ctx.drawing.fillStyle = "#FFFFFF";
|
||||
ctx.drawing.fillRect(0, 0, canvas.drawing.width, canvas.drawing.height);
|
||||
}
|
||||
|
||||
let loop = ({ once = false } = {}) => {
|
||||
if (mouse_has_moved || values_changed) {
|
||||
const pointer = lazy.getPointerCoordinates();
|
||||
const brush = lazy.getBrushCoordinates();
|
||||
draw_interface(ctx.interface, pointer, brush);
|
||||
mouse_has_moved = false;
|
||||
values_changed = false;
|
||||
}
|
||||
if (!once) {
|
||||
window.requestAnimationFrame(() => {
|
||||
loop();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let draw_interface = (ctx, pointer, brush) => {
|
||||
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||
|
||||
// brush preview
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = brush_color;
|
||||
ctx.arc(brush.x, brush.y, brush_radius / 2, 0, Math.PI * 2, true);
|
||||
ctx.fill();
|
||||
|
||||
// mouse point dangler
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = catenary_color;
|
||||
ctx.arc(pointer.x, pointer.y, 4, 0, Math.PI * 2, true);
|
||||
ctx.fill();
|
||||
|
||||
// catenary
|
||||
if (lazy.isEnabled()) {
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = 2;
|
||||
ctx.lineCap = "round";
|
||||
ctx.setLineDash([2, 4]);
|
||||
ctx.strokeStyle = catenary_color;
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
// tiny brush point dot
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = catenary_color;
|
||||
ctx.arc(brush.x, brush.y, 2, 0, Math.PI * 2, true);
|
||||
ctx.fill();
|
||||
};
|
||||
|
||||
export function get_image_data() {
|
||||
return canvas.drawing.toDataURL("image/png");
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="container"
|
||||
style="height:100%; width:100%; background-color:{background_color}"
|
||||
bind:this={canvas_container}
|
||||
bind:offsetWidth={canvas_width}
|
||||
bind:offsetHeight={canvas_height}
|
||||
>
|
||||
{#each canvas_types as { name, zIndex }}
|
||||
<canvas
|
||||
key={name}
|
||||
style="display:block;position:absolute; z-index:{zIndex}; width: {canvas_width}px; height: {canvas_height}px"
|
||||
bind:this={canvas[name]}
|
||||
on:mousedown={name === "interface" ? handle_draw_start : undefined}
|
||||
on:mousemove={name === "interface" ? handle_draw_move : undefined}
|
||||
on:mouseup={name === "interface" ? handle_draw_end : undefined}
|
||||
on:mouseout={name === "interface" ? handle_draw_end : undefined}
|
||||
on:touchstart={name === "interface" ? handle_draw_start : undefined}
|
||||
on:touchmove={name === "interface" ? handle_draw_move : undefined}
|
||||
on:touchend={name === "interface" ? handle_draw_end : undefined}
|
||||
on:touchcancel={name === "interface" ? handle_draw_end : undefined}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
display: block;
|
||||
touch-action: none;
|
||||
}
|
||||
</style>
|
74
ui/packages/image/src/Webcam.svelte
Normal file
74
ui/packages/image/src/Webcam.svelte
Normal file
@ -0,0 +1,74 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher, onMount } from "svelte";
|
||||
import camera from "./camera.svg";
|
||||
|
||||
let video_source: HTMLVideoElement;
|
||||
let canvas: HTMLCanvasElement;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
onMount(() => (canvas = document.createElement("canvas")));
|
||||
|
||||
async function access_webcam() {
|
||||
try {
|
||||
const stream = await navigator.mediaDevices.getUserMedia({
|
||||
video: true
|
||||
});
|
||||
video_source.srcObject = stream;
|
||||
video_source.play();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
function clearphoto() {
|
||||
var context = canvas.getContext("2d")!;
|
||||
context.fillStyle = "#AAA";
|
||||
context.fillRect(0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
|
||||
function takepicture() {
|
||||
var context = canvas.getContext("2d")!;
|
||||
|
||||
if (video_source.videoWidth && video_source.videoHeight) {
|
||||
canvas.width = video_source.videoWidth;
|
||||
canvas.height = video_source.videoHeight;
|
||||
context.drawImage(
|
||||
video_source,
|
||||
0,
|
||||
0,
|
||||
video_source.videoWidth,
|
||||
video_source.videoHeight
|
||||
);
|
||||
|
||||
var data = canvas.toDataURL("image/png");
|
||||
dispatch("capture", data);
|
||||
}
|
||||
}
|
||||
|
||||
access_webcam();
|
||||
</script>
|
||||
|
||||
<div class="h-full w-full relative">
|
||||
<!-- svelte-ignore a11y-media-has-caption -->
|
||||
<video bind:this={video_source} class=" h-full w-full" />
|
||||
<button
|
||||
on:click={takepicture}
|
||||
style="background-color: #333;"
|
||||
class="rounded-full w-10 h-10 flex justify-center items-center absolute inset-x-0 bottom-2 m-auto drop-shadow-lg"
|
||||
>
|
||||
<img
|
||||
style="color: white"
|
||||
src={camera}
|
||||
alt="take a screenshot"
|
||||
class="w-2/4 h-2/4"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<style lang="postcss" global>
|
||||
video {
|
||||
-webkit-transform: scaleX(-1);
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
</style>
|
1
ui/packages/image/src/camera.svg
Normal file
1
ui/packages/image/src/camera.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-camera"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"></path><circle cx="12" cy="13" r="4"></circle></svg>
|
After Width: | Height: | Size: 349 B |
67
ui/packages/image/src/clear.svg
Normal file
67
ui/packages/image/src/clear.svg
Normal file
@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="5.9403949mm"
|
||||
height="5.9403949mm"
|
||||
viewBox="0 0 5.9403949 5.9403949"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
|
||||
sodipodi:docname="clear.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="10.925474"
|
||||
inkscape:cx="4.1188143"
|
||||
inkscape:cy="15.559965"
|
||||
inkscape:window-width="1248"
|
||||
inkscape:window-height="770"
|
||||
inkscape:window-x="-6"
|
||||
inkscape:window-y="-6"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1" />
|
||||
<defs
|
||||
id="defs2" />
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-115.10942,-119.22353)">
|
||||
<g
|
||||
id="g239"
|
||||
transform="matrix(0.05138986,0.05138986,-0.05138986,0.05138986,117.0869,112.75317)">
|
||||
<rect
|
||||
style="fill:#000000;stroke-width:0.295287"
|
||||
id="rect31"
|
||||
width="20"
|
||||
height="80"
|
||||
x="-111.51107"
|
||||
y="42.193726"
|
||||
rx="2.9434128"
|
||||
ry="2.6448057"
|
||||
transform="scale(-1,1)" />
|
||||
<rect
|
||||
style="fill:#000000;stroke-width:0.295287"
|
||||
id="rect31-3"
|
||||
width="20"
|
||||
height="80"
|
||||
x="-92.193726"
|
||||
y="-141.51106"
|
||||
rx="2.9434128"
|
||||
ry="2.6448057"
|
||||
transform="matrix(0,-1,-1,0,0,0)" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
1
ui/packages/image/src/index.ts
Normal file
1
ui/packages/image/src/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as Image } from "./Image.svelte";
|
1
ui/packages/image/src/undo-solid.svg
Normal file
1
ui/packages/image/src/undo-solid.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="undo" class="svg-inline--fa fa-undo fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M212.333 224.333H12c-6.627 0-12-5.373-12-12V12C0 5.373 5.373 0 12 0h48c6.627 0 12 5.373 12 12v78.112C117.773 39.279 184.26 7.47 258.175 8.007c136.906.994 246.448 111.623 246.157 248.532C504.041 393.258 393.12 504 256.333 504c-64.089 0-122.496-24.313-166.51-64.215-5.099-4.622-5.334-12.554-.467-17.42l33.967-33.967c4.474-4.474 11.662-4.717 16.401-.525C170.76 415.336 211.58 432 256.333 432c97.268 0 176-78.716 176-176 0-97.267-78.716-176-176-176-58.496 0-110.28 28.476-142.274 72.333h98.274c6.627 0 12 5.373 12 12v48c0 6.627-5.373 12-12 12z"></path></svg>
|
After Width: | Height: | Size: 767 B |
11
ui/packages/json/README.md
Normal file
11
ui/packages/json/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# `@gradio/button`
|
||||
|
||||
```html
|
||||
<script>
|
||||
import { Button } from "@gradio/button";
|
||||
</script>
|
||||
|
||||
<button type="primary|secondary" href="string" on:click="{e.detail === href}">
|
||||
content
|
||||
</button>
|
||||
```
|
10
ui/packages/json/package.json
Normal file
10
ui/packages/json/package.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "@gradio/json",
|
||||
"version": "0.0.1",
|
||||
"description": "Gradio UI packages",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"private": true
|
||||
}
|
16
ui/packages/json/src/JSON.svelte
Normal file
16
ui/packages/json/src/JSON.svelte
Normal file
@ -0,0 +1,16 @@
|
||||
<script lang="ts">
|
||||
import JSONNode from "./JSONNode.svelte";
|
||||
|
||||
export let value: any;
|
||||
export let theme: string = "default";
|
||||
</script>
|
||||
|
||||
<div class="output-json font-mono leading-relaxed" {theme}>
|
||||
<JSONNode {value} depth={0} {theme} />
|
||||
</div>
|
||||
|
||||
<style lang="postcss" global>
|
||||
.output-text[theme="default"] {
|
||||
@apply shadow transition hover:shadow-md dark:bg-gray-800;
|
||||
}
|
||||
</style>
|
84
ui/packages/json/src/JSONNode.svelte
Normal file
84
ui/packages/json/src/JSONNode.svelte
Normal file
@ -0,0 +1,84 @@
|
||||
<script lang="ts">
|
||||
export let value: any;
|
||||
export let theme: string = "default";
|
||||
export let depth: number;
|
||||
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} />
|
||||
{#if i !== value.length - 1}
|
||||
,
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
]
|
||||
{/if}
|
||||
{:else if value instanceof Object}
|
||||
{#if collapsed}
|
||||
<button
|
||||
on:click={() => {
|
||||
collapsed = false;
|
||||
}}
|
||||
>
|
||||
{+{Object.keys(value).length} items}
|
||||
</button>
|
||||
{:else}
|
||||
{
|
||||
<div class="json-children pl-4">
|
||||
{#each Object.entries(value) as node, i}
|
||||
<div class="json-item">
|
||||
{node[0]}: <svelte:self
|
||||
value={node[1]}
|
||||
depth={depth + 1}
|
||||
key={i}
|
||||
{theme}
|
||||
/><!--
|
||||
-->{#if i !== Object.keys(value).length - 1}<!--
|
||||
-->,
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
}
|
||||
{/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>
|
1
ui/packages/json/src/index.ts
Normal file
1
ui/packages/json/src/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as JSON } from "./JSON.svelte";
|
11
ui/packages/label/README.md
Normal file
11
ui/packages/label/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# `@gradio/button`
|
||||
|
||||
```html
|
||||
<script>
|
||||
import { Button } from "@gradio/button";
|
||||
</script>
|
||||
|
||||
<button type="primary|secondary" href="string" on:click="{e.detail === href}">
|
||||
content
|
||||
</button>
|
||||
```
|
10
ui/packages/label/package.json
Normal file
10
ui/packages/label/package.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "@gradio/label",
|
||||
"version": "0.0.1",
|
||||
"description": "Gradio UI packages",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"private": true
|
||||
}
|
55
ui/packages/label/src/Label.svelte
Normal file
55
ui/packages/label/src/Label.svelte
Normal file
@ -0,0 +1,55 @@
|
||||
<script lang="ts">
|
||||
export let value: {
|
||||
label: string;
|
||||
confidences?: Array<{ label: string; confidence: number }>;
|
||||
};
|
||||
export let theme: string = "default";
|
||||
</script>
|
||||
|
||||
<div class="output-label" {theme}>
|
||||
<div
|
||||
class="output-class font-bold text-2xl py-6 px-4 flex-grow flex items-center justify-center"
|
||||
class:no-confidence={!("confidences" in value)}
|
||||
>
|
||||
{value.label}
|
||||
</div>
|
||||
{#if value.confidences}
|
||||
<div class="confidence-intervals flex text-xl">
|
||||
<div class="labels mr-2" style="maxWidth: 120px">
|
||||
{#each value.confidences as confidence_set}
|
||||
<div
|
||||
class="label overflow-hidden whitespace-nowrap h-7 mb-2 overflow-ellipsis text-right"
|
||||
title={confidence_set.label}
|
||||
>
|
||||
{confidence_set.label}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="confidences flex flex-grow flex-col items-baseline">
|
||||
{#each value.confidences as confidence_set, i}
|
||||
<div
|
||||
class="confidence flex justify-end items-center overflow-hidden whitespace-nowrap h-7 mb-2 px-1"
|
||||
style="min-width: calc(
|
||||
{Math.round(confidence_set.confidence * 100)}% - 12px)"
|
||||
>
|
||||
{Math.round(confidence_set.confidence * 100)}%
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
.output-label[theme="default"] {
|
||||
.label {
|
||||
@apply text-base h-7;
|
||||
}
|
||||
.confidence {
|
||||
@apply font-mono box-border border-b-2 border-gray-300 bg-gray-200 dark:bg-gray-500 dark:border-gray-600 text-sm h-7 font-semibold rounded;
|
||||
}
|
||||
.confidence:first-child {
|
||||
@apply bg-amber-500 dark:bg-red-600 border-red-700 text-white;
|
||||
}
|
||||
}
|
||||
</style>
|
1
ui/packages/label/src/index.ts
Normal file
1
ui/packages/label/src/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as Label } from "./Label.svelte";
|
1
ui/packages/table/README.md
Normal file
1
ui/packages/table/README.md
Normal file
@ -0,0 +1 @@
|
||||
# `@gradio/table`
|
10
ui/packages/table/package.json
Normal file
10
ui/packages/table/package.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "@gradio/table",
|
||||
"version": "0.0.1",
|
||||
"description": "Gradio UI packages",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"private": true
|
||||
}
|
400
ui/packages/table/src/Table.svelte
Normal file
400
ui/packages/table/src/Table.svelte
Normal file
@ -0,0 +1,400 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher, tick } from "svelte";
|
||||
|
||||
export let headers: Array<string> = [];
|
||||
export let values: Array<Array<string | number>> = [["", "", ""]];
|
||||
// export let default_data: Array<Array<string | number>> = [];
|
||||
|
||||
if ($$props.default) values = $$props.default;
|
||||
|
||||
// export let setValue: (val: typeof values) => typeof values;
|
||||
export let editable = true;
|
||||
|
||||
const dispatch = createEventDispatcher<{ change: typeof values }>();
|
||||
|
||||
let id = 0;
|
||||
let editing: boolean | number = false;
|
||||
let selected: boolean | number = false;
|
||||
let els: Record<
|
||||
string,
|
||||
{ cell: null | HTMLTableCellElement; input: null | HTMLInputElement }
|
||||
> = {};
|
||||
|
||||
type Headers = Array<{ value: string; id: number }>;
|
||||
|
||||
function make_headers(_h: Array<string>): Headers {
|
||||
if (_h.length === 0) {
|
||||
return values[0].map((_, i) => {
|
||||
const _id = ++id;
|
||||
els[_id] = { cell: null, input: null };
|
||||
return { id: _id, value: JSON.stringify(i + 1) };
|
||||
});
|
||||
} else {
|
||||
return _h.map((h) => {
|
||||
const _id = ++id;
|
||||
els[_id] = { cell: null, input: null };
|
||||
return { id: _id, value: h };
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let _headers = make_headers(headers);
|
||||
|
||||
let data = values.map((x) =>
|
||||
x.map((n) => {
|
||||
const _id = ++id;
|
||||
els[id] = { input: null, cell: null };
|
||||
return { value: n, id: _id };
|
||||
})
|
||||
) || [
|
||||
Array(headers.length)
|
||||
.fill(0)
|
||||
|
||||
.map((_) => {
|
||||
const _id = ++id;
|
||||
els[id] = { input: null, cell: null };
|
||||
|
||||
return { value: "", id: _id };
|
||||
})
|
||||
];
|
||||
|
||||
$: dispatch(
|
||||
"change",
|
||||
data.map((r) => r.map(({ value }) => value))
|
||||
);
|
||||
|
||||
function get_sort_status(
|
||||
name: string,
|
||||
sort: number,
|
||||
direction?: SortDirection
|
||||
) {
|
||||
if (!sort) return "none";
|
||||
if (headers[sort] === name) {
|
||||
if (direction === "asc") return "ascending";
|
||||
if (direction === "des") return "descending";
|
||||
}
|
||||
}
|
||||
|
||||
async function start_edit(id: number) {
|
||||
if (!editable) return;
|
||||
editing = id;
|
||||
await tick();
|
||||
const { input } = els[id];
|
||||
input?.focus();
|
||||
}
|
||||
|
||||
function handle_keydown(
|
||||
event: KeyboardEvent,
|
||||
i: number,
|
||||
j: number,
|
||||
id: number
|
||||
) {
|
||||
let is_data;
|
||||
switch (event.key) {
|
||||
case "ArrowRight":
|
||||
if (editing) break;
|
||||
event.preventDefault();
|
||||
is_data = data[i][j + 1];
|
||||
selected = is_data ? is_data.id : selected;
|
||||
break;
|
||||
case "ArrowLeft":
|
||||
if (editing) break;
|
||||
event.preventDefault();
|
||||
is_data = data[i][j - 1];
|
||||
selected = is_data ? is_data.id : selected;
|
||||
break;
|
||||
case "ArrowDown":
|
||||
if (editing) break;
|
||||
event.preventDefault();
|
||||
is_data = data[i + 1];
|
||||
selected = is_data ? is_data[j].id : selected;
|
||||
break;
|
||||
case "ArrowUp":
|
||||
if (editing) break;
|
||||
event.preventDefault();
|
||||
is_data = data[i - 1];
|
||||
selected = is_data ? is_data[j].id : selected;
|
||||
break;
|
||||
case "Escape":
|
||||
if (!editable) break;
|
||||
event.preventDefault();
|
||||
editing = false;
|
||||
break;
|
||||
case "Enter":
|
||||
if (!editable) break;
|
||||
event.preventDefault();
|
||||
if (editing === id) {
|
||||
editing = false;
|
||||
} else {
|
||||
editing = id;
|
||||
}
|
||||
break;
|
||||
case "Backspace":
|
||||
if (!editable) break;
|
||||
if (!editing) {
|
||||
event.preventDefault();
|
||||
data[i][j].value = "";
|
||||
}
|
||||
break;
|
||||
case "Delete":
|
||||
if (!editable) break;
|
||||
if (!editing) {
|
||||
event.preventDefault();
|
||||
data[i][j].value = "";
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
async function handle_cell_click(id: number) {
|
||||
editing = false;
|
||||
selected = id;
|
||||
}
|
||||
|
||||
async function set_focus(id: number | boolean, type: "edit" | "select") {
|
||||
if (type === "edit" && typeof id == "number") {
|
||||
await tick();
|
||||
els[id].input?.focus();
|
||||
}
|
||||
|
||||
if (
|
||||
type === "edit" &&
|
||||
typeof id == "boolean" &&
|
||||
typeof selected === "number"
|
||||
) {
|
||||
let cell = els[selected]?.cell;
|
||||
await tick();
|
||||
cell?.focus();
|
||||
}
|
||||
|
||||
if (type === "select" && typeof id == "number") {
|
||||
const { cell } = els[id];
|
||||
cell?.setAttribute("tabindex", "0");
|
||||
await tick();
|
||||
els[id].cell?.focus();
|
||||
}
|
||||
}
|
||||
|
||||
$: set_focus(editing, "edit");
|
||||
$: set_focus(selected, "select");
|
||||
|
||||
type SortDirection = "asc" | "des";
|
||||
let sort_direction: SortDirection;
|
||||
let sort_by: number;
|
||||
|
||||
function sort(col: number, dir: SortDirection) {
|
||||
if (dir === "asc") {
|
||||
data = data.sort((a, b) => (a[col].value < b[col].value ? -1 : 1));
|
||||
} else if (dir === "des") {
|
||||
data = data.sort((a, b) => (a[col].value > b[col].value ? -1 : 1));
|
||||
}
|
||||
}
|
||||
|
||||
function handle_sort(col: number) {
|
||||
if (typeof sort_by !== "number" || sort_by !== col) {
|
||||
sort_direction = "asc";
|
||||
sort_by = col;
|
||||
} else {
|
||||
if (sort_direction === "asc") {
|
||||
sort_direction = "des";
|
||||
} else if (sort_direction === "des") {
|
||||
sort_direction = "asc";
|
||||
}
|
||||
}
|
||||
|
||||
sort(col, sort_direction);
|
||||
}
|
||||
|
||||
let header_edit: number | boolean;
|
||||
|
||||
async function edit_header(_id: number, select?: boolean) {
|
||||
if (!editable) return;
|
||||
header_edit = _id;
|
||||
await tick();
|
||||
els[_id].input?.focus();
|
||||
|
||||
if (select) els[_id].input?.select();
|
||||
}
|
||||
|
||||
function end_header_edit(event: KeyboardEvent) {
|
||||
if (!editable) return;
|
||||
|
||||
switch (event.key) {
|
||||
case "Escape":
|
||||
event.preventDefault();
|
||||
header_edit = false;
|
||||
break;
|
||||
case "Enter":
|
||||
event.preventDefault();
|
||||
header_edit = false;
|
||||
}
|
||||
}
|
||||
|
||||
function add_row() {
|
||||
data.push(
|
||||
headers.map(() => {
|
||||
const _id = ++id;
|
||||
els[_id] = { cell: null, input: null };
|
||||
return { id: _id, value: "" };
|
||||
})
|
||||
);
|
||||
data = data;
|
||||
}
|
||||
|
||||
async function add_col() {
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const _id = ++id;
|
||||
els[_id] = { cell: null, input: null };
|
||||
data[i].push({ id: _id, value: "" });
|
||||
}
|
||||
|
||||
const _id = ++id;
|
||||
els[_id] = { cell: null, input: null };
|
||||
_headers.push({ id: _id, value: `Header ${_headers.length + 1}` });
|
||||
|
||||
data = data;
|
||||
_headers = _headers;
|
||||
|
||||
await tick();
|
||||
|
||||
edit_header(_id, true);
|
||||
}
|
||||
|
||||
const double_click = (
|
||||
node: HTMLElement,
|
||||
{ click, dblclick }: { click: Function; dblclick: Function }
|
||||
) => {
|
||||
let timer: NodeJS.Timeout | undefined;
|
||||
|
||||
function handler(event: MouseEvent) {
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
timer = undefined;
|
||||
dblclick(event);
|
||||
} else {
|
||||
timer = setTimeout(() => {
|
||||
click(event);
|
||||
timer = undefined;
|
||||
}, 250);
|
||||
}
|
||||
}
|
||||
|
||||
node.addEventListener("click", handler);
|
||||
|
||||
return {
|
||||
destroy: () => node.removeEventListener("click", handler)
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="shadow overflow-hidden border-gray-200 rounded-sm relative">
|
||||
<table
|
||||
id="grid"
|
||||
role="grid"
|
||||
aria-labelledby="title"
|
||||
class="min-w-full divide-y divide-gray-200 "
|
||||
>
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
{#each _headers as { value, id }, i (id)}
|
||||
<th
|
||||
use:double_click={{
|
||||
click: () => handle_sort(i),
|
||||
dblclick: () => edit_header(id)
|
||||
}}
|
||||
aria-sort={get_sort_status(value, sort_by, sort_direction)}
|
||||
class="after:absolute after:opacity-0 after:content-['▲'] after:ml-2 after:inset-y-0 after:h-[1.05rem] after:m-auto relative px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
|
||||
class:sorted={sort_by === i}
|
||||
class:des={sort_by === i && sort_direction === "des"}
|
||||
>
|
||||
{#if header_edit === id}
|
||||
<input
|
||||
class="bg-transparent inset-y-0 left-6 outline-none absolute p-0 w-3/4 text-xs font-medium text-gray-500 uppercase tracking-wider"
|
||||
tabindex="-1"
|
||||
bind:value
|
||||
bind:this={els[id].input}
|
||||
on:keydown={end_header_edit}
|
||||
on:blur={({ currentTarget }) =>
|
||||
currentTarget.setAttribute("tabindex", "-1")}
|
||||
/>
|
||||
{/if}
|
||||
<span
|
||||
tabindex="-1"
|
||||
role="button"
|
||||
class="min-h-full"
|
||||
class:opacity-0={header_edit === id}>{value}</span
|
||||
>
|
||||
</th>
|
||||
{/each}
|
||||
</tr></thead
|
||||
><tbody class="bg-white divide-y divide-gray-200">
|
||||
{#each data as row, i (row)}
|
||||
<tr>
|
||||
{#each row as { value, id }, j (id)}
|
||||
<td
|
||||
tabindex="-1"
|
||||
class="p-0 whitespace-nowrap display-block outline-none relative "
|
||||
on:dblclick={() => start_edit(id)}
|
||||
on:click={() => handle_cell_click(id)}
|
||||
on:keydown={(e) => handle_keydown(e, i, j, id)}
|
||||
bind:this={els[id].cell}
|
||||
on:blur={({ currentTarget }) =>
|
||||
currentTarget.setAttribute("tabindex", "-1")}
|
||||
>
|
||||
<div
|
||||
class:border-gray-600={selected === id}
|
||||
class:border-transparent={selected !== id}
|
||||
class="min-h-[3.3rem] px-5 py-3 border-[0.125rem]"
|
||||
>
|
||||
{#if editing === id}
|
||||
<input
|
||||
class="outline-none absolute p-0 w-3/4"
|
||||
tabindex="-1"
|
||||
bind:value
|
||||
bind:this={els[id].input}
|
||||
on:blur={({ currentTarget }) =>
|
||||
currentTarget.setAttribute("tabindex", "-1")}
|
||||
/>
|
||||
{/if}
|
||||
<span
|
||||
class=" cursor-default w-full"
|
||||
class:opacity-0={editing === id}
|
||||
tabindex="-1"
|
||||
role="button"
|
||||
>
|
||||
{value}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
{/each}
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{#if editable}
|
||||
<div class="flex justify-end ">
|
||||
<button
|
||||
on:click={add_col}
|
||||
class="hover:bg-gray-100 dark:hover:bg-gray-600 shadow py-1 px-3 rounded transition focus:outline-none m-2 mr-0"
|
||||
>New Column</button
|
||||
>
|
||||
<button
|
||||
on:click={add_row}
|
||||
class="bg-amber-500 hover:bg-amber-400 dark:bg-red-700 dark:hover:bg-red-600 text-white shadow py-1 px-3 rounded transition focus:outline-none m-2 mr-0"
|
||||
>New Row</button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.sorted::after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.des::after {
|
||||
transform: rotate(180deg) translateY(1.5px);
|
||||
}
|
||||
</style>
|
1
ui/packages/table/src/index.ts
Normal file
1
ui/packages/table/src/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as Table } from "./Table.svelte";
|
@ -6,8 +6,5 @@
|
||||
"main": "src/index.ts",
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@gradio/upload": "workspace:^0.0.1"
|
||||
}
|
||||
"private": true
|
||||
}
|
||||
|
11
ui/packages/tooltip/README.md
Normal file
11
ui/packages/tooltip/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# `@gradio/button`
|
||||
|
||||
```html
|
||||
<script>
|
||||
import { Button } from "@gradio/button";
|
||||
</script>
|
||||
|
||||
<button type="primary|secondary" href="string" on:click="{e.detail === href}">
|
||||
content
|
||||
</button>
|
||||
```
|
10
ui/packages/tooltip/package.json
Normal file
10
ui/packages/tooltip/package.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "@gradio/tooltip",
|
||||
"version": "0.0.1",
|
||||
"description": "Gradio UI packages",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"private": true
|
||||
}
|
20
ui/packages/tooltip/src/Tooltip.svelte
Normal file
20
ui/packages/tooltip/src/Tooltip.svelte
Normal file
@ -0,0 +1,20 @@
|
||||
<script lang="ts">
|
||||
export let text: string;
|
||||
export let x: number;
|
||||
export let y: number;
|
||||
export let color: string;
|
||||
|
||||
let w: number;
|
||||
let h: number;
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="bg-black bg-opacity-80 text-white border-r-2 p-1 absolute text-xs flex items-center justify-center"
|
||||
bind:offsetWidth={w}
|
||||
bind:offsetHeight={h}
|
||||
style="
|
||||
top: {y - h / 2}px;
|
||||
left: {x - w - 7}px;"
|
||||
>
|
||||
<span class="inline-block w-3 h-3 mr-1" style="background: {color}" />{text}
|
||||
</div>
|
1
ui/packages/tooltip/src/index.ts
Normal file
1
ui/packages/tooltip/src/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { tooltip } from "./tooltip";
|
49
ui/packages/tooltip/src/tooltip.ts
Normal file
49
ui/packages/tooltip/src/tooltip.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import Tooltip from "./Tooltip.svelte";
|
||||
|
||||
interface ActionArgs {
|
||||
color: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export function tooltip(
|
||||
element: HTMLElement | SVGElement,
|
||||
{ color, text }: ActionArgs
|
||||
) {
|
||||
let tooltipComponent: Tooltip;
|
||||
function mouse_over(event: MouseEvent) {
|
||||
tooltipComponent = new Tooltip({
|
||||
props: {
|
||||
text,
|
||||
x: event.pageX,
|
||||
y: event.pageY,
|
||||
color
|
||||
},
|
||||
target: document.body
|
||||
});
|
||||
|
||||
return event;
|
||||
}
|
||||
function mouseMove(event: MouseEvent) {
|
||||
tooltipComponent.$set({
|
||||
x: event.pageX,
|
||||
y: event.pageY
|
||||
});
|
||||
}
|
||||
function mouseLeave() {
|
||||
tooltipComponent.$destroy();
|
||||
}
|
||||
|
||||
const el = element as HTMLElement;
|
||||
|
||||
el.addEventListener("mouseover", mouse_over);
|
||||
el.addEventListener("mouseleave", mouseLeave);
|
||||
el.addEventListener("mousemove", mouseMove);
|
||||
|
||||
return {
|
||||
destroy() {
|
||||
el.removeEventListener("mouseover", mouse_over);
|
||||
el.removeEventListener("mouseleave", mouseLeave);
|
||||
el.removeEventListener("mousemove", mouseMove);
|
||||
}
|
||||
};
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import type { FileData } from "./types";
|
||||
import edit from "./edit.svg";
|
||||
import clear from "./clear.svg";
|
||||
|
||||
@ -8,8 +9,7 @@
|
||||
export let theme: string;
|
||||
export let absolute = true;
|
||||
|
||||
const dispatch =
|
||||
createEventDispatcher<{ edit: undefined; clear: undefined }>();
|
||||
const dispatch = createEventDispatcher<{ edit: FileData; clear: null }>();
|
||||
</script>
|
||||
|
||||
<div
|
||||
|
@ -1,17 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from "svelte";
|
||||
interface FileData {
|
||||
name: string;
|
||||
size: number;
|
||||
data: string;
|
||||
is_example: false;
|
||||
}
|
||||
import type { FileData } from "./types";
|
||||
|
||||
// export let load: (
|
||||
// val: Array<string | FileData> | string | FileData | null
|
||||
// ) => Array<string | FileData> | string | FileData | null;
|
||||
export let filetype: string | undefined = undefined;
|
||||
export let theme: string;
|
||||
export let theme: string = "default";
|
||||
export let single_file: boolean = true;
|
||||
export let include_file_metadata = true;
|
||||
let hidden_upload: HTMLInputElement;
|
||||
|
@ -1,2 +1,3 @@
|
||||
export { default as Upload } from "./Upload.svelte";
|
||||
export { default as ModifyUpload } from "./ModifyUpload.svelte";
|
||||
export type { FileData } from "./types";
|
||||
|
6
ui/packages/upload/src/types.ts
Normal file
6
ui/packages/upload/src/types.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export interface FileData {
|
||||
name: string;
|
||||
size: number;
|
||||
data: string;
|
||||
is_example: false;
|
||||
}
|
11
ui/packages/video/README.md
Normal file
11
ui/packages/video/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# `@gradio/button`
|
||||
|
||||
```html
|
||||
<script>
|
||||
import { Button } from "@gradio/button";
|
||||
</script>
|
||||
|
||||
<button type="primary|secondary" href="string" on:click="{e.detail === href}">
|
||||
content
|
||||
</button>
|
||||
```
|
13
ui/packages/video/package.json
Normal file
13
ui/packages/video/package.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@gradio/video",
|
||||
"version": "0.0.1",
|
||||
"description": "Gradio UI packages",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@gradio/upload": "workspace:^0.0.1"
|
||||
}
|
||||
}
|
62
ui/packages/video/src/Video.svelte
Normal file
62
ui/packages/video/src/Video.svelte
Normal file
@ -0,0 +1,62 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { Upload, ModifyUpload } from "@gradio/upload";
|
||||
import type { FileData } from "@gradio/upload";
|
||||
import { prettyBytes, playable } from "./utils";
|
||||
|
||||
export let value: FileData | null = null;
|
||||
export let theme: string = "default";
|
||||
export let source: string;
|
||||
|
||||
export let drop_text: string = "Drop a video file";
|
||||
export let or_text: string = "or";
|
||||
export let upload_text: string = "click to upload";
|
||||
|
||||
const dispatch = createEventDispatcher<{ change: FileData | null }>();
|
||||
|
||||
function handle_load({ detail }: CustomEvent<FileData | null>) {
|
||||
dispatch("change", detail);
|
||||
value = detail;
|
||||
}
|
||||
|
||||
function handle_clear({ detail }: CustomEvent<FileData | null>) {
|
||||
dispatch("change", null);
|
||||
value = null;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="video-preview w-full h-60 object-contain flex justify-center items-center dark:bg-gray-600 relative"
|
||||
class:bg-gray-200={value}
|
||||
>
|
||||
{#if value === null}
|
||||
{#if source === "upload"}
|
||||
<Upload
|
||||
filetype="video/mp4,video/x-m4v,video/*"
|
||||
on:load={handle_load}
|
||||
{theme}
|
||||
>
|
||||
{drop_text}
|
||||
<br />- {or_text} -<br />
|
||||
{upload_text}
|
||||
</Upload>
|
||||
{/if}
|
||||
{:else}
|
||||
<ModifyUpload on:clear={handle_clear} {theme} />
|
||||
{#if playable(value.name)}
|
||||
<!-- svelte-ignore a11y-media-has-caption -->
|
||||
<video
|
||||
class="w-full h-full object-contain bg-black"
|
||||
controls
|
||||
playsInline
|
||||
preload="auto"
|
||||
src={value.data}
|
||||
/>
|
||||
{:else}
|
||||
<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>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
1
ui/packages/video/src/index.ts
Normal file
1
ui/packages/video/src/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as Video } from "./Video.svelte";
|
17
ui/packages/video/src/utils.ts
Normal file
17
ui/packages/video/src/utils.ts
Normal file
@ -0,0 +1,17 @@
|
||||
export const prettyBytes = (bytes: number): string => {
|
||||
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;
|
||||
};
|
||||
|
||||
export const playable = (filename: string): boolean => {
|
||||
// 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
|
||||
};
|
@ -25,6 +25,19 @@
|
||||
"dependencies": {
|
||||
"@gradio/audio": "workspace:^0.0.1",
|
||||
"@gradio/button": "workspace:^0.0.1",
|
||||
"@gradio/theme": "workspace:^0.0.1"
|
||||
"@gradio/carousel": "workspace:^0.0.1",
|
||||
"@gradio/chart": "workspace:^0.0.1",
|
||||
"@gradio/chatbot": "workspace:^0.0.1",
|
||||
"@gradio/file": "workspace:^0.0.1",
|
||||
"@gradio/form": "workspace:^0.0.1",
|
||||
"@gradio/highlighted-text": "workspace:^0.0.1",
|
||||
"@gradio/html": "workspace:^0.0.1",
|
||||
"@gradio/image": "workspace:^0.0.1",
|
||||
"@gradio/json": "workspace:^0.0.1",
|
||||
"@gradio/label": "workspace:^0.0.1",
|
||||
"@gradio/table": "workspace:^0.0.1",
|
||||
"@gradio/theme": "workspace:^0.0.1",
|
||||
"@gradio/upload": "workspace:^0.0.1",
|
||||
"@gradio/video": "workspace:^0.0.1"
|
||||
}
|
||||
}
|
||||
|
BIN
ui/packages/workbench/src/assets/cheetah1.jpg
Normal file
BIN
ui/packages/workbench/src/assets/cheetah1.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
8
ui/packages/workbench/src/assets/time.csv
Normal file
8
ui/packages/workbench/src/assets/time.csv
Normal file
@ -0,0 +1,8 @@
|
||||
time,value,price
|
||||
1,1,4
|
||||
2,3,8
|
||||
3,6,12
|
||||
4,10,16
|
||||
5,15,20
|
||||
6,21,24
|
||||
7,28,28
|
|
@ -3,7 +3,6 @@
|
||||
const comp_routes = [
|
||||
"Audio",
|
||||
"Button",
|
||||
"DataFrame",
|
||||
"Carousel",
|
||||
"Chatbot",
|
||||
"Chart",
|
||||
@ -14,13 +13,10 @@
|
||||
"Image",
|
||||
"JSON",
|
||||
"Label",
|
||||
"Markdown",
|
||||
"Tooltip",
|
||||
"Table",
|
||||
"Upload",
|
||||
"Video"
|
||||
].map((n) => [n, n.toLowerCase()]);
|
||||
|
||||
$: console.log($page.url);
|
||||
</script>
|
||||
|
||||
<nav class="inline-block">
|
||||
|
@ -1,6 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { Audio } from "@gradio/audio";
|
||||
import audio from "../assets/cantina.wav";
|
||||
</script>
|
||||
|
||||
<h2>upload input</h2>
|
||||
|
13
ui/packages/workbench/src/routes/carousel.svelte
Normal file
13
ui/packages/workbench/src/routes/carousel.svelte
Normal file
@ -0,0 +1,13 @@
|
||||
<script lang="ts">
|
||||
import { Carousel, CarouselItem } from "@gradio/carousel";
|
||||
import cheetah from "../assets/cheetah1.jpg";
|
||||
</script>
|
||||
|
||||
<Carousel>
|
||||
<CarouselItem>
|
||||
<h1>hello</h1>
|
||||
</CarouselItem>
|
||||
<CarouselItem>
|
||||
<img src={cheetah} alt="" />
|
||||
</CarouselItem>
|
||||
</Carousel>
|
14
ui/packages/workbench/src/routes/chart.svelte
Normal file
14
ui/packages/workbench/src/routes/chart.svelte
Normal file
@ -0,0 +1,14 @@
|
||||
<script lang="ts">
|
||||
import { Chart } from "@gradio/chart";
|
||||
|
||||
const csv = `time,value,price
|
||||
1,1,4
|
||||
2,3,8
|
||||
3,6,12
|
||||
4,10,16
|
||||
5,15,20
|
||||
6,21,24
|
||||
7,28,28`;
|
||||
</script>
|
||||
|
||||
<Chart value={csv} />
|
14
ui/packages/workbench/src/routes/chatbot.svelte
Normal file
14
ui/packages/workbench/src/routes/chatbot.svelte
Normal file
@ -0,0 +1,14 @@
|
||||
<script lang="ts">
|
||||
import { ChatBot } from "@gradio/chatbot";
|
||||
|
||||
let messages: Array<[string, string]> = [
|
||||
["message one", "message two"],
|
||||
["message three", "message four"]
|
||||
];
|
||||
|
||||
setTimeout(() => {
|
||||
messages = [...messages, ["message five", "message six"]];
|
||||
}, 1000);
|
||||
</script>
|
||||
|
||||
<ChatBot value={messages} />
|
5
ui/packages/workbench/src/routes/file.svelte
Normal file
5
ui/packages/workbench/src/routes/file.svelte
Normal file
@ -0,0 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { File } from "@gradio/file";
|
||||
</script>
|
||||
|
||||
<File value={null} />
|
31
ui/packages/workbench/src/routes/forms.svelte
Normal file
31
ui/packages/workbench/src/routes/forms.svelte
Normal file
@ -0,0 +1,31 @@
|
||||
<script lang="ts">
|
||||
import {
|
||||
Checkbox,
|
||||
CheckboxGroup,
|
||||
Dropdown,
|
||||
Radio,
|
||||
TextBox,
|
||||
Number
|
||||
} from "@gradio/form";
|
||||
</script>
|
||||
|
||||
<h2>TextBox</h2>
|
||||
<TextBox on:change={({ detail }) => console.log(detail)} />
|
||||
|
||||
<h2>TextArea</h2>
|
||||
<TextBox lines={5} on:change={({ detail }) => console.log(detail)} />
|
||||
|
||||
<h2>Number</h2>
|
||||
<Number on:change={({ detail }) => console.log(detail)} />
|
||||
|
||||
<h2>Radio</h2>
|
||||
<Radio choices={["true", "false"]} value={"true"} />
|
||||
|
||||
<h2>CheckBox</h2>
|
||||
<Checkbox value={false} />
|
||||
|
||||
<h2>Checkbox Group</h2>
|
||||
<CheckboxGroup choices={["one", "two"]} value={[]} />
|
||||
|
||||
<h2>Dropdown</h2>
|
||||
<Dropdown label={"choose"} choices={["one", "two", "three"]} />
|
51
ui/packages/workbench/src/routes/highlightedtext.svelte
Normal file
51
ui/packages/workbench/src/routes/highlightedtext.svelte
Normal file
@ -0,0 +1,51 @@
|
||||
<script lang="ts">
|
||||
import { HighlightedText } from "@gradio/highlighted-text";
|
||||
</script>
|
||||
|
||||
<h2>With legend</h2>
|
||||
<HighlightedText
|
||||
show_legend={true}
|
||||
value={[
|
||||
["one", "1"],
|
||||
["two", "2"],
|
||||
["three", "3"],
|
||||
["two", "2"],
|
||||
["three", "3"]
|
||||
]}
|
||||
/>
|
||||
|
||||
<h2>Without legend</h2>
|
||||
<HighlightedText
|
||||
value={[
|
||||
["one", "1"],
|
||||
["two", "2"],
|
||||
["three", "3"],
|
||||
["two", "2"],
|
||||
["three", "3"]
|
||||
]}
|
||||
/>
|
||||
|
||||
<h2>Custom color_map with legend</h2>
|
||||
<HighlightedText
|
||||
show_legend={true}
|
||||
value={[
|
||||
["one", "+"],
|
||||
["two", "-"],
|
||||
["three", "-"],
|
||||
["two", "+"],
|
||||
["three", "-"]
|
||||
]}
|
||||
color_map={{ "+": "limegreen", "-": "crimson" }}
|
||||
/>
|
||||
|
||||
<h2>Custom color_map without legend</h2>
|
||||
<HighlightedText
|
||||
value={[
|
||||
["one", "+"],
|
||||
["two", "-"],
|
||||
["three", "-"],
|
||||
["two", "+"],
|
||||
["three", "-"]
|
||||
]}
|
||||
color_map={{ "+": "limegreen", "-": "crimson" }}
|
||||
/>
|
7
ui/packages/workbench/src/routes/html.svelte
Normal file
7
ui/packages/workbench/src/routes/html.svelte
Normal file
@ -0,0 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { HTML } from "@gradio/html";
|
||||
|
||||
const src = `<h1>hello</h1>`;
|
||||
</script>
|
||||
|
||||
<HTML value={src} />
|
20
ui/packages/workbench/src/routes/image.svelte
Normal file
20
ui/packages/workbench/src/routes/image.svelte
Normal file
@ -0,0 +1,20 @@
|
||||
<script lang="ts">
|
||||
import { browser } from "$app/env";
|
||||
import { Image } from "@gradio/image";
|
||||
|
||||
const src = `<h1>hello</h1>`;
|
||||
</script>
|
||||
|
||||
{#if browser}
|
||||
<h2>Editor with upload</h2>
|
||||
<Image value={null} source={"upload"} tool="editor" />
|
||||
|
||||
<h2>Editor with webcam</h2>
|
||||
<Image value={null} source={"webcam"} tool="editor" />
|
||||
|
||||
<h2>Editor with sketch</h2>
|
||||
<Image value={null} source={"canvas"} tool="editor" />
|
||||
|
||||
<h2>Cropper with upload</h2>
|
||||
<Image value={null} source={"upload"} tool="select" />
|
||||
{/if}
|
34
ui/packages/workbench/src/routes/json.svelte
Normal file
34
ui/packages/workbench/src/routes/json.svelte
Normal file
@ -0,0 +1,34 @@
|
||||
<script lang="ts">
|
||||
import { JSON } from "@gradio/json";
|
||||
|
||||
const json = {
|
||||
a: 1,
|
||||
b: "two",
|
||||
c: [
|
||||
1,
|
||||
"two",
|
||||
[1, 2, 3],
|
||||
{
|
||||
a: 1,
|
||||
b: "two",
|
||||
c: [1, "two", []]
|
||||
}
|
||||
],
|
||||
d: {
|
||||
a: 1,
|
||||
b: "two",
|
||||
c: [
|
||||
1,
|
||||
"two",
|
||||
[1, 2, 3],
|
||||
{
|
||||
a: 1,
|
||||
b: "two",
|
||||
c: [1, "two", []]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<JSON value={json} />
|
20
ui/packages/workbench/src/routes/label.svelte
Normal file
20
ui/packages/workbench/src/routes/label.svelte
Normal file
@ -0,0 +1,20 @@
|
||||
<script lang="ts">
|
||||
import { Label } from "@gradio/label";
|
||||
|
||||
const data = {
|
||||
label: "hello",
|
||||
confidences: [
|
||||
{ label: "hello", confidence: 0.8 },
|
||||
{ label: "helooo", confidence: 0.4 },
|
||||
{ label: "helloooo", confidence: 0.6 }
|
||||
]
|
||||
};
|
||||
</script>
|
||||
|
||||
<h2>Without confidence</h2>
|
||||
|
||||
<Label value={{ label: "hello" }} />
|
||||
|
||||
<h2>With confidence</h2>
|
||||
|
||||
<Label value={data} />
|
5
ui/packages/workbench/src/routes/table.svelte
Normal file
5
ui/packages/workbench/src/routes/table.svelte
Normal file
@ -0,0 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { Table } from "@gradio/table";
|
||||
</script>
|
||||
|
||||
<Table />
|
7
ui/packages/workbench/src/routes/upload.svelte
Normal file
7
ui/packages/workbench/src/routes/upload.svelte
Normal file
@ -0,0 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { Upload } from "@gradio/upload";
|
||||
</script>
|
||||
|
||||
<Upload on:load={({ detail }) => console.log("change", detail)}
|
||||
>drag or click</Upload
|
||||
>
|
5
ui/packages/workbench/src/routes/video.svelte
Normal file
5
ui/packages/workbench/src/routes/video.svelte
Normal file
@ -0,0 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { Video } from "@gradio/video";
|
||||
</script>
|
||||
|
||||
<Video value={null} source={"upload"} />
|
@ -79,17 +79,113 @@ importers:
|
||||
dependencies:
|
||||
'@gradio/upload': link:../upload
|
||||
|
||||
packages/carousel:
|
||||
specifiers:
|
||||
'@gradio/upload': workspace:^0.0.1
|
||||
dependencies:
|
||||
'@gradio/upload': link:../upload
|
||||
|
||||
packages/chart:
|
||||
specifiers:
|
||||
'@gradio/tooltip': workspace:^0.0.1
|
||||
d3-dsv: ^3.0.1
|
||||
d3-scale: ^4.0.2
|
||||
d3-shape: ^3.1.0
|
||||
dependencies:
|
||||
'@gradio/tooltip': link:../tooltip
|
||||
d3-dsv: 3.0.1
|
||||
d3-scale: 4.0.2
|
||||
d3-shape: 3.1.0
|
||||
|
||||
packages/chatbot:
|
||||
specifiers: {}
|
||||
|
||||
packages/components:
|
||||
specifiers: {}
|
||||
|
||||
packages/file:
|
||||
specifiers:
|
||||
'@gradio/upload': workspace:^0.0.1
|
||||
dependencies:
|
||||
'@gradio/upload': link:../upload
|
||||
|
||||
packages/form:
|
||||
specifiers:
|
||||
'@gradio/upload': workspace:^0.0.1
|
||||
dependencies:
|
||||
'@gradio/upload': link:../upload
|
||||
|
||||
packages/highlighted-text:
|
||||
specifiers:
|
||||
'@gradio/upload': workspace:^0.0.1
|
||||
dependencies:
|
||||
'@gradio/upload': link:../upload
|
||||
|
||||
packages/html:
|
||||
specifiers:
|
||||
'@gradio/upload': workspace:^0.0.1
|
||||
dependencies:
|
||||
'@gradio/upload': link:../upload
|
||||
|
||||
packages/image:
|
||||
specifiers:
|
||||
'@gradio/upload': workspace:^0.0.1
|
||||
cropperjs: ^1.5.12
|
||||
lazy-brush: ^1.0.1
|
||||
resize-observer-polyfill: ^1.5.1
|
||||
tui-image-editor: ^3.15.2
|
||||
dependencies:
|
||||
'@gradio/upload': link:../upload
|
||||
cropperjs: 1.5.12
|
||||
lazy-brush: 1.0.1
|
||||
resize-observer-polyfill: 1.5.1
|
||||
tui-image-editor: 3.15.2
|
||||
|
||||
packages/table:
|
||||
specifiers:
|
||||
'@gradio/upload': workspace:^0.0.1
|
||||
dependencies:
|
||||
'@gradio/upload': link:../upload
|
||||
|
||||
packages/theme:
|
||||
specifiers:
|
||||
'@gradio/upload': workspace:^0.0.1
|
||||
dependencies:
|
||||
'@gradio/upload': link:../upload
|
||||
|
||||
packages/tooltip:
|
||||
specifiers:
|
||||
'@gradio/upload': workspace:^0.0.1
|
||||
dependencies:
|
||||
'@gradio/upload': link:../upload
|
||||
|
||||
packages/upload:
|
||||
specifiers: {}
|
||||
|
||||
packages/video:
|
||||
specifiers:
|
||||
'@gradio/upload': workspace:^0.0.1
|
||||
dependencies:
|
||||
'@gradio/upload': link:../upload
|
||||
|
||||
packages/workbench:
|
||||
specifiers:
|
||||
'@gradio/audio': workspace:^0.0.1
|
||||
'@gradio/button': workspace:^0.0.1
|
||||
'@gradio/carousel': workspace:^0.0.1
|
||||
'@gradio/chart': workspace:^0.0.1
|
||||
'@gradio/chatbot': workspace:^0.0.1
|
||||
'@gradio/file': workspace:^0.0.1
|
||||
'@gradio/form': workspace:^0.0.1
|
||||
'@gradio/highlighted-text': workspace:^0.0.1
|
||||
'@gradio/html': workspace:^0.0.1
|
||||
'@gradio/image': workspace:^0.0.1
|
||||
'@gradio/json': workspace:^0.0.1
|
||||
'@gradio/label': workspace:^0.0.1
|
||||
'@gradio/table': workspace:^0.0.1
|
||||
'@gradio/theme': workspace:^0.0.1
|
||||
'@gradio/upload': workspace:^0.0.1
|
||||
'@gradio/video': workspace:^0.0.1
|
||||
'@sveltejs/adapter-auto': next
|
||||
'@sveltejs/kit': next
|
||||
autoprefixer: ^10.4.2
|
||||
@ -104,10 +200,23 @@ importers:
|
||||
dependencies:
|
||||
'@gradio/audio': link:../audio
|
||||
'@gradio/button': link:../button
|
||||
'@gradio/carousel': link:../carousel
|
||||
'@gradio/chart': link:../chart
|
||||
'@gradio/chatbot': link:../chatbot
|
||||
'@gradio/file': link:../file
|
||||
'@gradio/form': link:../form
|
||||
'@gradio/highlighted-text': link:../highlighted-text
|
||||
'@gradio/html': link:../html
|
||||
'@gradio/image': link:../image
|
||||
'@gradio/json': link:../json
|
||||
'@gradio/label': link:../label
|
||||
'@gradio/table': link:../table
|
||||
'@gradio/theme': link:../theme
|
||||
'@gradio/upload': link:../upload
|
||||
'@gradio/video': link:../video
|
||||
devDependencies:
|
||||
'@sveltejs/adapter-auto': 1.0.0-next.28
|
||||
'@sveltejs/kit': 1.0.0-next.286_svelte@3.46.3
|
||||
'@sveltejs/adapter-auto': 1.0.0-next.30
|
||||
'@sveltejs/kit': 1.0.0-next.288_svelte@3.46.3
|
||||
autoprefixer: 10.4.2_postcss@8.4.6
|
||||
postcss: 8.4.6
|
||||
postcss-load-config: 3.1.1
|
||||
@ -221,12 +330,12 @@ packages:
|
||||
picomatch: 2.3.1
|
||||
dev: true
|
||||
|
||||
/@sveltejs/adapter-auto/1.0.0-next.28:
|
||||
resolution: {integrity: sha512-MuJUghh7HYXeThS1btsKJFZBN//DM5BmfTXhSBRN3MgKwDMF1XYvadNegZI2nIVzb9RQTh/al3jGKKZfBVHGEg==}
|
||||
/@sveltejs/adapter-auto/1.0.0-next.30:
|
||||
resolution: {integrity: sha512-kKKDvv2bwH66l/A97u26973OG3Zh7V1WlTKgkOr95Koo1/Y6rBhBfkz0Jw+WLzsZ+hcTzgIrcegtWVt+GQRJpw==}
|
||||
dependencies:
|
||||
'@sveltejs/adapter-cloudflare': 1.0.0-next.14
|
||||
'@sveltejs/adapter-netlify': 1.0.0-next.47
|
||||
'@sveltejs/adapter-vercel': 1.0.0-next.44
|
||||
'@sveltejs/adapter-netlify': 1.0.0-next.49
|
||||
'@sveltejs/adapter-vercel': 1.0.0-next.45
|
||||
dev: true
|
||||
|
||||
/@sveltejs/adapter-cloudflare/1.0.0-next.14:
|
||||
@ -235,22 +344,22 @@ packages:
|
||||
esbuild: 0.14.23
|
||||
dev: true
|
||||
|
||||
/@sveltejs/adapter-netlify/1.0.0-next.47:
|
||||
resolution: {integrity: sha512-fyJ2UP14ViusPFMtnuXbLRxE9bQ3dQ7Aj5HkAddN4fgV1A15tGWthrSUOH0iozYmOYLu6AWvUxx9o01OhJbfZQ==}
|
||||
/@sveltejs/adapter-netlify/1.0.0-next.49:
|
||||
resolution: {integrity: sha512-EYP0/eK1foC90fBNn6V9fP0ngrMF3MrkaNN+wCnMj0rHGKOKUzXlIeyPtGtQnDL9cqHac39pbPhSyMaMuWZF/w==}
|
||||
dependencies:
|
||||
'@iarna/toml': 2.2.5
|
||||
esbuild: 0.14.23
|
||||
tiny-glob: 0.2.9
|
||||
dev: true
|
||||
|
||||
/@sveltejs/adapter-vercel/1.0.0-next.44:
|
||||
resolution: {integrity: sha512-q0o7JG0pAam2lkkM5XiaLzYBGSRtOogjXpKYwpc7iiMuaSV4nzsnYlLxwbtib7ZJQzE1D32pPZQuvf5pAIqrmQ==}
|
||||
/@sveltejs/adapter-vercel/1.0.0-next.45:
|
||||
resolution: {integrity: sha512-WoOMu4FSWjVeRiPq2NKfdZhobfOLmDykObZmm8JHptMDb0/I5SB+6wstzAeaNtQr2ZbQkUy0k7EX95riJifEEQ==}
|
||||
dependencies:
|
||||
esbuild: 0.14.23
|
||||
dev: true
|
||||
|
||||
/@sveltejs/kit/1.0.0-next.286_svelte@3.46.3:
|
||||
resolution: {integrity: sha512-txWUYKLpZohHPCEYc3oYIByMnZe+d/FItMBiCNJbTLdKaDop+SfCOzBar+K9IgP2MTXSScILSkPOms845uhz0A==}
|
||||
/@sveltejs/kit/1.0.0-next.288_svelte@3.46.3:
|
||||
resolution: {integrity: sha512-6ky4CUFNGKoU1QV+fY2LOIB7atHdUCIMRx3pX21B2g9yZzQzkot0zHBrMMBSKF0/4Wx19PLEWrlGxlhakYmi8Q==}
|
||||
engines: {node: '>=14.13'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
|
Loading…
Reference in New Issue
Block a user