mirror of
https://github.com/gradio-app/gradio.git
synced 2025-01-18 10:44:33 +08:00
Sketching + Inpainting Capabilities to Gradio (#2144)
* templates * working on backend * formatting * Sketching fe (#2184) * fix scaling on sketch + bg img * tweaks * ketch updates * cursor style * sketchpad * fixes * ensure background is white for bw sketch * fix everything * re-enable demos * updated demo and changed from dict to str * beta release * fix bugs, tweak webcam source * re-anable demos * fix clear button and tab changing * maybe fix test * maybe fix test again maybe * various fixes * fix img uplaod + color sketch * remove lazy brush but keep smoothing * fix sketch bg Co-authored-by: pngwn <hello@pngwn.io>
This commit is contained in:
parent
581fbabe07
commit
cecaf1a635
BIN
demo/all_demos/tmp.zip
Normal file
BIN
demo/all_demos/tmp.zip
Normal file
Binary file not shown.
@ -1,25 +1,135 @@
|
||||
import gradio as gr
|
||||
import os
|
||||
|
||||
def fn(mask):
|
||||
return [mask["image"], mask["mask"]]
|
||||
|
||||
from gradio.components import Markdown as md
|
||||
|
||||
demo = gr.Blocks()
|
||||
|
||||
with demo:
|
||||
with gr.Row():
|
||||
with gr.Column():
|
||||
img = gr.Image(
|
||||
tool="sketch", source="upload", label="Mask", value=os.path.join(os.path.dirname(__file__), "lion.jpg")
|
||||
)
|
||||
with gr.Row():
|
||||
btn = gr.Button("Run")
|
||||
with gr.Column():
|
||||
img2 = gr.Image()
|
||||
img3 = gr.Image()
|
||||
io1a = gr.Interface(lambda x: x, gr.Image(), gr.Image())
|
||||
io1b = gr.Interface(lambda x: x, gr.Image(source="webcam"), gr.Image())
|
||||
|
||||
btn.click(fn=fn, inputs=img, outputs=[img2, img3])
|
||||
io2a = gr.Interface(lambda x: x, gr.Image(source="canvas"), gr.Image())
|
||||
io2b = gr.Interface(lambda x: x, gr.Sketchpad(), gr.Image())
|
||||
|
||||
io3a = gr.Interface(
|
||||
lambda x: [x["mask"], x["image"]],
|
||||
gr.Image(source="upload", tool="sketch"),
|
||||
[gr.Image(), gr.Image()],
|
||||
)
|
||||
|
||||
io3b = gr.Interface(
|
||||
lambda x: [x["mask"], x["image"]],
|
||||
gr.ImageMask(),
|
||||
[gr.Image(), gr.Image()],
|
||||
)
|
||||
|
||||
io3b2 = gr.Interface(
|
||||
lambda x: [x["mask"], x["image"]],
|
||||
gr.ImageMask(),
|
||||
[gr.Image(), gr.Image()],
|
||||
)
|
||||
|
||||
io3b3 = gr.Interface(
|
||||
lambda x: [x["mask"], x["image"]],
|
||||
gr.ImageMask(),
|
||||
[gr.Image(), gr.Image()],
|
||||
)
|
||||
|
||||
io3c = gr.Interface(
|
||||
lambda x: [x["mask"], x["image"]],
|
||||
gr.Image(source="webcam", tool="sketch"),
|
||||
[gr.Image(), gr.Image()],
|
||||
)
|
||||
|
||||
io4a = gr.Interface(
|
||||
lambda x: x, gr.Image(source="canvas", tool="color-sketch"), gr.Image()
|
||||
)
|
||||
io4b = gr.Interface(lambda x: x, gr.Paint(), gr.Image())
|
||||
|
||||
io5a = gr.Interface(
|
||||
lambda x: x, gr.Image(source="upload", tool="color-sketch"), gr.Image()
|
||||
)
|
||||
io5b = gr.Interface(lambda x: x, gr.ImagePaint(), gr.Image())
|
||||
io5c = gr.Interface(
|
||||
lambda x: x, gr.Image(source="webcam", tool="color-sketch"), gr.Image()
|
||||
)
|
||||
|
||||
|
||||
with demo:
|
||||
md("# Different Ways to Use the Image Input Component")
|
||||
md(
|
||||
"**1a. Standalone Image Upload: `gr.Interface(lambda x: x, gr.Image(), gr.Image())`**"
|
||||
)
|
||||
io1a.render()
|
||||
md(
|
||||
"**1b. Standalone Image from Webcam: `gr.Interface(lambda x: x, gr.Image(source='webcam'), gr.Image())`**"
|
||||
)
|
||||
io1b.render()
|
||||
md(
|
||||
"**2a. Black and White Sketchpad: `gr.Interface(lambda x: x, gr.Image(source='canvas'), gr.Image())`**"
|
||||
)
|
||||
io2a.render()
|
||||
md(
|
||||
"**2b. Black and White Sketchpad: `gr.Interface(lambda x: x, gr.Sketchpad(), gr.Image())`**"
|
||||
)
|
||||
io2b.render()
|
||||
md("**3a. Binary Mask with image upload:**")
|
||||
md(
|
||||
"""```python
|
||||
gr.Interface(
|
||||
lambda x: [x['mask'], x['image']],
|
||||
gr.Image(source='upload', tool='sketch'),
|
||||
[gr.Image(), gr.Image()],
|
||||
)
|
||||
```
|
||||
"""
|
||||
)
|
||||
io3a.render()
|
||||
md("**3b. Binary Mask with image upload:**")
|
||||
md(
|
||||
"""```python
|
||||
gr.Interface(
|
||||
lambda x: [x['mask'], x['image']],
|
||||
gr.ImageMask(),
|
||||
[gr.Image(), gr.Image()],
|
||||
)
|
||||
```
|
||||
"""
|
||||
)
|
||||
io3b.render()
|
||||
md("**3c. Binary Mask with webcam upload:**")
|
||||
md(
|
||||
"""```python
|
||||
gr.Interface(
|
||||
lambda x: [x['mask'], x['image']],
|
||||
gr.Image(source='webcam', tool='sketch'),
|
||||
[gr.Image(), gr.Image()],
|
||||
)
|
||||
```
|
||||
"""
|
||||
)
|
||||
io3c.render()
|
||||
md(
|
||||
"**4a. Color Sketchpad: `gr.Interface(lambda x: x, gr.Image(source='canvas', tool='color-sketch'), gr.Image())`**"
|
||||
)
|
||||
io4a.render()
|
||||
md("**4b. Color Sketchpad: `gr.Interface(lambda x: x, gr.Paint(), gr.Image())`**")
|
||||
io4b.render()
|
||||
md(
|
||||
"**5a. Color Sketchpad with image upload: `gr.Interface(lambda x: x, gr.Image(source='upload', tool='color-sketch'), gr.Image())`**"
|
||||
)
|
||||
io5a.render()
|
||||
md(
|
||||
"**5b. Color Sketchpad with image upload: `gr.Interface(lambda x: x, gr.ImagePaint(), gr.Image())`**"
|
||||
)
|
||||
io5b.render()
|
||||
md(
|
||||
"**5c. Color Sketchpad with webcam upload: `gr.Interface(lambda x: x, gr.Image(source='webcam', tool='color-sketch'), gr.Image())`**"
|
||||
)
|
||||
io5c.render()
|
||||
md("**Tabs**")
|
||||
with gr.Tab("One"):
|
||||
io3b2.render()
|
||||
with gr.Tab("Two"):
|
||||
io3b3.render()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -12,7 +12,7 @@ demo = gr.Interface(
|
||||
headers=["name", "age", "gender"],
|
||||
datatype=["str", "number", "str"],
|
||||
row_count=5,
|
||||
col_count=(3, "fixed")
|
||||
col_count=(3, "fixed"),
|
||||
),
|
||||
gr.Dropdown(["M", "F", "O"]),
|
||||
],
|
||||
|
@ -60,11 +60,14 @@ from gradio.mix import Parallel, Series
|
||||
from gradio.templates import (
|
||||
Files,
|
||||
Highlight,
|
||||
ImageMask,
|
||||
ImagePaint,
|
||||
List,
|
||||
Matrix,
|
||||
Mic,
|
||||
Microphone,
|
||||
Numpy,
|
||||
Paint,
|
||||
Pil,
|
||||
PlayableVideo,
|
||||
Sketchpad,
|
||||
|
@ -32,6 +32,7 @@ import matplotlib.figure
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import PIL
|
||||
import PIL.ImageOps
|
||||
from ffmpy import FFmpeg
|
||||
from markdown_it import MarkdownIt
|
||||
|
||||
@ -1175,11 +1176,11 @@ class Dropdown(Radio):
|
||||
)
|
||||
|
||||
|
||||
@document("edit", "clear", "change", "stream", "change")
|
||||
@document("edit", "clear", "change", "stream", "change", "style")
|
||||
class Image(Editable, Clearable, Changeable, Streamable, IOComponent, ImgSerializable):
|
||||
"""
|
||||
Creates an image component that can be used to upload/draw images (as an input) or display images (as an output).
|
||||
Preprocessing: passes the uploaded image as a {numpy.array}, {PIL.Image} or {str} filepath depending on `type` -- unless `tool` is `sketch`. In the special case, a {dict} with keys `image` and `mask` is passed, and the format of the corresponding values depends on `type`.
|
||||
Preprocessing: passes the uploaded image as a {numpy.array}, {PIL.Image} or {str} filepath depending on `type` -- unless `tool` is `sketch` AND source is one of `upload` or `webcam`. In these cases, a {dict} with keys `image` and `mask` is passed, and the format of the corresponding values depends on `type`.
|
||||
Postprocessing: expects a {numpy.array}, {PIL.Image} or {str} or {pathlib.Path} filepath to an image and displays the image.
|
||||
Examples-format: a {str} filepath to a local file that contains the image.
|
||||
Demos: image_mod, image_mod_default_image
|
||||
@ -1194,7 +1195,7 @@ class Image(Editable, Clearable, Changeable, Streamable, IOComponent, ImgSeriali
|
||||
image_mode: str = "RGB",
|
||||
invert_colors: bool = False,
|
||||
source: str = "upload",
|
||||
tool: str = "editor",
|
||||
tool: str = None,
|
||||
type: str = "numpy",
|
||||
label: Optional[str] = None,
|
||||
show_label: bool = True,
|
||||
@ -1212,7 +1213,7 @@ class Image(Editable, Clearable, Changeable, Streamable, IOComponent, ImgSeriali
|
||||
image_mode: "RGB" if color, or "L" if black and white.
|
||||
invert_colors: whether to invert the image as a preprocessing step.
|
||||
source: Source of image. "upload" creates a box where user can drop an image file, "webcam" allows user to take snapshot from their webcam, "canvas" defaults to a white image that can be edited and drawn upon with tools.
|
||||
tool: Tools used for editing. "editor" allows a full screen editor, "select" provides a cropping and zoom tool, "sketch" allows you to create a mask over the image and both the image and mask are passed into the function.
|
||||
tool: Tools used for editing. "editor" allows a full screen editor (and is the default if source is "upload" or "webcam"), "select" provides a cropping and zoom tool, "sketch" allows you to create a binary sketch (and is the default if source="canvas"), and "color-sketch" allows you to created a sketch in different colors. "color-sketch" can be used with source="upload" or "webcam" to allow sketching on an image. "sketch" can also be used with "upload" or "webcam" to create a mask over an image and in that case both the image and mask are passed into the function as a dictionary with keys "image" and "mask" respectively.
|
||||
type: The format the image is converted to before being passed into the prediction function. "numpy" converts the image to a numpy array with shape (width, height, 3) and values from 0 to 255, "pil" converts the image to a PIL image object, "file" produces a temporary file object whose path can be retrieved by file_obj.name, "filepath" passes a str path to a temporary file containing the image.
|
||||
label: component name in interface.
|
||||
show_label: if True, will display label.
|
||||
@ -1228,7 +1229,10 @@ class Image(Editable, Clearable, Changeable, Streamable, IOComponent, ImgSeriali
|
||||
self.image_mode = image_mode
|
||||
self.source = source
|
||||
requires_permissions = source == "webcam"
|
||||
self.tool = tool
|
||||
if tool is None:
|
||||
self.tool = "sketch" if source == "canvas" else "editor"
|
||||
else:
|
||||
self.tool = tool
|
||||
self.invert_colors = invert_colors
|
||||
self.test_input = deepcopy(media_data.BASE64_IMAGE)
|
||||
self.interpret_by_tokens = True
|
||||
@ -1279,9 +1283,10 @@ class Image(Editable, Clearable, Changeable, Streamable, IOComponent, ImgSeriali
|
||||
return IOComponent.add_interactive_to_config(updated_config, interactive)
|
||||
|
||||
def _format_image(
|
||||
self, im: Optional[PIL.Image], fmt: str
|
||||
self, im: Optional[PIL.Image]
|
||||
) -> np.array | PIL.Image | str | None:
|
||||
"""Helper method to format an image based on self.type"""
|
||||
fmt = im.format
|
||||
if im is None:
|
||||
return im
|
||||
if self.type == "pil":
|
||||
@ -1314,17 +1319,15 @@ class Image(Editable, Clearable, Changeable, Streamable, IOComponent, ImgSeriali
|
||||
def preprocess(self, x: str | Dict) -> np.array | PIL.Image | str | None:
|
||||
"""
|
||||
Parameters:
|
||||
x: base64 url data, or (if tool == "sketch) a dict of image and mask base64 url data
|
||||
x: base64 url data, or (if tool == "sketch") a dict of image and mask base64 url data
|
||||
Returns:
|
||||
image in requested format
|
||||
image in requested format, or (if tool == "sketch") a dict of image and mask in requested format
|
||||
"""
|
||||
if x is None:
|
||||
return x
|
||||
if self.tool == "sketch":
|
||||
if self.tool == "sketch" and self.source in ["upload", "webcam"]:
|
||||
x, mask = x["image"], x["mask"]
|
||||
|
||||
im = processing_utils.decode_base64_to_image(x)
|
||||
fmt = im.format
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
im = im.convert(self.image_mode)
|
||||
@ -1332,18 +1335,21 @@ class Image(Editable, Clearable, Changeable, Streamable, IOComponent, ImgSeriali
|
||||
im = processing_utils.resize_and_crop(im, self.shape)
|
||||
if self.invert_colors:
|
||||
im = PIL.ImageOps.invert(im)
|
||||
if self.source == "webcam" and self.mirror_webcam is True:
|
||||
if (
|
||||
self.source == "webcam"
|
||||
and self.mirror_webcam is True
|
||||
and self.tool != "color-sketch"
|
||||
):
|
||||
im = PIL.ImageOps.mirror(im)
|
||||
|
||||
if not (self.tool == "sketch"):
|
||||
return self._format_image(im, fmt)
|
||||
if self.tool == "sketch" and self.source in ["upload", "webcam"]:
|
||||
mask_im = processing_utils.decode_base64_to_image(mask)
|
||||
return {
|
||||
"image": self._format_image(im),
|
||||
"mask": self._format_image(mask_im),
|
||||
}
|
||||
|
||||
mask_im = processing_utils.decode_base64_to_image(mask)
|
||||
mask_fmt = mask_im.format
|
||||
return {
|
||||
"image": self._format_image(im, fmt),
|
||||
"mask": self._format_image(mask_im, mask_fmt),
|
||||
}
|
||||
return self._format_image(im)
|
||||
|
||||
def postprocess(self, y: np.ndarray | PIL.Image | str | Path) -> str:
|
||||
"""
|
||||
|
@ -31,7 +31,7 @@ class Webcam(components.Image):
|
||||
is_template = True
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(source="webcam", **kwargs)
|
||||
super().__init__(source="webcam", interactive=True, **kwargs)
|
||||
|
||||
|
||||
class Sketchpad(components.Image):
|
||||
@ -47,10 +47,48 @@ class Sketchpad(components.Image):
|
||||
source="canvas",
|
||||
shape=(28, 28),
|
||||
invert_colors=True,
|
||||
interactive=True,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
|
||||
class Paint(components.Image):
|
||||
"""
|
||||
Sets source="canvas", tool="color-sketch"
|
||||
"""
|
||||
|
||||
is_template = True
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(
|
||||
source="canvas", tool="color-sketch", interactive=True, **kwargs
|
||||
)
|
||||
|
||||
|
||||
class ImageMask(components.Image):
|
||||
"""
|
||||
Sets source="canvas", tool="sketch"
|
||||
"""
|
||||
|
||||
is_template = True
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(source="upload", tool="sketch", interactive=True, **kwargs)
|
||||
|
||||
|
||||
class ImagePaint(components.Image):
|
||||
"""
|
||||
Sets source="upload", tool="color-sketch"
|
||||
"""
|
||||
|
||||
is_template = True
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(
|
||||
source="upload", tool="color-sketch", interactive=True, **kwargs
|
||||
)
|
||||
|
||||
|
||||
class Pil(components.Image):
|
||||
"""
|
||||
Sets: type="pil"
|
||||
|
@ -1 +1 @@
|
||||
3.3.1
|
||||
3.4b0
|
||||
|
@ -61,13 +61,13 @@ test("can run an api request and display the data", async ({ page }) => {
|
||||
await page.check("label:has-text('Covid')");
|
||||
await page.check("label:has-text('Lung Cancer')");
|
||||
|
||||
const run_button = await page.locator("button", { hasText: /Run/ });
|
||||
const run_button = await page.locator("button", { hasText: /Run/ }).first();
|
||||
|
||||
await Promise.all([
|
||||
run_button.click(),
|
||||
page.waitForResponse("**/api/predict/")
|
||||
]);
|
||||
|
||||
const json = await page.locator("data-testid=json");
|
||||
const json = await page.locator("data-testid=json").first();
|
||||
await expect(json).toContainText(`Covid: 0.75, Lung Cancer: 0.25`);
|
||||
});
|
||||
|
9
ui/packages/icons/src/Brush.svelte
Normal file
9
ui/packages/icons/src/Brush.svelte
Normal file
@ -0,0 +1,9 @@
|
||||
<svg width="100%" height="100%" viewBox="0 0 32 32"
|
||||
><path
|
||||
d="M28.828 3.172a4.094 4.094 0 0 0-5.656 0L4.05 22.292A6.954 6.954 0 0 0 2 27.242V30h2.756a6.952 6.952 0 0 0 4.95-2.05L28.828 8.829a3.999 3.999 0 0 0 0-5.657zM10.91 18.26l2.829 2.829l-2.122 2.121l-2.828-2.828zm-2.619 8.276A4.966 4.966 0 0 1 4.756 28H4v-.759a4.967 4.967 0 0 1 1.464-3.535l1.91-1.91l2.829 2.828zM27.415 7.414l-12.261 12.26l-2.829-2.828l12.262-12.26a2.047 2.047 0 0 1 2.828 0a2 2 0 0 1 0 2.828z"
|
||||
fill="currentColor"
|
||||
/><path
|
||||
d="M6.5 15a3.5 3.5 0 0 1-2.475-5.974l3.5-3.5a1.502 1.502 0 0 0 0-2.121a1.537 1.537 0 0 0-2.121 0L3.415 5.394L2 3.98l1.99-1.988a3.585 3.585 0 0 1 4.95 0a3.504 3.504 0 0 1 0 4.949L5.439 10.44a1.502 1.502 0 0 0 0 2.121a1.537 1.537 0 0 0 2.122 0l4.024-4.024L13 9.95l-4.025 4.024A3.475 3.475 0 0 1 6.5 15z"
|
||||
fill="currentColor"
|
||||
/></svg
|
||||
>
|
After Width: | Height: | Size: 840 B |
@ -1,18 +1,9 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="100%"
|
||||
height="100%"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="feather feather-bar-chart-2"
|
||||
><line x1="18" y1="20" x2="18" y2="10" /><line
|
||||
x1="12"
|
||||
y1="20"
|
||||
x2="12"
|
||||
y2="4"
|
||||
/><line x1="6" y1="20" x2="6" y2="14" /></svg
|
||||
<svg width="1em" height="1em" viewBox="0 0 32 32"
|
||||
><path
|
||||
d="M28.828 3.172a4.094 4.094 0 0 0-5.656 0L4.05 22.292A6.954 6.954 0 0 0 2 27.242V30h2.756a6.952 6.952 0 0 0 4.95-2.05L28.828 8.829a3.999 3.999 0 0 0 0-5.657zM10.91 18.26l2.829 2.829l-2.122 2.121l-2.828-2.828zm-2.619 8.276A4.966 4.966 0 0 1 4.756 28H4v-.759a4.967 4.967 0 0 1 1.464-3.535l1.91-1.91l2.829 2.828zM27.415 7.414l-12.261 12.26l-2.829-2.828l12.262-12.26a2.047 2.047 0 0 1 2.828 0a2 2 0 0 1 0 2.828z"
|
||||
fill="currentColor"
|
||||
/><path
|
||||
d="M6.5 15a3.5 3.5 0 0 1-2.475-5.974l3.5-3.5a1.502 1.502 0 0 0 0-2.121a1.537 1.537 0 0 0-2.121 0L3.415 5.394L2 3.98l1.99-1.988a3.585 3.585 0 0 1 4.95 0a3.504 3.504 0 0 1 0 4.949L5.439 10.44a1.502 1.502 0 0 0 0 2.121a1.537 1.537 0 0 0 2.122 0l4.024-4.024L13 9.95l-4.025 4.024A3.475 3.475 0 0 1 6.5 15z"
|
||||
fill="currentColor"
|
||||
/></svg
|
||||
>
|
||||
|
Before Width: | Height: | Size: 369 B After Width: | Height: | Size: 838 B |
16
ui/packages/icons/src/Color.svelte
Normal file
16
ui/packages/icons/src/Color.svelte
Normal file
@ -0,0 +1,16 @@
|
||||
<svg width="100%" height="100%" viewBox="0 0 32 32"
|
||||
><circle cx="10" cy="12" r="2" fill="currentColor" /><circle
|
||||
cx="16"
|
||||
cy="9"
|
||||
r="2"
|
||||
fill="currentColor"
|
||||
/><circle cx="22" cy="12" r="2" fill="currentColor" /><circle
|
||||
cx="23"
|
||||
cy="18"
|
||||
r="2"
|
||||
fill="currentColor"
|
||||
/><circle cx="19" cy="23" r="2" fill="currentColor" /><path
|
||||
fill="currentColor"
|
||||
d="M16.54 2A14 14 0 0 0 2 16a4.82 4.82 0 0 0 6.09 4.65l1.12-.31a3 3 0 0 1 3.79 2.9V27a3 3 0 0 0 3 3a14 14 0 0 0 14-14.54A14.05 14.05 0 0 0 16.54 2Zm8.11 22.31A11.93 11.93 0 0 1 16 28a1 1 0 0 1-1-1v-3.76a5 5 0 0 0-5-5a5.07 5.07 0 0 0-1.33.18l-1.12.31A2.82 2.82 0 0 1 4 16A12 12 0 0 1 16.47 4A12.18 12.18 0 0 1 28 15.53a11.89 11.89 0 0 1-3.35 8.79Z"
|
||||
/></svg
|
||||
>
|
After Width: | Height: | Size: 720 B |
@ -1,22 +1,24 @@
|
||||
export { default as Clear } from "./Clear.svelte";
|
||||
export { default as Brush } from "./Brush.svelte";
|
||||
export { default as Camera } from "./Camera.svelte";
|
||||
export { default as Chart } from "./Chart.svelte";
|
||||
export { default as Chat } from "./Chat.svelte";
|
||||
export { default as Circle } from "./Circle.svelte";
|
||||
export { default as Clear } from "./Clear.svelte";
|
||||
export { default as Color } from "./Color.svelte";
|
||||
export { default as Edit } from "./Edit.svelte";
|
||||
export { default as File } from "./File.svelte";
|
||||
export { default as Image } from "./Image.svelte";
|
||||
export { default as JSON } from "./JSON.svelte";
|
||||
export { default as LineChart } from "./LineChart.svelte";
|
||||
export { default as Maximise } from "./Maximise.svelte";
|
||||
export { default as Music } from "./Music.svelte";
|
||||
export { default as Pause } from "./Pause.svelte";
|
||||
export { default as Play } from "./Play.svelte";
|
||||
export { default as Plot } from "./Plot.svelte";
|
||||
export { default as Sketch } from "./Sketch.svelte";
|
||||
export { default as Square } from "./Square.svelte";
|
||||
export { default as Table } from "./Table.svelte";
|
||||
export { default as TextHighlight } from "./TextHighlight.svelte";
|
||||
export { default as Tree } from "./Tree.svelte";
|
||||
export { default as Undo } from "./Undo.svelte";
|
||||
export { default as Video } from "./Video.svelte";
|
||||
export { default as Image } from "./Image.svelte";
|
||||
export { default as Chart } from "./Chart.svelte";
|
||||
export { default as Music } from "./Music.svelte";
|
||||
export { default as File } from "./File.svelte";
|
||||
export { default as LineChart } from "./LineChart.svelte";
|
||||
export { default as TextHighlight } from "./TextHighlight.svelte";
|
||||
export { default as JSON } from "./JSON.svelte";
|
||||
export { default as Tree } from "./Tree.svelte";
|
||||
export { default as Chat } from "./Chat.svelte";
|
||||
export { default as Plot } from "./Plot.svelte";
|
||||
export { default as Play } from "./Play.svelte";
|
||||
export { default as Pause } from "./Pause.svelte";
|
||||
export { default as Maximise } from "./Maximise.svelte";
|
||||
|
@ -11,6 +11,7 @@
|
||||
"@gradio/atoms": "workspace:^0.0.1",
|
||||
"@gradio/icons": "workspace:^0.0.1",
|
||||
"@gradio/upload": "workspace:^0.0.1",
|
||||
"@gradio/utils": "workspace:^0.0.1",
|
||||
"cropperjs": "^1.5.12",
|
||||
"lazy-brush": "^1.0.1",
|
||||
"resize-observer-polyfill": "^1.5.1"
|
||||
|
@ -8,6 +8,7 @@
|
||||
import Sketch from "./Sketch.svelte";
|
||||
import Webcam from "./Webcam.svelte";
|
||||
import ModifySketch from "./ModifySketch.svelte";
|
||||
import SketchSettings from "./SketchSettings.svelte";
|
||||
|
||||
import { Upload, ModifyUpload } from "@gradio/upload";
|
||||
|
||||
@ -39,22 +40,43 @@
|
||||
}
|
||||
|
||||
function handle_upload({ detail }: CustomEvent<string>) {
|
||||
value =
|
||||
(source === "upload" || source === "webcam") && tool === "sketch"
|
||||
? { image: detail, mask: null }
|
||||
: detail;
|
||||
if (tool === "color-sketch") {
|
||||
static_image = detail;
|
||||
} else {
|
||||
value =
|
||||
(source === "upload" || source === "webcam") && tool === "sketch"
|
||||
? { image: detail, mask: null }
|
||||
: detail;
|
||||
}
|
||||
}
|
||||
|
||||
function handle_clear({ detail }: CustomEvent<null>) {
|
||||
value = null;
|
||||
static_image = undefined;
|
||||
dispatch("clear");
|
||||
}
|
||||
|
||||
async function handle_save({ detail }: { detail: string }) {
|
||||
value =
|
||||
(source === "upload" || source === "webcam") && tool === "sketch"
|
||||
? { image: detail, mask: null }
|
||||
: detail;
|
||||
async function handle_save({ detail }: { detail: string }, initial) {
|
||||
if (mode === "mask") {
|
||||
if (source === "webcam" && initial) {
|
||||
value = {
|
||||
image: detail,
|
||||
mask: null
|
||||
};
|
||||
} else {
|
||||
value = {
|
||||
image: typeof value === "string" ? value : value?.image || null,
|
||||
mask: detail
|
||||
};
|
||||
}
|
||||
} else if (
|
||||
(source === "upload" || source === "webcam") &&
|
||||
tool === "sketch"
|
||||
) {
|
||||
value = { image: detail, mask: null };
|
||||
} else {
|
||||
value = detail;
|
||||
}
|
||||
|
||||
await tick();
|
||||
|
||||
@ -79,23 +101,52 @@
|
||||
const element = event.composedPath()[0] as HTMLImageElement;
|
||||
img_width = element.naturalWidth;
|
||||
img_height = element.naturalHeight;
|
||||
}
|
||||
|
||||
function handle_mask_save({ detail }: { detail: string }) {
|
||||
value = {
|
||||
image: typeof value === "string" ? value : value?.image || null,
|
||||
mask: detail
|
||||
};
|
||||
container_height = element.getBoundingClientRect().height;
|
||||
}
|
||||
|
||||
async function handle_mask_clear() {
|
||||
sketch.clear();
|
||||
await tick();
|
||||
value = null;
|
||||
static_image = undefined;
|
||||
}
|
||||
|
||||
let img_height = 0;
|
||||
let img_width = 0;
|
||||
let container_height = 0;
|
||||
|
||||
let brush_radius = 20;
|
||||
|
||||
let mode;
|
||||
|
||||
$: {
|
||||
if (source === "canvas" && tool === "sketch") {
|
||||
mode = "bw-sketch";
|
||||
} else if (tool === "color-sketch") {
|
||||
mode = "color-sketch";
|
||||
} else if (
|
||||
(source === "upload" || source === "webcam") &&
|
||||
tool === "sketch"
|
||||
) {
|
||||
mode = "mask";
|
||||
} else {
|
||||
mode = "editor";
|
||||
}
|
||||
}
|
||||
|
||||
$: brush_color = mode == "mask" ? "#000000" : "#000";
|
||||
|
||||
let value_img;
|
||||
let max_height;
|
||||
let max_width;
|
||||
|
||||
let static_image = undefined;
|
||||
|
||||
$: {
|
||||
if (value === null || (value.image === null && value.mask === null)) {
|
||||
static_image = undefined;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<BlockLabel
|
||||
@ -106,16 +157,39 @@
|
||||
|
||||
<div
|
||||
class:bg-gray-200={value}
|
||||
class:h-60={source !== "webcam" || tool === "sketch"}
|
||||
class:h-60={source !== "webcam" ||
|
||||
tool === "sketch" ||
|
||||
tool === "color-sketch"}
|
||||
data-testid="image"
|
||||
bind:offsetHeight={max_height}
|
||||
bind:offsetWidth={max_width}
|
||||
>
|
||||
{#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 || streaming}
|
||||
{#if tool === "color-sketch"}
|
||||
<SketchSettings
|
||||
bind:brush_radius
|
||||
bind:brush_color
|
||||
container_height={container_height || max_height}
|
||||
img_width={img_width || max_width}
|
||||
img_height={img_height || max_height}
|
||||
/>
|
||||
{/if}
|
||||
<Sketch
|
||||
{value}
|
||||
bind:brush_radius
|
||||
bind:brush_color
|
||||
bind:this={sketch}
|
||||
on:change={handle_save}
|
||||
{mode}
|
||||
width={img_width || max_width}
|
||||
height={img_height || max_height}
|
||||
container_height={container_height || max_height}
|
||||
/>
|
||||
{:else if (value === null && !static_image) || streaming}
|
||||
{#if source === "upload"}
|
||||
<Upload
|
||||
bind:dragging
|
||||
@ -129,9 +203,10 @@
|
||||
{upload_text}
|
||||
</div>
|
||||
</Upload>
|
||||
{:else if source === "webcam"}
|
||||
{:else if source === "webcam" && !static_image}
|
||||
<Webcam
|
||||
on:capture={handle_save}
|
||||
on:capture={(e) =>
|
||||
tool === "color-sketch" ? handle_upload(e) : handle_save(e, true)}
|
||||
on:stream={handle_save}
|
||||
{streaming}
|
||||
{pending}
|
||||
@ -154,34 +229,50 @@
|
||||
alt=""
|
||||
class:scale-x-[-1]={source === "webcam" && mirror_webcam}
|
||||
/>
|
||||
{:else if tool === "sketch" && value !== null}
|
||||
<img
|
||||
class="absolute w-full h-full object-contain"
|
||||
src={value.image}
|
||||
alt=""
|
||||
on:load={handle_image_load}
|
||||
class:scale-x-[-1]={source === "webcam" && mirror_webcam}
|
||||
/>
|
||||
{:else if (tool === "sketch" || tool === "color-sketch") && (value !== null || static_image)}
|
||||
{#key static_image}
|
||||
<img
|
||||
bind:this={value_img}
|
||||
class="absolute w-full h-full object-contain"
|
||||
src={static_image || value?.image || value}
|
||||
alt=""
|
||||
on:load={handle_image_load}
|
||||
class:scale-x-[-1]={source === "webcam" && mirror_webcam}
|
||||
/>
|
||||
{/key}
|
||||
{#if img_width > 0}
|
||||
<Sketch
|
||||
{value}
|
||||
bind:this={sketch}
|
||||
brush_radius={25}
|
||||
brush_color="rgba(255, 255, 255, 0.65)"
|
||||
on:change={handle_mask_save}
|
||||
mode="mask"
|
||||
width={img_width}
|
||||
height={img_height}
|
||||
bind:brush_radius
|
||||
bind:brush_color
|
||||
on:change={handle_save}
|
||||
{mode}
|
||||
width={img_width || max_width}
|
||||
height={img_height || max_height}
|
||||
container_height={container_height || max_height}
|
||||
{value_img}
|
||||
{source}
|
||||
/>
|
||||
<ModifySketch
|
||||
on:undo={() => sketch.undo()}
|
||||
on:clear={handle_mask_clear}
|
||||
/>
|
||||
{#if tool === "color-sketch" || tool === "sketch"}
|
||||
<SketchSettings
|
||||
bind:brush_radius
|
||||
bind:brush_color
|
||||
container_height={container_height || max_height}
|
||||
img_width={img_width || max_width}
|
||||
img_height={img_height || max_height}
|
||||
{mode}
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
{:else}
|
||||
<img
|
||||
class="w-full h-full object-contain"
|
||||
src={value}
|
||||
src={value.image || value}
|
||||
alt=""
|
||||
class:scale-x-[-1]={source === "webcam" && mirror_webcam}
|
||||
/>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<script>
|
||||
// @ts-nocheck
|
||||
|
||||
import { onMount, onDestroy, createEventDispatcher } from "svelte";
|
||||
import { onMount, onDestroy, createEventDispatcher, tick } from "svelte";
|
||||
import { fade } from "svelte/transition";
|
||||
import { LazyBrush } from "lazy-brush/src";
|
||||
import ResizeObserver from "resize-observer-polyfill";
|
||||
@ -9,19 +9,22 @@
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let value;
|
||||
export let value_img;
|
||||
export let mode = "sketch";
|
||||
export let brush_color = "#0b0f19";
|
||||
export let brush_radius = 50;
|
||||
export let source;
|
||||
|
||||
export let width = undefined;
|
||||
export let height = undefined;
|
||||
export let width = 400;
|
||||
export let height = 200;
|
||||
export let container_height = 200;
|
||||
|
||||
let mounted;
|
||||
|
||||
let catenary_color = "#aaa";
|
||||
|
||||
let canvas_width = width || 400;
|
||||
let canvas_height = height || 400;
|
||||
let canvas_width = width;
|
||||
let canvas_height = height;
|
||||
|
||||
$: mounted && !value && clear();
|
||||
|
||||
@ -64,59 +67,85 @@
|
||||
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 = "";
|
||||
let line_count = 0;
|
||||
|
||||
let display_natural_ratio = 1;
|
||||
|
||||
function calculate_ratio() {
|
||||
const x = canvas.interface.getBoundingClientRect();
|
||||
display_natural_ratio = width / x.width;
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
onMount(async () => {
|
||||
Object.keys(canvas).forEach((key) => {
|
||||
ctx[key] = canvas[key].getContext("2d");
|
||||
});
|
||||
|
||||
await tick();
|
||||
|
||||
if (value_img) {
|
||||
value_img.addEventListener("load", (_) => {
|
||||
if (source === "webcam") {
|
||||
ctx.temp.save();
|
||||
ctx.temp.translate(width, 0);
|
||||
ctx.temp.scale(-1, 1);
|
||||
ctx.temp.drawImage(value_img, 0, 0);
|
||||
ctx.temp.restore();
|
||||
} else {
|
||||
ctx.temp.drawImage(value_img, 0, 0);
|
||||
}
|
||||
ctx.drawing.drawImage(canvas.temp, 0, 0, width, height);
|
||||
|
||||
trigger_on_change();
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
if (source === "webcam") {
|
||||
ctx.temp.save();
|
||||
ctx.temp.translate(width, 0);
|
||||
ctx.temp.scale(-1, 1);
|
||||
ctx.temp.drawImage(value_img, 0, 0);
|
||||
ctx.temp.restore();
|
||||
} else {
|
||||
ctx.temp.drawImage(value_img, 0, 0);
|
||||
}
|
||||
|
||||
ctx.drawing.drawImage(canvas.temp, 0, 0, width, height);
|
||||
|
||||
draw_lines({ lines: lines.slice() });
|
||||
trigger_on_change();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
lazy = new LazyBrush({
|
||||
radius: brush_radius / 1.5,
|
||||
radius: brush_radius * 0.05,
|
||||
enabled: true,
|
||||
initialPoint: {
|
||||
x: window.innerWidth / 2,
|
||||
y: window.innerHeight / 2
|
||||
x: width / 2,
|
||||
y: height / 2
|
||||
}
|
||||
});
|
||||
chain_length = brush_radius;
|
||||
|
||||
canvas_observer = new ResizeObserver((entries, observer) => {
|
||||
canvas_observer = new ResizeObserver((entries, observer, ...rest) => {
|
||||
handle_canvas_resize(entries, observer);
|
||||
|
||||
calculate_ratio();
|
||||
});
|
||||
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);
|
||||
|
||||
calculate_ratio();
|
||||
requestAnimationFrame(() => {
|
||||
init();
|
||||
requestAnimationFrame(() => {
|
||||
clear();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function init() {
|
||||
const initX = width / 2;
|
||||
const initY = height / 2;
|
||||
lazy.update({ x: initX, y: initY }, { both: true });
|
||||
lazy.update({ x: initX, y: initY }, { both: false });
|
||||
mouse_has_moved = true;
|
||||
values_changed = true;
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
mounted = false;
|
||||
canvas_observer.unobserve(canvas_container);
|
||||
@ -124,9 +153,32 @@
|
||||
|
||||
export function undo() {
|
||||
const _lines = lines.slice(0, -1);
|
||||
clear();
|
||||
|
||||
clear_canvas();
|
||||
|
||||
if (value_img) {
|
||||
if (source === "webcam") {
|
||||
ctx.temp.save();
|
||||
ctx.temp.translate(width, 0);
|
||||
ctx.temp.scale(-1, 1);
|
||||
ctx.temp.drawImage(value_img, 0, 0);
|
||||
ctx.temp.restore();
|
||||
} else {
|
||||
ctx.temp.drawImage(value_img, 0, 0);
|
||||
}
|
||||
|
||||
if (!lines || !lines.length) {
|
||||
ctx.drawing.drawImage(canvas.temp, 0, 0, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
draw_lines({ lines: _lines });
|
||||
line_count = lines.length;
|
||||
line_count = _lines.length;
|
||||
|
||||
if (lines.length) {
|
||||
lines = _lines;
|
||||
}
|
||||
|
||||
trigger_on_change();
|
||||
}
|
||||
|
||||
@ -138,39 +190,9 @@
|
||||
});
|
||||
};
|
||||
|
||||
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,
|
||||
@ -179,19 +201,20 @@
|
||||
|
||||
if (mode === "mask") {
|
||||
draw_fake_points({
|
||||
points: points,
|
||||
points: _points,
|
||||
brush_color,
|
||||
brush_radius
|
||||
});
|
||||
}
|
||||
|
||||
points = _points;
|
||||
saveLine({ brush_color, brush_radius });
|
||||
if (mode === "mask") {
|
||||
save_mask_line();
|
||||
}
|
||||
|
||||
return;
|
||||
});
|
||||
saveLine({ brush_color, brush_radius });
|
||||
if (mode === "mask") {
|
||||
save_mask_line();
|
||||
}
|
||||
};
|
||||
|
||||
let handle_draw_start = (e) => {
|
||||
@ -223,29 +246,72 @@
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
set_canvas_size(canvas.temp_fake, width, height);
|
||||
set_canvas_size(canvas.mask, width, height);
|
||||
let old_width = 0;
|
||||
let old_height = 0;
|
||||
let old_container_height = 0;
|
||||
|
||||
loop({ once: true });
|
||||
let handle_canvas_resize = async () => {
|
||||
if (
|
||||
width === old_width &&
|
||||
height === old_height &&
|
||||
old_container_height === container_height
|
||||
) {
|
||||
return;
|
||||
}
|
||||
load_save_data(save_data, true);
|
||||
const dimensions = { width: width, height: height };
|
||||
|
||||
const container_dimensions = {
|
||||
height: container_height,
|
||||
width: container_height * (dimensions.width / dimensions.height)
|
||||
};
|
||||
|
||||
await Promise.all([
|
||||
set_canvas_size(canvas.interface, dimensions, container_dimensions),
|
||||
set_canvas_size(canvas.drawing, dimensions, container_dimensions),
|
||||
set_canvas_size(canvas.temp, dimensions, container_dimensions),
|
||||
set_canvas_size(canvas.temp_fake, dimensions, container_dimensions),
|
||||
set_canvas_size(canvas.mask, dimensions, container_dimensions, false)
|
||||
]);
|
||||
|
||||
brush_radius = 20 * (dimensions.width / container_dimensions.width);
|
||||
|
||||
loop({ once: true });
|
||||
|
||||
setTimeout(() => {
|
||||
old_height = height;
|
||||
old_width = width;
|
||||
old_container_height = container_height;
|
||||
}, 100);
|
||||
|
||||
clear();
|
||||
};
|
||||
|
||||
let set_canvas_size = (canvas, _width, _height) => {
|
||||
canvas.width = width || _width * 3;
|
||||
canvas.height = height || _height * 3;
|
||||
if (mode === "sketch") {
|
||||
$: {
|
||||
if (lazy) {
|
||||
init();
|
||||
lazy.setRadius(brush_radius * 0.05);
|
||||
}
|
||||
}
|
||||
|
||||
canvas.style.width = mode === "mask" ? "auto" : _width;
|
||||
canvas.style.height = mode === "mask" ? "100%" : _height;
|
||||
$: {
|
||||
if (width || height) {
|
||||
handle_canvas_resize();
|
||||
}
|
||||
}
|
||||
|
||||
let set_canvas_size = async (canvas, dimensions, container, scale = true) => {
|
||||
if (!mounted) return;
|
||||
await tick();
|
||||
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
canvas.width = dimensions.width * (scale ? dpr : 1);
|
||||
canvas.height = dimensions.height * (scale ? dpr : 1);
|
||||
|
||||
const ctx = canvas.getContext("2d");
|
||||
scale && ctx.scale(dpr, dpr);
|
||||
|
||||
canvas.style.width = `${container.width}px`;
|
||||
canvas.style.height = `${container.height}px`;
|
||||
};
|
||||
|
||||
let get_pointer_pos = (e) => {
|
||||
@ -257,18 +323,15 @@
|
||||
clientX = e.changedTouches[0].clientX;
|
||||
clientY = e.changedTouches[0].clientY;
|
||||
}
|
||||
|
||||
return {
|
||||
x: clientX - rect.left,
|
||||
y: clientY - rect.top
|
||||
x: ((clientX - rect.left) / rect.width) * width,
|
||||
y: ((clientY - rect.top) / rect.height) * height
|
||||
};
|
||||
};
|
||||
|
||||
let handle_pointer_move = (x, y) => {
|
||||
lazy.update(
|
||||
mode === "sketch"
|
||||
? { x: x * 3, y: y * 3 }
|
||||
: { x: x * display_natural_ratio, y: y * display_natural_ratio }
|
||||
);
|
||||
lazy.update({ x: x, y: y });
|
||||
const is_disabled = !lazy.isEnabled();
|
||||
if ((is_pressing && !is_drawing) || (is_disabled && is_pressing)) {
|
||||
is_drawing = true;
|
||||
@ -276,7 +339,6 @@
|
||||
}
|
||||
if (is_drawing) {
|
||||
points.push(lazy.brush.toObject());
|
||||
|
||||
draw_points({
|
||||
points: points,
|
||||
brush_color,
|
||||
@ -295,11 +357,13 @@
|
||||
};
|
||||
|
||||
let draw_points = ({ points, brush_color, brush_radius }) => {
|
||||
if (!points || points.length < 2) return;
|
||||
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;
|
||||
if (!points || points.length < 2) return;
|
||||
let p1 = points[0];
|
||||
let p2 = points[1];
|
||||
ctx.temp.moveTo(p2.x, p2.y);
|
||||
@ -316,15 +380,12 @@
|
||||
};
|
||||
|
||||
let draw_fake_points = ({ points, brush_color, brush_radius }) => {
|
||||
if (!points || points.length < 2) return;
|
||||
|
||||
ctx.temp_fake.lineJoin = "round";
|
||||
ctx.temp_fake.lineCap = "round";
|
||||
ctx.temp_fake.strokeStyle = "#fff";
|
||||
ctx.temp_fake.clearRect(
|
||||
0,
|
||||
0,
|
||||
ctx.temp.canvas.width,
|
||||
ctx.temp.canvas.height
|
||||
);
|
||||
// ctx.temp_fake.clearRect(0, 0, width, height);
|
||||
ctx.temp_fake.lineWidth = brush_radius;
|
||||
let p1 = points[0];
|
||||
let p2 = points[1];
|
||||
@ -342,52 +403,51 @@
|
||||
};
|
||||
|
||||
let save_mask_line = () => {
|
||||
// if (points.length < 2) return;
|
||||
|
||||
lines.push({
|
||||
points: [...points],
|
||||
brush_color: "#fff",
|
||||
brush_radius
|
||||
});
|
||||
|
||||
if (points.length < 1) return;
|
||||
points.length = 0;
|
||||
const width = canvas.temp_fake.width;
|
||||
const height = canvas.temp_fake.height;
|
||||
ctx.mask.drawImage(canvas.temp_fake, 0, 0, width, height);
|
||||
|
||||
ctx.temp_fake.clearRect(0, 0, width, height);
|
||||
trigger_on_change();
|
||||
};
|
||||
|
||||
let saveLine = () => {
|
||||
if (points.length < 2) return;
|
||||
if (points.length < 1) return;
|
||||
|
||||
lines.push({
|
||||
points: [...points],
|
||||
points: points.slice(),
|
||||
brush_color: brush_color,
|
||||
brush_radius
|
||||
});
|
||||
points.length = 0;
|
||||
const width = canvas.temp.width;
|
||||
const height = canvas.temp.height;
|
||||
|
||||
if (mode !== "mask") {
|
||||
points.length = 0;
|
||||
}
|
||||
|
||||
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());
|
||||
const x = get_image_data();
|
||||
dispatch("change", x);
|
||||
};
|
||||
|
||||
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);
|
||||
clear_canvas();
|
||||
line_count = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function clear_canvas() {
|
||||
values_changed = true;
|
||||
ctx.temp.clearRect(0, 0, width, height);
|
||||
|
||||
ctx.temp.fillStyle = mode === "mask" ? "transparent" : "#FFFFFF";
|
||||
ctx.temp.fillRect(0, 0, width, height);
|
||||
|
||||
ctx.drawing.fillStyle = mode === "sketch" ? "#FFFFFF" : "transparent";
|
||||
ctx.drawing.fillRect(0, 0, canvas.drawing.width, canvas.drawing.height);
|
||||
if (mode === "mask") {
|
||||
ctx.temp_fake.clearRect(
|
||||
0,
|
||||
@ -395,12 +455,10 @@
|
||||
canvas.temp_fake.width,
|
||||
canvas.temp_fake.height
|
||||
);
|
||||
ctx.mask.clearRect(0, 0, canvas.temp_fake.width, canvas.temp_fake.height);
|
||||
ctx.mask.clearRect(0, 0, width, height);
|
||||
ctx.mask.fillStyle = "#000";
|
||||
ctx.mask.fillRect(0, 0, canvas.mask.width, canvas.mask.height);
|
||||
ctx.mask.fillRect(0, 0, width, height);
|
||||
}
|
||||
|
||||
line_count = 0;
|
||||
}
|
||||
|
||||
let loop = ({ once = false } = {}) => {
|
||||
@ -418,8 +476,10 @@
|
||||
}
|
||||
};
|
||||
|
||||
$: brush_dot = brush_radius * 0.075;
|
||||
|
||||
let draw_interface = (ctx, pointer, brush) => {
|
||||
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
|
||||
// brush preview
|
||||
ctx.beginPath();
|
||||
@ -427,33 +487,17 @@
|
||||
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.arc(brush.x, brush.y, brush_dot, 0, Math.PI * 2, true);
|
||||
ctx.fill();
|
||||
};
|
||||
|
||||
export function get_image_data() {
|
||||
return mode === "mask"
|
||||
? canvas.mask.toDataURL("image/png")
|
||||
: canvas.drawing.toDataURL("image/png");
|
||||
? canvas.mask.toDataURL("image/jpg")
|
||||
: canvas.drawing.toDataURL("image/jpg");
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -474,11 +518,8 @@
|
||||
{#each canvas_types as { name, zIndex }}
|
||||
<canvas
|
||||
key={name}
|
||||
class="inset-0 m-auto"
|
||||
style=" display:block;position:absolute; z-index:{zIndex}; width: {mode ===
|
||||
'sketch'
|
||||
? canvas_width
|
||||
: width}px; height: {mode === 'sketch' ? canvas_height : height}px"
|
||||
class="inset-0 m-auto hover:cursor-none"
|
||||
style=" display:block;position:absolute; z-index:{zIndex};"
|
||||
bind:this={canvas[name]}
|
||||
on:mousedown={name === "interface" ? handle_draw_start : undefined}
|
||||
on:mousemove={name === "interface" ? handle_draw_move : undefined}
|
||||
|
49
ui/packages/image/src/SketchSettings.svelte
Normal file
49
ui/packages/image/src/SketchSettings.svelte
Normal file
@ -0,0 +1,49 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { IconButton } from "@gradio/atoms";
|
||||
import { Brush, Color } from "@gradio/icons";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let show_size = false;
|
||||
let show_col = false;
|
||||
|
||||
export let brush_radius = 20;
|
||||
export let brush_color = "#000";
|
||||
export let container_height: number;
|
||||
export let img_width: number;
|
||||
export let img_height: number;
|
||||
export let mode: "mask" | "other" = "other";
|
||||
|
||||
$: width = container_height * (img_width / img_height);
|
||||
</script>
|
||||
|
||||
<div class="z-50 top-10 right-2 justify-end flex gap-1 absolute">
|
||||
<!-- <IconButton Icon={Undo} on:click={() => dispatch("undo")} /> -->
|
||||
|
||||
<span class="absolute top-0 right-0">
|
||||
<IconButton Icon={Brush} on:click={() => (show_size = !show_size)} />
|
||||
{#if show_size}
|
||||
<input
|
||||
bind:value={brush_radius}
|
||||
class="absolute top-[2px] right-6"
|
||||
type="range"
|
||||
min={0.5 * (img_width / width)}
|
||||
max={75 * (img_width / width)}
|
||||
/>
|
||||
{/if}
|
||||
</span>
|
||||
|
||||
{#if mode !== "mask"}
|
||||
<span class="absolute top-6 right-0">
|
||||
<IconButton Icon={Color} on:click={() => (show_col = !show_col)} />
|
||||
{#if show_col}
|
||||
<input
|
||||
bind:value={brush_color}
|
||||
class="absolute top-[-3px] right-6"
|
||||
type="color"
|
||||
/>
|
||||
{/if}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
@ -20,13 +20,12 @@
|
||||
$: $selected_tab === id && tick().then(() => dispatch("select"));
|
||||
</script>
|
||||
|
||||
{#if $selected_tab === id}
|
||||
<div
|
||||
id={elem_id}
|
||||
class="tabitem p-2 border-2 border-t-0 border-gray-200 relative flex"
|
||||
>
|
||||
<Column>
|
||||
<slot />
|
||||
</Column>
|
||||
</div>
|
||||
{/if}
|
||||
<div
|
||||
id={elem_id}
|
||||
class="tabitem p-2 border-2 border-t-0 border-gray-200 relative flex"
|
||||
style:display={$selected_tab === id ? "block" : "none"}
|
||||
>
|
||||
<Column>
|
||||
<slot />
|
||||
</Column>
|
||||
</div>
|
||||
|
@ -1,2 +1,3 @@
|
||||
export * from "./color";
|
||||
export * from "./styles";
|
||||
export * from "./utils";
|
||||
|
9
ui/packages/utils/src/utils.ts
Normal file
9
ui/packages/utils/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);
|
||||
};
|
||||
};
|
@ -232,6 +232,7 @@ importers:
|
||||
'@gradio/atoms': workspace:^0.0.1
|
||||
'@gradio/icons': workspace:^0.0.1
|
||||
'@gradio/upload': workspace:^0.0.1
|
||||
'@gradio/utils': workspace:^0.0.1
|
||||
cropperjs: ^1.5.12
|
||||
lazy-brush: ^1.0.1
|
||||
resize-observer-polyfill: ^1.5.1
|
||||
@ -239,6 +240,7 @@ importers:
|
||||
'@gradio/atoms': link:../atoms
|
||||
'@gradio/icons': link:../icons
|
||||
'@gradio/upload': link:../upload
|
||||
'@gradio/utils': link:../utils
|
||||
cropperjs: 1.5.12
|
||||
lazy-brush: 1.0.1
|
||||
resize-observer-polyfill: 1.5.1
|
||||
@ -387,7 +389,7 @@ importers:
|
||||
'@gradio/upload': link:../upload
|
||||
'@gradio/video': link:../video
|
||||
devDependencies:
|
||||
'@sveltejs/adapter-auto': 1.0.0-next.70
|
||||
'@sveltejs/adapter-auto': 1.0.0-next.75
|
||||
'@sveltejs/kit': 1.0.0-next.318_svelte@3.49.0
|
||||
autoprefixer: 10.4.2_postcss@8.4.6
|
||||
postcss: 8.4.6
|
||||
@ -437,8 +439,8 @@ packages:
|
||||
resolution: {integrity: sha512-B1/plF62pt+H2IJHvApK8fdOJAVsvojvacuac8x8s+JIyqbropMyqNqHTKLm3YD8ZFLGwYeFTudU+PQ7vGvBdA==}
|
||||
dev: true
|
||||
|
||||
/@esbuild/linux-loong64/0.14.53:
|
||||
resolution: {integrity: sha512-W2dAL6Bnyn4xa/QRSU3ilIK4EzD5wgYXKXJiS1HDF5vU3675qc2bvFyLwbUcdmssDveyndy7FbitrCoiV/eMLg==}
|
||||
/@esbuild/linux-loong64/0.15.7:
|
||||
resolution: {integrity: sha512-IKznSJOsVUuyt7cDzzSZyqBEcZe+7WlBqTVXiF1OXP/4Nm387ToaXZ0fyLwI1iBlI/bzpxVq411QE2/Bt2XWWw==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
@ -567,39 +569,38 @@ packages:
|
||||
estree-walker: 2.0.2
|
||||
picomatch: 2.3.1
|
||||
|
||||
/@sveltejs/adapter-auto/1.0.0-next.70:
|
||||
resolution: {integrity: sha512-FJlDO6oUqbuFJjQoguGb4gdBj3iCSM3evFXkBpQ7hvwu3y2gKbcdzsxdn9tZ5LzkHh79CeJcwiszXFQ8usKk/A==}
|
||||
/@sveltejs/adapter-auto/1.0.0-next.75:
|
||||
resolution: {integrity: sha512-UEE6XkeXVrNhpEceqcCbtfV5EYzulIt1D/L+RsjIVsPVtUIZMMpPWzuHHzVvPemFRAuYho+4C1hJjIJ9iCgPeQ==}
|
||||
dependencies:
|
||||
'@sveltejs/adapter-cloudflare': 1.0.0-next.32
|
||||
'@sveltejs/adapter-netlify': 1.0.0-next.75
|
||||
'@sveltejs/adapter-vercel': 1.0.0-next.72
|
||||
'@sveltejs/adapter-cloudflare': 1.0.0-next.34
|
||||
'@sveltejs/adapter-netlify': 1.0.0-next.78
|
||||
'@sveltejs/adapter-vercel': 1.0.0-next.76
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@sveltejs/adapter-cloudflare/1.0.0-next.32:
|
||||
resolution: {integrity: sha512-tzkUsdQlBk9xUjcGUOBYos4HKaeaXvz9v4TQ1QS2yIHEtL5xvMEDPZ94/DB2gPL4LZCnYbdY2lsy5HCsoN0hkQ==}
|
||||
/@sveltejs/adapter-cloudflare/1.0.0-next.34:
|
||||
resolution: {integrity: sha512-9/YJsx5O+iy2+XGuH0vVzZ9OSeHGjkInh8JG8CLmIc0cKkv2t7sEu7qQ/qXA5CcvmS1AqNSUgIMxGoeEDVlO3g==}
|
||||
dependencies:
|
||||
'@cloudflare/workers-types': 3.14.1
|
||||
esbuild: 0.14.53
|
||||
esbuild: 0.15.7
|
||||
worktop: 0.8.0-next.14
|
||||
dev: true
|
||||
|
||||
/@sveltejs/adapter-netlify/1.0.0-next.75:
|
||||
resolution: {integrity: sha512-1zTR/U/ceEAyqIGJ74v54G+JbIR+fSmTN9qfqGOM0gBwVoBVRUujGm4tDFJQNYzvuGzVnC7br/rhYMLZd2JluQ==}
|
||||
/@sveltejs/adapter-netlify/1.0.0-next.78:
|
||||
resolution: {integrity: sha512-Yyn/j/0QcLK3Db442ducLUZmyvkO74j7Gdcwu9xN0fQN3kBlCJP9Itx5o4SySrPFGc4Q8cLJ5ELNg+mWduLBAA==}
|
||||
dependencies:
|
||||
'@iarna/toml': 2.2.5
|
||||
esbuild: 0.14.53
|
||||
esbuild: 0.15.7
|
||||
set-cookie-parser: 2.4.8
|
||||
tiny-glob: 0.2.9
|
||||
dev: true
|
||||
|
||||
/@sveltejs/adapter-vercel/1.0.0-next.72:
|
||||
resolution: {integrity: sha512-oNs8FQaYC2NnwDcvX/jc9MDNqXc9HxwGPQNkd+1vBpFVWZl9mShQgCcOMzfTOIH0ka984jYNa0ZawYYHex79xg==}
|
||||
/@sveltejs/adapter-vercel/1.0.0-next.76:
|
||||
resolution: {integrity: sha512-Od9DBfeMwWC/sZNeCJw4TYVE3LMR8lGJivSdkXWgpvksgG+QizLyzTfvBacapId3wcu+7X4PPTLoH00o5iQGEQ==}
|
||||
dependencies:
|
||||
'@vercel/nft': 0.22.0
|
||||
esbuild: 0.14.53
|
||||
esbuild: 0.15.7
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
@ -1531,8 +1532,8 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/esbuild-android-64/0.14.53:
|
||||
resolution: {integrity: sha512-fIL93sOTnEU+NrTAVMIKiAw0YH22HWCAgg4N4Z6zov2t0kY9RAJ50zY9ZMCQ+RT6bnOfDt8gCTnt/RaSNA2yRA==}
|
||||
/esbuild-android-64/0.15.7:
|
||||
resolution: {integrity: sha512-p7rCvdsldhxQr3YHxptf1Jcd86dlhvc3EQmQJaZzzuAxefO9PvcI0GLOa5nCWem1AJ8iMRu9w0r5TG8pHmbi9w==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [android]
|
||||
@ -1548,8 +1549,8 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/esbuild-android-arm64/0.14.53:
|
||||
resolution: {integrity: sha512-PC7KaF1v0h/nWpvlU1UMN7dzB54cBH8qSsm7S9mkwFA1BXpaEOufCg8hdoEI1jep0KeO/rjZVWrsH8+q28T77A==}
|
||||
/esbuild-android-arm64/0.15.7:
|
||||
resolution: {integrity: sha512-L775l9ynJT7rVqRM5vo+9w5g2ysbOCfsdLV4CWanTZ1k/9Jb3IYlQ06VCI1edhcosTYJRECQFJa3eAvkx72eyQ==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
@ -1565,8 +1566,8 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/esbuild-darwin-64/0.14.53:
|
||||
resolution: {integrity: sha512-gE7P5wlnkX4d4PKvLBUgmhZXvL7lzGRLri17/+CmmCzfncIgq8lOBvxGMiQ4xazplhxq+72TEohyFMZLFxuWvg==}
|
||||
/esbuild-darwin-64/0.15.7:
|
||||
resolution: {integrity: sha512-KGPt3r1c9ww009t2xLB6Vk0YyNOXh7hbjZ3EecHoVDxgtbUlYstMPDaReimKe6eOEfyY4hBEEeTvKwPsiH5WZg==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
@ -1582,8 +1583,8 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/esbuild-darwin-arm64/0.14.53:
|
||||
resolution: {integrity: sha512-otJwDU3hnI15Q98PX4MJbknSZ/WSR1I45il7gcxcECXzfN4Mrpft5hBDHXNRnCh+5858uPXBXA1Vaz2jVWLaIA==}
|
||||
/esbuild-darwin-arm64/0.15.7:
|
||||
resolution: {integrity: sha512-kBIHvtVqbSGajN88lYMnR3aIleH3ABZLLFLxwL2stiuIGAjGlQW741NxVTpUHQXUmPzxi6POqc9npkXa8AcSZQ==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
@ -1599,8 +1600,8 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/esbuild-freebsd-64/0.14.53:
|
||||
resolution: {integrity: sha512-WkdJa8iyrGHyKiPF4lk0MiOF87Q2SkE+i+8D4Cazq3/iqmGPJ6u49je300MFi5I2eUsQCkaOWhpCVQMTKGww2w==}
|
||||
/esbuild-freebsd-64/0.15.7:
|
||||
resolution: {integrity: sha512-hESZB91qDLV5MEwNxzMxPfbjAhOmtfsr9Wnuci7pY6TtEh4UDuevmGmkUIjX/b+e/k4tcNBMf7SRQ2mdNuK/HQ==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
@ -1616,8 +1617,8 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/esbuild-freebsd-arm64/0.14.53:
|
||||
resolution: {integrity: sha512-9T7WwCuV30NAx0SyQpw8edbKvbKELnnm1FHg7gbSYaatH+c8WJW10g/OdM7JYnv7qkimw2ZTtSA+NokOLd2ydQ==}
|
||||
/esbuild-freebsd-arm64/0.15.7:
|
||||
resolution: {integrity: sha512-dLFR0ChH5t+b3J8w0fVKGvtwSLWCv7GYT2Y2jFGulF1L5HftQLzVGN+6pi1SivuiVSmTh28FwUhi9PwQicXI6Q==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [freebsd]
|
||||
@ -1633,8 +1634,8 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/esbuild-linux-32/0.14.53:
|
||||
resolution: {integrity: sha512-VGanLBg5en2LfGDgLEUxQko2lqsOS7MTEWUi8x91YmsHNyzJVT/WApbFFx3MQGhkf+XdimVhpyo5/G0PBY91zg==}
|
||||
/esbuild-linux-32/0.15.7:
|
||||
resolution: {integrity: sha512-v3gT/LsONGUZcjbt2swrMjwxo32NJzk+7sAgtxhGx1+ZmOFaTRXBAi1PPfgpeo/J//Un2jIKm/I+qqeo4caJvg==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [ia32]
|
||||
os: [linux]
|
||||
@ -1650,8 +1651,8 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/esbuild-linux-64/0.14.53:
|
||||
resolution: {integrity: sha512-pP/FA55j/fzAV7N9DF31meAyjOH6Bjuo3aSKPh26+RW85ZEtbJv9nhoxmGTd9FOqjx59Tc1ZbrJabuiXlMwuZQ==}
|
||||
/esbuild-linux-64/0.15.7:
|
||||
resolution: {integrity: sha512-LxXEfLAKwOVmm1yecpMmWERBshl+Kv5YJ/1KnyAr6HRHFW8cxOEsEfisD3sVl/RvHyW//lhYUVSuy9jGEfIRAQ==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
@ -1667,8 +1668,8 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/esbuild-linux-arm/0.14.53:
|
||||
resolution: {integrity: sha512-/u81NGAVZMopbmzd21Nu/wvnKQK3pT4CrvQ8BTje1STXcQAGnfyKgQlj3m0j2BzYbvQxSy+TMck4TNV2onvoPA==}
|
||||
/esbuild-linux-arm/0.15.7:
|
||||
resolution: {integrity: sha512-JKgAHtMR5f75wJTeuNQbyznZZa+pjiUHV7sRZp42UNdyXC6TiUYMW/8z8yIBAr2Fpad8hM1royZKQisqPABPvQ==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
@ -1684,8 +1685,8 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/esbuild-linux-arm64/0.14.53:
|
||||
resolution: {integrity: sha512-GDmWITT+PMsjCA6/lByYk7NyFssW4Q6in32iPkpjZ/ytSyH+xeEx8q7HG3AhWH6heemEYEWpTll/eui3jwlSnw==}
|
||||
/esbuild-linux-arm64/0.15.7:
|
||||
resolution: {integrity: sha512-P3cfhudpzWDkglutWgXcT2S7Ft7o2e3YDMrP1n0z2dlbUZghUkKCyaWw0zhp4KxEEzt/E7lmrtRu/pGWnwb9vw==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
@ -1701,8 +1702,8 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/esbuild-linux-mips64le/0.14.53:
|
||||
resolution: {integrity: sha512-d6/XHIQW714gSSp6tOOX2UscedVobELvQlPMkInhx1NPz4ThZI9uNLQ4qQJHGBGKGfu+rtJsxM4NVHLhnNRdWQ==}
|
||||
/esbuild-linux-mips64le/0.15.7:
|
||||
resolution: {integrity: sha512-T7XKuxl0VpeFLCJXub6U+iybiqh0kM/bWOTb4qcPyDDwNVhLUiPcGdG2/0S7F93czUZOKP57YiLV8YQewgLHKw==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [mips64el]
|
||||
os: [linux]
|
||||
@ -1718,8 +1719,8 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/esbuild-linux-ppc64le/0.14.53:
|
||||
resolution: {integrity: sha512-ndnJmniKPCB52m+r6BtHHLAOXw+xBCWIxNnedbIpuREOcbSU/AlyM/2dA3BmUQhsHdb4w3amD5U2s91TJ3MzzA==}
|
||||
/esbuild-linux-ppc64le/0.15.7:
|
||||
resolution: {integrity: sha512-6mGuC19WpFN7NYbecMIJjeQgvDb5aMuvyk0PDYBJrqAEMkTwg3Z98kEKuCm6THHRnrgsdr7bp4SruSAxEM4eJw==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
@ -1735,8 +1736,8 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/esbuild-linux-riscv64/0.14.53:
|
||||
resolution: {integrity: sha512-yG2sVH+QSix6ct4lIzJj329iJF3MhloLE6/vKMQAAd26UVPVkhMFqFopY+9kCgYsdeWvXdPgmyOuKa48Y7+/EQ==}
|
||||
/esbuild-linux-riscv64/0.15.7:
|
||||
resolution: {integrity: sha512-uUJsezbswAYo/X7OU/P+PuL/EI9WzxsEQXDekfwpQ23uGiooxqoLFAPmXPcRAt941vjlY9jtITEEikWMBr+F/g==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
@ -1752,8 +1753,8 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/esbuild-linux-s390x/0.14.53:
|
||||
resolution: {integrity: sha512-OCJlgdkB+XPYndHmw6uZT7jcYgzmx9K+28PVdOa/eLjdoYkeAFvH5hTwX4AXGLZLH09tpl4bVsEtvuyUldaNCg==}
|
||||
/esbuild-linux-s390x/0.15.7:
|
||||
resolution: {integrity: sha512-+tO+xOyTNMc34rXlSxK7aCwJgvQyffqEM5MMdNDEeMU3ss0S6wKvbBOQfgd5jRPblfwJ6b+bKiz0g5nABpY0QQ==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
@ -1769,8 +1770,8 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/esbuild-netbsd-64/0.14.53:
|
||||
resolution: {integrity: sha512-gp2SB+Efc7MhMdWV2+pmIs/Ja/Mi5rjw+wlDmmbIn68VGXBleNgiEZG+eV2SRS0kJEUyHNedDtwRIMzaohWedQ==}
|
||||
/esbuild-netbsd-64/0.15.7:
|
||||
resolution: {integrity: sha512-yVc4Wz+Pu3cP5hzm5kIygNPrjar/v5WCSoRmIjCPWfBVJkZNb5brEGKUlf+0Y759D48BCWa0WHrWXaNy0DULTQ==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [netbsd]
|
||||
@ -1786,8 +1787,8 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/esbuild-openbsd-64/0.14.53:
|
||||
resolution: {integrity: sha512-eKQ30ZWe+WTZmteDYg8S+YjHV5s4iTxeSGhJKJajFfQx9TLZJvsJX0/paqwP51GicOUruFpSUAs2NCc0a4ivQQ==}
|
||||
/esbuild-openbsd-64/0.15.7:
|
||||
resolution: {integrity: sha512-GsimbwC4FSR4lN3wf8XmTQ+r8/0YSQo21rWDL0XFFhLHKlzEA4SsT1Tl8bPYu00IU6UWSJ+b3fG/8SB69rcuEQ==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [openbsd]
|
||||
@ -1803,8 +1804,8 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/esbuild-sunos-64/0.14.53:
|
||||
resolution: {integrity: sha512-OWLpS7a2FrIRukQqcgQqR1XKn0jSJoOdT+RlhAxUoEQM/IpytS3FXzCJM6xjUYtpO5GMY0EdZJp+ur2pYdm39g==}
|
||||
/esbuild-sunos-64/0.15.7:
|
||||
resolution: {integrity: sha512-8CDI1aL/ts0mDGbWzjEOGKXnU7p3rDzggHSBtVryQzkSOsjCHRVe0iFYUuhczlxU1R3LN/E7HgUO4NXzGGP/Ag==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [sunos]
|
||||
@ -1820,8 +1821,8 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/esbuild-windows-32/0.14.53:
|
||||
resolution: {integrity: sha512-m14XyWQP5rwGW0tbEfp95U6A0wY0DYPInWBB7D69FAXUpBpBObRoGTKRv36lf2RWOdE4YO3TNvj37zhXjVL5xg==}
|
||||
/esbuild-windows-32/0.15.7:
|
||||
resolution: {integrity: sha512-cOnKXUEPS8EGCzRSFa1x6NQjGhGsFlVgjhqGEbLTPsA7x4RRYiy2RKoArNUU4iR2vHmzqS5Gr84MEumO/wxYKA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
@ -1837,8 +1838,8 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/esbuild-windows-64/0.14.53:
|
||||
resolution: {integrity: sha512-s9skQFF0I7zqnQ2K8S1xdLSfZFsPLuOGmSx57h2btSEswv0N0YodYvqLcJMrNMXh6EynOmWD7rz+0rWWbFpIHQ==}
|
||||
/esbuild-windows-64/0.15.7:
|
||||
resolution: {integrity: sha512-7MI08Ec2sTIDv+zH6StNBKO+2hGUYIT42GmFyW6MBBWWtJhTcQLinKS6ldIN1d52MXIbiJ6nXyCJ+LpL4jBm3Q==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
@ -1854,8 +1855,8 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/esbuild-windows-arm64/0.14.53:
|
||||
resolution: {integrity: sha512-E+5Gvb+ZWts+00T9II6wp2L3KG2r3iGxByqd/a1RmLmYWVsSVUjkvIxZuJ3hYTIbhLkH5PRwpldGTKYqVz0nzQ==}
|
||||
/esbuild-windows-arm64/0.15.7:
|
||||
resolution: {integrity: sha512-R06nmqBlWjKHddhRJYlqDd3Fabx9LFdKcjoOy08YLimwmsswlFBJV4rXzZCxz/b7ZJXvrZgj8DDv1ewE9+StMw==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
@ -1890,33 +1891,33 @@ packages:
|
||||
esbuild-windows-64: 0.14.31
|
||||
esbuild-windows-arm64: 0.14.31
|
||||
|
||||
/esbuild/0.14.53:
|
||||
resolution: {integrity: sha512-ohO33pUBQ64q6mmheX1mZ8mIXj8ivQY/L4oVuAshr+aJI+zLl+amrp3EodrUNDNYVrKJXGPfIHFGhO8slGRjuw==}
|
||||
/esbuild/0.15.7:
|
||||
resolution: {integrity: sha512-7V8tzllIbAQV1M4QoE52ImKu8hT/NLGlGXkiDsbEU5PS6K8Mn09ZnYoS+dcmHxOS9CRsV4IRAMdT3I67IyUNXw==}
|
||||
engines: {node: '>=12'}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
optionalDependencies:
|
||||
'@esbuild/linux-loong64': 0.14.53
|
||||
esbuild-android-64: 0.14.53
|
||||
esbuild-android-arm64: 0.14.53
|
||||
esbuild-darwin-64: 0.14.53
|
||||
esbuild-darwin-arm64: 0.14.53
|
||||
esbuild-freebsd-64: 0.14.53
|
||||
esbuild-freebsd-arm64: 0.14.53
|
||||
esbuild-linux-32: 0.14.53
|
||||
esbuild-linux-64: 0.14.53
|
||||
esbuild-linux-arm: 0.14.53
|
||||
esbuild-linux-arm64: 0.14.53
|
||||
esbuild-linux-mips64le: 0.14.53
|
||||
esbuild-linux-ppc64le: 0.14.53
|
||||
esbuild-linux-riscv64: 0.14.53
|
||||
esbuild-linux-s390x: 0.14.53
|
||||
esbuild-netbsd-64: 0.14.53
|
||||
esbuild-openbsd-64: 0.14.53
|
||||
esbuild-sunos-64: 0.14.53
|
||||
esbuild-windows-32: 0.14.53
|
||||
esbuild-windows-64: 0.14.53
|
||||
esbuild-windows-arm64: 0.14.53
|
||||
'@esbuild/linux-loong64': 0.15.7
|
||||
esbuild-android-64: 0.15.7
|
||||
esbuild-android-arm64: 0.15.7
|
||||
esbuild-darwin-64: 0.15.7
|
||||
esbuild-darwin-arm64: 0.15.7
|
||||
esbuild-freebsd-64: 0.15.7
|
||||
esbuild-freebsd-arm64: 0.15.7
|
||||
esbuild-linux-32: 0.15.7
|
||||
esbuild-linux-64: 0.15.7
|
||||
esbuild-linux-arm: 0.15.7
|
||||
esbuild-linux-arm64: 0.15.7
|
||||
esbuild-linux-mips64le: 0.15.7
|
||||
esbuild-linux-ppc64le: 0.15.7
|
||||
esbuild-linux-riscv64: 0.15.7
|
||||
esbuild-linux-s390x: 0.15.7
|
||||
esbuild-netbsd-64: 0.15.7
|
||||
esbuild-openbsd-64: 0.15.7
|
||||
esbuild-sunos-64: 0.15.7
|
||||
esbuild-windows-32: 0.15.7
|
||||
esbuild-windows-64: 0.15.7
|
||||
esbuild-windows-arm64: 0.15.7
|
||||
dev: true
|
||||
|
||||
/escalade/3.1.1:
|
||||
@ -2060,9 +2061,11 @@ packages:
|
||||
|
||||
/globalyzer/0.1.0:
|
||||
resolution: {integrity: sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==}
|
||||
dev: false
|
||||
|
||||
/globrex/0.1.2:
|
||||
resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==}
|
||||
dev: false
|
||||
|
||||
/graceful-fs/4.2.9:
|
||||
resolution: {integrity: sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==}
|
||||
@ -3481,6 +3484,7 @@ packages:
|
||||
/svelte/3.49.0:
|
||||
resolution: {integrity: sha512-+lmjic1pApJWDfPCpUUTc1m8azDqYCG1JN9YEngrx/hUyIcFJo6VZhj0A1Ai0wqoHcEIuQy+e9tk+4uDgdtsFA==}
|
||||
engines: {node: '>= 8'}
|
||||
dev: false
|
||||
|
||||
/sync-request/6.1.0:
|
||||
resolution: {integrity: sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw==}
|
||||
@ -3595,6 +3599,7 @@ packages:
|
||||
dependencies:
|
||||
globalyzer: 0.1.0
|
||||
globrex: 0.1.2
|
||||
dev: false
|
||||
|
||||
/tinydate/1.3.0:
|
||||
resolution: {integrity: sha512-7cR8rLy2QhYHpsBDBVYnnWXm8uRTr38RoZakFSW7Bs7PzfMPNZthuMLkwqZv7MTu8lhQ91cOFYS5a7iFj2oR3w==}
|
||||
|
Loading…
Reference in New Issue
Block a user