From 63d0a28c08fb67a2ebb1429450f14e54c21d7dbb Mon Sep 17 00:00:00 2001 From: Abubakar Abid Date: Fri, 13 May 2022 22:48:46 -0400 Subject: [PATCH] Website Design Changes (#1015) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * adding gallery * added netlify files * new navbar design * header section new design * used by section new design * cards section new design * integrates with section new design * customer stories section new design * footer and gradient * demos section new design * docs fixes * docs reorg * docs reorg * upgrading to tailwind 3 * tailwind config changes * navbar new design * fixing body on all pages * Updating Guides (#1012) * updating getting started * updated codecov version * tweaks to gs * added netlify file * added netlify file * website prebuild script * increased code size * blocks * edits * blocks_hello * hello world * guide * merge main * added flipper demo * guide * guide * add guides * tweak to refresh website * header section new design * demos section new design * cards design * used by section * tweets section * footer on all pages * mobile responsive fixes * mobile responsive fixes * https fonts * completed blocks guide * unify components * minor tweaks * docs headers styling and navigation pane * parameter code blocks * styling description and input type * parameter tables and other styling * only documenting interactive components when possible * guides * embedding not working * demos not working * fixing demo code * fixing demos * demo fix * updated demos * updated demos * ui update * updated docstrings * updated code snippets so they run * updating docs * Interface docs * updating interface * fixing parameters in interface.py * required and defaults for interface, and styling * fixing up interface (#1207) * fixing up interface * fixed interface methods * formatting * updating interface docs * updating interface docs * formatting * docstring to load from docstrings * fixed colors * finalize interface content * interface examples * fixed examples * added some blocks docs * blocks * component fixes * reorganized some files (#1210) * formatting * added info for every component * fixes * blocks docs * added blocks demos * adding combined interfaces * added parallel, series * Doc: layout update (#1216) * doc layout * home spacing Co-authored-by: Abubakar Abid * adding layouts * layouts done * added events for components * formatting and https * brings back dropdown and other components * fix header ids * moved ids and fixed nav * added parameters for remaining component * docstring fixes * landing page demos * demo window placeholder * demo nav * fixed test * formatting * demo code * correctly importing gradio css/js * remove keyvalues * modify launch script to move gradio assetS * components embedded test * correct demo name * hide try demo and embedding * local devserver changes * create embedding json with configs * changes * fixes * comment out layout docs * demo work * demo fixes * demo embedding fixes * typo * jinja fix * demo nav fix * hide demo button * formatting * removed whitespace * remove newline from parameter * reverting comments Co-authored-by: aliabd Co-authored-by: Victor Muštar Co-authored-by: Ali Abid --- demo/blocks_essay/run.py | 20 + demo/blocks_flipper/run.py | 34 + demo/blocks_gpt/run.py | 16 + demo/blocks_hello/run.py | 22 + demo/blocks_js_methods/run.py | 5 +- demo/blocks_simple_squares/run.py | 7 +- demo/blocks_speech_text_length/run.py | 26 +- demo/blocks_update/run.py | 6 +- demo/blocks_webcam/run.py | 1 - .../run.py | 2 +- demo/hello_world/run.py | 1 - demo/hello_world_2/run.py | 2 +- demo/hello_world_3/run.py | 2 +- gradio.egg-info/PKG-INFO | 6 +- gradio/.dockerignore | 3 +- gradio/__init__.py | 3 +- gradio/blocks.py | 178 +-- gradio/components.py | 410 ++---- gradio/deprecation.py | 3 + gradio/events.py | 181 +++ gradio/inputs.py | 4 +- gradio/interface.py | 128 +- gradio/layouts.py | 124 ++ gradio/mix.py | 32 +- gradio/outputs.py | 2 +- gradio/templates/frontend/index.html | 4 +- guides/Gradio_and_ONNX_on_Hugging_Face.md | 2 + guides/building_a_pictionary_app.md | 1 + guides/create_your_own_friends_with_a_gan.md | 1 + guides/creating_a_chatbot.md | 1 + guides/getting_started.md | 26 +- guides/image_classification_in_pytorch.md | 1 + guides/image_classification_in_tensorflow.md | 1 + ...classification_with_vision_transformers.md | 1 + guides/introduction_to_blocks.md | 132 ++ guides/real_time_speech_recognition.md | 1 + scripts/launch_website.sh | 3 + test/test_components.py | 2 +- test/test_interfaces.py | 3 +- ui/packages/app/src/themes/typography.min.css | 2 +- website/demos/map_demos.py | 16 +- website/demos/run_demos.py | 4 +- website/homepage/embedding-configs.json | 724 ++++++++++ website/homepage/netlify.toml | 2 + website/homepage/nginx.conf | 4 +- website/homepage/package-lock.json | 1180 ++++------------- website/homepage/package.json | 11 +- website/homepage/render_html.py | 310 ++++- website/homepage/runtime.txt | 1 + website/homepage/src/assets/img/thunder.svg | 34 + .../assets/img/twitter/8vyTl51q_400x400.jpeg | Bin 0 -> 34835 bytes .../assets/img/twitter/GDLc7Oe4_400x400.jpeg | Bin 0 -> 16287 bytes .../assets/img/twitter/LsCnjnsl_400x400.jpeg | Bin 0 -> 13321 bytes .../assets/img/twitter/ksO1TT2P_400x400.jpeg | Bin 0 -> 28922 bytes .../assets/img/twitter/pwMrDOBv_400x400.jpeg | Bin 0 -> 27123 bytes website/homepage/src/docs_template.html | 787 +++++++++-- website/homepage/src/gallery.html | 156 +++ .../homepage/src/guides_main_template.html | 23 +- website/homepage/src/guides_template.html | 42 +- website/homepage/src/index_template.html | 429 +++--- website/homepage/src/navbar.html | 63 +- .../kitchen_sink_template.html | 6 +- website/homepage/src/style.css | 34 + website/homepage/src/tweets.json | 81 +- website/homepage/tailwind.config.js | 48 +- 65 files changed, 3379 insertions(+), 1975 deletions(-) create mode 100644 demo/blocks_essay/run.py create mode 100644 demo/blocks_flipper/run.py create mode 100644 demo/blocks_gpt/run.py create mode 100644 demo/blocks_hello/run.py create mode 100644 gradio/events.py create mode 100644 gradio/layouts.py create mode 100644 guides/introduction_to_blocks.md create mode 100644 website/homepage/embedding-configs.json create mode 100644 website/homepage/netlify.toml create mode 100644 website/homepage/runtime.txt create mode 100644 website/homepage/src/assets/img/thunder.svg create mode 100644 website/homepage/src/assets/img/twitter/8vyTl51q_400x400.jpeg create mode 100644 website/homepage/src/assets/img/twitter/GDLc7Oe4_400x400.jpeg create mode 100644 website/homepage/src/assets/img/twitter/LsCnjnsl_400x400.jpeg create mode 100644 website/homepage/src/assets/img/twitter/ksO1TT2P_400x400.jpeg create mode 100644 website/homepage/src/assets/img/twitter/pwMrDOBv_400x400.jpeg create mode 100644 website/homepage/src/gallery.html diff --git a/demo/blocks_essay/run.py b/demo/blocks_essay/run.py new file mode 100644 index 0000000000..dd91527a05 --- /dev/null +++ b/demo/blocks_essay/run.py @@ -0,0 +1,20 @@ +import gradio as gr + +def change_textbox(choice): + if choice == "short": + return gr.Radio.update(lines=2, visible=True) + elif choice == "long": + return gr.Radio.update(lines=8, visible=True) + else: + return gr.Radio.update(visible=False) + + +with gr.Blocks() as demo: + radio = gr.Radio(["short", "long", "none"], + label="What kind of essay would you like to write?") + text = gr.Textbox(lines=2, interactive=True) + + radio.change(fn=change_textbox, inputs=radio, outputs=text) + +if __name__ == "__main__": + demo.launch() \ No newline at end of file diff --git a/demo/blocks_flipper/run.py b/demo/blocks_flipper/run.py new file mode 100644 index 0000000000..3e2f48e618 --- /dev/null +++ b/demo/blocks_flipper/run.py @@ -0,0 +1,34 @@ +import numpy as np +import gradio as gr + +demo = gr.Blocks() + + +def flip_text(x): + return x[::-1] + +def flip_image(x): + return np.fliplr(x) + + +with demo: + gr.Markdown("Flip text or image files using this demo.") + with gr.Tabs(): + with gr.TabItem("Flip Text"): + with gr.Column(): + with gr.Row(): + text_input = gr.Textbox() + text_output = gr.Textbox() + text_button = gr.Button("Flip") + with gr.TabItem("Flip Image"): + with gr.Column(): + with gr.Row(): + image_input = gr.Image() + image_output = gr.Image() + image_button = gr.Button("Flip") + + text_button.click(flip_text, inputs=text_input, outputs=text_output) + image_button.click(flip_image, inputs=image_input, outputs=image_output) + +if __name__ == "__main__": + demo.launch() \ No newline at end of file diff --git a/demo/blocks_gpt/run.py b/demo/blocks_gpt/run.py new file mode 100644 index 0000000000..8799cde02d --- /dev/null +++ b/demo/blocks_gpt/run.py @@ -0,0 +1,16 @@ +import gradio as gr + +api = gr.Interface.load("huggingface/EleutherAI/gpt-j-6B") + +def complete_with_gpt(text): + # Use the last 50 characters of the text as context + return text[:-50] + api(text[-50:]) + +with gr.Blocks() as demo: + textbox = gr.Textbox(placeholder="Type here and press enter...", lines=4) + btn = gr.Button("Generate") + + btn.click(complete_with_gpt, textbox, textbox) + +if __name__ == "__main__": + demo.launch() \ No newline at end of file diff --git a/demo/blocks_hello/run.py b/demo/blocks_hello/run.py new file mode 100644 index 0000000000..b4c3fb6529 --- /dev/null +++ b/demo/blocks_hello/run.py @@ -0,0 +1,22 @@ +import gradio as gr + + +def update(name): + return f"Welcome to Gradio, {name}!" + +demo = gr.Blocks() + +with demo: + gr.Markdown( + """ + # Hello World! + Start typing below to see the output. + """) + inp = gr.Textbox(placeholder="What is your name?") + out = gr.Textbox() + + inp.change(fn=update, + inputs=inp, + outputs=out) + +demo.launch() \ No newline at end of file diff --git a/demo/blocks_js_methods/run.py b/demo/blocks_js_methods/run.py index e225382512..fd7ab727af 100644 --- a/demo/blocks_js_methods/run.py +++ b/demo/blocks_js_methods/run.py @@ -2,7 +2,7 @@ import gradio as gr blocks = gr.Blocks() -with blocks: +with blocks as demo: subject = gr.Textbox(placeholder="subject") verb = gr.Radio(["ate", "loved", "hated"]) object = gr.Textbox(placeholder="object") @@ -24,4 +24,5 @@ with blocks: verb.change(lambda x: x, verb, output3, _js="(x) => [...x].reverse().join('')") foo_bar_btn.click(None, [], subject, _js="(x) => x + ' foo'") -blocks.launch() \ No newline at end of file +if __name__ == "__main__": + demo.launch() \ No newline at end of file diff --git a/demo/blocks_simple_squares/run.py b/demo/blocks_simple_squares/run.py index b1227eb835..ad47a2eca4 100644 --- a/demo/blocks_simple_squares/run.py +++ b/demo/blocks_simple_squares/run.py @@ -1,8 +1,8 @@ import gradio as gr -test = gr.Blocks(css="#btn {color: red}") +demo = gr.Blocks(css="#btn {color: red}") -with test: +with demo: num = gr.Variable(value=0) squared = gr.Number(value=0).style(text_color="blue", container_bg_color="yellow") btn = gr.Button("Next Square", elem_id="btn").style(rounded=False, bg_color="purple") @@ -13,4 +13,5 @@ with test: btn.click(increase, [num], [num, squared]) -test.launch() +if __name__ == "__main__": + demo.launch() \ No newline at end of file diff --git a/demo/blocks_speech_text_length/run.py b/demo/blocks_speech_text_length/run.py index bd56ec0e8e..769b214385 100644 --- a/demo/blocks_speech_text_length/run.py +++ b/demo/blocks_speech_text_length/run.py @@ -1,32 +1,32 @@ -from transformers import pipeline +# from transformers import pipeline import gradio as gr -asr = pipeline("automatic-speech-recognition", "facebook/wav2vec2-base-960h") -classifier = pipeline("text-classification") +# asr = pipeline("automatic-speech-recognition", "facebook/wav2vec2-base-960h") +# classifier = pipeline("text-classification") -def speech_to_text(speech): - text = asr(speech)["text"] - return text +# def speech_to_text(speech): +# text = asr(speech)["text"] +# return text -def text_to_sentiment(text): - return classifier(text)[0]["label"] +# def text_to_sentiment(text): +# return classifier(text)[0]["label"] demo = gr.Blocks() with demo: - m = gr.Audio(type="filepath") - t = gr.Textbox() - l = gr.Label() + audio_file = gr.Audio(type="filepath") + text = gr.Textbox() + label = gr.Label() b1 = gr.Button("Recognize Speech") b2 = gr.Button("Classify Sentiment") - b1.click(speech_to_text, inputs=m, outputs=t) - b2.click(text_to_sentiment, inputs=t, outputs=l) + # b1.click(speech_to_text, inputs=audio_file, outputs=text) + # b2.click(text_to_sentiment, inputs=text, outputs=label) if __name__ == "__main__": demo.launch() diff --git a/demo/blocks_update/run.py b/demo/blocks_update/run.py index 7daa74ea44..7dbb37b65e 100644 --- a/demo/blocks_update/run.py +++ b/demo/blocks_update/run.py @@ -1,6 +1,6 @@ import gradio as gr -with gr.Blocks() as block: +with gr.Blocks() as demo: gr.Markdown( """ # Animal Generator @@ -41,4 +41,6 @@ with gr.Blocks() as block: generate_btn.click(lambda x: x, details, output) -block.launch() + +if __name__ == "__main__": + demo.launch() \ No newline at end of file diff --git a/demo/blocks_webcam/run.py b/demo/blocks_webcam/run.py index 67943cfa30..e18acc7870 100644 --- a/demo/blocks_webcam/run.py +++ b/demo/blocks_webcam/run.py @@ -1,7 +1,6 @@ import numpy as np import gradio as gr -from gradio import Templates def snap(image): diff --git a/demo/gender_sentence_custom_interpretation/run.py b/demo/gender_sentence_custom_interpretation/run.py index c7ad179ecb..d94123007d 100644 --- a/demo/gender_sentence_custom_interpretation/run.py +++ b/demo/gender_sentence_custom_interpretation/run.py @@ -39,4 +39,4 @@ demo = gr.Interface( ) if __name__ == "__main__": - demo.launch(auth=("a", "b")) + demo.launch() diff --git a/demo/hello_world/run.py b/demo/hello_world/run.py index b4302d5ffd..b481574e3e 100644 --- a/demo/hello_world/run.py +++ b/demo/hello_world/run.py @@ -4,7 +4,6 @@ import gradio as gr def greet(name): return "Hello " + name + "!!" - demo = gr.Interface(fn=greet, inputs="text", outputs="text") if __name__ == "__main__": diff --git a/demo/hello_world_2/run.py b/demo/hello_world_2/run.py index 721988c528..d08ba74ab9 100644 --- a/demo/hello_world_2/run.py +++ b/demo/hello_world_2/run.py @@ -12,4 +12,4 @@ demo = gr.Interface( ) if __name__ == "__main__": - app, local_url, share_url = demo.launch() + demo.launch() diff --git a/demo/hello_world_3/run.py b/demo/hello_world_3/run.py index 6437f3cc9b..def891c5ad 100644 --- a/demo/hello_world_3/run.py +++ b/demo/hello_world_3/run.py @@ -10,7 +10,7 @@ def greet(name, is_morning, temperature): demo = gr.Interface( fn=greet, - inputs=["text", "checkbox", gr.Slider(0, 100)], + inputs=["text", "checkbox", gr.Slider(minimum=0, maximum=100)], outputs=["text", "number"], ) if __name__ == "__main__": diff --git a/gradio.egg-info/PKG-INFO b/gradio.egg-info/PKG-INFO index 157449ad2c..af1d69a412 100644 --- a/gradio.egg-info/PKG-INFO +++ b/gradio.egg-info/PKG-INFO @@ -1,4 +1,4 @@ -Metadata-Version: 2.1 +Metadata-Version: 1.0 Name: gradio Version: 2.9b50 Summary: Python library for easily interacting with trained machine learning models @@ -6,6 +6,7 @@ Home-page: https://github.com/gradio-app/gradio-UI Author: Abubakar Abid, Ali Abid, Ali Abdalla, Dawood Khan, Ahsen Khaliq, Pete Allen, Ömer Faruk Özdemir Author-email: team@gradio.app License: Apache License 2.0 +Description: UNKNOWN Keywords: machine learning,visualization,reproducibility Platform: UNKNOWN Description-Content-Type: text/markdown @@ -633,5 +634,4 @@ author={Abid, Abubakar and Abdalla, Ali and Abid, Ali and Khan, Dawood and Alfoz journal={arXiv preprint arXiv:1906.02569}, year={2019} } -``` - +``` \ No newline at end of file diff --git a/gradio/.dockerignore b/gradio/.dockerignore index b06bba75d0..450a3af270 100644 --- a/gradio/.dockerignore +++ b/gradio/.dockerignore @@ -1 +1,2 @@ -templates/frontend \ No newline at end of file +templates/frontend +templates/frontend/**/* diff --git a/gradio/__init__.py b/gradio/__init__.py index e6b2632f89..3fdeb128ed 100644 --- a/gradio/__init__.py +++ b/gradio/__init__.py @@ -5,7 +5,7 @@ import gradio.inputs as inputs import gradio.outputs as outputs import gradio.processing_utils import gradio.templates -from gradio.blocks import Blocks, Box, Column, Group, Row, TabItem, Tabs +from gradio.blocks import Blocks from gradio.components import ( HTML, JSON, @@ -48,6 +48,7 @@ from gradio.flagging import ( SimpleCSVLogger, ) from gradio.interface import Interface, TabbedInterface, close_all +from gradio.layouts import Box, Column, Group, Row, TabItem, Tabs from gradio.mix import Parallel, Series from gradio.templates import * diff --git a/gradio/blocks.py b/gradio/blocks.py index d483d0596f..a9fb963225 100644 --- a/gradio/blocks.py +++ b/gradio/blocks.py @@ -158,94 +158,6 @@ class BlockContext(Block): return y -class Row(BlockContext): - def get_config(self): - return {"type": "row", **super().get_config()} - - @staticmethod - def update( - visible: Optional[bool] = None, - ): - return { - "visible": visible, - "__type__": "update", - } - - def style( - self, - equal_height: Optional[bool] = None, - mobile_collapse: Optional[bool] = None, - ): - if equal_height is not None: - self._style["equal_height"] = equal_height - if mobile_collapse is not None: - self._style["mobile_collapse"] = mobile_collapse - - return self - - -class Column(BlockContext): - def __init__( - self, - visible: bool = True, - variant: str = "default", - ): - """ - variant: column type, 'default' (no background) or 'panel' (gray background color and rounded corners) - """ - self.variant = variant - super().__init__(visible=visible) - - def get_config(self): - return { - "type": "column", - "variant": self.variant, - **super().get_config(), - } - - @staticmethod - def update( - variant: Optional[str] = None, - visible: Optional[bool] = None, - ): - return { - "variant": variant, - "visible": visible, - "__type__": "update", - } - - -class Tabs(BlockContext): - def change(self, fn: Callable, inputs: List[Component], outputs: List[Component]): - """ - Parameters: - fn: Callable function - inputs: List of inputs - outputs: List of outputs - Returns: None - """ - self.set_event_trigger("change", fn, inputs, outputs) - - -class TabItem(BlockContext): - def __init__(self, label, **kwargs): - super().__init__(**kwargs) - self.label = label - - def get_config(self): - return {"label": self.label, **super().get_config()} - - def select(self, fn: Callable, inputs: List[Component], outputs: List[Component]): - """ - Parameters: - fn: Callable function - inputs: List of inputs - outputs: List of outputs - Returns: None - """ - self.set_event_trigger("select", fn, inputs, outputs) - - class BlockFunction: def __init__(self, fn: Optional[Callable], preprocess: bool, postprocess: bool): self.fn = fn @@ -256,6 +168,19 @@ class BlockFunction: class Blocks(BlockContext): + """ + The Blocks class is a low-level API that allows you to create custom web + applications entirely in Python. Compared to the Interface class, Blocks offers + more flexibility and control over: (1) the layout of components (2) the events that + trigger the execution of functions (3) data flows (e.g. inputs can trigger outputs, + which can trigger the next level of outputs). Blocks also offers ways to group + together related demos e.g. using tabs. + + The basic usage of Blocks is as follows: create a Blocks object, then use it as a + context (with the "with" statement), and then define layouts, components, or events + within the Blocks context. Finally, call the launch() method to launch the demo. + """ + def __init__( self, theme: str = "default", @@ -264,7 +189,12 @@ class Blocks(BlockContext): css: Optional[str] = None, **kwargs, ): - + """ + Parameters: + theme (str): which theme to use - right now, only "default" is supported. + analytics_enabled (bool | None): whether to allow basic telemetry. If None, will use GRADIO_ANALYTICS_ENABLED environment variable or default to True. + mode (str): a human-friendly name for the kind of Blocks interface being created. + """ # Cleanup shared parameters with Interface #TODO: is this part still necessary after Interface with Blocks? self.save_to = None self.api_mode = False @@ -467,13 +397,12 @@ class Blocks(BlockContext): def launch( self, inline: bool = None, - inbrowser: bool = None, + inbrowser: bool = False, share: bool = False, debug: bool = False, enable_queue: bool = None, auth: Optional[Callable | Tuple[str, str] | List[Tuple[str, str]]] = None, auth_message: Optional[str] = None, - private_endpoint: Optional[str] = None, prevent_thread_lock: bool = False, show_error: bool = True, server_name: Optional[str] = None, @@ -488,27 +417,28 @@ class Blocks(BlockContext): ssl_keyfile_password: Optional[str] = None, ) -> Tuple[FastAPI, str, str]: """ - Launches the webserver that serves the UI for the interface. + Launches a simple web server that serves the demo. Can also be used to create a + shareable link. Parameters: - inline (bool): whether to display in the interface inline on python notebooks. + inline (bool | None): whether to display in the interface inline in an iframe. Defaults to True in python notebooks; False otherwise. inbrowser (bool): whether to automatically launch the interface in a new tab on the default browser. - share (bool): whether to create a publicly shareable link from your computer for the interface. - debug (bool): if True, and the interface was launched from Google Colab, prints the errors in the cell output. - auth (Callable, Union[Tuple[str, str], List[Tuple[str, str]]]): If provided, username and password (or list of username-password tuples) required to access interface. Can also provide function that takes username and password and returns True if valid login. - auth_message (str): If provided, HTML message provided on login page. - private_endpoint (str): If provided, the public URL of the interface will be this endpoint (should generally be unchanged). + share (bool): whether to create a publicly shareable link for the interface. Creates an SSH tunnel to make your UI accessible from anywhere. + debug (bool): if True, blocks the main thread from running. If running in Google Colab, this is needed to print the errors in the cell output. + auth (Callable | Union[Tuple[str, str] | List[Tuple[str, str]]] | None): If provided, username and password (or list of username-password tuples) required to access interface. Can also provide function that takes username and password and returns True if valid login. + auth_message (str | None): If provided, HTML message provided on login page. prevent_thread_lock (bool): If True, the interface will block the main thread while the server is running. show_error (bool): If True, any errors in the interface will be printed in the browser console log - server_port (int): will start gradio app on this port (if available). Can be set by environment variable GRADIO_SERVER_PORT. - server_name (str): to make app accessible on local network, set this to "0.0.0.0". Can be set by environment variable GRADIO_SERVER_NAME. + server_port (int | None): will start gradio app on this port (if available). Can be set by environment variable GRADIO_SERVER_PORT. If None, will search for an available port starting at 7860. + server_name (str | None): to make app accessible on local network, set this to "0.0.0.0". Can be set by environment variable GRADIO_SERVER_NAME. If None, will use "127.0.0.1". show_tips (bool): if True, will occasionally show tips about new Gradio features + enable_queue (bool | None): if True, inference requests will be served through a queue instead of with parallel threads. Required for longer inference times (> 1min) to prevent timeout. The default option in HuggingFace Spaces is True. The default option elsewhere is False. width (int): The width in pixels of the iframe element containing the interface (used if inline=True) height (int): The height in pixels of the iframe element containing the interface (used if inline=True) encrypt (bool): If True, flagged data will be encrypted by key provided by creator at launch - favicon_path (str): If a path to a file (.png, .gif, or .ico) is provided, it will be used as the favicon for the web page. - ssl_keyfile (str): If a path to a file is provided, will use this as the private key file to create a local server running on https. - ssl_certfile (str): If a path to a file is provided, will use this as the signed certificate for https. Needs to be provided if ssl_keyfile is provided. - ssl_keyfile_password (str): If a password is provided, will use this with the ssl certificate for https. + favicon_path (str | None): If a path to a file (.png, .gif, or .ico) is provided, it will be used as the favicon for the web page. + ssl_keyfile (str | None): If a path to a file is provided, will use this as the private key file to create a local server running on https. + ssl_certfile (str | None): If a path to a file is provided, will use this as the signed certificate for https. Needs to be provided if ssl_keyfile is provided. + ssl_keyfile_password (str | None): If a password is provided, will use this with the ssl certificate for https. Returns: app (FastAPI): FastAPI app object that is running the demo local_url (str): Locally accessible link to the demo @@ -578,23 +508,15 @@ class Blocks(BlockContext): if is_colab and self.requires_permissions: print(strings.en["MEDIA_PERMISSIONS_IN_COLAB"]) - if private_endpoint is not None: - share = True - if share: if self.is_space: raise RuntimeError("Share is not supported when you are in Spaces") try: if self.share_url is None: - share_url = networking.setup_tunnel( - self.server_port, private_endpoint - ) + share_url = networking.setup_tunnel(self.server_port, None) self.share_url = share_url print(strings.en["SHARE_LINK_DISPLAY"].format(self.share_url)) - if private_endpoint: - print(strings.en["PRIVATE_LINK_MESSAGE"]) - else: - print(strings.en["SHARE_LINK_MESSAGE"]) + print(strings.en["SHARE_LINK_MESSAGE"]) except RuntimeError: if self.analytics_enabled: utils.error_analytics(self.ip_address, "Not able to set up tunnel") @@ -686,31 +608,3 @@ class Blocks(BlockContext): self.server.close() if self.enable_queue: queueing.close() - - -class Group(BlockContext): - def get_config(self): - return {"type": "group", **super().get_config()} - - @staticmethod - def update( - visible: Optional[bool] = None, - ): - return { - "visible": visible, - "__type__": "update", - } - - -class Box(BlockContext): - def get_config(self): - return {"type": "box", **super().get_config()} - - @staticmethod - def update( - visible: Optional[bool] = None, - ): - return { - "visible": visible, - "__type__": "update", - } diff --git a/gradio/components.py b/gradio/components.py index 7a5f8ca158..f53c148a77 100644 --- a/gradio/components.py +++ b/gradio/components.py @@ -1,3 +1,7 @@ +"""Contains all of the components that can be used used with Gradio Interface / Blocks. +Along with the docs for each component, you can find the names of example demos that use +each component. These demos are located in the `demo` directory.""" + from __future__ import annotations import inspect @@ -23,6 +27,14 @@ from markdown_it import MarkdownIt from gradio import media_data, processing_utils from gradio.blocks import Block +from gradio.events import ( + Changeable, + Clearable, + Clickable, + Editable, + Playable, + Submittable, +) class Component(Block): @@ -248,186 +260,13 @@ class IOComponent(Component): return self -class Changeable(Component): - def change( - self, - fn: Callable, - inputs: List[Component], - outputs: List[Component], - status_tracker: Optional[StatusTracker] = None, - _js: Optional[str] = None, - ): - """ - Parameters: - fn: Callable function - inputs: List of inputs - outputs: List of outputs - status_tracker: StatusTracker to visualize function progress - _js: Optional frontend js method to run before running 'fn'. Input arguments for js method are values of input and outputs components, return should be a list of values for output component. - Returns: None - """ - self.set_event_trigger( - "change", fn, inputs, outputs, status_tracker=status_tracker, js=_js - ) - - -class Clickable(Component): - def click( - self, - fn: Callable, - inputs: List[Component], - outputs: List[Component], - status_tracker: Optional[StatusTracker] = None, - queue=None, - _js: Optional[str] = None, - _preprocess: bool = True, - _postprocess: bool = True, - ): - """ - Parameters: - fn: Callable function - inputs: List of inputs - outputs: List of outputs - status_tracker: StatusTracker to visualize function progress - _js: Optional frontend js method to run before running 'fn'. Input arguments for js method are values of 'inputs' and 'outputs', return should be a list of values for output components. - _preprocess: If False, will not run preprocessing of component data before running 'fn'. - _postprocess: If False, will not run postprocessing of component data before returning 'fn' output. - Returns: None - """ - self.set_event_trigger( - "click", - fn, - inputs, - outputs, - status_tracker=status_tracker, - queue=queue, - js=_js, - preprocess=_preprocess, - postprocess=_postprocess, - ) - - -class Submittable(Component): - def submit( - self, - fn: Callable, - inputs: List[Component], - outputs: List[Component], - status_tracker: Optional[StatusTracker] = None, - _js: Optional[str] = None, - ): - """ - Parameters: - fn: Callable function - inputs: List of inputs - outputs: List of outputs - status_tracker: StatusTracker to visualize function progress - _js: Optional frontend js method to run before running 'fn'. Input arguments for js method are values of 'inputs' and 'outputs', return should be a list of values for output components. - Returns: None - """ - self.set_event_trigger( - "submit", fn, inputs, outputs, status_tracker=status_tracker, js=_js - ) - - -class Editable(Component): - def edit( - self, - fn: Callable, - inputs: List[Component], - outputs: List[Component], - _js: Optional[str] = None, - ): - """ - Parameters: - fn: Callable function - inputs: List of inputs - outputs: List of outputs - _js: Optional frontend js method to run before running 'fn'. Input arguments for js method are values of 'inputs' and 'outputs', return should be a list of values for output components. - Returns: None - """ - self.set_event_trigger("edit", fn, inputs, outputs, js=_js) - - -class Clearable(Component): - def clear( - self, - fn: Callable, - inputs: List[Component], - outputs: List[Component], - _js: Optional[str] = None, - ): - """ - Parameters: - fn: Callable function - inputs: List of inputs - outputs: List of outputs - _js: Optional frontend js method to run before running 'fn'. Input arguments for js method are values of 'inputs' and 'outputs', return should be a list of values for output components. - Returns: None - """ - self.set_event_trigger("submit", fn, inputs, outputs, js=_js) - - -class Playable(Component): - def play( - self, - fn: Callable, - inputs: List[Component], - outputs: List[Component], - _js: Optional[str] = None, - ): - """ - Parameters: - fn: Callable function - inputs: List of inputs - outputs: List of outputs - _js: Optional frontend js method to run before running 'fn'. Input arguments for js method are values of 'inputs' and 'outputs', return should be a list of values for output components. - Returns: None - """ - self.set_event_trigger("play", fn, inputs, outputs, js=_js) - - def pause( - self, - fn: Callable, - inputs: List[Component], - outputs: List[Component], - _js: Optional[str] = None, - ): - """ - Parameters: - fn: Callable function - inputs: List of inputs - outputs: List of outputs - _js: Optional frontend js method to run before running 'fn'. Input arguments for js method are values of 'inputs' and 'outputs', return should be a list of values for output components. - Returns: None - """ - self.set_event_trigger("pause", fn, inputs, outputs, js=_js) - - def stop( - self, - fn: Callable, - inputs: List[Component], - outputs: List[Component], - _js: Optional[str] = None, - ): - """ - Parameters: - fn: Callable function - inputs: List of inputs - outputs: List of outputs - _js: Optional frontend js method to run before running 'fn'. Input arguments for js method are values of 'inputs' and 'outputs', return should be a list of values for output components. - Returns: None - """ - self.set_event_trigger("stop", fn, inputs, outputs, js=_js) - - class Textbox(Changeable, Submittable, IOComponent): """ - Component creates a textbox for user to enter string input or display string output. Provides a string as an argument to the wrapped function. - Input type: str - Output type: str + Creates a textarea for user to enter string input or display string output. + Preprocessing: passes textarea value as a {str} into the function. + Postprocessing: expects a {str} returned from function and sets textarea value to it. - Demos: hello_world, diff_texts, sentence_builder + Demos: hello_world, diff_texts, sentence_builder, blocks_gpt """ def __init__( @@ -628,12 +467,11 @@ class Textbox(Changeable, Submittable, IOComponent): class Number(Changeable, Submittable, IOComponent): """ - Component creates a field for user to enter numeric input or display numeric output. Provides a number as an argument to the wrapped function. - Can be used as an output as well. + Creates a numeric field for user to enter numbers as input or display numeric output. + Preprocessing: passes field value as a {float} or {int} into the function, depending on `precision`. + Postprocessing: expects an {int} or {float} returned from the function and sets field value to it. - Input type: float or int. - Output type: float or int. - Demos: tax_calculator, titanic_survival + Demos: tax_calculator, titanic_survival, blocks_static_textbox, blocks_simple_squares """ def __init__( @@ -654,8 +492,7 @@ class Number(Changeable, Submittable, IOComponent): label (Optional[str]): component name in interface. show_label (bool): if True, will display label. visible (bool): If False, component will be hidden. - precision (Optional[int]): Precision to round input/output to. If set to 0, will - round to nearest integer and covert type to int. If None, no rounding happens. + precision (Optional[int]): Precision to round input/output to. If set to 0, will round to nearest integer and covert type to int. If None, no rounding happens. """ self.value = self.round_to_precision(value, precision) self.precision = precision @@ -825,9 +662,10 @@ class Number(Changeable, Submittable, IOComponent): class Slider(Changeable, IOComponent): """ - Component creates a slider that ranges from `minimum` to `maximum`. Provides a number as an argument to the wrapped function. + Creates a slider that ranges from `minimum` to `maximum` with a step size of `step`. + Preprocessing: passes slider value as a {float} into the function. + Postprocessing: expects an {int} or {float} returned from function and sets slider value to it as long as it is within range. - Input type: float Demos: sentence_builder, generate_tone, titanic_survival """ @@ -953,7 +791,7 @@ class Slider(Changeable, IOComponent): # Output Functionalities - def postprocess(self, y: float | None): + def postprocess(self, y: int | float | None): """ Any postprocessing needed to be performed on function output. """ @@ -979,10 +817,10 @@ class Slider(Changeable, IOComponent): class Checkbox(Changeable, IOComponent): """ - Component creates a checkbox that can be set to `True` or `False`. Provides a boolean as an argument to the wrapped function. + Creates a checkbox that can be set to `True` or `False`. - Input type: bool - Output type: bool + Preprocessing: passes the status of the checkbox as a {bool} into the function. + Postprocessing: expects a {bool} returned from the function and, if it is True, checks the checkbox. Demos: sentence_builder, titanic_survival """ @@ -1105,9 +943,10 @@ class Checkbox(Changeable, IOComponent): class CheckboxGroup(Changeable, IOComponent): """ - Component creates a set of checkboxes of which a subset can be selected. Provides a list of strings representing the selected choices as an argument to the wrapped function. + Creates a set of checkboxes of which a subset can be checked. + Preprocessing: passes the list of checked checkboxes as a {List[str]} or their indices as a {List[int]} into the function, depending on `type`. + Postprocessing: expects a {List[str]}, each element of which becomes a checked checkbox. - Input type: Union[List[str], List[int]] Demos: sentence_builder, titanic_survival, fraud_detector """ @@ -1271,10 +1110,11 @@ class CheckboxGroup(Changeable, IOComponent): class Radio(Changeable, IOComponent): """ - Component creates a set of radio buttons of which only one can be selected. Provides string representing selected choice as an argument to the wrapped function. + Creates a set of radio buttons of which only one can be selected. + Preprocessing: passes the value of the selected radio button as a {str} or its index as an {int} into the function, depending on `type`. + Postprocessing: expects a {str} corresponding to the value of the radio button to be selected. - Input type: Union[str, int] - Demos: sentence_builder, tax_calculator, titanic_survival + Demos: sentence_builder, tax_calculator, titanic_survival, blocks_essay """ def __init__( @@ -1421,9 +1261,10 @@ class Radio(Changeable, IOComponent): class Dropdown(Radio): """ - Component creates a dropdown of which only one can be selected. Provides string representing selected choice as an argument to the wrapped function. + Creates a dropdown of which only one entry can be selected. + Preprocessing: passes the value of the selected dropdown entry as a {str} or its index as an {int} into the function, depending on `type`. + Postprocessing: expects a {str} corresponding to the value of the dropdown entry to be selected. - Input type: Union[str, int] Demos: sentence_builder, filter_records, titanic_survival """ @@ -1466,10 +1307,10 @@ class Dropdown(Radio): class Image(Editable, Clearable, Changeable, IOComponent): """ - Component creates an image component with input and output capabilities. + 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`. + Postprocessing: expects a {numpy.array}, {PIL.Image} or {str} filepath to an image and displays the image. - Input type: Union[numpy.array, PIL.Image, file-object] - Output type: Union[numpy.array, PIL.Image, str, matplotlib.pyplot, Tuple[Union[numpy.array, PIL.Image, str], List[Tuple[str, float, float, float, float]]]] Demos: image_classifier, image_mod, webcam, digit_classifier """ @@ -1796,10 +1637,10 @@ class Image(Editable, Clearable, Changeable, IOComponent): class Video(Changeable, Clearable, Playable, IOComponent): """ - Component creates a video file upload that is converted to a file path. + Creates an video component that can be used to upload/record videos (as an input) or display videos (as an output). + Preprocessing: passes the uploaded video as a {str} filepath whose extension can be set by `format`. + Postprocessing: expects a {str} filepath to a video which is displayed. - Input type: filepath - Output type: filepath Demos: video_flip """ @@ -1807,7 +1648,7 @@ class Video(Changeable, Clearable, Playable, IOComponent): self, value: str = "", *, - type: Optional[str] = None, + format: Optional[str] = None, source: str = "upload", label: Optional[str] = None, show_label: bool = True, @@ -1819,7 +1660,7 @@ class Video(Changeable, Clearable, Playable, IOComponent): """ Parameters: value(str): A path or URL for the default value that Video component is going to take. - type (str): Type of video format to be returned by component, such as 'avi' or 'mp4'. Use 'mp4' to ensure browser playability. If set to None, video will keep uploaded format. + format (str): Format of video format to be returned by component, such as 'avi' or 'mp4'. Use 'mp4' to ensure browser playability. If set to None, video will keep uploaded format. source (str): Source of video. "upload" creates a box where user can drop an video file, "webcam" allows user to record a video from their webcam. label (Optional[str]): component name in interface. show_label (bool): if True, will display label. @@ -1828,7 +1669,7 @@ class Video(Changeable, Clearable, Playable, IOComponent): self.value = ( processing_utils.encode_url_or_file_to_base64(value) if value else None ) - self.type = type + self.format = format self.source = source IOComponent.__init__( self, @@ -1891,8 +1732,8 @@ class Video(Changeable, Clearable, Playable, IOComponent): ) file_name = file.name uploaded_format = file_name.split(".")[-1].lower() - if self.type is not None and uploaded_format != self.type: - output_file_name = file_name[0 : file_name.rindex(".") + 1] + self.type + if self.format is not None and uploaded_format != self.format: + output_file_name = file_name[0 : file_name.rindex(".") + 1] + self.format ff = FFmpeg(inputs={file_name: None}, outputs={output_file_name: None}) ff.run() return output_file_name @@ -1921,8 +1762,8 @@ class Video(Changeable, Clearable, Playable, IOComponent): (str): base64 url data """ returned_format = y.split(".")[-1].lower() - if self.type is not None and returned_format != self.type: - output_file_name = y[0 : y.rindex(".") + 1] + self.type + if self.format is not None and returned_format != self.format: + output_file_name = y[0 : y.rindex(".") + 1] + self.format ff = FFmpeg(inputs={y: None}, outputs={output_file_name: None}) ff.run() y = output_file_name @@ -1937,17 +1778,16 @@ class Video(Changeable, Clearable, Playable, IOComponent): class Audio(Changeable, Clearable, Playable, IOComponent): """ - Component accepts audio input files or creates an audio player that plays the output audio. + Creates an audio component that can be used to upload/record audio (as an input) or display audio (as an output). + Preprocessing: passes the uploaded audio as a {Tuple(int, numpy.array)} corresponding to (sample rate, data) or as a {str} filepath, depending on `type` + Postprocessing: expects a {Tuple(int, numpy.array)} corresponding to (sample rate, data) or as a {str} filepath to an audio file, which gets displayed - - Input type: Union[Tuple[int, numpy.array], file-object, numpy.array] - Output type: Union[Tuple[int, numpy.array], str] Demos: main_note, generate_tone, reverse_audio, spectogram """ def __init__( self, - value="", + value: str = "", *, source: str = "upload", type: str = "numpy", @@ -1960,7 +1800,7 @@ class Audio(Changeable, Clearable, Playable, IOComponent): ): """ Parameters: - value (str): IGNORED + value (str): A path or URL for the default value that Audio component is going to take. source (str): Source of audio. "upload" creates a box where user can drop an audio file, "microphone" creates a microphone input. type (str): 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" returns the path directly. label (Optional[str]): component name in interface. @@ -2218,10 +2058,10 @@ class Audio(Changeable, Clearable, Playable, IOComponent): class File(Changeable, Clearable, IOComponent): """ - Component accepts generic file uploads and output.. + Creates a file component that allows uploading generic file (when used as an input) and or displaying generic files (output). + Preprocessing: passes the uploaded file as a {file-object} or {List[file-object]} depending on `file_count` (or a {bytes}/{List{bytes}} depending on `type`) + Postprocessing: expects a {str} path to a file returned by the function. - Input type: Union[file-object, bytes, List[Union[file-object, bytes]]] - Output type: Union[file-like, str] Demos: zip_to_json, zip_two_files """ @@ -2362,9 +2202,10 @@ class File(Changeable, Clearable, IOComponent): class Dataframe(Changeable, IOComponent): """ - Component accepts or displays 2D input through a spreadsheet interface. + Accepts or displays 2D input through a spreadsheet-like component for dataframes. + Preprocessing: passes the uploaded spreadsheet data as a {pandas.DataFrame}, {numpy.array}, {List[List]}, or {List} depending on `type` + Postprocessing: expects a {pandas.DataFrame}, {numpy.array}, {List[List]}, or {List} which is rendered in the spreadsheet. - Input or Output type: Union[pandas.DataFrame, numpy.array, List[Union[str, float]], List[List[Union[str, float]]]] Demos: filter_records, matrix_transpose, tax_calculator """ @@ -2388,13 +2229,14 @@ class Dataframe(Changeable, IOComponent): **kwargs, ): """ - Input Parameters: - value (List[List[Any]]): Default value as a pandas DataFrame. TODO: Add support for default value as a filepath - row_count (Union[int, Tuple[int, str]]): Limit number of rows for input and decide whether user can create new rows. The first element of the tuple is an `int`, the row count; the second should be 'fixed' or 'dynamic', the new row behaviour. If an `int` is passed the rows default to 'dynamic' - col_count (Union[int, Tuple[int, str]]): Limit number of columns for input and decide whether user can create new columns. The first element of the tuple is an `int`, the number of columns; the second should be 'fixed' or 'dynamic', the new column behaviour. If an `int` is passed the columns default to 'dynamic' - datatype (Union[str, List[str]]): Datatype of values in sheet. Can be provided per column as a list of strings, or for the entire sheet as a single string. Valid datatypes are "str", "number", "bool", and "date". + Parameters: + value (List[List[Any]]): Default value as a 2-dimensional list of values. + headers (List[str] | None): List of str header names. If None, no headers are shown. + row_count (int | Tuple[int, str]): Limit number of rows for input and decide whether user can create new rows. The first element of the tuple is an `int`, the row count; the second should be 'fixed' or 'dynamic', the new row behaviour. If an `int` is passed the rows default to 'dynamic' + col_count (int | Tuple[int, str]): Limit number of columns for input and decide whether user can create new columns. The first element of the tuple is an `int`, the number of columns; the second should be 'fixed' or 'dynamic', the new column behaviour. If an `int` is passed the columns default to 'dynamic' + datatype (str | List[str]): Datatype of values in sheet. Can be provided per column as a list of strings, or for the entire sheet as a single string. Valid datatypes are "str", "number", "bool", and "date". type (str): Type of value to be returned by component. "pandas" for pandas dataframe, "numpy" for numpy array, or "array" for a Python array. - headers (List[str]): Header names to dataframe. Only applicable if type is "numpy" or "array". + label (str): component name in interface. max_rows (int): Maximum number of rows to display at once. Set to None for infinite. max_cols (int): Maximum number of columns to display at once. Set to None for infinite. overflow_row_behaviour (str): If set to "paginate", will create pages for overflow rows. If set to "show_ends", will show initial and final rows and truncate middle rows. @@ -2569,10 +2411,10 @@ class Dataframe(Changeable, IOComponent): class Timeseries(Changeable, IOComponent): """ - Component accepts pandas.DataFrame uploaded as a timeseries csv file or renders a dataframe consisting of a time series as output. + Creates a component that can be used to upload/preview timeseries csv files or display a dataframe consisting of a time series graphically. + Preprocessing: passes the uploaded timeseries data as a {pandas.DataFrame} into the function + Postprocessing: expects a {pandas.DataFrame} to be returned, which is then displayed as a timeseries graph - Input type: pandas.DataFrame - Output type: pandas.DataFrame Demos: fraud_detector """ @@ -2592,13 +2434,13 @@ class Timeseries(Changeable, IOComponent): ): """ Parameters: - value: File path for the timeseries csv file. TODO: Add support for default value as a pd.DataFrame + value: File path for the timeseries csv file. x (str): Column name of x (time) series. None if csv has no headers, in which case first column is x series. y (Union[str, List[str]]): Column name of y series, or list of column names if multiple series. None if csv has no headers, in which case every column after first is a y series. - label (Optional[str]): component name in interface. + label (str): component name in interface. + colors (List[str]): an ordered list of colors to use for each line plot show_label (bool): if True, will display label. visible (bool): If False, component will be hidden. - colors List[str]: an ordered list of colors to use for each line plot """ self.value = pd.read_csv(value) if value is not None else None self.x = x @@ -2691,11 +2533,12 @@ class Timeseries(Changeable, IOComponent): class Variable(IOComponent): """ - Special hidden component that stores state across runs of the interface. + Special hidden component that stores session state across runs of the demo by the + same user. The value of the Variable is cleared when the user refreshes the page. - Input type: Any - Output type: Any - Demos: chatbot + Preprocessing: No preprocessing is performed + Postprocessing: No postprocessing is performed + Demos: chatbot, blocks_simple_squares """ def __init__( @@ -2725,8 +2568,10 @@ class Variable(IOComponent): class Label(Changeable, IOComponent): """ - Component outputs a classification label, along with confidence scores of top categories if provided. Confidence scores are represented as a dictionary mapping labels to scores between 0 and 1. - Output type: Union[Dict[str, float], str, int, float] + Displays a classification label, along with confidence scores of top categories, if provided. + Preprocessing: this component does *not* accept input. + Postprocessing: expects a {Dict[str, float]} of classes and confidences, or {str} with just the class or an {int}/{float} for regression outputs. + Demos: image_classifier, main_note, titanic_survival """ @@ -2852,9 +2697,10 @@ class Label(Changeable, IOComponent): class HighlightedText(Changeable, IOComponent): """ - Component creates text that contains spans that are highlighted by category or numerical value. - Output is represent as a list of Tuple pairs, where the first element represents the span of text represented by the tuple, and the second element represents the category or value of the text. - Output type: List[Tuple[str, Union[float, str]]] + Displays text that contains spans that are highlighted by category or numerical value. + Preprocessing: this component does *not* accept input. + Postprocessing: expects a {List[Tuple[str, float | str]]]} consisting of spans of text and their associated labels. + Demos: diff_texts, text_analysis """ @@ -2955,9 +2801,11 @@ class HighlightedText(Changeable, IOComponent): class JSON(Changeable, IOComponent): """ - Used for JSON output. Expects a JSON string or a Python object that is JSON serializable. - Output type: Union[str, Any] - Demos: zip_to_json + Used to display arbitrary JSON output prettily. + Preprocessing: this component does *not* accept input. + Postprocessing: expects a valid JSON {str} -- or a {list} or {dict} that is JSON serializable. + + Demos: zip_to_json, blocks_xray """ def __init__( @@ -3029,8 +2877,10 @@ class JSON(Changeable, IOComponent): class HTML(Changeable, IOComponent): """ - Used for HTML output. Expects an HTML valid string. - Output type: str + Used to display arbitrary HTML output. + Preprocessing: this component does *not* accept input. + Postprocessing: expects a valid HTML {str}. + Demos: text_analysis """ @@ -3093,6 +2943,14 @@ class HTML(Changeable, IOComponent): class Gallery(IOComponent): + """ + Used to display a list of images as a gallery that can be scrolled through. + Preprocessing: this component does *not* accept input. + Postprocessing: expects a list of images in any format, {List[numpy.array | PIL.Image | str]}, and displays them. + + Demos: fake_gan + """ + def __init__( self, *, @@ -3178,8 +3036,10 @@ class Gallery(IOComponent): # max_grid=[3], grid_behavior="scale", height="auto" class Carousel(IOComponent): """ - Component displays a set of output components that can be scrolled through. - Output type: List[List[Any]] + Used to display a list of arbitrary components that can be scrolled through. + Preprocessing: this component does *not* accept input. + Postprocessing: Expects a nested {List[List]} where the inner elements depend on the components in the Carousel. + Demos: disease_report """ @@ -3280,8 +3140,10 @@ class Carousel(IOComponent): class Chatbot(Changeable, IOComponent): """ - Component displays a chatbot output showing both user submitted messages and responses - Output type: List[Tuple[str, str]] + Displays a chatbot output showing both user submitted messages and responses + Preprocessing: this component does *not* accept input. + Postprocessing: expects a {List[Tuple[str, str]]}, a list of tuples with user inputs and responses. + Demos: chatbot """ @@ -3355,7 +3217,7 @@ class Model3D(Changeable, Editable, Clearable, IOComponent): Component creates a 3D Model component with input and output capabilities. Input type: File object of type (.obj, glb, or .gltf) Output type: filepath - Demos: Model3D + Demos: model3D """ def __init__( @@ -3475,15 +3337,16 @@ class Model3D(Changeable, Editable, Clearable, IOComponent): class Plot(Changeable, Clearable, IOComponent): """ - Used for plot output. - Output type: matplotlib plt, plotly figure, or Bokeh fig (json_item format) - Demos: outbreak_forecast + Used to display various kinds of plots (matplotlib, plotly, or bokeh are supported) + Preprocessing: this component does *not* accept input. + Postprocessing: expects either a {matplotlib.pyplot.Figure}, a {plotly.graph_objects._figure.Figure}, or a {dict} corresponding to a bokeh plot (json_item format) + + Demos: outbreak_forecast, blocks_kinematics """ def __init__( self, *, - type: str = None, label: Optional[str] = None, show_label: bool = True, visible: bool = True, @@ -3492,12 +3355,10 @@ class Plot(Changeable, Clearable, IOComponent): ): """ Parameters: - type (str): type of plot (matplotlib, plotly) label (Optional[str]): component name in interface. show_label (bool): if True, will display label. visible (bool): If False, component will be hidden. """ - self.type = type IOComponent.__init__( self, label=label, @@ -3534,32 +3395,25 @@ class Plot(Changeable, Clearable, IOComponent): (str): plot base64 or json """ dtype = self.type - if self.type == "plotly": - out_y = y.to_json() - elif self.type == "matplotlib": + if isinstance(y, (ModuleType, matplotlib.pyplot.Figure)): + dtype = "matplotlib" out_y = processing_utils.encode_plot_to_base64(y) - elif self.type == "bokeh": + elif isinstance(y, dict): + dtype = "bokeh" out_y = json.dumps(y) - elif self.type == "auto": - if isinstance(y, (ModuleType, matplotlib.pyplot.Figure)): - dtype = "matplotlib" - out_y = processing_utils.encode_plot_to_base64(y) - elif isinstance(y, dict): - dtype = "bokeh" - out_y = json.dumps(y) - else: - dtype = "plotly" - out_y = y.to_json() else: - raise ValueError( - "Unknown type. Please choose from: 'plotly', 'matplotlib', 'bokeh'." - ) + dtype = "plotly" + out_y = y.to_json() return {"type": dtype, "plot": out_y} class Markdown(Component): """ - Used for Markdown output. Expects a valid string that is rendered into Markdown. + Used to render arbitrary Markdown output. + Preprocessing: this component does *not* accept input. + Postprocessing: expects a valid {str} that can be rendered as Markdown. + + Demos: blocks_hello, blocks_kinematics, blocks_neural_instrument_coding """ def __init__( @@ -3609,7 +3463,9 @@ class Markdown(Component): class Button(Clickable, Component): """ - Used to create a button, that can be assigned arbitrary click() events. + Used to create a button, that can be assigned arbitrary click() events. Accepts neither input nor output. + + Demos: blocks_inputs, blocks_kinematics """ def __init__( diff --git a/gradio/deprecation.py b/gradio/deprecation.py index 25bfd38e41..46ea2c5c52 100644 --- a/gradio/deprecation.py +++ b/gradio/deprecation.py @@ -15,6 +15,9 @@ DEPRECATION_MESSAGE = { "numeric": simple_deprecated_notice("numeric"), "verbose": simple_deprecated_notice("verbose"), "allow_screenshot": simple_deprecated_notice("allow_screenshot"), + "layout": simple_deprecated_notice("layout"), + "show_input": simple_deprecated_notice("show_input"), + "show_output": simple_deprecated_notice("show_output"), "capture_session": simple_deprecated_notice("capture_session"), "api_mode": simple_deprecated_notice("api_mode"), "show_tips": use_in_launch("show_tips"), diff --git a/gradio/events.py b/gradio/events.py new file mode 100644 index 0000000000..76deaa8cc4 --- /dev/null +++ b/gradio/events.py @@ -0,0 +1,181 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple + +from gradio.blocks import Block + +if TYPE_CHECKING: # Only import for type checking (is False at runtime). + from gradio.components import Component, StatusTracker + + +class Changeable(Block): + def change( + self, + fn: Callable, + inputs: List[Component], + outputs: List[Component], + status_tracker: Optional[StatusTracker] = None, + _js: Optional[str] = None, + ): + """ + Parameters: + fn: Callable function + inputs: List of inputs + outputs: List of outputs + status_tracker: StatusTracker to visualize function progress + _js: Optional frontend js method to run before running 'fn'. Input arguments for js method are values of input and outputs components, return should be a list of values for output component. + Returns: None + """ + self.set_event_trigger( + "change", fn, inputs, outputs, status_tracker=status_tracker, js=_js + ) + + +class Clickable(Block): + def click( + self, + fn: Callable, + inputs: List[Component], + outputs: List[Component], + status_tracker: Optional[StatusTracker] = None, + queue=None, + _js: Optional[str] = None, + _preprocess: bool = True, + _postprocess: bool = True, + ): + """ + Parameters: + fn: Callable function + inputs: List of inputs + outputs: List of outputs + status_tracker: StatusTracker to visualize function progress + _js: Optional frontend js method to run before running 'fn'. Input arguments for js method are values of 'inputs' and 'outputs', return should be a list of values for output components. + _preprocess: If False, will not run preprocessing of component data before running 'fn'. + _postprocess: If False, will not run postprocessing of component data before returning 'fn' output. + Returns: None + """ + self.set_event_trigger( + "click", + fn, + inputs, + outputs, + status_tracker=status_tracker, + queue=queue, + js=_js, + preprocess=_preprocess, + postprocess=_postprocess, + ) + + +class Submittable(Block): + def submit( + self, + fn: Callable, + inputs: List[Component], + outputs: List[Component], + status_tracker: Optional[StatusTracker] = None, + _js: Optional[str] = None, + ): + """ + Parameters: + fn: Callable function + inputs: List of inputs + outputs: List of outputs + status_tracker: StatusTracker to visualize function progress + _js: Optional frontend js method to run before running 'fn'. Input arguments for js method are values of 'inputs' and 'outputs', return should be a list of values for output components. + Returns: None + """ + self.set_event_trigger( + "submit", fn, inputs, outputs, status_tracker=status_tracker, js=_js + ) + + +class Editable(Block): + def edit( + self, + fn: Callable, + inputs: List[Component], + outputs: List[Component], + _js: Optional[str] = None, + ): + """ + Parameters: + fn: Callable function + inputs: List of inputs + outputs: List of outputs + _js: Optional frontend js method to run before running 'fn'. Input arguments for js method are values of 'inputs' and 'outputs', return should be a list of values for output components. + Returns: None + """ + self.set_event_trigger("edit", fn, inputs, outputs, js=_js) + + +class Clearable(Block): + def clear( + self, + fn: Callable, + inputs: List[Component], + outputs: List[Component], + _js: Optional[str] = None, + ): + """ + Parameters: + fn: Callable function + inputs: List of inputs + outputs: List of outputs + _js: Optional frontend js method to run before running 'fn'. Input arguments for js method are values of 'inputs' and 'outputs', return should be a list of values for output components. + Returns: None + """ + self.set_event_trigger("submit", fn, inputs, outputs, js=_js) + + +class Playable(Block): + def play( + self, + fn: Callable, + inputs: List[Component], + outputs: List[Component], + _js: Optional[str] = None, + ): + """ + Parameters: + fn: Callable function + inputs: List of inputs + outputs: List of outputs + _js: Optional frontend js method to run before running 'fn'. Input arguments for js method are values of 'inputs' and 'outputs', return should be a list of values for output components. + Returns: None + """ + self.set_event_trigger("play", fn, inputs, outputs, js=_js) + + def pause( + self, + fn: Callable, + inputs: List[Component], + outputs: List[Component], + _js: Optional[str] = None, + ): + """ + Parameters: + fn: Callable function + inputs: List of inputs + outputs: List of outputs + _js: Optional frontend js method to run before running 'fn'. Input arguments for js method are values of 'inputs' and 'outputs', return should be a list of values for output components. + Returns: None + """ + self.set_event_trigger("pause", fn, inputs, outputs, js=_js) + + def stop( + self, + fn: Callable, + inputs: List[Component], + outputs: List[Component], + _js: Optional[str] = None, + ): + """ + Parameters: + fn: Callable function + inputs: List of inputs + outputs: List of outputs + _js: Optional frontend js method to run before running 'fn'. Input arguments for js method are values of 'inputs' and 'outputs', return should be a list of values for output components. + Returns: None + """ + self.set_event_trigger("stop", fn, inputs, outputs, js=_js) diff --git a/gradio/inputs.py b/gradio/inputs.py index 7ce719f4a6..129a570597 100644 --- a/gradio/inputs.py +++ b/gradio/inputs.py @@ -80,7 +80,7 @@ class Number(C_Number): class Slider(C_Slider): """ - Component creates a slider that ranges from `minimum` to `maximum`. Provides a number as an argument to the wrapped function. + Component creates a slider that ranges from `minimum` to `maximum`. Provides number as an argument to the wrapped function. Input type: float Demos: sentence_builder, generate_tone, titanic_survival """ @@ -323,7 +323,7 @@ class Video(C_Video): "Usage of gradio.inputs is deprecated, and will not be supported in the future, please import your components from gradio.components", DeprecationWarning, ) - super().__init__(type=type, source=source, label=label, optional=optional) + super().__init__(format=type, source=source, label=label, optional=optional) class Audio(C_Audio): diff --git a/gradio/interface.py b/gradio/interface.py index 5add26f805..4c18f95d4a 100644 --- a/gradio/interface.py +++ b/gradio/interface.py @@ -21,7 +21,7 @@ from markdown_it import MarkdownIt from mdit_py_plugins.footnote import footnote_plugin from gradio import interpretation, utils -from gradio.blocks import Blocks, Column, Row, TabItem, Tabs +from gradio.blocks import Blocks from gradio.components import ( Button, Component, @@ -34,6 +34,7 @@ from gradio.components import ( ) from gradio.external import load_from_pipeline, load_interface # type: ignore from gradio.flagging import CSVLogger, FlaggingCallback # type: ignore +from gradio.layouts import Column, Row, TabItem, Tabs from gradio.process_examples import cache_interface_examples, load_from_cache if TYPE_CHECKING: # Only import for type checking (is False at runtime). @@ -42,9 +43,9 @@ if TYPE_CHECKING: # Only import for type checking (is False at runtime). class Interface(Blocks): """ - Gradio interfaces are created by constructing a `Interface` object - with a locally-defined function, or with `Interface.load()` with the path - to a repo or by `Interface.from_pipeline()` with a Transformers Pipeline. + The Interface class is a high-level abstraction that allows you to create a + web-based demo around a machine learning model or arbitrary Python function + by specifying: (1) the function (2) the desired input components and (3) desired output components. """ # stores references to all currently existing Interface instances @@ -73,12 +74,14 @@ class Interface(Blocks): **kwargs, ) -> Interface: """ - Class method to construct an Interface from an external source repository, such as huggingface. + Class method that constructs an Interface from a Hugging Face repo. Can accept + model repos (if src is "models") or Space repos (if src is "spaces"). The input + and output components are automatically loaded from the repo. Parameters: - name (str): the name of the model (e.g. "gpt2"), can include the `src` as prefix (e.g. "huggingface/gpt2") - src (str): the source of the model: `huggingface` or `gradio` (or empty if source is provided as a prefix in `name`) - api_key (str): optional api key for use with Hugging Face Model Hub - alias (str): optional, used as the name of the loaded model instead of the default name + name (str): the name of the model (e.g. "gpt2"), can include the `src` as prefix (e.g. "models/gpt2") + src (str | None): the source of the model: `models` or `spaces` (or empty if source is provided as a prefix in `name`) + api_key (str | None): optional api key for use with Hugging Face Hub + alias (str | None): optional string used as the name of the loaded model instead of the default name Returns: (gradio.Interface): a Gradio Interface object for the given model """ @@ -91,17 +94,12 @@ class Interface(Blocks): @classmethod def from_pipeline(cls, pipeline: transformers.Pipeline, **kwargs) -> Interface: """ - Construct an Interface from a Hugging Face transformers.Pipeline. + Class method that constructs an Interface from a Hugging Face transformers.Pipeline object. + The input and output components are automatically determined from the pipeline. Parameters: - pipeline (transformers.Pipeline): + pipeline (transformers.Pipeline): the pipeline object to use. Returns: (gradio.Interface): a Gradio Interface object from the given Pipeline - - Example usage: - import gradio as gr - from transformers import pipeline - pipe = pipeline(model="lysandre/tiny-vit-random") - gr.Interface.from_pipeline(pipe).launch() """ interface_info = load_from_pipeline(pipeline) kwargs = dict(interface_info, **kwargs) @@ -117,49 +115,44 @@ class Interface(Blocks): cache_examples: Optional[bool] = None, examples_per_page: int = 10, live: bool = False, - layout: str = "unaligned", - show_input: bool = True, - show_output: bool = True, interpretation: Optional[Callable | str] = None, num_shap: float = 2.0, - theme: Optional[str] = None, - repeat_outputs_per_model: bool = True, title: Optional[str] = None, description: Optional[str] = None, article: Optional[str] = None, thumbnail: Optional[str] = None, + theme: Optional[str] = None, css: Optional[str] = None, allow_flagging: Optional[str] = None, flagging_options: List[str] = None, flagging_dir: str = "flagged", - analytics_enabled: Optional[bool] = None, flagging_callback: FlaggingCallback = CSVLogger(), + analytics_enabled: Optional[bool] = None, + _repeat_outputs_per_model: bool = True, **kwargs, ): """ Parameters: - fn (Union[Callable, List[Callable]]): the function to wrap an interface around. - inputs (Union[str, InputComponent, List[Union[str, InputComponent]]]): a single Gradio input component, or list of Gradio input components. Components can either be passed as instantiated objects, or referred to by their string shortcuts. The number of input components should match the number of parameters in fn. - outputs (Union[str, OutputComponent, List[Union[str, OutputComponent]]]): a single Gradio output component, or list of Gradio output components. Components can either be passed as instantiated objects, or referred to by their string shortcuts. The number of output components should match the number of values returned by fn. - examples (Union[List[List[Any]], str]): sample inputs for the function; if provided, appears below the UI components and can be used to populate the interface. Should be nested list, in which the outer list consists of samples and each inner list consists of an input corresponding to each input component. A string path to a directory of examples can also be provided. If there are multiple input components and a directory is provided, a log.csv file must be present in the directory to link corresponding inputs. + fn (Callable): the function to wrap an interface around. Often a machine learning model's prediction function. + inputs (str | Component | List[str] | List[Component] | None): a single Gradio component, or list of Gradio components. Components can either be passed as instantiated objects, or referred to by their string shortcuts. The number of input components should match the number of parameters in fn. If set to None, then only the output components will be displayed. + outputs (str | Component | List[str] | List[Component] | None): a single Gradio component, or list of Gradio components. Components can either be passed as instantiated objects, or referred to by their string shortcuts. The number of output components should match the number of values returned by fn. If set to None, then only the input components will be displayed. + examples (List[List[Any]] | str | None): sample inputs for the function; if provided, appear below the UI components and can be clicked to populate the interface. Should be nested list, in which the outer list consists of samples and each inner list consists of an input corresponding to each input component. A string path to a directory of examples can also be provided. If there are multiple input components and a directory is provided, a log.csv file must be present in the directory to link corresponding inputs. + cache_examples (bool | None): If True, caches examples in the server for fast runtime in examples. The default option in HuggingFace Spaces is True. The default option elsewhere is False. examples_per_page (int): If examples are provided, how many to display per page. - cache_examples(Optional[bool]): - If True, caches examples in the server for fast runtime in examples. - The default option in HuggingFace Spaces is True. - The default option elsewhere is False. - live (bool): whether the interface should automatically reload on change. - layout (str): Layout of input and output panels. "horizontal" arranges them as two columns of equal height, "unaligned" arranges them as two columns of unequal height, and "vertical" arranges them vertically. - interpretation (Union[Callable, str]): function that provides interpretation explaining prediction output. Pass "default" to use simple built-in interpreter, "shap" to use a built-in shapley-based interpreter, or your own custom interpretation function. + live (bool): whether the interface should automatically rerun if any of the inputs change. + interpretation (Callable | str): function that provides interpretation explaining prediction output. Pass "default" to use simple built-in interpreter, "shap" to use a built-in shapley-based interpreter, or your own custom interpretation function. num_shap (float): a multiplier that determines how many examples are computed for shap-based interpretation. Increasing this value will increase shap runtime, but improve results. Only applies if interpretation is "shap". - title (str): a title for the interface; if provided, appears above the input and output components. - description (str): a description for the interface; if provided, appears above the input and output components. - article (str): an expanded article explaining the interface; if provided, appears below the input and output components. Accepts Markdown and HTML content. - thumbnail (str): path to image or src to use as display picture for models listed in gradio.app/hub - theme (str): Theme to use - one of "default", "huggingface", "seafoam", "grass", "peach". Add "dark-" prefix, e.g. "dark-peach" for dark theme (or just "dark" for the default dark theme). - css (str): custom css or path to custom css file to use with interface. - allow_flagging (str): one of "never", "auto", or "manual". If "never" or "auto", users will not see a button to flag an input and output. If "manual", users will see a button to flag. If "auto", every prediction will be automatically flagged. If "manual", samples are flagged when the user clicks flag button. Can be set with environmental variable GRADIO_ALLOW_FLAGGING. - flagging_options (List[str]): if provided, allows user to select from the list of options when flagging. Only applies if allow_flagging is "manual". - flagging_dir (str): what to name the dir where flagged data is stored. + title (str | None): a title for the interface; if provided, appears above the input and output components in large font. + description (str | None): a description for the interface; if provided, appears above the input and output components and beneath the title in regular font. Accepts Markdown and HTML content. + article (str | None): an expanded article explaining the interface; if provided, appears below the input and output components in regular font. Accepts Markdown and HTML content. + thumbnail (str | None): path or url to image to use as display image when the web demo is shared on social media. + theme (str | None): Theme to use - right now, only "default" is supported. Can be set with the GRADIO_THEME environment variable. + css (str | None): custom css or path to custom css file to use with interface. + allow_flagging (str | None): one of "never", "auto", or "manual". If "never" or "auto", users will not see a button to flag an input and output. If "manual", users will see a button to flag. If "auto", every prediction will be automatically flagged. If "manual", samples are flagged when the user clicks flag button. Can be set with environmental variable GRADIO_ALLOW_FLAGGING; otherwise defaults to "manual". + flagging_options (List[str] | None): if provided, allows user to select from the list of options when flagging. Only applies if allow_flagging is "manual". + flagging_dir (str): what to name the directory where flagged data is stored. + flagging_callback (FlaggingCallback): An instance of a subclass of FlaggingCallback which will be called when a sample is flagged. By default logs to a local CSV file. + analytics_enabled (bool | None): Whether to allow basic telemetry. If None, will use GRADIO_ANALYTICS_ENABLED environment variable if defined, or default to True. """ super().__init__( analytics_enabled=analytics_enabled, mode="interface", css=css, **kwargs @@ -227,7 +220,7 @@ class Interface(Blocks): for o in self.output_components: o.interactive = False # Force output components to be non-interactive - if repeat_outputs_per_model: + if _repeat_outputs_per_model: self.output_components *= len(fn) if ( @@ -250,9 +243,6 @@ class Interface(Blocks): self.__name__ = ", ".join(self.function_names) self.live = live - self.layout = layout - self.show_input = show_input - self.show_output = show_output self.flag_hash = random.getrandbits(32) self.title = title @@ -284,35 +274,9 @@ class Interface(Blocks): self.article = article self.thumbnail = thumbnail - theme = theme if theme is not None else os.getenv("GRADIO_THEME", "default") - DEPRECATED_THEME_MAP = { - "darkdefault": "default", - "darkhuggingface": "dark-huggingface", - "darkpeach": "dark-peach", - "darkgrass": "dark-grass", - } - VALID_THEME_SET = ( - "default", - "huggingface", - "seafoam", - "grass", - "peach", - "dark", - "dark-huggingface", - "dark-seafoam", - "dark-grass", - "dark-peach", - ) - if theme in DEPRECATED_THEME_MAP: - warnings.warn( - f"'{theme}' theme name is deprecated, using {DEPRECATED_THEME_MAP[theme]} instead." - ) - theme = DEPRECATED_THEME_MAP[theme] - elif theme not in VALID_THEME_SET: - raise ValueError( - f"Invalid theme name, theme must be one of: {', '.join(VALID_THEME_SET)}" - ) - self.theme = theme + self.theme = theme or os.getenv("GRADIO_THEME", "default") + if not (self.theme == "default"): + warnings.warn("Currently, only the 'default' theme is supported.") if examples is None or ( isinstance(examples, list) @@ -819,9 +783,21 @@ class Interface(Blocks): class TabbedInterface(Blocks): + """ + A TabbedInterface is created by providing a list of Interfaces, each of which gets + rendered in a separate tab. + """ + def __init__( self, interface_list: List[Interface], tab_names: Optional[List[str]] = None ): + """ + Parameters: + interface_list (List[Interface]): a list of interfaces to be rendered in tabs. + tab_names (List[str] | None): a list of tab names. If None, the tab names will be "Tab 1", "Tab 2", etc. + Returns: + (gradio.TabbedInterface): a Gradio Tabbed Interface for the given interfaces + """ if tab_names is None: tab_names = ["Tab {}".format(i) for i in range(len(interface_list))] super().__init__() diff --git a/gradio/layouts.py b/gradio/layouts.py new file mode 100644 index 0000000000..695235ac67 --- /dev/null +++ b/gradio/layouts.py @@ -0,0 +1,124 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple + +from gradio.blocks import BlockContext + +if TYPE_CHECKING: # Only import for type checking (is False at runtime). + from gradio.components import Component + + +class Row(BlockContext): + def get_config(self): + return {"type": "row", **super().get_config()} + + @staticmethod + def update( + visible: Optional[bool] = None, + ): + return { + "visible": visible, + "__type__": "update", + } + + def style( + self, + equal_height: Optional[bool] = None, + mobile_collapse: Optional[bool] = None, + ): + if equal_height is not None: + self._style["equal_height"] = equal_height + if mobile_collapse is not None: + self._style["mobile_collapse"] = mobile_collapse + + return self + + +class Column(BlockContext): + def __init__( + self, + visible: bool = True, + variant: str = "default", + ): + """ + variant: column type, 'default' (no background) or 'panel' (gray background color and rounded corners) + """ + self.variant = variant + super().__init__(visible=visible) + + def get_config(self): + return { + "type": "column", + "variant": self.variant, + **super().get_config(), + } + + @staticmethod + def update( + variant: Optional[str] = None, + visible: Optional[bool] = None, + ): + return { + "variant": variant, + "visible": visible, + "__type__": "update", + } + + +class Tabs(BlockContext): + def change(self, fn: Callable, inputs: List[Component], outputs: List[Component]): + """ + Parameters: + fn: Callable function + inputs: List of inputs + outputs: List of outputs + Returns: None + """ + self.set_event_trigger("change", fn, inputs, outputs) + + +class TabItem(BlockContext): + def __init__(self, label, **kwargs): + super().__init__(**kwargs) + self.label = label + + def get_config(self): + return {"label": self.label, **super().get_config()} + + def select(self, fn: Callable, inputs: List[Component], outputs: List[Component]): + """ + Parameters: + fn: Callable function + inputs: List of inputs + outputs: List of outputs + Returns: None + """ + self.set_event_trigger("select", fn, inputs, outputs) + + +class Group(BlockContext): + def get_config(self): + return {"type": "group", **super().get_config()} + + @staticmethod + def update( + visible: Optional[bool] = None, + ): + return { + "visible": visible, + "__type__": "update", + } + + +class Box(BlockContext): + def get_config(self): + return {"type": "box", **super().get_config()} + + @staticmethod + def update( + visible: Optional[bool] = None, + ): + return { + "visible": visible, + "__type__": "update", + } diff --git a/gradio/mix.py b/gradio/mix.py index 4da1be3749..81f5cebbc8 100644 --- a/gradio/mix.py +++ b/gradio/mix.py @@ -6,15 +6,18 @@ import gradio class Parallel(gradio.Interface): """ - Creates a new Interface consisting of multiple models in parallel - Parameters: - interfaces: any number of Interface objects that are to be compared in parallel - options: additional kwargs that are passed into the new Interface object to customize it - Returns: - (Interface): an Interface object comparing the given models + Creates a new Interface consisting of multiple models in parallel (comparing their outputs). + The Interfaces to put in Parallel must share the same input components (but can have different output components). """ def __init__(self, *interfaces, **options): + """ + Parameters: + *interfaces (Interface): any number of Interface objects that are to be compared in parallel + **options (optional): additional kwargs that are passed into the new Interface object to customize it + Returns: + (Interface): an Interface object comparing the given models + """ fns = [] outputs = [] @@ -26,7 +29,7 @@ class Parallel(gradio.Interface): "fn": fns, "inputs": interfaces[0].input_components, "outputs": outputs, - "repeat_outputs_per_model": False, + "_repeat_outputs_per_model": False, } kwargs.update(options) super().__init__(**kwargs) @@ -37,15 +40,18 @@ class Parallel(gradio.Interface): class Series(gradio.Interface): """ - Creates a new Interface from multiple models in series (the output of one is fed as the input to the next) - Parameters: - interfaces: any number of Interface objects that are to be connected in series - options: additional kwargs that are passed into the new Interface object to customize it - Returns: - (Interface): an Interface object connecting the given models + Creates a new Interface from multiple models in series (the output of one is fed as the input to the next, + and so the input and output components must agree between the interfaces). """ def __init__(self, *interfaces, **options): + """ + Parameters: + *interfaces (Interface): any number of Interface objects that are to be connected in series + **options (optional): additional kwargs that are passed into the new Interface object to customize it + Returns: + (Interface): an Interface object connecting the given models + """ fns = [io.predict for io in interfaces] def connected_fn( diff --git a/gradio/outputs.py b/gradio/outputs.py index f1846dda1d..52911c94c2 100644 --- a/gradio/outputs.py +++ b/gradio/outputs.py @@ -82,7 +82,7 @@ class Video(C_Video): "Usage of gradio.outputs is deprecated, and will not be supported in the future, please import your components from gradio.components", DeprecationWarning, ) - super().__init__(type=type, label=label) + super().__init__(format=type, label=label) class Audio(C_Audio): diff --git a/gradio/templates/frontend/index.html b/gradio/templates/frontend/index.html index 9d9af92a14..cfad5de367 100644 --- a/gradio/templates/frontend/index.html +++ b/gradio/templates/frontend/index.html @@ -59,8 +59,8 @@ rel="stylesheet" /> - - + + ONNX team +Docs: image, label + ## Introduction diff --git a/guides/building_a_pictionary_app.md b/guides/building_a_pictionary_app.md index 752578c3d4..238d0689ff 100644 --- a/guides/building_a_pictionary_app.md +++ b/guides/building_a_pictionary_app.md @@ -2,6 +2,7 @@ related_spaces: https://huggingface.co/spaces/nateraw/quickdraw tags: SKETCHPAD, LABELS, LIVE +Docs: image, label ## Introduction diff --git a/guides/create_your_own_friends_with_a_gan.md b/guides/create_your_own_friends_with_a_gan.md index 7b0c32a735..59b5672b68 100644 --- a/guides/create_your_own_friends_with_a_gan.md +++ b/guides/create_your_own_friends_with_a_gan.md @@ -2,6 +2,7 @@ related_spaces: https://huggingface.co/spaces/NimaBoscarino/cryptopunks, https://huggingface.co/spaces/nateraw/cryptopunks-generator tags: GAN, IMAGE, HUB +Docs: slider, image Contributed by Nima Boscarino and Nate Raw diff --git a/guides/creating_a_chatbot.md b/guides/creating_a_chatbot.md index ae62bff318..513b5be4bf 100644 --- a/guides/creating_a_chatbot.md +++ b/guides/creating_a_chatbot.md @@ -2,6 +2,7 @@ related_spaces: https://huggingface.co/spaces/abidlabs/chatbot-minimal, https://huggingface.co/spaces/ThomasSimonini/Chat-with-Gandalf-GPT-J6B, https://huggingface.co/spaces/gorkemgoknar/moviechatbot, https://huggingface.co/spaces/Kirili4ik/chat-with-Kirill tags: NLP, TEXT, HTML +Docs: textbox, state ## Introduction diff --git a/guides/getting_started.md b/guides/getting_started.md index 13a267a826..c059a49d74 100644 --- a/guides/getting_started.md +++ b/guides/getting_started.md @@ -1,8 +1,28 @@ -## Getting Started +## Quickstart -**Prerequisite**: Python 3.7+ and that's it! +**Prerequisite**: Gradio requires Python 3.7 or above, that's it! -### Quick Start +### Introduction + +Gradio allows you to easily build machine learning demos, data science dashboards, or other kinds of web apps, **entirely in Python**. These web apps can be launched from wherever you use Python (jupyter notebooks, colab notebooks, Python terminal, etc.) and shared with anyone instantly using Gradio's auto-generated share links. + +To offer both simplicity and more powerful and flexible control for advanced web apps, Gradio offers two different APIs to users: + +* `gradio.Interface`: a high-level API that allows you to create a machine learning demo simply by specifying a list of inputs and outputs. It often takes just a few lines of Python to create a fully usable and share machine learning demo. + +* `gradio.Blocks`: a low-level API that allows you to have full control over the data flows and layout of your application. You can build very complex, multi-step applications using Blocks. + +This Quickstart focuses on the high-level `gradio.Interface` API. For more information on Blocks, read our dedicated guide. + +### What Problem is Gradio Solving? + +One of the best ways to share your machine learning model, API, or data science workflow with others is to create a web-based *interactive demo* that allows your users or colleagues to try out the demo with their inputs and visualize the outputs. These web-based demos are great as they allow *anyone who can use a browser* (not just technical people) to understand what you've built. + +However, creating such web-based demos has traditionally been difficult, as you needed to know backend frameworks (like Flask or Django) to serve your web app, containerization tools (like Docker), databases to store data or users, and front end web development (HTML, CSS, JavaScript) to build a GUI for your demo. + +Gradio lets you do all of this, directly in Python. And usually in just a few lines of code! So let's get started. + +### Getting Started To get Gradio running with a simple "Hello, World" example, follow these three steps: diff --git a/guides/image_classification_in_pytorch.md b/guides/image_classification_in_pytorch.md index 6c608ce1e8..2da9aa16c6 100644 --- a/guides/image_classification_in_pytorch.md +++ b/guides/image_classification_in_pytorch.md @@ -2,6 +2,7 @@ related_spaces: https://huggingface.co/spaces/abidlabs/pytorch-image-classifier, https://huggingface.co/spaces/pytorch/ResNet, https://huggingface.co/spaces/pytorch/ResNext, https://huggingface.co/spaces/pytorch/SqueezeNet tags: VISION, RESNET, PYTORCH +Docs: image, label ## Introduction diff --git a/guides/image_classification_in_tensorflow.md b/guides/image_classification_in_tensorflow.md index 495870ddaf..528514b4cb 100644 --- a/guides/image_classification_in_tensorflow.md +++ b/guides/image_classification_in_tensorflow.md @@ -2,6 +2,7 @@ related_spaces: https://huggingface.co/spaces/abidlabs/keras-image-classifier tags: VISION, MOBILENET, TENSORFLOW +Docs: image, label ## Introduction diff --git a/guides/image_classification_with_vision_transformers.md b/guides/image_classification_with_vision_transformers.md index cc6ea4f22e..193ac6acec 100644 --- a/guides/image_classification_with_vision_transformers.md +++ b/guides/image_classification_with_vision_transformers.md @@ -2,6 +2,7 @@ related_spaces: https://huggingface.co/spaces/abidlabs/vision-transformer tags: VISION, TRANSFORMERS, HUB +Docs: image, label ## Introduction diff --git a/guides/introduction_to_blocks.md b/guides/introduction_to_blocks.md new file mode 100644 index 0000000000..ca0a400d9b --- /dev/null +++ b/guides/introduction_to_blocks.md @@ -0,0 +1,132 @@ +## Introduction to Gradio Blocks 🧱 + +Gradio is a Python library that allows you to quickly build web-based machine learning demos, data science dashboards, or other kinds of web apps, **entirely in Python**. These web apps can be launched from wherever you use Python (jupyter notebooks, colab notebooks, Python terminal, etc.) and shared with anyone instantly using Gradio's auto-generated share links. + +To offer both simplicity and more powerful and flexible control for advanced web apps, Gradio offers two different APIs to users: + +* ⚡`gradio.Interface`: a high-level API that allows you to create a full machine learning demo simply by providing a list of inputs and outputs. + +* 🧱 `gradio.Blocks`: a low-level API that allows you to have full control over the data flows and layout of your application. You can build very complex, multi-step applications using Blocks (as in "building blocks"). + +This Guide will teach you the **Blocks API** and we will create several custom web apps in the process. It will be helpful but not necessary to be familiar with the Interface API before you read this tutorial. + +### Why Blocks 🧱? + +If you have already used `gradio.Interface`, you know that you can easily create fully-fledged machine learning demos with just a few lines of code. The Interface API is very convenient but in some cases may not be sufficiently flexible for your needs. For example, you might want to: + +* Group together related demos as multiple tabs in one web app +* Change the layout of your demo instead of just having all of the inputs on the left and outputs on the right +* Have multi-step interfaces, in which the output of one model becomes the input to the next model, or have more flexible data flows in general +* Change a component's properties (for example, the choices in a Dropdown) or its visibilty based on user input + +These are all use cases where you should use the Blocks API! + + +### "Hello World" with Blocks + +After you have installed Gradio, run the code below as a Python script or in a Python notebook (or in a [colab notebook](https://colab.research.google.com/drive/1n_uB44G_uENGf0zroeVKgcytFQ-7UwZt?usp=sharing)) + +{{ code["blocks_hello"] }} + +The interface below will appear automatically within the Python notebook, or pop in a browser on [http://localhost:7860](http://localhost:7860/) if running from a script. + +{{ demos["blocks_hello"] }} + + +### Understanding this Example + +This simple example introduces 5 concepts that underlie Blocks: + +1. Blocks allow you to build web applications that combine markdown, HTML, buttons, and interactive **components** simply by *instantiating* objects in Python inside of a "`with gradio.Blocks`" context. The *order* in which you instantiate components matters as each element gets rendered into the web app in the order it was created. (More complex layouts are discussed below) + +2. You can define **regular Python functions** anywhere in your code and run them with user input using BLocks. In our example, we have a simple function that adds a welcome message before a user's name, but you can write *any* Python function, from a simple calculation to large machine learning model's inference. + +3. You can assign **events** to any Blocks component. This will run your function when the component is clicked/changed/etc. When you assign an event, you pass in three parameters: `fn`: the function that should be called, `inputs`: the (list) of input component(s), and `outputs`: the (list) of output components that should be called.

+In this example, we run the `update()` function when the value in the `Textbox` named `inp` changes. The event reads the value in `inp`, passes it as the `name` parameter to `update()`, which then returns a value that gets assigned to our second `Textbox` named `out`.

To see a list of events that each component supports, see [the documentation](https://www.gradio.app/docs). + + +4. Blocks automatically figures out whether a component should be **interactive** (accept user input) or not, based on the event triggers you define. In our example, the first textbox is interactive, since its value is used by the `update()` function. The second textbox is not interactive, since its value is never used as an input. In some cases, you might want to override this, which you can do by passing the appropriate boolean to `interactive`, a parameter that every component accepts. + +5. You can write and `launch()` your Blocks anywhere: jupyter notebooks, colab notebooks, or regular Python IDEs since Gradio uses the standard Python interpreter. You can also share Blocks with other people by setting a single parameter: `launch(share=True)`, which we will discuss towards the end of this guide. + +### Layouts + +By default, `Blocks` renders the components that you create *vertically in one column*. You can change that by creating additional columns (`with gradio.Column():`) or rows (`with gradio.Row():`) and creating components within those contexts. + +Here's what you should keep in mind: any components created under a `Column` (this is also the default) will be laid out *vertically*. Any component created under a `Row` will be laid out *horizontally*, similar to the [flexbox model in web development](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox). + +Finally, you can also create a `with gradio.Tabs():` and within it create multiple `with gradio.TabItem(name_of_tab):` children. Any component created inside of a `with gradio.TabItem(name_of_tab):` context appears in that tab. + +Here is a example with tabs, rows, and columns: + +{{ code["blocks_flipper"] }} + +{{ demos["blocks_flipper"] }} + +You'll notice that in this example, we've also created a `Button` component in each tab, and we've assigned a click event to each button, which is what actually runs the function. So let's talk more about events... + +### Events + +Just as you can control the layout, `Blocks` gives you fine-grained control over what events trigger function calls. Each component and many layouts have specific events that they support. + +For example, the `Textbox` component has 2 events: `change()` (when the value inside of the textbox changes), and `submit()` (when a user presses the enter key while focused on the textbox). More complex components can have even more events: for example, the `Audio` component also has separate events for when the audio file is played, cleared, paused, etc. See [the documentation](https://www.gradio.app/docs) for the events each component supports. + +You can attach event trigger to none, one, or more of these events. You create an event trigger by calling the name of the event on the component instance as a function -- e.g. `textbox.change(...)` or `btn.click(...)`. The function takes in three parameters, as discussed above: + +* `fn`: the function to run +* `inputs`: a (list of) component(s) whose values should supplied as the input parameters to the function. Each component's value gets mapped to the corresponding function parameter, in order. This parameter can be `None` if the function does not take any parameters. +* `outputs`: a (list of) component(s) whose values should be updated based on the values returned by the function. Each return value gets sets the corresponding component's value, in order. This parameter can be `None` if the function does not return anything. + +You can even make the input and output component be the same component, as we do in this example that uses a GPT model to do text completion: + +{{ code["blocks_gpt"] }} + +{{ demos["blocks_gpt"] }} + +### Multistep Demos + +In some cases, you might want want a "multi-step" demo, in which you reuse the output of one function as the input to the next. This is really easy to do with Blocks, as you can use a component for the input of one event trigger but the output of another. Take a look at the `text` component in the example below, its value is the result of a speech-to-text model, but also gets passed into a sentiment analysis model: + +{{ code["blocks_speech_text_length"] }} + +{{ demos["blocks_speech_text_length"] }} + +### Updating Component Properties + +So far, we have seen how to create events to update the value of another component. But if you want to change *other properties* of a component (like the visibility of a textbox or the choices in a radio button group)? You can do this by returning a component class's `update()` method instead of a regular return value from your function. + +This is perhaps most easily illustrated with an example: + +{{ code["blocks_essay"] }} + +{{ demos["blocks_essay"] }} + +### Sharing Blocks Publicly + +Blocks can be easily shared publicly by setting `share=True` in the `launch()` method. Like this: + +```python +demo = gr.Blocks() + +with demo: + ... # define components & events here + +demo.launch(share=True) +``` + +This generates a public, shareable link that you can send to anybody! When you send this link, the user on the other side can try out the demo in their browser. Because the processing happens on your device (as long as your device stays on!), you don't have to worry about any packaging any dependencies. If you're working out of colab notebook, a share link is always automatically created. It usually looks something like this: **XXXXX.gradio.app**. Although the link is served through a gradio link, we are only a proxy for your local server, and do not store any data sent through the demo. + +Keep in mind, however, that these links are publicly accessible, meaning that anyone can use your model for prediction! Therefore, make sure not to expose any sensitive information through the functions you write, or allow any critical changes to occur on your device. If you set `share=False` (the default), only a local link is created, which can be shared by [port-forwarding](https://www.ssh.com/ssh/tunneling/example) with specific users. + +Share links expire after 72 hours. For permanent hosting, see Hosting Gradio Blocks on Spaces below. + +![Sharing diagram](/assets/img/sharing.svg) + +### Hosting Gradio Blocks on Spaces + +Huggingface provides the infrastructure to permanently host your Gradio demo on the internet, for free! You can either drag and drop a folder containing your Gradio model and all related files, or you can point HF Spaces to your Git repository and HF Spaces will pull the Gradio interface from there. It's just as easy to share a Blocks demo as it is a regular Gradio Interface. + +See [Huggingface Spaces](http://huggingface.co/spaces/) for more information. + +![Hosting Demo](/assets/img/hf_demo.gif) + diff --git a/guides/real_time_speech_recognition.md b/guides/real_time_speech_recognition.md index 449aa6ee98..686fb0e077 100644 --- a/guides/real_time_speech_recognition.md +++ b/guides/real_time_speech_recognition.md @@ -2,6 +2,7 @@ related_spaces: https://huggingface.co/spaces/abidlabs/streaming-asr-paused, https://huggingface.co/spaces/abidlabs/full-context-asr tags: ASR, SPEECH, STREAMING +Docs: audio, state, textbox ## Introduction diff --git a/scripts/launch_website.sh b/scripts/launch_website.sh index 3b1a056c34..17fe2b095a 100644 --- a/scripts/launch_website.sh +++ b/scripts/launch_website.sh @@ -8,5 +8,8 @@ else npm install npm run build cd dist + rm -rf ./gradio_static/ + mkdir ./gradio_static/ + cp -r ../../../gradio/templates/frontend/. ./gradio_static/ python -m http.server fi diff --git a/test/test_components.py b/test/test_components.py index 3b254218b1..2008fb68ee 100644 --- a/test/test_components.py +++ b/test/test_components.py @@ -1123,7 +1123,7 @@ class TestVideo(unittest.TestCase): self.assertIsNone(video_input.preprocess(None)) x_video["is_example"] = True self.assertIsNotNone(video_input.preprocess(x_video)) - video_input = gr.Video(type="avi") + video_input = gr.Video(format="avi") self.assertEqual(video_input.preprocess(x_video)[-3:], "avi") with self.assertRaises(NotImplementedError): video_input.serialize(x_video, True) diff --git a/test/test_interfaces.py b/test/test_interfaces.py index 7b89d201eb..b2439b08e5 100644 --- a/test/test_interfaces.py +++ b/test/test_interfaces.py @@ -8,8 +8,9 @@ import mlflow import requests import wandb -from gradio.blocks import Blocks, TabItem, Tabs +from gradio.blocks import Blocks from gradio.interface import Interface, TabbedInterface, close_all, os +from gradio.layouts import TabItem, Tabs from gradio.utils import assert_configs_are_equivalent_besides_ids os.environ["GRADIO_ANALYTICS_ENABLED"] = "False" diff --git a/ui/packages/app/src/themes/typography.min.css b/ui/packages/app/src/themes/typography.min.css index 501aa29569..a51acf8d66 100644 --- a/ui/packages/app/src/themes/typography.min.css +++ b/ui/packages/app/src/themes/typography.min.css @@ -133,7 +133,7 @@ .prose code { color: #111827; font-weight: 600; - font-size: 0.875em; + font-size: 1em; } .prose code::before { content: "`"; diff --git a/website/demos/map_demos.py b/website/demos/map_demos.py index ce2d511320..5bbb51f27e 100644 --- a/website/demos/map_demos.py +++ b/website/demos/map_demos.py @@ -13,11 +13,16 @@ sys.path.insert(0, GRADIO_DEMO_DIR) port = 7860 DEMO_PATTERN = r'demos\["([A-Za-z0-9_]+)"]' -demos_to_run = ["kitchen_sink"] -for guide_filename in os.listdir(GRADIO_GUIDES_DIR): - with open(os.path.join(GRADIO_GUIDES_DIR, guide_filename)) as guide_file: - guide_content = guide_file.read() - demos_to_run += re.findall(DEMO_PATTERN, guide_content) + +demos_to_skip = ["no_input", "disease_report", "fake_gan", "hello_login"] # skipping some demos +demos_to_run = [demo for demo in os.listdir(GRADIO_DEMO_DIR) + if os.path.isdir(os.path.join(GRADIO_DEMO_DIR, demo)) + and demo not in demos_to_skip] +# for guide_filename in os.listdir(GRADIO_GUIDES_DIR): +# for guide_filename in ["getting_started.md"]: +# with open(os.path.join(GRADIO_GUIDES_DIR, guide_filename)) as guide_file: +# guide_content = guide_file.read() +# demos_to_run += re.findall(DEMO_PATTERN, guide_content) demo_port_sets = [] for demo_name in demos_to_run: @@ -29,6 +34,7 @@ for demo_name in demos_to_run: ) setup_file = os.path.join(demo_folder, "setup.sh") if os.path.exists(setup_file): + continue # ignore for now subprocess.check_call(["sh", setup_file]) demo_port_sets.append((demo_name, port)) port += 1 diff --git a/website/demos/run_demos.py b/website/demos/run_demos.py index a92484c60c..cff2148596 100644 --- a/website/demos/run_demos.py +++ b/website/demos/run_demos.py @@ -25,7 +25,7 @@ for demo_name, port in demo_port_sets: demo_file = os.path.join(demo_folder, "run.py") with open(demo_file, "r") as file: filedata = file.read() - assert "demo.launch()" in filedata + assert "demo.launch()" in filedata, demo_name + " has no demo.launch()\n" + filedata filedata = filedata.replace(f"demo.launch()", f"demo.launch(server_port={port})") with open(demo_file, "w") as file: file.write(filedata) @@ -35,7 +35,7 @@ for demo_name, port in demo_port_sets: start_time = time.time() while True: for demo_name, _ in demo_port_sets: - r = requests.head(f"http://localhost:80/demo/{demo_name}/") + r = requests.get(f"http://localhost:80/demo/{demo_name}/config") if r.status_code != 200: print(demo_name, "down") if time.time() - start_time > LAUNCH_PERIOD: diff --git a/website/homepage/embedding-configs.json b/website/homepage/embedding-configs.json new file mode 100644 index 0000000000..98c3f5ea87 --- /dev/null +++ b/website/homepage/embedding-configs.json @@ -0,0 +1,724 @@ +{ + "textbox": { + "mode": "blocks", + "components": [ + { + "id": 29, + "type": "textbox", + "props": { + "lines": 1, + "max_lines": 20, + "placeholder": "Placeholder", + "value": "", + "show_label": true, + "name": "textbox", + "visible": true, + "style": {} + } + } + ], + "theme": "default", + "css": null, + "enable_queue": false, + "layout": { + "id": 28, + "children": [ + { + "id": 29 + } + ] + }, + "dependencies": [ + { + "targets": [ + 20 + ], + "trigger": "click", + "inputs": [ + 29 + ], + "outputs": [ + 4 + ], + "backend_fn": true, + "js": null, + "status_tracker": null, + "queue": null + } + ] + }, + "number": { + "mode": "blocks", + "components": [ + { + "id": 31, + "type": "number", + "props": { + "show_label": true, + "name": "number", + "visible": true, + "style": {} + } + } + ], + "theme": "default", + "css": null, + "enable_queue": false, + "layout": { + "id": 30, + "children": [ + { + "id": 31 + } + ] + }, + "dependencies": [ + { + "targets": [ + 20 + ], + "trigger": "click", + "inputs": [ + 31 + ], + "outputs": [ + 4 + ], + "backend_fn": true, + "js": null, + "status_tracker": null, + "queue": null + } + ] + }, + "slider": { + "mode": "blocks", + "components": [ + { + "id": 33, + "type": "slider", + "props": { + "minimum": 0, + "maximum": 100, + "step": 1, + "value": 0, + "show_label": true, + "name": "slider", + "visible": true, + "style": {} + } + } + ], + "theme": "default", + "css": null, + "enable_queue": false, + "layout": { + "id": 32, + "children": [ + { + "id": 33 + } + ] + }, + "dependencies": [ + { + "targets": [ + 20 + ], + "trigger": "click", + "inputs": [ + 33 + ], + "outputs": [ + 4 + ], + "backend_fn": true, + "js": null, + "status_tracker": null, + "queue": null + } + ] + }, + "checkbox": { + "mode": "blocks", + "components": [ + { + "id": 35, + "type": "checkbox", + "props": { + "value": false, + "show_label": true, + "name": "checkbox", + "visible": true, + "style": {} + } + } + ], + "theme": "default", + "css": null, + "enable_queue": false, + "layout": { + "id": 34, + "children": [ + { + "id": 35 + } + ] + }, + "dependencies": [ + { + "targets": [ + 20 + ], + "trigger": "click", + "inputs": [ + 35 + ], + "outputs": [ + 4 + ], + "backend_fn": true, + "js": null, + "status_tracker": null, + "queue": null + } + ] + }, + "checkboxgroup": { + "mode": "blocks", + "components": [ + { + "id": 37, + "type": "checkboxgroup", + "props": { + "choices": [ + "First Choice", + "Second Choice", + "Third Choice" + ], + "value": [], + "show_label": true, + "name": "checkboxgroup", + "visible": true, + "style": {} + } + } + ], + "theme": "default", + "css": null, + "enable_queue": false, + "layout": { + "id": 36, + "children": [ + { + "id": 37 + } + ] + }, + "dependencies": [ + { + "targets": [ + 20 + ], + "trigger": "click", + "inputs": [ + 37 + ], + "outputs": [ + 4 + ], + "backend_fn": true, + "js": null, + "status_tracker": null, + "queue": null + } + ] + }, + "radio": { + "mode": "blocks", + "components": [ + { + "id": 39, + "type": "radio", + "props": { + "choices": [ + "First Choice", + "Second Choice", + "Third Choice" + ], + "value": "First Choice", + "show_label": true, + "name": "radio", + "visible": true, + "style": {} + } + } + ], + "theme": "default", + "css": null, + "enable_queue": false, + "layout": { + "id": 38, + "children": [ + { + "id": 39 + } + ] + }, + "dependencies": [ + { + "targets": [ + 20 + ], + "trigger": "click", + "inputs": [ + 39 + ], + "outputs": [ + 4 + ], + "backend_fn": true, + "js": null, + "status_tracker": null, + "queue": null + } + ] + }, + "dropdown": { + "mode": "blocks", + "components": [ + { + "id": 41, + "type": "dropdown", + "props": { + "choices": [ + "First Choice", + "Second Choice", + "Third Choice" + ], + "value": "First Choice", + "show_label": true, + "name": "dropdown", + "visible": true, + "style": {} + } + } + ], + "theme": "default", + "css": null, + "enable_queue": false, + "layout": { + "id": 40, + "children": [ + { + "id": 41 + } + ] + }, + "dependencies": [ + { + "targets": [ + 20 + ], + "trigger": "click", + "inputs": [ + 41 + ], + "outputs": [ + 4 + ], + "backend_fn": true, + "js": null, + "status_tracker": null, + "queue": null + } + ] + }, + "image": { + "mode": "blocks", + "components": [ + { + "id": 43, + "type": "image", + "props": { + "image_mode": "RGB", + "source": "upload", + "tool": "editor", + "show_label": true, + "name": "image", + "visible": true, + "style": {} + } + } + ], + "theme": "default", + "css": null, + "enable_queue": false, + "layout": { + "id": 42, + "children": [ + { + "id": 43 + } + ] + }, + "dependencies": [ + { + "targets": [ + 20 + ], + "trigger": "click", + "inputs": [ + 43 + ], + "outputs": [ + 4 + ], + "backend_fn": true, + "js": null, + "status_tracker": null, + "queue": null + } + ] + }, + "video": { + "mode": "blocks", + "components": [ + { + "id": 45, + "type": "video", + "props": { + "source": "upload", + "show_label": true, + "name": "video", + "visible": true, + "style": {} + } + } + ], + "theme": "default", + "css": null, + "enable_queue": false, + "layout": { + "id": 44, + "children": [ + { + "id": 45 + } + ] + }, + "dependencies": [ + { + "targets": [ + 20 + ], + "trigger": "click", + "inputs": [ + 45 + ], + "outputs": [ + 4 + ], + "backend_fn": true, + "js": null, + "status_tracker": null, + "queue": null + } + ] + }, + "audio": { + "mode": "blocks", + "components": [ + { + "id": 49, + "type": "audio", + "props": { + "source": "microphone", + "show_label": true, + "name": "audio", + "visible": true, + "style": {} + } + } + ], + "theme": "default", + "css": null, + "enable_queue": false, + "layout": { + "id": 48, + "children": [ + { + "id": 49 + } + ] + }, + "dependencies": [ + { + "targets": [ + 20 + ], + "trigger": "click", + "inputs": [ + 49 + ], + "outputs": [ + 4 + ], + "backend_fn": true, + "js": null, + "status_tracker": null, + "queue": null + } + ] + }, + "file": { + "mode": "blocks", + "components": [ + { + "id": 51, + "type": "file", + "props": { + "file_count": "single", + "show_label": true, + "name": "file", + "visible": true, + "style": {} + } + } + ], + "theme": "default", + "css": null, + "enable_queue": false, + "layout": { + "id": 50, + "children": [ + { + "id": 51 + } + ] + }, + "dependencies": [ + { + "targets": [ + 20 + ], + "trigger": "click", + "inputs": [ + 51 + ], + "outputs": [ + 4 + ], + "backend_fn": true, + "js": null, + "status_tracker": null, + "queue": null + } + ] + }, + "dataframe": { + "mode": "blocks", + "components": [ + { + "id": 53, + "type": "dataframe", + "props": { + "datatype": "str", + "row_count": [ + 3, + "dynamic" + ], + "col_count": [ + 3, + "dynamic" + ], + "value": [ + [ + "", + "", + "" + ], + [ + "", + "", + "" + ], + [ + "", + "", + "" + ] + ], + "max_rows": 20, + "overflow_row_behaviour": "paginate", + "show_label": true, + "name": "dataframe", + "visible": true, + "style": {} + } + } + ], + "theme": "default", + "css": null, + "enable_queue": false, + "layout": { + "id": 52, + "children": [ + { + "id": 53 + } + ] + }, + "dependencies": [ + { + "targets": [ + 20 + ], + "trigger": "click", + "inputs": [ + 53 + ], + "outputs": [ + 4 + ], + "backend_fn": true, + "js": null, + "status_tracker": null, + "queue": null + } + ] + }, + "timeseries": { + "mode": "blocks", + "components": [ + { + "id": 55, + "type": "timeseries", + "props": { + "show_label": true, + "name": "timeseries", + "visible": true, + "style": {} + } + } + ], + "theme": "default", + "css": null, + "enable_queue": false, + "layout": { + "id": 54, + "children": [ + { + "id": 55 + } + ] + }, + "dependencies": [ + { + "targets": [ + 20 + ], + "trigger": "click", + "inputs": [ + 55 + ], + "outputs": [ + 4 + ], + "backend_fn": true, + "js": null, + "status_tracker": null, + "queue": null + } + ] + }, + "model3d": { + "mode": "blocks", + "components": [ + { + "id": 57, + "type": "model3d", + "props": { + "show_label": true, + "name": "model3d", + "visible": true, + "style": {} + } + } + ], + "theme": "default", + "css": null, + "enable_queue": false, + "layout": { + "id": 56, + "children": [ + { + "id": 57 + } + ] + }, + "dependencies": [ + { + "targets": [ + 20 + ], + "trigger": "click", + "inputs": [ + 57 + ], + "outputs": [ + 4 + ], + "backend_fn": true, + "js": null, + "status_tracker": null, + "queue": null + } + ] + }, + "button": { + "mode": "blocks", + "components": [ + { + "id": 61, + "type": "button", + "props": { + "value": "Button", + "variant": "primary", + "name": "button", + "visible": true, + "style": {} + } + } + ], + "theme": "default", + "css": null, + "enable_queue": false, + "layout": { + "id": 60, + "children": [ + { + "id": 61 + } + ] + }, + "dependencies": [ + { + "targets": [ + 61 + ], + "trigger": "click", + "inputs": [ + 57 + ], + "outputs": [ + 4 + ], + "backend_fn": true, + "js": null, + "status_tracker": null, + "queue": null + } + ] + } +} \ No newline at end of file diff --git a/website/homepage/netlify.toml b/website/homepage/netlify.toml new file mode 100644 index 0000000000..71a5573cb2 --- /dev/null +++ b/website/homepage/netlify.toml @@ -0,0 +1,2 @@ +[build] + ignore = "/bin/false" diff --git a/website/homepage/nginx.conf b/website/homepage/nginx.conf index d533d1333e..475546d3f7 100644 --- a/website/homepage/nginx.conf +++ b/website/homepage/nginx.conf @@ -7,12 +7,12 @@ server { location / { root /usr/share/nginx/html; - try_files $uri $uri/ /index.html; + try_files $uri $uri/ $uri/index.html =404; index index.html index.htm; autoindex on; } - #error_page 404 /404.html; + error_page 404 /404.html; # redirect server error pages to the static page /50x.html # diff --git a/website/homepage/package-lock.json b/website/homepage/package-lock.json index 906c29bcdc..efbcb0a35e 100644 --- a/website/homepage/package-lock.json +++ b/website/homepage/package-lock.json @@ -7,102 +7,16 @@ "name": "gradio-website", "dependencies": { "@fullhuman/postcss-purgecss": "^4.0.3", - "@tailwindcss/typography": "^0.4.1", + "@tailwindcss/forms": "^0.5.0", + "@tailwindcss/typography": "^0.5.0", "autoprefixer": "^10.4.0", "chokidar-cli": "^3.0.0", "cssnano": "^5.0.8", "html-minifier": "^4.0.0", + "pnpm": "^6.32.7", "postcss-cli": "^9.0.1", "postcss-hash": "^3.0.0", - "tailwindcss": "^2.2.19" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", - "integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==", - "dependencies": { - "@babel/highlight": "^7.16.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.15.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", - "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", - "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", - "dependencies": { - "@babel/helper-validator-identifier": "^7.15.7", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" + "tailwindcss": "^3.0.24" } }, "node_modules/@fullhuman/postcss-purgecss": { @@ -148,18 +62,28 @@ "node": ">= 8" } }, + "node_modules/@tailwindcss/forms": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.1.tgz", + "integrity": "sha512-QSwsFORnC2BAP0lRzQkz1pw+EzIiiPdk4e27vGQjyXkwJPeC7iLIRVndJzf9CJVbcrrIcirb/TfxF3gRTyFEVA==", + "dependencies": { + "mini-svg-data-uri": "^1.2.3" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1" + } + }, "node_modules/@tailwindcss/typography": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.4.1.tgz", - "integrity": "sha512-ovPPLUhs7zAIJfr0y1dbGlyCuPhpuv/jpBoFgqAc658DWGGrOBWBMpAWLw2KlzbNeVk4YBJMzue1ekvIbdw6XA==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.2.tgz", + "integrity": "sha512-coq8DBABRPFcVhVIk6IbKyyHUt7YTEC/C992tatFB+yEx5WGBQrCgsSFjxHUr8AWXphWckadVJbominEduYBqw==", "dependencies": { "lodash.castarray": "^4.4.0", "lodash.isplainobject": "^4.0.6", - "lodash.merge": "^4.6.2", - "lodash.uniq": "^4.5.0" + "lodash.merge": "^4.6.2" }, "peerDependencies": { - "tailwindcss": ">=2.0.0" + "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || insiders" } }, "node_modules/@trysound/sax": { @@ -170,11 +94,6 @@ "node": ">=10.13.0" } }, - "node_modules/@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" - }, "node_modules/acorn": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", @@ -345,22 +264,6 @@ "url": "https://opencollective.com/browserslist" } }, - "node_modules/bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "engines": { - "node": ">=6" - } - }, "node_modules/camel-case": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", @@ -406,25 +309,16 @@ "url": "https://opencollective.com/browserslist" } }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -602,15 +496,6 @@ "wrap-ansi": "^7.0.0" } }, - "node_modules/color": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/color/-/color-4.0.1.tgz", - "integrity": "sha512-rpZjOKN5O7naJxkH2Rx1sZzzBgaiWECc6BYXjeCE6kF0kcASJYbUq02u7JqIHwCb/j3NhV+QhRL2683aICeGZA==", - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.6.0" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -627,15 +512,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "node_modules/color-string": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.6.0.tgz", - "integrity": "sha512-c/hGS+kRWJutUBEngKKmk4iH3sD59MBkoxVapS/0wgpCz2u7XsNloxknyvBhzwEs1IbV36D9PwqLPJ2DTu3vMA==", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, "node_modules/colord": { "version": "2.9.1", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.1.tgz", @@ -651,21 +527,6 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, - "node_modules/cosmiconfig": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", - "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/css-color-names": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-1.0.1.tgz", @@ -715,11 +576,6 @@ "node": ">=8.0.0" } }, - "node_modules/css-unit-converter": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz", - "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==" - }, "node_modules/css-what": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", @@ -954,14 +810,6 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -970,18 +818,10 @@ "node": ">=6" } }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -990,7 +830,7 @@ "micromatch": "^4.0.4" }, "engines": { - "node": ">=8" + "node": ">=8.6.0" } }, "node_modules/fastq": { @@ -1155,14 +995,6 @@ "node": ">= 0.4.0" } }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -1171,21 +1003,6 @@ "he": "bin/he" } }, - "node_modules/hex-color-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", - "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" - }, - "node_modules/hsl-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", - "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=" - }, - "node_modules/hsla-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", - "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=" - }, "node_modules/html-minifier": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-4.0.0.tgz", @@ -1206,14 +1023,6 @@ "node": ">=6" } }, - "node_modules/html-tags": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz", - "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==", - "engines": { - "node": ">=8" - } - }, "node_modules/ignore": { "version": "5.1.9", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", @@ -1222,51 +1031,6 @@ "node": ">= 4" } }, - "node_modules/import-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz", - "integrity": "sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==", - "dependencies": { - "import-from": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "engines": { - "node": ">=4" - } - }, - "node_modules/import-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", - "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -1289,11 +1053,6 @@ "node": ">=8" } }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" - }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -1305,31 +1064,10 @@ "node": ">=8" } }, - "node_modules/is-color-stop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", - "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", - "dependencies": { - "css-color-names": "^0.0.4", - "hex-color-regex": "^1.1.0", - "hsl-regex": "^1.0.0", - "hsla-regex": "^1.0.0", - "rgb-regex": "^1.0.1", - "rgba-regex": "^1.0.0" - } - }, - "node_modules/is-color-stop/node_modules/css-color-names": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", - "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", - "engines": { - "node": "*" - } - }, "node_modules/is-core-module": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", - "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", "dependencies": { "has": "^1.0.3" }, @@ -1377,16 +1115,6 @@ "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==" }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" - }, "node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", @@ -1399,18 +1127,13 @@ } }, "node_modules/lilconfig": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.3.tgz", - "integrity": "sha512-EHKqr/+ZvdKCifpNrJCKxBTgk5XupZA3y/aCPY9mxfgBzmgh93Mt/WqjjQ38oMxXuvDokaKiM3lAgvSH2sjtHg==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.5.tgz", + "integrity": "sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==", "engines": { "node": ">=10" } }, - "node_modules/lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=" - }, "node_modules/locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", @@ -1423,11 +1146,6 @@ "node": ">=6" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, "node_modules/lodash.castarray": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", @@ -1483,11 +1201,6 @@ "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=" }, - "node_modules/lodash.topath": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz", - "integrity": "sha1-NhY1Hzu6YZlKCTGYlmC9AyVP0Ak=" - }, "node_modules/lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", @@ -1523,6 +1236,14 @@ "node": ">=8.6" } }, + "node_modules/mini-svg-data-uri": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", + "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", + "bin": { + "mini-svg-data-uri": "cli.js" + } + }, "node_modules/minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -1550,26 +1271,15 @@ "mkdirp": "bin/cmd.js" } }, - "node_modules/modern-normalize": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/modern-normalize/-/modern-normalize-1.1.0.tgz", - "integrity": "sha512-2lMlY1Yc1+CUy0gw4H95uNN7vjbpoED7NNRSBHE25nWfLBdmMzFCsPshlzbxHz+gYMcBEUN8V4pU16prcdPSgA==", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/nanocolors": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/nanocolors/-/nanocolors-0.2.13.tgz", "integrity": "sha512-0n3mSAQLPpGLV9ORXT5+C/D4mwew7Ebws69Hx4E2sgz2ZA5+32Q80B9tL8PbL7XHnRDiAxH/pnrUJ9a4fkTNTA==" }, "node_modules/nanoid": { - "version": "3.1.30", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.30.tgz", - "integrity": "sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -1585,14 +1295,6 @@ "lower-case": "^1.1.1" } }, - "node_modules/node-emoji": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", - "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", - "dependencies": { - "lodash": "^4.17.21" - } - }, "node_modules/node-releases": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", @@ -1637,9 +1339,9 @@ } }, "node_modules/object-hash": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", - "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", "engines": { "node": ">= 6" } @@ -1693,34 +1395,6 @@ "no-case": "^2.2.0" } }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", @@ -1774,21 +1448,42 @@ "node": ">=0.10.0" } }, + "node_modules/pnpm": { + "version": "6.32.11", + "resolved": "https://registry.npmjs.org/pnpm/-/pnpm-6.32.11.tgz", + "integrity": "sha512-KIb3ImAEGl03aJNy0pai7J0mWn52GJwRrUllxUZwibhLdUoeOuDjXhNSgRxxW9BH2gR91b3UadxzdAvhehmM8w==", + "bin": { + "pnpm": "bin/pnpm.cjs", + "pnpx": "bin/pnpx.cjs" + }, + "engines": { + "node": ">=12.17" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, "node_modules/postcss": { - "version": "8.3.11", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.11.tgz", - "integrity": "sha512-hCmlUAIlUiav8Xdqw3Io4LcpA1DOt7h3LSTAC4G6JGHFFaWzI6qvFt9oilvl8BmkbBRX1IhM90ZAmpk68zccQA==", + "version": "8.4.13", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.13.tgz", + "integrity": "sha512-jtL6eTBrza5MPzy8oJLFuUscHDXTV5KcLlqAWHl5q5WYRfnNRGSmOZmOZ1T6Gy7A99mOZfqungmZMpMmCVJ8ZA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], "dependencies": { - "nanoid": "^3.1.30", + "nanoid": "^3.3.3", "picocolors": "^1.0.0", - "source-map-js": "^0.6.2" + "source-map-js": "^1.0.2" }, "engines": { "node": "^10 || ^12 || >=14" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" } }, "node_modules/postcss-calc": { @@ -1915,28 +1610,29 @@ } }, "node_modules/postcss-js": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-3.0.3.tgz", - "integrity": "sha512-gWnoWQXKFw65Hk/mi2+WTQTHdPD5UJdDXZmX073EY/B3BWnYjO4F4t0VneTCnCGQ5E5GsCdMkzPaTXwl3r5dJw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.0.tgz", + "integrity": "sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==", "dependencies": { - "camelcase-css": "^2.0.1", - "postcss": "^8.1.6" + "camelcase-css": "^2.0.1" }, "engines": { - "node": ">=10.0" + "node": "^12 || ^14 || >= 16" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.3.3" } }, "node_modules/postcss-load-config": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.0.tgz", - "integrity": "sha512-ipM8Ds01ZUophjDTQYSVP70slFSYg3T0/zyfII5vzhN6V57YSxMgG5syXuwi5VtS8wSf3iL30v0uBdoIVx4Q0g==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", + "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", "dependencies": { - "import-cwd": "^3.0.0", - "lilconfig": "^2.0.3", + "lilconfig": "^2.0.5", "yaml": "^1.10.2" }, "engines": { @@ -1947,9 +1643,13 @@ "url": "https://opencollective.com/postcss/" }, "peerDependencies": { + "postcss": ">=8.0.9", "ts-node": ">=9.0.0" }, "peerDependenciesMeta": { + "postcss": { + "optional": true + }, "ts-node": { "optional": true } @@ -2268,9 +1968,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", - "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -2311,9 +2011,9 @@ } }, "node_modules/postcss-value-parser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", - "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==" + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "node_modules/pretty-hrtime": { "version": "1.0.3", @@ -2394,20 +2094,6 @@ "node": ">=8.10.0" } }, - "node_modules/reduce-css-calc": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz", - "integrity": "sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==", - "dependencies": { - "css-unit-converter": "^1.1.1", - "postcss-value-parser": "^3.3.0" - } - }, - "node_modules/reduce-css-calc/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - }, "node_modules/relateurl": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", @@ -2430,25 +2116,21 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" }, "node_modules/resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "engines": { - "node": ">=8" - } - }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -2458,30 +2140,6 @@ "node": ">=0.10.0" } }, - "node_modules/rgb-regex": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", - "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=" - }, - "node_modules/rgba-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", - "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=" - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -2509,19 +2167,6 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/simple-swizzle/node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, "node_modules/slash": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", @@ -2542,9 +2187,9 @@ } }, "node_modules/source-map-js": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", - "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", "engines": { "node": ">=0.10.0" } @@ -2593,15 +2238,15 @@ "postcss": "^8.2.15" } }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/svgo": { @@ -2633,42 +2278,31 @@ } }, "node_modules/tailwindcss": { - "version": "2.2.19", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-2.2.19.tgz", - "integrity": "sha512-6Ui7JSVtXadtTUo2NtkBBacobzWiQYVjYW0ZnKaP9S1ZCKQ0w7KVNz+YSDI/j7O7KCMHbOkz94ZMQhbT9pOqjw==", + "version": "3.0.24", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.0.24.tgz", + "integrity": "sha512-H3uMmZNWzG6aqmg9q07ZIRNIawoiEcNFKDfL+YzOPuPsXuDXxJxB9icqzLgdzKNwjG3SAro2h9SYav8ewXNgig==", "dependencies": { "arg": "^5.0.1", - "bytes": "^3.0.0", - "chalk": "^4.1.2", - "chokidar": "^3.5.2", - "color": "^4.0.1", - "cosmiconfig": "^7.0.1", + "chokidar": "^3.5.3", + "color-name": "^1.1.4", "detective": "^5.2.0", "didyoumean": "^1.2.2", "dlv": "^1.1.3", - "fast-glob": "^3.2.7", - "fs-extra": "^10.0.0", - "glob-parent": "^6.0.1", - "html-tags": "^3.1.0", - "is-color-stop": "^1.1.0", - "is-glob": "^4.0.1", - "lodash": "^4.17.21", - "lodash.topath": "^4.5.2", - "modern-normalize": "^1.1.0", - "node-emoji": "^1.11.0", + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "lilconfig": "^2.0.5", "normalize-path": "^3.0.0", - "object-hash": "^2.2.0", - "postcss-js": "^3.0.3", - "postcss-load-config": "^3.1.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.12", + "postcss-js": "^4.0.0", + "postcss-load-config": "^3.1.4", "postcss-nested": "5.0.6", - "postcss-selector-parser": "^6.0.6", - "postcss-value-parser": "^4.1.0", - "pretty-hrtime": "^1.0.3", - "purgecss": "^4.0.3", + "postcss-selector-parser": "^6.0.10", + "postcss-value-parser": "^4.2.0", "quick-lru": "^5.1.1", - "reduce-css-calc": "^2.1.8", - "resolve": "^1.20.0", - "tmp": "^0.2.1" + "resolve": "^1.22.0" }, "bin": { "tailwind": "lib/cli.js", @@ -2678,7 +2312,6 @@ "node": ">=12.13.0" }, "peerDependencies": { - "autoprefixer": "^10.0.2", "postcss": "^8.0.9" } }, @@ -2698,17 +2331,6 @@ "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" }, - "node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dependencies": { - "rimraf": "^3.0.0" - }, - "engines": { - "node": ">=8.17.0" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -2840,75 +2462,6 @@ } }, "dependencies": { - "@babel/code-frame": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", - "integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==", - "requires": { - "@babel/highlight": "^7.16.0" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.15.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", - "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==" - }, - "@babel/highlight": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", - "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", - "requires": { - "@babel/helper-validator-identifier": "^7.15.7", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, "@fullhuman/postcss-purgecss": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/@fullhuman/postcss-purgecss/-/postcss-purgecss-4.0.3.tgz", @@ -2940,15 +2493,22 @@ "fastq": "^1.6.0" } }, + "@tailwindcss/forms": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.1.tgz", + "integrity": "sha512-QSwsFORnC2BAP0lRzQkz1pw+EzIiiPdk4e27vGQjyXkwJPeC7iLIRVndJzf9CJVbcrrIcirb/TfxF3gRTyFEVA==", + "requires": { + "mini-svg-data-uri": "^1.2.3" + } + }, "@tailwindcss/typography": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.4.1.tgz", - "integrity": "sha512-ovPPLUhs7zAIJfr0y1dbGlyCuPhpuv/jpBoFgqAc658DWGGrOBWBMpAWLw2KlzbNeVk4YBJMzue1ekvIbdw6XA==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.2.tgz", + "integrity": "sha512-coq8DBABRPFcVhVIk6IbKyyHUt7YTEC/C992tatFB+yEx5WGBQrCgsSFjxHUr8AWXphWckadVJbominEduYBqw==", "requires": { "lodash.castarray": "^4.4.0", "lodash.isplainobject": "^4.0.6", - "lodash.merge": "^4.6.2", - "lodash.uniq": "^4.5.0" + "lodash.merge": "^4.6.2" } }, "@trysound/sax": { @@ -2956,11 +2516,6 @@ "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==" }, - "@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" - }, "acorn": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", @@ -3075,16 +2630,6 @@ "picocolors": "^1.0.0" } }, - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" - }, "camel-case": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", @@ -3120,19 +2665,10 @@ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001276.tgz", "integrity": "sha512-psUNoaG1ilknZPxi8HuhQWobuhLqtYSRUxplfVkEJdgZNB9TETVYGSBtv4YyfAdGvE6gn2eb0ztiXqHoWJcGnw==" }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "requires": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -3280,15 +2816,6 @@ "wrap-ansi": "^7.0.0" } }, - "color": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/color/-/color-4.0.1.tgz", - "integrity": "sha512-rpZjOKN5O7naJxkH2Rx1sZzzBgaiWECc6BYXjeCE6kF0kcASJYbUq02u7JqIHwCb/j3NhV+QhRL2683aICeGZA==", - "requires": { - "color-convert": "^2.0.1", - "color-string": "^1.6.0" - } - }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -3302,15 +2829,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "color-string": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.6.0.tgz", - "integrity": "sha512-c/hGS+kRWJutUBEngKKmk4iH3sD59MBkoxVapS/0wgpCz2u7XsNloxknyvBhzwEs1IbV36D9PwqLPJ2DTu3vMA==", - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, "colord": { "version": "2.9.1", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.1.tgz", @@ -3326,18 +2844,6 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, - "cosmiconfig": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", - "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - } - }, "css-color-names": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-1.0.1.tgz", @@ -3372,11 +2878,6 @@ "source-map": "^0.6.1" } }, - "css-unit-converter": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz", - "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==" - }, "css-what": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", @@ -3539,28 +3040,15 @@ "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "requires": { - "is-arrayish": "^0.2.1" - } - }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, "fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", "requires": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -3681,31 +3169,11 @@ "function-bind": "^1.1.1" } }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" }, - "hex-color-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", - "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" - }, - "hsl-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", - "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=" - }, - "hsla-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", - "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=" - }, "html-minifier": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-4.0.0.tgz", @@ -3720,48 +3188,11 @@ "uglify-js": "^3.5.1" } }, - "html-tags": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz", - "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==" - }, "ignore": { "version": "5.1.9", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==" }, - "import-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz", - "integrity": "sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==", - "requires": { - "import-from": "^3.0.0" - } - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" - } - } - }, - "import-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", - "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", - "requires": { - "resolve-from": "^5.0.0" - } - }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -3781,11 +3212,6 @@ "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==" }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" - }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -3794,30 +3220,10 @@ "binary-extensions": "^2.0.0" } }, - "is-color-stop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", - "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", - "requires": { - "css-color-names": "^0.0.4", - "hex-color-regex": "^1.1.0", - "hsl-regex": "^1.0.0", - "hsla-regex": "^1.0.0", - "rgb-regex": "^1.0.1", - "rgba-regex": "^1.0.0" - }, - "dependencies": { - "css-color-names": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", - "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=" - } - } - }, "is-core-module": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", - "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", "requires": { "has": "^1.0.3" } @@ -3850,16 +3256,6 @@ "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==" }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" - }, "jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", @@ -3870,14 +3266,9 @@ } }, "lilconfig": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.3.tgz", - "integrity": "sha512-EHKqr/+ZvdKCifpNrJCKxBTgk5XupZA3y/aCPY9mxfgBzmgh93Mt/WqjjQ38oMxXuvDokaKiM3lAgvSH2sjtHg==" - }, - "lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.5.tgz", + "integrity": "sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==" }, "locate-path": { "version": "3.0.0", @@ -3888,11 +3279,6 @@ "path-exists": "^3.0.0" } }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, "lodash.castarray": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", @@ -3948,11 +3334,6 @@ "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=" }, - "lodash.topath": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz", - "integrity": "sha1-NhY1Hzu6YZlKCTGYlmC9AyVP0Ak=" - }, "lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", @@ -3982,6 +3363,11 @@ "picomatch": "^2.2.3" } }, + "mini-svg-data-uri": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", + "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==" + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -4003,20 +3389,15 @@ "minimist": "^1.2.5" } }, - "modern-normalize": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/modern-normalize/-/modern-normalize-1.1.0.tgz", - "integrity": "sha512-2lMlY1Yc1+CUy0gw4H95uNN7vjbpoED7NNRSBHE25nWfLBdmMzFCsPshlzbxHz+gYMcBEUN8V4pU16prcdPSgA==" - }, "nanocolors": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/nanocolors/-/nanocolors-0.2.13.tgz", "integrity": "sha512-0n3mSAQLPpGLV9ORXT5+C/D4mwew7Ebws69Hx4E2sgz2ZA5+32Q80B9tL8PbL7XHnRDiAxH/pnrUJ9a4fkTNTA==" }, "nanoid": { - "version": "3.1.30", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.30.tgz", - "integrity": "sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==" + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" }, "no-case": { "version": "2.3.2", @@ -4026,14 +3407,6 @@ "lower-case": "^1.1.1" } }, - "node-emoji": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", - "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", - "requires": { - "lodash": "^4.17.21" - } - }, "node-releases": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", @@ -4063,9 +3436,9 @@ } }, "object-hash": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", - "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==" }, "once": { "version": "1.4.0", @@ -4104,25 +3477,6 @@ "no-case": "^2.2.0" } }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", @@ -4158,14 +3512,19 @@ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" }, + "pnpm": { + "version": "6.32.11", + "resolved": "https://registry.npmjs.org/pnpm/-/pnpm-6.32.11.tgz", + "integrity": "sha512-KIb3ImAEGl03aJNy0pai7J0mWn52GJwRrUllxUZwibhLdUoeOuDjXhNSgRxxW9BH2gR91b3UadxzdAvhehmM8w==" + }, "postcss": { - "version": "8.3.11", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.11.tgz", - "integrity": "sha512-hCmlUAIlUiav8Xdqw3Io4LcpA1DOt7h3LSTAC4G6JGHFFaWzI6qvFt9oilvl8BmkbBRX1IhM90ZAmpk68zccQA==", + "version": "8.4.13", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.13.tgz", + "integrity": "sha512-jtL6eTBrza5MPzy8oJLFuUscHDXTV5KcLlqAWHl5q5WYRfnNRGSmOZmOZ1T6Gy7A99mOZfqungmZMpMmCVJ8ZA==", "requires": { - "nanoid": "^3.1.30", + "nanoid": "^3.3.3", "picocolors": "^1.0.0", - "source-map-js": "^0.6.2" + "source-map-js": "^1.0.2" } }, "postcss-calc": { @@ -4248,21 +3607,19 @@ } }, "postcss-js": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-3.0.3.tgz", - "integrity": "sha512-gWnoWQXKFw65Hk/mi2+WTQTHdPD5UJdDXZmX073EY/B3BWnYjO4F4t0VneTCnCGQ5E5GsCdMkzPaTXwl3r5dJw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.0.tgz", + "integrity": "sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==", "requires": { - "camelcase-css": "^2.0.1", - "postcss": "^8.1.6" + "camelcase-css": "^2.0.1" } }, "postcss-load-config": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.0.tgz", - "integrity": "sha512-ipM8Ds01ZUophjDTQYSVP70slFSYg3T0/zyfII5vzhN6V57YSxMgG5syXuwi5VtS8wSf3iL30v0uBdoIVx4Q0g==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", + "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", "requires": { - "import-cwd": "^3.0.0", - "lilconfig": "^2.0.3", + "lilconfig": "^2.0.5", "yaml": "^1.10.2" } }, @@ -4452,9 +3809,9 @@ } }, "postcss-selector-parser": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", - "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", "requires": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -4480,9 +3837,9 @@ } }, "postcss-value-parser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", - "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==" + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "pretty-hrtime": { "version": "1.0.3", @@ -4533,22 +3890,6 @@ "picomatch": "^2.2.1" } }, - "reduce-css-calc": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz", - "integrity": "sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==", - "requires": { - "css-unit-converter": "^1.1.1", - "postcss-value-parser": "^3.3.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - } - } - }, "relateurl": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", @@ -4565,42 +3906,20 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" }, "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" } }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" - }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" }, - "rgb-regex": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", - "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=" - }, - "rgba-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", - "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=" - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "requires": { - "glob": "^7.1.3" - } - }, "run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -4614,21 +3933,6 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "requires": { - "is-arrayish": "^0.3.1" - }, - "dependencies": { - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - } - } - }, "slash": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", @@ -4640,9 +3944,9 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, "source-map-js": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", - "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" }, "stable": { "version": "0.1.8", @@ -4676,13 +3980,10 @@ "postcss-selector-parser": "^6.0.4" } }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" }, "svgo": { "version": "2.8.0", @@ -4706,42 +4007,31 @@ } }, "tailwindcss": { - "version": "2.2.19", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-2.2.19.tgz", - "integrity": "sha512-6Ui7JSVtXadtTUo2NtkBBacobzWiQYVjYW0ZnKaP9S1ZCKQ0w7KVNz+YSDI/j7O7KCMHbOkz94ZMQhbT9pOqjw==", + "version": "3.0.24", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.0.24.tgz", + "integrity": "sha512-H3uMmZNWzG6aqmg9q07ZIRNIawoiEcNFKDfL+YzOPuPsXuDXxJxB9icqzLgdzKNwjG3SAro2h9SYav8ewXNgig==", "requires": { "arg": "^5.0.1", - "bytes": "^3.0.0", - "chalk": "^4.1.2", - "chokidar": "^3.5.2", - "color": "^4.0.1", - "cosmiconfig": "^7.0.1", + "chokidar": "^3.5.3", + "color-name": "^1.1.4", "detective": "^5.2.0", "didyoumean": "^1.2.2", "dlv": "^1.1.3", - "fast-glob": "^3.2.7", - "fs-extra": "^10.0.0", - "glob-parent": "^6.0.1", - "html-tags": "^3.1.0", - "is-color-stop": "^1.1.0", - "is-glob": "^4.0.1", - "lodash": "^4.17.21", - "lodash.topath": "^4.5.2", - "modern-normalize": "^1.1.0", - "node-emoji": "^1.11.0", + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "lilconfig": "^2.0.5", "normalize-path": "^3.0.0", - "object-hash": "^2.2.0", - "postcss-js": "^3.0.3", - "postcss-load-config": "^3.1.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.12", + "postcss-js": "^4.0.0", + "postcss-load-config": "^3.1.4", "postcss-nested": "5.0.6", - "postcss-selector-parser": "^6.0.6", - "postcss-value-parser": "^4.1.0", - "pretty-hrtime": "^1.0.3", - "purgecss": "^4.0.3", + "postcss-selector-parser": "^6.0.10", + "postcss-value-parser": "^4.2.0", "quick-lru": "^5.1.1", - "reduce-css-calc": "^2.1.8", - "resolve": "^1.20.0", - "tmp": "^0.2.1" + "resolve": "^1.22.0" }, "dependencies": { "glob-parent": { @@ -4759,14 +4049,6 @@ "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" }, - "tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "requires": { - "rimraf": "^3.0.0" - } - }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", diff --git a/website/homepage/package.json b/website/homepage/package.json index a75996489e..ef16746f09 100644 --- a/website/homepage/package.json +++ b/website/homepage/package.json @@ -1,22 +1,25 @@ { "name": "gradio-website", "scripts": { + "prebuild": "(ls ../../gradio/templates/frontend/assets) || (cd ../../ui && pnpm i --frozen-lockfile && pnpm build && cd ../website/homepage) || (npm install pnpm && cd ../../ui && pnpm i --frozen-lockfile && npm install pnpm && pnpm build && cd ../website/homepage)", "build:setup": "mkdir -p dist generated", "build:html": "python render_html.py && html-minifier --input-dir generated --output-dir dist --file-ext html --remove-comments --collapse-whitespace --minify-js true", "build:css": "postcss src/style.css -o dist/style.css && python replace_style_names.py", "build:assets": "cp -R src/assets/. dist/assets/", - "build": "npm run build:setup && npm run build:html && npm run build:css && npm run build:assets", + "build": "npm run prebuild && npm run build:setup && npm run build:html && npm run build:css && npm run build:assets", "start": "npm run build && chokidar 'src/**' '../guides/*' -c 'echo '{path}' && if [[ {path} =~ ^src/.*.html ]]; then npm run build:html && npm run build:css; elif [[ {path} =~ ^../guides/.* ]]; then npm run build:html && npm ; elif [[ {path} =~ ^src/.*.css ]]; then npm run build:html && npm run build:css; elif [[ {path} =~ ^src/assets/.* ]]; then npm run build:assets; fi; echo 0 ' " }, "dependencies": { "@fullhuman/postcss-purgecss": "^4.0.3", - "@tailwindcss/typography": "^0.4.1", + "@tailwindcss/typography": "^0.5.0", + "@tailwindcss/forms": "^0.5.0", "autoprefixer": "^10.4.0", "chokidar-cli": "^3.0.0", "cssnano": "^5.0.8", "html-minifier": "^4.0.0", + "pnpm": "^6.32.7", "postcss-cli": "^9.0.1", "postcss-hash": "^3.0.0", - "tailwindcss": "^2.2.19" + "tailwindcss": "^3.0.24" } -} +} \ No newline at end of file diff --git a/website/homepage/render_html.py b/website/homepage/render_html.py index e6fbd85972..8e4e03a599 100644 --- a/website/homepage/render_html.py +++ b/website/homepage/render_html.py @@ -9,20 +9,50 @@ import requests from jinja2 import Template from render_html_helpers import generate_meta_image -# from gradio.inputs import InputComponent -from gradio.interface import Interface -# from gradio.outputs import OutputComponent +from gradio.components import ( + Textbox, + Number, + Slider, + Checkbox, + CheckboxGroup, + Radio, + Dropdown, + Image, + Video, + Audio, + File, + Dataframe, + Timeseries, + Label, + HighlightedText, + JSON, + HTML, + Gallery, + Carousel, + Chatbot, + Model3D, + Plot, + Markdown, + Button, + Dataset, + Variable +) + +from gradio.interface import Interface, TabbedInterface +from gradio.mix import Series, Parallel +from gradio.blocks import Blocks +from gradio.layouts import Row, Column, Tabs, TabItem +from gradio.events import Changeable, Clearable, Submittable, Editable, Playable, Clickable GRADIO_DIR = "../../" GRADIO_GUIDES_DIR = os.path.join(GRADIO_DIR, "guides") GRADIO_DEMO_DIR = os.path.join(GRADIO_DIR, "demo") -GRADIO_ASSETS_LIST = os.listdir( - os.path.join(GRADIO_DIR, "gradio", "templates", "frontend", "assets") -) -GRADIO_ASSETS = { - f"{asset.split('.')[0]}_{asset.split('.')[-1]}_file": asset - for asset in GRADIO_ASSETS_LIST -} +GRADIO_INDEX_FILE = os.path.join(GRADIO_DIR, "gradio", "templates", "frontend", "index.html") +with open(GRADIO_INDEX_FILE) as index_file: + index_html = index_file.read() + +ENTRY_JS_FILE=re.findall(r'"\.\/assets\/(.*\.js)"', index_html)[0] +ENTRY_CSS_FILE=re.findall(r'"\.\/assets\/(.*\.css)"', index_html)[0] with open("src/navbar.html", encoding="utf-8") as navbar_file: navbar_html = navbar_file.read() @@ -44,7 +74,8 @@ def render_index(): tweets=tweets, star_count=star_count, navbar_html=navbar_html, - **GRADIO_ASSETS, + entry_js_file=ENTRY_JS_FILE, + entry_css_file=ENTRY_CSS_FILE ) with open( os.path.join("generated", "index.html"), "w", encoding="utf-8" @@ -80,6 +111,9 @@ for guide in guide_files: contributor = None if "Contributed by " in guide_content: contributor = guide_content.split("Contributed by ")[1].split("\n")[0] + docs = [] + if "Docs: " in guide_content: + docs = guide_content.split("Docs: ")[1].split("\n")[0].split(", ") url = f"https://gradio.app/{guide_name}/" @@ -91,6 +125,7 @@ for guide in guide_files: line.startswith("tags: ") or line.startswith("related_spaces: ") or line.startswith("Contributed by ") + or line.startswith("Docs: ") or line == title ) ] @@ -105,6 +140,7 @@ for guide in guide_files: "spaces": spaces, "url": url, "contributor": contributor, + "docs": docs } ) @@ -124,6 +160,17 @@ def render_guides_main(): generated_template.write(output_html) +def render_gallery(): + with open("src/gallery.html", encoding="utf-8") as template_file: + template = Template(template_file.read()) + output_html = template.render(navbar_html=navbar_html) + os.makedirs(os.path.join("generated", "gallery"), exist_ok=True) + with open( + os.path.join("generated", "gallery", "index.html"), "w", encoding="utf-8" + ) as generated_template: + generated_template.write(output_html) + + def render_guides(): for guide in guides: generate_meta_image(guide) @@ -202,7 +249,8 @@ def render_guides(): spaces=guide["spaces"], tags=guide["tags"], contributor=guide["contributor"], - **GRADIO_ASSETS, + entry_js_file=ENTRY_JS_FILE, + entry_css_file=ENTRY_CSS_FILE ) generated_template.write(output_html) @@ -267,19 +315,29 @@ def render_docs(): param_set.insert(0, (params.args[neg_index],)) return "\n".join(func_doc), param_set, params_doc, return_doc - def get_class_documentation(cls): + def get_class_documentation(cls, get_interpret=True, lines=1, replace_brackets=False): inp = {} inp["name"] = cls.__name__ doc = inspect.getdoc(cls) doc_lines = doc.split("\n") - inp["doc"] = "\n".join(doc_lines[:-2]) - inp["type"] = doc_lines[-2].split("type: ")[-1] - inp["demos"] = doc_lines[-1][7:].split(", ") + inp["doc"] = "" + parameters_started = False + inp["demos"] = [] + for l, line in enumerate(doc_lines): + if not(parameters_started): + inp["doc"] += line + " " + if line.startswith("Demos: "): + inp["demos"] = [demo for demo in line.split("Demos: ")[1].split(", ")] + if "Parameters:" in line or (lines is not None and l >= lines-1): + parameters_started = True + if parameters_started and ": " in line: + key, value = line.split(": ") + inp[key] = value.replace("{","").replace("}","") if replace_brackets else value + _, inp["params"], inp["params_doc"], _ = get_function_documentation( cls.__init__ ) - inp["shortcuts"] = list(cls.get_shortcut_implementations().items()) - if "interpret" in cls.__dict__: + if get_interpret and "interpret" in cls.__dict__: ( inp["interpret"], inp["interpret_params"], @@ -289,40 +347,233 @@ def render_docs(): _, _, _, inp["interpret_returns_doc"] = get_function_documentation( cls.get_interpretation_scores ) - + inp["guides"] = [guide for guide in guides if inp['name'].lower() in guide["docs"]] + + + inp["events"] = [] + if issubclass(cls, Changeable): + inp["events"].append("change()") + if issubclass(cls, Clickable): + inp["events"].append("click()") + if issubclass(cls, Clearable): + inp["events"].append("clear()") + if issubclass(cls, Playable): + inp["events"].append("play()") + inp["events"].append("pause()") + inp["events"].append("stop()") + if issubclass(cls, Editable): + inp["events"].append("edit()") + if issubclass(cls, Submittable): + inp["events"].append("submit()") + inp["events"] = ", ".join(inp["events"]) + return inp - inputs = [get_class_documentation(cls) for cls in InputComponent.__subclasses__()] - outputs = [get_class_documentation(cls) for cls in OutputComponent.__subclasses__()] + components = [ + Textbox, + Number, + Slider, + Checkbox, + CheckboxGroup, + Radio, + Dropdown, + Image, + Video, + Audio, + File, + Dataframe, + Timeseries, + Label, + HighlightedText, + JSON, + HTML, + Gallery, + Carousel, + Chatbot, + Model3D, + Plot, + Markdown, + Button, + Dataset, + Variable + ] + + components_docs = [get_class_documentation(cls, replace_brackets=True) for cls in components] interface_params = get_function_documentation(Interface.__init__) + interface_docs = get_class_documentation(Interface, get_interpret=False, lines=None)["doc"] interface = { - "doc": inspect.getdoc(Interface), + "doc": interface_docs, "params": interface_params[1], "params_doc": interface_params[2], + "example": [ +"""import gradio as gr + +def image_classifier(inp): + pass # image classifier model defined here +gr.Interface(fn=image_classifier, inputs="image", outputs="label")""", +"""import gradio as gr + +def speech_to_text(inp): + pass # speech recognition model defined here + +gr.Interface(speech_to_text, inputs="mic", outputs=gr.Textbox(label="Predicted text", lines=4))"""], + "demos": ["hello_world", "hello_world_3", "gpt_j"] } launch_params = get_function_documentation(Interface.launch) launch = { + "doc": launch_params[0], "params": launch_params[1], "params_doc": launch_params[2], + "example": """import gradio as gr\n\ndef image_classifier(inp):\n pass # image classifier model defined here\n\ndemo = gr.Interface(image_classifier, "image", "label")\ndemo.launch(share=True)""" } load_params = get_function_documentation(Interface.load) load = { + "doc": load_params[0], "params": load_params[1], "params_doc": load_params[2], "return_doc": load_params[3], + "example": +"""description = "Story generation with GPT" +examples = [["An adventurer is approached by a mysterious stranger in the tavern for a new quest."]] + +demo = gr.Interface.load("models/EleutherAI/gpt-neo-1.3B", description=description, examples=examples) + +demo.launch()""" } + from_pipeline_params = get_function_documentation(Interface.from_pipeline) + from_pipeline = { + "doc": from_pipeline_params[0], + "params": from_pipeline_params[1], + "params_doc": from_pipeline_params[2], + "return_doc": from_pipeline_params[3], + "example": +"""import gradio as gr +from transformers import pipeline + +pipe = pipeline("image-classification") + +gr.Interface.from_pipeline(pipe).launch()""" + } + blocks_docs = get_class_documentation(Blocks, lines=None)["doc"] + blocks_params = get_function_documentation(Blocks.__init__) + blocks_docs = { + "doc": blocks_docs, + "params": blocks_params[1], + "params_doc": blocks_params[2], + "example": +"""import gradio as gr + +def update(name): + return f"Welcome to Gradio, {name}!" + +demo = gr.Blocks() + +with demo: + gr.Markdown("Start typing below and then click **Run** to see the output.") + with gr.Row(): + inp = gr.Textbox(placeholder="What is your name?") + out = gr.Textbox() + btn = gr.Button("Run") + btn.click(fn=update, inputs=inp, outputs=out) + +demo.launch()""" + } + tabbed_interface_docs = get_class_documentation(TabbedInterface, lines=None)["doc"] + tabbed_interface_params = get_function_documentation(TabbedInterface.__init__) + tabbed_interface = { + "doc": tabbed_interface_docs, + "params": tabbed_interface_params[1], + "params_doc": tabbed_interface_params[2], + } + + series_docs = get_class_documentation(Series, lines=None)["doc"] + series_params = get_function_documentation(Series.__init__) + series = { + "doc": series_docs, + "params": series_params[1], + "params_doc": series_params[2], + } + + parallel_docs = get_class_documentation(Parallel, lines=None)["doc"] + parallel_params = get_function_documentation(Parallel.__init__) + parallel = { + "doc": parallel_docs, + "params": parallel_params[1], + "params_doc": parallel_params[2], + } + + # row = { + # "doc": get_class_documentation(Row, lines=None)["doc"], + # "params": get_function_documentation(Row.__init__)[1], + # "params_doc": get_function_documentation(Row.__init__)[2], + # } + + # column = { + # "doc": get_class_documentation(Column, lines=None)["doc"], + # "params": get_function_documentation(Column.__init__)[1], + # "params_doc": get_function_documentation(Column.__init__)[2], + # } + + # tabs = { + # "doc": get_class_documentation(Tabs, lines=None)["doc"], + # "params": get_function_documentation(Tabs.__init__)[1], + # "params_doc": get_function_documentation(Tabs.__init__)[2], + # } + + # tabitem = { + # "doc": get_class_documentation(TabItem, lines=None)["doc"], + # "params": get_function_documentation(TabItem.__init__)[1], + # "params_doc": get_function_documentation(TabItem.__init__)[2], + # } + + + SCREENSHOT_FOLDER = "dist/assets/demo_screenshots" + os.makedirs(SCREENSHOT_FOLDER, exist_ok=True) + + demo_code = {} + for component in components_docs: + for code_src in component["demos"]: + with open(os.path.join(GRADIO_DEMO_DIR, code_src, "run.py")) as code_file: + python_code = code_file.read().replace( + 'if __name__ == "__main__":\n iface.launch()', "iface.launch()" + ) + demo_code[code_src] = python_code + + for code_src in interface["demos"]: + with open(os.path.join(GRADIO_DEMO_DIR, code_src, "run.py")) as code_file: + python_code = code_file.read().replace( + 'if __name__ == "__main__":\n iface.launch()', "iface.launch()" + ) + demo_code[code_src] = python_code + + with open('embedding-configs.json') as json_file: + embedding_configs = json.load(json_file) + docs = { - "input": inputs, - "output": outputs, + "components": components_docs, "interface": interface, "launch": launch, "load": load, + "from_pipeline": from_pipeline, + "blocks": blocks_docs, + "tabbed_interface": tabbed_interface, + "parallel": parallel, + "series": series, + # "row": row, + # "column": column, + # "tabs": tabs, + # "tabitem": tabitem, + "demo_code": demo_code, + "embedding_configs": embedding_configs } + os.makedirs("generated", exist_ok=True) with open("src/docs_template.html") as template_file: template = Template(template_file.read()) output_html = template.render( - docs=docs, demo_links=demo_links, navbar_html=navbar_html + docs=docs, demo_links=demo_links, navbar_html=navbar_html, + entry_js_file=ENTRY_JS_FILE, + entry_css_file=ENTRY_CSS_FILE ) os.makedirs(os.path.join("generated", "docs"), exist_ok=True) with open( @@ -349,7 +600,10 @@ def render_other(): os.path.join("src/other_templates", template_filename) ) as template_file: template = Template(template_file.read()) - output_html = template.render(GRADIO_ASSETS) + output_html = template.render( + entry_js_file=ENTRY_JS_FILE, + entry_css_file=ENTRY_CSS_FILE + ) folder_name = template_filename[:-14] os.makedirs(os.path.join("generated", folder_name), exist_ok=True) with open( @@ -362,5 +616,7 @@ if __name__ == "__main__": render_index() render_guides_main() render_guides() - render_static_docs() + render_docs() + # render_static_docs() + render_gallery() render_other() diff --git a/website/homepage/runtime.txt b/website/homepage/runtime.txt new file mode 100644 index 0000000000..98fccd6d02 --- /dev/null +++ b/website/homepage/runtime.txt @@ -0,0 +1 @@ +3.8 \ No newline at end of file diff --git a/website/homepage/src/assets/img/thunder.svg b/website/homepage/src/assets/img/thunder.svg new file mode 100644 index 0000000000..75a9cf6917 --- /dev/null +++ b/website/homepage/src/assets/img/thunder.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/website/homepage/src/assets/img/twitter/8vyTl51q_400x400.jpeg b/website/homepage/src/assets/img/twitter/8vyTl51q_400x400.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..f87d805f4c4e0676c4968680c4f83483ec1cc262 GIT binary patch literal 34835 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

J7odOpzB(CJJ>I6L^Fz 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 literal 0 HcmV?d00001 diff --git a/website/homepage/src/assets/img/twitter/GDLc7Oe4_400x400.jpeg b/website/homepage/src/assets/img/twitter/GDLc7Oe4_400x400.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..928e10fda385bd9c5b7d873013b97b819d1efa0a GIT binary patch literal 16287 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 M+-Zv}`}FO90a-8XUjP6A literal 0 HcmV?d00001 diff --git a/website/homepage/src/assets/img/twitter/LsCnjnsl_400x400.jpeg b/website/homepage/src/assets/img/twitter/LsCnjnsl_400x400.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..0c03a76ba0291933cda4bc5ddbe1bec6d37d18e6 GIT binary patch literal 13321 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 u!m)A-hxAjY2bWPweG z1Sdpa@5_jUz0SXdHMZ?;xTYJ+Ye&vW*oE#=Cx(nrZY8G&JCRBsOz7H`(8%rEWmPkh zH+!|PY~_ttjIA||dKz9bSSxD%10Y=Dn#qhj9h3G&n0JWM5gmKf0|-9V-<=NWSbuNC zaN_25O+(VzTdM3F0q{z-=*B`rwN*2S$}VhuA;K0~_5e0b!LQAN8FU?q#~LS-veXUD zG7v|Zv~yev@BV*)RwoUDJ#Rpb3ICZcv%ycpJ;zCpnAF;4l`G$(PqCGMgk&|&#Yb;Y z9nq4tHSP{%_KRVqR`Vo*YN8;=1-A0N8H;bi$zY6O3q03-`$?FlXkTtht}S4H-$im7 z7=YWJt_S*%i~d}WRURNX#NC7l3A8fH^f0L4E&KV44d8_`jlnXf{pp3~FwQwWGRj99 z_}EunHELJp)(o*|)f~5lxZ@Keo3?`6$pU!tuxcm=iugPw@q$KI+5#4t8_;w}e_M!w zey|k!6D`93jcnD28?)_a3%N}ek%-M--!xgT_0{p9ZIMqn4fIxBdS;H=4lAa>Kzs0u zwd9G^fbo@Ivixoqs-7Gg6M78dJX%brCikVMU1w3!t%rGOAS17G``BYd$D2Swir!pN`ZdbrPmCk4+(m156OCwLcYIp&F!a16ReHy~0~V31S_df$5|SZZVIJA9U>?8i<7wZcn_08upU@LGrh zye8Z7)`E+FH~fIpA&M??cA~D(Qx>@UkVVpZ3UGhc$0U^CyX>v z=n<_+F=!Ll9JZfFGipIs<4HF)FgQApzt8^~r0wqY?oAq02Az7TG+navxo6SpHWq6`sMG_VSD{LH z5HaOiWY5Wi2%CM(OxXSzFFOHy6FgH8%S$%i&>-2v>vbl~zNr8`72>qK|5D$qs(-k+ zwlH(aGKak7w&(fwcckk8H60S3&z6@Y2#!S|IEr!Q8c9w#CeQu8Hc)QzT5LINPxRV@ zPK3rPX~S7vexSLy5~!0-mr>jbzvGNDe)TZ|jKuYz-@RQGa_!dX*_+sVeA;rl;maD#x%rA)**h3%64|2&n7vI|pvzaNP ztW&~q&Bga%oOt|%po7lDKpH6hHM33k8&BZ`9eC%}KR84!nP>X3BO6=-~6+dr@4>MQ7w|ZuM>nIGZ6L*Baf#$rzGM zw|!A{yv^to&KZo$fpPZ~x+VqNEff`zS1WetYRZg`D=@$RHbx#$>--1Lc{@sKF&E^H zmDP+%q?`vNA)5N>up^y7UVnr7wD|N%|M9OnM~Hf3R7I6wb` z!ga>zleD7V8bw`-CZ=W?QG)lgvZJhEWNCJINqXO$UAXCwK%ls?@tv21)9e0oNf}nl ziXh_ktH>FGbR(@u><8y>@>}$utv>h;lo!JyeDh#=H*Gm1%A!D6Wd9q($ zv8Lv@@J5dD4xsnwN8Zx#^JQO3I?kvsr0VeDXYaBZ@o#70h|rmpYHab0J(*X?$vxyq U5W3D@L4bS>=3{E&u=k literal 0 HcmV?d00001 diff --git a/website/homepage/src/assets/img/twitter/pwMrDOBv_400x400.jpeg b/website/homepage/src/assets/img/twitter/pwMrDOBv_400x400.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..4b674f4ebd6898c920e46fe7490e91ebf551b07b GIT binary patch literal 27123 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

5zahmUx713kMMDHGv9Q zAR-ah;ATPM*TZh$~)r*bz2y=!mL;Q8U#_3Tyo*mQc^L zES*_Qs0I)&qEQgvXntheq*zfeE^1w4wkieyK{lTdVB?F;U$u}hH|r@jESwDRAdVsh zWNbCt8}NNa)UFL=8FI0=2ndRlR8576ana`DOO1cQuz}c6#|TdN7EAZ2Mhs#~TnkMa zSi`G;=z}KfRm(4wyd#EiLIQ4Tq~L~l0wBIv%Szm;aJ)jn*V;Mqiz~#I`h6xWuS~H+ z(3X`qNdj9*!OI-5bSRbgqsW3t90!@MVcJ$l17LLmd4qRk8Ck`~z{zGaEr2pw!)o9N zJgva;O6b&Uh0d)Tuw> - + + + + - - + {{navbar_html|safe}} -
-
- +
- - + {{navbar_html|safe}}
@@ -143,35 +143,41 @@ {{ template_html|safe }}
- - - + + + - + + {{navbar_html|safe}} -
-
- Build & share delightful machine learning apps -
-

Gradio is the fastest way to demo your machine learning model with a friendly web interface so that anyone can use it, anywhere!

- -
- Get Started - with 5 lines of pure Python -
-
-
-
-
Sketch Recognition
-
- Question Answer
-
Image Segmentation
-
Speech Verification
-
-
-
- -
import gradio as gr
 
-def sketch_recognition(img):
-  # Implement sketch recognition model here...
-  # Return labels and confidences as dictionary
-
-iface = gr.Interface(fn=sketch_recognition, inputs="sketchpad", outputs="label").launch()
-
-
-
- -
-
- - - diff --git a/website/homepage/src/other_templates/kitchen_sink_template.html b/website/homepage/src/other_templates/kitchen_sink_template.html index 7ab2e18b14..d5478740d0 100644 --- a/website/homepage/src/other_templates/kitchen_sink_template.html +++ b/website/homepage/src/other_templates/kitchen_sink_template.html @@ -7,8 +7,7 @@ Gradio - - + @@ -106,8 +105,7 @@ var output_count = 0; window.gradio_mode = "website"; - - +