diff --git a/CHANGELOG.md b/CHANGELOG.md index 17502aa7eae1d..668551f8bd08e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,67 @@ -# Upcoming Release +# Upcoming Release + +## New Features: + +### iOS image rotation fixed ๐Ÿ”„ + +Previously photos uploaded via iOS would be rotated after processing. This has been fixed by [@freddyaboulton](https://github.com/freddyaboulton) in [PR 3089](https://github.com/gradio-app/gradio/pull/3091) + +#### Before +![image](https://user-images.githubusercontent.com/41651716/215846507-a36e9d05-1ac2-4867-8ab3-ce045a9415d9.png) + +#### After +![image](https://user-images.githubusercontent.com/41651716/215846554-e41773ed-70f0-491a-9952-6a18babf91ef.png) + +<<<<<<< HEAD +======= +### Run on Kaggle kernels ๐Ÿงช + +A share link will automatically be created when running on Kaggle kernels (notebooks) so that +the front-end is properly displayed. + +![image](https://user-images.githubusercontent.com/41651716/216104254-2cf55599-449c-436c-b57e-40f6a83f9eee.png) + +By [@freddyaboulton](https://github.com/freddyaboulton) in [PR 3101](https://github.com/gradio-app/gradio/pull/3101) + +>>>>>>> 42ad0cbe (Enable gradio to work on kaggle (#3101)) +## Bug Fixes: +<<<<<<< HEAD +<<<<<<< HEAD + +======= +* Fix bug where examples were not rendered correctly for demos created with Blocks api that had multiple input compinents by [@freddyaboulton](https://github.com/freddyaboulton) in [PR 3090](https://github.com/gradio-app/gradio/pull/3090) +>>>>>>> 792289cd (Fix example gallery mode (#3090)) +- Fix change event listener for JSON, HighlightedText, Chatbot by [@aliabid94](https://github.com/aliabid94) in [PR 3095](https://github.com/gradio-app/gradio/pull/3095) + +## Documentation Changes: +<<<<<<< HEAD + +No changes to highlight. +======= +======= +* Fix bug where examples were not rendered correctly for demos created with Blocks api that had multiple input compinents by [@freddyaboulton](https://github.com/freddyaboulton) in [PR 3090](https://github.com/gradio-app/gradio/pull/3090) +- Fix change event listener for JSON, HighlightedText, Chatbot by [@aliabid94](https://github.com/aliabid94) in [PR 3095](https://github.com/gradio-app/gradio/pull/3095) +- Fixes bug where video and file change event not working [@tomchang25](https://github.com/tomchang25) in [PR 3098](https://github.com/gradio-app/gradio/pull/3098) +- Fixes bug where static_video play and pause event not working [@tomchang25](https://github.com/tomchang25) in [PR 3098](https://github.com/gradio-app/gradio/pull/3098) + +## Documentation Changes: +>>>>>>> 2cf3e25c (fix changelog) +- Fix a broken link in the Quick Start guide, by [@cakiki](https://github.com/cakiki) in [PR 3109](https://github.com/gradio-app/gradio/pull/3109) +>>>>>>> 4d94d4b3 ([Minor] Correct link (#3109)) + +## Testing and Infrastructure Changes: +No changes to highlight. + +## Breaking Changes: +No changes to highlight. + +## Full Changelog: +* Set minimum `markdown-it-py` version to `2.0.0` so that the dollar math plugin is compatible by [@freddyaboulton](https://github.com/freddyaboulton) in [PR 3102](https://github.com/gradio-app/gradio/pull/3102) + +## Contributors Shoutout: +No changes to highlight. + +# Version 3.17.0 ## New Features: @@ -25,7 +88,7 @@ chatbot.launch() By [@freddyaboulton](https://github.com/freddyaboulton) in [PR 3011](https://github.com/gradio-app/gradio/pull/3011) -### Download Button added to Model3D Output Component +### Download Button added to Model3D Output Component ๐Ÿ“ฅ No need for an additional file output component to enable model3d file downloads anymore. We now added a download button to the model3d component itself. @@ -33,6 +96,14 @@ No need for an additional file output component to enable model3d file downloads By [@dawoodkhan82](https://github.com/dawoodkhan82) in [PR 3014](https://github.com/gradio-app/gradio/pull/3014) +### Fixing Auth on Spaces ๐Ÿ”‘ + +Authentication on spaces works now! Third party cookies must be enabled on your browser to be able +to log in. Some browsers disable third party cookies by default (Safari, Chrome Incognito). + +![auth_spaces](https://user-images.githubusercontent.com/41651716/215528417-09538933-0576-4d1d-b3b9-1e877ab01905.gif) + + ## Bug Fixes: * Fixes bug where interpretation event was not configured correctly by [@freddyaboulton](https://github.com/freddyaboulton) in [PR 2993](https://github.com/gradio-app/gradio/pull/2993) * Fix relative import bug in reload mode by [@freddyaboulton](https://github.com/freddyaboulton) in [PR 2992](https://github.com/gradio-app/gradio/pull/2992) @@ -55,7 +126,14 @@ By [@dawoodkhan82](https://github.com/dawoodkhan82) in [PR 3014](https://github. * Fix bug where the queue was not properly restarted after launching a `closed` app by [@freddyaboulton](https://github.com/freddyaboulton) in [PR 3022](https://github.com/gradio-app/gradio/pull/3022) * Adding missing embedded components on docs by [@aliabd](https://github.com/aliabd) in [PR 3027](https://github.com/gradio-app/gradio/pull/3027) * Fixes bug where app would crash if the `file_types` parameter of `gr.File` or `gr.UploadButton` was not a list by [@freddyaboulton](https://github.com/freddyaboulton) in [PR 3048](https://github.com/gradio-app/gradio/pull/3048) +* Ensure CSS mounts correctly regardless of how many Gradio instances are on the page [@pngwn](https://github.com/pngwn) in [PR 3059](https://github.com/gradio-app/gradio/pull/3059). * Fix bug where input component was not hidden in the frontend for `UploadButton` by [@freddyaboulton](https://github.com/freddyaboulton) in [PR 3053](https://github.com/gradio-app/gradio/pull/3053) +* Fixes issue where after clicking submit or undo, the sketch output wouldn't clear. [@dawoodkhan82](https://github.com/dawoodkhan82) in [PR 3047](https://github.com/gradio-app/gradio/pull/3047) +* Ensure spaces embedded via the web component always use the correct URLs for server requests and change ports for testing to avoid strange collisions when users are working with embedded apps locally by [@pngwn](https://github.com/pngwn) in [PR 3065](https://github.com/gradio-app/gradio/pull/3065) +* Preserve selected image of Gallery through updated by [@freddyaboulton](https://github.com/freddyaboulton) in [PR 3061](https://github.com/gradio-app/gradio/pull/3061) +* Fix bug where auth was not respected on HF spaces by [@freddyaboulton](https://github.com/freddyaboulton) and [@aliabid94](https://github.com/aliabid94) in [PR 3049](https://github.com/gradio-app/gradio/pull/3049) +* Fixes bug where tabs selected attribute not working if manually change tab by [@tomchang25](https://github.com/tomchang25) in [3055](https://github.com/gradio-app/gradio/pull/3055) +* Change chatbot to show dots on progress, and fix bug where chatbot would not stick to bottom in the case of images by [@aliabid94](https://github.com/aliabid94) in [PR 3067](https://github.com/gradio-app/gradio/pull/3079) ## Documentation Changes: * SEO improvements to guides by[@aliabd](https://github.com/aliabd) in [PR 2915](https://github.com/gradio-app/gradio/pull/2915) @@ -1895,4 +1973,4 @@ We've introduced a lot of new components in `3.0`, including `Model3D`, `Dataset * [@NimaBoscarino](https://github.com/NimaBoscarino) made their first contribution in [PR 1000](https://github.com/gradio-app/gradio/pull/1000) * [@ronvoluted](https://github.com/ronvoluted) made their first contribution in [PR 1050](https://github.com/gradio-app/gradio/pull/1050) * [@radames](https://github.com/radames) made their first contribution in [PR 1074](https://github.com/gradio-app/gradio/pull/1074) -* [@freddyaboulton](https://github.com/freddyaboulton) made their first contribution in [PR 1085](https://github.com/gradio-app/gradio/pull/1085) +* [@freddyaboulton](https://github.com/freddyaboulton) made their first contribution in [PR 1085](https://github.com/gradio-app/gradio/pull/1085) \ No newline at end of file diff --git a/demo/event_trigger/img/a.jpg b/demo/event_trigger/img/a.jpg new file mode 100644 index 0000000000000..765f840973dae Binary files /dev/null and b/demo/event_trigger/img/a.jpg differ diff --git a/demo/event_trigger/img/b.jpg b/demo/event_trigger/img/b.jpg new file mode 100644 index 0000000000000..7774fb80bb4f0 Binary files /dev/null and b/demo/event_trigger/img/b.jpg differ diff --git a/demo/event_trigger/mp4/a.mp4 b/demo/event_trigger/mp4/a.mp4 new file mode 100644 index 0000000000000..95a61f6b4a753 Binary files /dev/null and b/demo/event_trigger/mp4/a.mp4 differ diff --git a/demo/event_trigger/mp4/b.mp4 b/demo/event_trigger/mp4/b.mp4 new file mode 100644 index 0000000000000..7b2d7c723ea53 Binary files /dev/null and b/demo/event_trigger/mp4/b.mp4 differ diff --git a/demo/event_trigger/run.ipynb b/demo/event_trigger/run.ipynb new file mode 100644 index 0000000000000..2a3131640f273 --- /dev/null +++ b/demo/event_trigger/run.ipynb @@ -0,0 +1 @@ +{"cells": [{"cell_type": "markdown", "id": 302934307671667531413257853548643485645, "metadata": {}, "source": ["# Gradio Demo: event_trigger"]}, {"cell_type": "code", "execution_count": null, "id": 272996653310673477252411125948039410165, "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": 288918539441861185822528903084949547379, "metadata": {}, "outputs": [], "source": ["# Downloading files from the demo repo\n", "import os\n", "os.mkdir('img')\n", "!wget -q -O img/a.jpg https://github.com/gradio-app/gradio/raw/main/demo/event_trigger/img/a.jpg\n", "!wget -q -O img/b.jpg https://github.com/gradio-app/gradio/raw/main/demo/event_trigger/img/b.jpg\n", "os.mkdir('mp4')\n", "!wget -q -O mp4/a.mp4 https://github.com/gradio-app/gradio/raw/main/demo/event_trigger/mp4/a.mp4\n", "!wget -q -O mp4/b.mp4 https://github.com/gradio-app/gradio/raw/main/demo/event_trigger/mp4/b.mp4"]}, {"cell_type": "code", "execution_count": null, "id": 44380577570523278879349135829904343037, "metadata": {}, "outputs": [], "source": ["# %%\n", "import gradio as gr\n", "\n", "\n", "TEST_VIDEO_A = \"mp4/a.mp4\"\n", "TEST_VIDEO_B = \"mp4/b.mp4\"\n", "\n", "TEST_IMAGE_A = \"img/a.jpg\"\n", "TEST_IMAGE_B = \"img/b.jpg\"\n", "\n", "\n", "def alert_change(component, value):\n", " print(f\"Detected {component} change, {type(value)}\")\n", "\n", " if type(value) == list or type(value) == str:\n", " print(value)\n", "\n", "\n", "def change_interactive(state):\n", " return gr.update(interactive=not state), not state\n", "\n", "\n", "with gr.Blocks() as demo:\n", " with gr.Tab(label=\"Text change\"):\n", " with gr.Row():\n", " with gr.Column():\n", " textbox1 = gr.Textbox()\n", " textbox2 = gr.Textbox(interactive=True)\n", "\n", " with gr.Column():\n", " btn = gr.Button()\n", "\n", " def btn_click(state):\n", " return state\n", "\n", " def text_change(value):\n", " print(\"text_change\", value)\n", "\n", " btn.click(fn=btn_click, inputs=textbox1, outputs=textbox2)\n", " textbox2.change(fn=alert_change, inputs=[gr.State(\"Text\"), textbox2])\n", "\n", " with gr.Tab(label=\"Video change, play, pause\"):\n", " with gr.Row():\n", " with gr.Column():\n", " radio1 = gr.Radio(\n", " choices=[TEST_VIDEO_A, TEST_VIDEO_B],\n", " interactive=True,\n", " type=\"index\",\n", " )\n", "\n", " video_btn = gr.Button(\"Change interactive\")\n", "\n", " with gr.Column():\n", " video1 = gr.Video(value=TEST_VIDEO_A, interactive=False)\n", " video1_interactive = gr.State(value=False)\n", "\n", " def change_video(index):\n", " if index == 0:\n", " return TEST_VIDEO_A\n", " elif index == 1:\n", " return TEST_VIDEO_B\n", "\n", " def video_play():\n", " print(\"video_play\")\n", "\n", " def video_pause():\n", " print(\"video_pause\")\n", "\n", " def video_stop():\n", " print(\"video_stop\")\n", "\n", " video1.play(fn=video_play)\n", " video1.pause(fn=video_pause)\n", " video1.stop(fn=video_stop)\n", "\n", " radio1.change(fn=change_video, inputs=radio1, outputs=video1)\n", " video1.change(fn=alert_change, inputs=[gr.State(\"Video\"), video1])\n", "\n", " video_btn.click(\n", " fn=change_interactive,\n", " inputs=video1_interactive,\n", " outputs=[video1, video1_interactive],\n", " )\n", "\n", " with gr.Tab(label=\"Image change\"):\n", " with gr.Row():\n", " with gr.Column():\n", " radio2 = gr.Radio(\n", " choices=[TEST_IMAGE_A, TEST_IMAGE_B],\n", " interactive=True,\n", " type=\"index\",\n", " )\n", "\n", " with gr.Column():\n", " image1 = gr.Image(value=TEST_IMAGE_A, interactive=True)\n", "\n", " def change_image(index):\n", " if index == 0:\n", " return TEST_IMAGE_A\n", " elif index == 1:\n", " return TEST_IMAGE_B\n", "\n", " radio2.change(fn=change_image, inputs=radio2, outputs=image1)\n", " image1.change(fn=alert_change, inputs=[gr.State(\"Image\"), image1])\n", "\n", " with gr.Tab(label=\"File\"):\n", " with gr.Row():\n", " with gr.Column():\n", " radio3 = gr.Radio(\n", " choices=[\"A\", \"B\", \"AB\"],\n", " interactive=True,\n", " type=\"index\",\n", " )\n", "\n", " file_btn = gr.Button(\"Change interactive\")\n", "\n", " with gr.Column():\n", " file1 = gr.File(\n", " value=[TEST_IMAGE_A, TEST_IMAGE_B],\n", " interactive=False,\n", " file_count=\"multiple\",\n", " )\n", " file1_interactive = gr.State(value=False)\n", "\n", " def change_file(index):\n", " if index == 0:\n", " return [TEST_IMAGE_A]\n", " elif index == 1:\n", " return [TEST_IMAGE_A]\n", " elif index == 2:\n", " return [TEST_IMAGE_A, TEST_IMAGE_B]\n", "\n", " radio3.change(fn=change_file, inputs=radio3, outputs=file1)\n", " file1.change(fn=alert_change, inputs=[gr.State(\"File\"), file1])\n", "\n", " file_btn.click(\n", " fn=change_interactive,\n", " inputs=file1_interactive,\n", " outputs=[file1, file1_interactive],\n", " )\n", "\n", "demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file diff --git a/demo/event_trigger/run.py b/demo/event_trigger/run.py new file mode 100644 index 0000000000000..4b7d16707e923 --- /dev/null +++ b/demo/event_trigger/run.py @@ -0,0 +1,142 @@ +# %% +import gradio as gr + + +TEST_VIDEO_A = "mp4/a.mp4" +TEST_VIDEO_B = "mp4/b.mp4" + +TEST_IMAGE_A = "img/a.jpg" +TEST_IMAGE_B = "img/b.jpg" + + +def alert_change(component, value): + print(f"Detected {component} change, {type(value)}") + + if type(value) == list or type(value) == str: + print(value) + + +def change_interactive(state): + return gr.update(interactive=not state), not state + + +with gr.Blocks() as demo: + with gr.Tab(label="Text change"): + with gr.Row(): + with gr.Column(): + textbox1 = gr.Textbox() + textbox2 = gr.Textbox(interactive=True) + + with gr.Column(): + btn = gr.Button() + + def btn_click(state): + return state + + def text_change(value): + print("text_change", value) + + btn.click(fn=btn_click, inputs=textbox1, outputs=textbox2) + textbox2.change(fn=alert_change, inputs=[gr.State("Text"), textbox2]) + + with gr.Tab(label="Video change, play, pause"): + with gr.Row(): + with gr.Column(): + radio1 = gr.Radio( + choices=[TEST_VIDEO_A, TEST_VIDEO_B], + interactive=True, + type="index", + ) + + video_btn = gr.Button("Change interactive") + + with gr.Column(): + video1 = gr.Video(value=TEST_VIDEO_A, interactive=False) + video1_interactive = gr.State(value=False) + + def change_video(index): + if index == 0: + return TEST_VIDEO_A + elif index == 1: + return TEST_VIDEO_B + + def video_play(): + print("video_play") + + def video_pause(): + print("video_pause") + + def video_stop(): + print("video_stop") + + video1.play(fn=video_play) + video1.pause(fn=video_pause) + video1.stop(fn=video_stop) + + radio1.change(fn=change_video, inputs=radio1, outputs=video1) + video1.change(fn=alert_change, inputs=[gr.State("Video"), video1]) + + video_btn.click( + fn=change_interactive, + inputs=video1_interactive, + outputs=[video1, video1_interactive], + ) + + with gr.Tab(label="Image change"): + with gr.Row(): + with gr.Column(): + radio2 = gr.Radio( + choices=[TEST_IMAGE_A, TEST_IMAGE_B], + interactive=True, + type="index", + ) + + with gr.Column(): + image1 = gr.Image(value=TEST_IMAGE_A, interactive=True) + + def change_image(index): + if index == 0: + return TEST_IMAGE_A + elif index == 1: + return TEST_IMAGE_B + + radio2.change(fn=change_image, inputs=radio2, outputs=image1) + image1.change(fn=alert_change, inputs=[gr.State("Image"), image1]) + + with gr.Tab(label="File"): + with gr.Row(): + with gr.Column(): + radio3 = gr.Radio( + choices=["A", "B", "AB"], + interactive=True, + type="index", + ) + + file_btn = gr.Button("Change interactive") + + with gr.Column(): + file1 = gr.File( + value=[TEST_IMAGE_A, TEST_IMAGE_B], + interactive=False, + file_count="multiple", + ) + file1_interactive = gr.State(value=False) + + def change_file(index): + if index == 0: + return [TEST_IMAGE_A] + elif index == 1: + return [TEST_IMAGE_A] + elif index == 2: + return [TEST_IMAGE_A, TEST_IMAGE_B] + + radio3.change(fn=change_file, inputs=radio3, outputs=file1) + file1.change(fn=alert_change, inputs=[gr.State("File"), file1]) + + file_btn.click( + fn=change_interactive, + inputs=file1_interactive, + outputs=[file1, file1_interactive], + ) + +demo.launch() diff --git a/gradio/blocks.py b/gradio/blocks.py index de88893269156..f9813f3609192 100644 --- a/gradio/blocks.py +++ b/gradio/blocks.py @@ -1383,6 +1383,7 @@ def reverse(text): self.server = server self.is_running = True self.is_colab = utils.colab_check() + self.is_kaggle = utils.kaggle_check() self.protocol = ( "https" if self.local_url.startswith("https") or self.is_colab @@ -1405,7 +1406,7 @@ def reverse(text): share if share is not None else True - if self.is_colab and self.enable_queue + if (self.is_colab and self.enable_queue) or self.is_kaggle else False ) diff --git a/gradio/processing_utils.py b/gradio/processing_utils.py index 39ac6b5e7dca0..c181a9bef7c59 100644 --- a/gradio/processing_utils.py +++ b/gradio/processing_utils.py @@ -52,7 +52,12 @@ def to_binary(x: str | Dict) -> bytes: def decode_base64_to_image(encoding: str) -> Image.Image: content = encoding.split(";")[1] image_encoded = content.split(",")[1] - return Image.open(BytesIO(base64.b64decode(image_encoded))) + img = Image.open(BytesIO(base64.b64decode(image_encoded))) + exif = img.getexif() + # 274 is the code for image rotation and 1 means "correct orientation" + if exif.get(274, 1) != 1 and hasattr(ImageOps, "exif_transpose"): + img = ImageOps.exif_transpose(img) + return img def encode_url_or_file_to_base64(path: str | Path, encryption_key: bytes | None = None): diff --git a/gradio/routes.py b/gradio/routes.py index c263c08b3e848..1a0d00c9cc7c5 100644 --- a/gradio/routes.py +++ b/gradio/routes.py @@ -184,8 +184,14 @@ def login(form_data: OAuth2PasswordRequestForm = Depends()): ) or (callable(app.auth) and app.auth.__call__(username, password)): token = secrets.token_urlsafe(16) app.tokens[token] = username - response = RedirectResponse(url="/", status_code=status.HTTP_302_FOUND) - response.set_cookie(key="access-token", value=token, httponly=True) + response = JSONResponse(content={"success": True}) + response.set_cookie( + key="access-token", + value=token, + httponly=True, + samesite="none", + secure=True, + ) return response else: raise HTTPException(status_code=400, detail="Incorrect credentials.") @@ -206,6 +212,7 @@ def main(request: fastapi.Request, user: str = Depends(get_current_user)): config = { "auth_required": True, "auth_message": blocks.auth_message, + "is_space": app.get_blocks().is_space, } try: diff --git a/gradio/utils.py b/gradio/utils.py index 674154f4c311c..9ac48152e9946 100644 --- a/gradio/utils.py +++ b/gradio/utils.py @@ -189,6 +189,12 @@ def colab_check() -> bool: return is_colab +def kaggle_check() -> bool: + return bool( + os.environ.get("KAGGLE_KERNEL_RUN_TYPE") or os.environ.get("GFOOTBALL_DATA_DIR") + ) + + def ipython_check() -> bool: """ Check if interface is launching from iPython (not colab) diff --git a/gradio/version.txt b/gradio/version.txt index 21221d0e7a479..3f67e25cea13b 100644 --- a/gradio/version.txt +++ b/gradio/version.txt @@ -1 +1 @@ -3.16.2 +3.17.0 diff --git a/guides/01_getting-started/01_quickstart.md b/guides/01_getting-started/01_quickstart.md index 170bd0a440a70..88ce233df2ef3 100644 --- a/guides/01_getting-started/01_quickstart.md +++ b/guides/01_getting-started/01_quickstart.md @@ -103,6 +103,6 @@ Here's an app to give you a taste of what's possible with `Blocks`: $code_blocks_flipper $demo_blocks_flipper -A lot more going on here! We'll cover how to create complex `Blocks` apps like this in the [building with blocks](https://github.com/gradio-app/gradio/tree/main/guides/3\)building_with_blocks) section for you. +A lot more going on here! We'll cover how to create complex `Blocks` apps like this in the [building with blocks](https://gradio.app/building_with_blocks) section for you. Congrats, you're now familiar with the basics of Gradio! ๐Ÿฅณ Go to our [next guide](https://gradio.app/key_features) to learn more about the key features of Gradio. diff --git a/guides/01_getting-started/03_sharing-your-app.md b/guides/01_getting-started/03_sharing-your-app.md index f32540797653e..51ab2dc74bfcc 100644 --- a/guides/01_getting-started/03_sharing-your-app.md +++ b/guides/01_getting-started/03_sharing-your-app.md @@ -77,6 +77,8 @@ fetch("https://pypi.org/pypi/gradio/json" }); +_Note: While Gradio's CSS will never impact the embedding page, the embedding page can affect the style of the embedded Gradio app. Make sure that any CSS in the parent page isn't so general that it could also apply to the embedded Gradio app and cause the styling to break. Element selectors such as `header { ... }` and `footer { ... }` will be the most likely to cause issues._ + ### Embedding with IFrames To embed with IFrames instead, simply add this element: @@ -121,6 +123,9 @@ def same_auth(username, password): demo.launch(auth=same_auth) ``` +For authentication to work properly, third party cookies must be enabled in your browser. +This is not the case by default for Safari, Chrome Incognito Mode. + ## Accessing the Network Request Directly When a user makes a prediction to your app, you may need the underlying network request, in order to get the request headers (e.g. for advanced authentication), log the client's IP address, or for other reasons. Gradio supports this in a similar manner to FastAPI: simply add a function parameter whose type hint is `gr.Request` and Gradio will pass in the network request as that parameter. Here is an example: diff --git a/guides/06_other-tutorials/creating-a-new-component.md b/guides/06_other-tutorials/creating-a-new-component.md index 0bf0f855fe5af..6256cecdea5d6 100644 --- a/guides/06_other-tutorials/creating-a-new-component.md +++ b/guides/06_other-tutorials/creating-a-new-component.md @@ -385,7 +385,7 @@ You can take a look at the [demo](https://github.com/gradio-app/gradio/tree/main To test the application: - run on a terminal `python path/demo/run.py` which starts the backend at the address [http://localhost:7860](http://localhost:7860); -- in another terminal, from the ui folder, run `pnpm dev` to start the frontend at [http://localhost:3000](http://localhost:3000) with hot reload functionalities. +- in another terminal, from the ui folder, run `pnpm dev` to start the frontend at [http://localhost:9876](http://localhost:9876) with hot reload functionalities. ## Conclusion diff --git a/requirements.txt b/requirements.txt index 6cb909beaf096..14c6edb81d456 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ aiohttp altair>=4.2.0 fastapi ffmpy -markdown-it-py[linkify,plugins] +markdown-it-py[linkify,plugins]>=2.0.0 markupsafe matplotlib numpy diff --git a/test/test_blocks.py b/test/test_blocks.py index fe334ad51ada2..ea6df70c9ed70 100644 --- a/test/test_blocks.py +++ b/test/test_blocks.py @@ -1338,7 +1338,7 @@ async def say_hello(name): data={"username": "abc", "password": "123"}, follow_redirects=False, ) - assert resp.status_code == 302 + assert resp.status_code == 200 token = resp.cookies.get("access-token") assert token diff --git a/test/test_processing_utils.py b/test/test_processing_utils.py index 141bed35e7f58..07c6851eae8bc 100644 --- a/test/test_processing_utils.py +++ b/test/test_processing_utils.py @@ -73,6 +73,14 @@ def test_encode_pil_to_base64_keeps_pnginfo(self): assert decoded_image.info == input_img.info + @patch("PIL.Image.Image.getexif", return_value={274: 3}) + @patch("PIL.ImageOps.exif_transpose") + def test_base64_to_image_does_rotation(self, mock_rotate, mock_exif): + input_img = Image.open("gradio/test_data/test_image.png") + base64 = processing_utils.encode_pil_to_base64(input_img) + processing_utils.decode_base64_to_image(base64) + mock_rotate.assert_called_once() + def test_resize_and_crop(self): img = Image.open("gradio/test_data/test_image.png") new_img = processing_utils.resize_and_crop(img, (20, 20)) diff --git a/test/test_utils.py b/test/test_utils.py index 9b4de02672ee0..cdbe453a5ca6d 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -29,6 +29,7 @@ format_ner_list, get_local_ip_address, ipython_check, + kaggle_check, launch_analytics, readme_to_html, sanitize_list_for_csv, @@ -103,6 +104,31 @@ def test_readme_to_html_doesnt_crash_on_connection_error(self, mock_get): def test_readme_to_html_correct_parse(self): readme_to_html("https://github.com/gradio-app/gradio/blob/master/README.md") + def test_kaggle_check_false(self): + assert not kaggle_check() + + def test_kaggle_check_true_when_run_type_set(self): + with mock.patch.dict( + os.environ, {"KAGGLE_KERNEL_RUN_TYPE": "Interactive"}, clear=True + ): + assert kaggle_check() + + def test_kaggle_check_true_when_both_set(self): + with mock.patch.dict( + os.environ, + {"KAGGLE_KERNEL_RUN_TYPE": "Interactive", "GFOOTBALL_DATA_DIR": "./"}, + clear=True, + ): + assert kaggle_check() + + def test_kaggle_check_false_when_neither_set(self): + with mock.patch.dict( + os.environ, + {"KAGGLE_KERNEL_RUN_TYPE": "", "GFOOTBALL_DATA_DIR": ""}, + clear=True, + ): + assert not kaggle_check() + class TestIPAddress: @pytest.mark.flaky diff --git a/ui/package.json b/ui/package.json index e2f12449a0488..1bed2031c0109 100644 --- a/ui/package.json +++ b/ui/package.json @@ -47,6 +47,7 @@ "postcss": "^8.4.6", "postcss-custom-media": "8", "postcss-nested": "^5.0.6", + "postcss-prefix-selector": "^1.16.0", "prettier": "^2.6.2", "prettier-plugin-css-order": "^1.3.0", "prettier-plugin-svelte": "^2.7.0", diff --git a/ui/packages/_cdn-test/index.html b/ui/packages/_cdn-test/index.html index 59f774ae07b9e..6585ebe2a4e57 100644 --- a/ui/packages/_cdn-test/index.html +++ b/ui/packages/_cdn-test/index.html @@ -39,7 +39,7 @@

hello subtitle

Hello text. Hello text.

hello subtitle

diff --git a/ui/packages/app/build_plugins.ts b/ui/packages/app/build_plugins.ts index 24e108ea52de3..dafe46f6bf6bf 100644 --- a/ui/packages/app/build_plugins.ts +++ b/ui/packages/app/build_plugins.ts @@ -110,17 +110,6 @@ export function handle_ce_css(): Plugin { enforce: "post", name: "custom-element-css", - transform(code, id) { - if (id === "vite/preload-helper") { - return { - code: code.replace( - "document.head.appendChild(link);", - "window.scoped_css_attach(link)" - ) - }; - } - }, - writeBundle(config, bundle) { let file_to_insert = { filename: "", diff --git a/ui/packages/app/package.json b/ui/packages/app/package.json index 982140dfdb58c..72f4c473035a3 100644 --- a/ui/packages/app/package.json +++ b/ui/packages/app/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "private": true, "scripts": { - "dev": "vite", + "dev": "vite --port 9876", "build:cdn": "vite build --mode production:cdn --emptyOutDir", "build:website": "vite build --mode production:website --emptyOutDir", "build:local": "vite build --mode production:local --emptyOutDir", @@ -42,6 +42,8 @@ "@gradio/video": "workspace:^0.0.1", "d3-dsv": "^3.0.1", "mime-types": "^2.1.34", + "postcss": "^8.4.21", + "postcss-prefix-selector": "^1.16.0", "svelte": "^3.25.1", "svelte-i18n": "^3.3.13" } diff --git a/ui/packages/app/postcss.config.js b/ui/packages/app/postcss.config.js index 7a7249ddfb424..f053ebf7976e3 100644 --- a/ui/packages/app/postcss.config.js +++ b/ui/packages/app/postcss.config.js @@ -1,4 +1 @@ -module.exports = { - extract: "themes.css", - plugins: [custom_media] -}; +module.exports = {}; diff --git a/ui/packages/app/snapshots/blocks_layout.spec.ts b/ui/packages/app/snapshots/blocks_layout.spec.ts index 990f7f3e3bd9a..2ad031705e853 100644 --- a/ui/packages/app/snapshots/blocks_layout.spec.ts +++ b/ui/packages/app/snapshots/blocks_layout.spec.ts @@ -13,6 +13,6 @@ function mock_demo(page: Page, demo: string) { test("blocks layout", async ({ page }) => { await mock_demo(page, "blocks_layout"); - await page.goto("http://localhost:3000"); + await page.goto("http://localhost:9876"); await expect(page).toHaveScreenshot(); }); diff --git a/ui/packages/app/snapshots/blocks_xray.spec.ts b/ui/packages/app/snapshots/blocks_xray.spec.ts index 6b6cf16f14b41..00987bbbbf42c 100644 --- a/ui/packages/app/snapshots/blocks_xray.spec.ts +++ b/ui/packages/app/snapshots/blocks_xray.spec.ts @@ -13,6 +13,6 @@ function mock_demo(page: Page, demo: string) { test("blocks xray", async ({ page }) => { await mock_demo(page, "blocks_xray"); - await page.goto("http://localhost:3000"); + await page.goto("http://localhost:9876"); await expect(page).toHaveScreenshot(); }); diff --git a/ui/packages/app/snapshots/kitchen_sink.spec.ts b/ui/packages/app/snapshots/kitchen_sink.spec.ts index 16ec00df7844a..713e31cd71cc8 100644 --- a/ui/packages/app/snapshots/kitchen_sink.spec.ts +++ b/ui/packages/app/snapshots/kitchen_sink.spec.ts @@ -13,6 +13,6 @@ function mock_demo(page: Page, demo: string) { test("kitchen sink", async ({ page }) => { await mock_demo(page, "kitchen_sink"); - await page.goto("http://localhost:3000"); + await page.goto("http://localhost:9876"); await expect(page).toHaveScreenshot(); }); diff --git a/ui/packages/app/src/Login.svelte b/ui/packages/app/src/Login.svelte index 4b000e9e9a086..03067e23c9b82 100644 --- a/ui/packages/app/src/Login.svelte +++ b/ui/packages/app/src/Login.svelte @@ -6,6 +6,7 @@ export let id: number; export let auth_message: string | null; export let app_mode: boolean; + export let is_space: boolean; window.__gradio_loader__[id].$set({ status: "complete" }); let username = ""; @@ -25,7 +26,7 @@ incorrect_credentials = true; username = ""; password = ""; - } else { + } else if (response.status == 200) { location.reload(); } }; @@ -37,6 +38,12 @@ {#if auth_message}

{auth_message}

{/if} + {#if is_space} +

+ If you are visiting a HuggingFace Space in Incognito mode, you must + enable third party cookies. +

+ {/if} {#if incorrect_credentials}

Incorrect Credentials

{/if} @@ -62,7 +69,12 @@ /> - @@ -80,6 +92,7 @@ .panel { display: flex; + flex-direction: column; flex-wrap: wrap; gap: var(--size-4); border-radius: var(--radius-lg); @@ -89,14 +102,16 @@ } h2 { - margin-bottom: var(--size-6); + margin-bottom: var(--size-3); + color: var(--color-text-body); font-weight: var(--weight-semibold); font-size: var(--scale-2); } .auth { - margin-top: var(--size-4); - margin-bottom: var(--size-4); + margin-top: var(--size-1); + margin-bottom: var(--size-1); + color: var(--color-text-body); } .creds { diff --git a/ui/packages/app/src/api.ts b/ui/packages/app/src/api.ts index 7e53d23f96d2a..ce7605d900854 100644 --- a/ui/packages/app/src/api.ts +++ b/ui/packages/app/src/api.ts @@ -130,7 +130,7 @@ export const fn = var ws_protocol = ws_endpoint.startsWith("https") ? "wss:" : "ws:"; var ws_path = location.pathname === "/" ? "/" : location.pathname; var ws_host = - BUILD_MODE === "dev" || location.origin === "http://localhost:3000" + BUILD_MODE === "dev" || location.origin === "http://localhost:9876" ? BACKEND_URL.replace("http://", "").slice(0, -1) : location.host; WS_ENDPOINT = `${ws_protocol}//${ws_host}${ws_path}queue/join`; diff --git a/ui/packages/app/src/components/Chatbot/Chatbot.svelte b/ui/packages/app/src/components/Chatbot/Chatbot.svelte index 464d677a68c96..f5ee85882d015 100644 --- a/ui/packages/app/src/components/Chatbot/Chatbot.svelte +++ b/ui/packages/app/src/components/Chatbot/Chatbot.svelte @@ -1,7 +1,6 @@ - {#if show_label} {/if} - + diff --git a/ui/packages/app/src/components/Dataset/Dataset.svelte b/ui/packages/app/src/components/Dataset/Dataset.svelte index 7808c411eae9d..ccbb2b32e59d5 100644 --- a/ui/packages/app/src/components/Dataset/Dataset.svelte +++ b/ui/packages/app/src/components/Dataset/Dataset.svelte @@ -18,7 +18,7 @@ let samples_dir: string = (root_url ?? root) + "file="; let page = 0; - $: gallery = headers.length < 2; + $: gallery = components.length < 2; let paginate = samples.length > samples_per_page; let selected_samples: Array>; diff --git a/ui/packages/app/src/components/File/File.svelte b/ui/packages/app/src/components/File/File.svelte index 5a725aed69824..88773ef74f1b3 100644 --- a/ui/packages/app/src/components/File/File.svelte +++ b/ui/packages/app/src/components/File/File.svelte @@ -1,7 +1,8 @@ (value = detail)} on:drag={({ detail }) => (dragging = detail)} - on:change on:clear on:upload > diff --git a/ui/packages/app/src/components/HighlightedText/HighlightedText.svelte b/ui/packages/app/src/components/HighlightedText/HighlightedText.svelte index d45d95174916b..44048a20c11a7 100644 --- a/ui/packages/app/src/components/HighlightedText/HighlightedText.svelte +++ b/ui/packages/app/src/components/HighlightedText/HighlightedText.svelte @@ -10,6 +10,7 @@ export let elem_id: string = ""; export let visible: boolean = true; export let value: Array<[string, string | number]>; + let old_value: Array<[string, string | number]>; export let show_legend: boolean; export let color_map: Record = {}; export let label: string; @@ -23,7 +24,12 @@ const dispatch = createEventDispatcher<{ change: undefined }>(); - $: value, dispatch("change"); + $: { + if (value !== old_value) { + old_value = value; + dispatch("change"); + } + } (); - $: value, dispatch("change"); + $: { + if (value !== old_value) { + old_value = value; + dispatch("change"); + } + } + import { createEventDispatcher } from "svelte"; import type { FileData } from "@gradio/upload"; import { normalise_file } from "@gradio/upload"; import { Block } from "@gradio/atoms"; @@ -13,6 +14,8 @@ export let elem_id: string = ""; export let visible: boolean = true; export let value: FileData | null | string = null; + let old_value: FileData | null | string = null; + export let label: string; export let source: string; export let root: string; @@ -29,6 +32,18 @@ $: _value = normalise_file(value, root_url ?? root); let dragging = false; + + const dispatch = createEventDispatcher<{ + change: undefined; + }>(); + + $: { + if (value !== old_value) { + old_value = value; + + dispatch("change"); + } + } {#if mode === "static"} - + {:else}