-
Notifications
You must be signed in to change notification settings - Fork 3k
Add Deep Links #10834
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Deep Links #10834
Changes from all commits
081ca97
d871c02
ff19606
234d815
80b5a1b
b6ae4cd
b02be2a
8f4c808
a7ea851
f0c64ec
c12787b
e6803ae
2fb0d43
e5bc12d
279846e
5aaf05e
688e398
746c493
11b14c2
6d53abc
73cff1a
7703142
67910b1
c91a08a
e96c56c
26173af
9c788e7
25a2f47
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
--- | ||
"@gradio/client": minor | ||
"@self/app": minor | ||
"@self/spa": minor | ||
"gradio": minor | ||
--- | ||
|
||
feat:Add Deep Links |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: deep_link"]}, {"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": ["import gradio as gr\n", "import random\n", "\n", "def random_response(message, history):\n", " return random.choice([\"Hi!\", \"Hello!\", \"Greetings!\"])\n", "\n", "with gr.Blocks() as demo:\n", " gr.ChatInterface(\n", " random_response,\n", " title=\"Greeting Bot\",\n", " description=\"Ask anything and receive a nice greeting!\",\n", " )\n", " gr.DeepLinkButton()\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch(share=True)\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import gradio as gr | ||
import random | ||
|
||
def random_response(message, history): | ||
return random.choice(["Hi!", "Hello!", "Greetings!"]) | ||
|
||
with gr.Blocks() as demo: | ||
gr.ChatInterface( | ||
random_response, | ||
title="Greeting Bot", | ||
description="Ask anything and receive a nice greeting!", | ||
) | ||
gr.DeepLinkButton() | ||
|
||
if __name__ == "__main__": | ||
demo.launch(share=True) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
"""Predefined button to copy a shareable link to the current Gradio Space.""" | ||
|
||
from __future__ import annotations | ||
|
||
import textwrap | ||
import time | ||
from collections.abc import Sequence | ||
from pathlib import Path | ||
from typing import TYPE_CHECKING, Literal | ||
|
||
from gradio_client.documentation import document | ||
|
||
from gradio import utils | ||
from gradio.components.base import Component | ||
from gradio.components.button import Button | ||
from gradio.context import get_blocks_context | ||
|
||
if TYPE_CHECKING: | ||
from gradio.components import Timer | ||
|
||
|
||
@document() | ||
class DeepLinkButton(Button): | ||
""" | ||
Creates a button that copies a shareable link to the current Gradio Space. | ||
The link includes the current session hash as a query parameter. | ||
""" | ||
|
||
is_template = True | ||
n_created = 0 | ||
|
||
def __init__( | ||
self, | ||
value: str = "Share via Link", | ||
copied_value: str = "Link Copied!", | ||
*, | ||
inputs: Component | Sequence[Component] | set[Component] | None = None, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the purpose of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just following the convention for all components. They should take inputs in case value is a callable but it doesn't really make sense in this case. |
||
variant: Literal["primary", "secondary"] = "primary", | ||
size: Literal["sm", "md", "lg"] = "lg", | ||
icon: str | Path | None = utils.get_icon_path("link.svg"), | ||
link: str | None = None, | ||
visible: bool = True, | ||
interactive: bool = True, | ||
elem_id: str | None = None, # noqa: ARG002 | ||
elem_classes: list[str] | str | None = None, | ||
render: bool = True, | ||
key: int | str | None = None, | ||
scale: int | None = None, | ||
min_width: int | None = None, | ||
every: Timer | float | None = None, | ||
): | ||
""" | ||
Parameters: | ||
value: The text to display on the button. | ||
copied_value: The text to display on the button after the link has been copied. | ||
""" | ||
self.copied_value = copied_value | ||
super().__init__( | ||
value, | ||
inputs=inputs, | ||
variant=variant, | ||
size=size, | ||
icon=icon, | ||
link=link, | ||
visible=visible, | ||
interactive=interactive, | ||
elem_id=f"gradio-share-link-button-{self.n_created}", | ||
elem_classes=elem_classes, | ||
render=render, | ||
key=key, | ||
scale=scale, | ||
min_width=min_width, | ||
every=every, | ||
) | ||
self.elem_id: str | ||
self.n_created += 1 | ||
if get_blocks_context(): | ||
self.activate() | ||
|
||
def activate(self): | ||
"""Attach the click event to copy the share link.""" | ||
_js = self.get_share_link(self.value, self.copied_value) | ||
# Need to separate events because can't run .then in a pure js | ||
# function. | ||
self.click(fn=None, inputs=[], outputs=[self], js=_js) | ||
self.click( | ||
fn=lambda: time.sleep(1) or self.value, | ||
inputs=[], | ||
outputs=[self], | ||
queue=False, | ||
) | ||
|
||
def get_share_link( | ||
self, value: str = "Share via Link", copied_value: str = "Link Copied!" | ||
): | ||
return textwrap.dedent( | ||
freddyaboulton marked this conversation as resolved.
Show resolved
Hide resolved
|
||
""" | ||
() => { | ||
const sessionHash = window.__gradio_session_hash__; | ||
fetch(`/gradio_api/deep_link?session_hash=${sessionHash}`) | ||
.then(response => { | ||
if (!response.ok) { | ||
throw new Error('Network response was not ok'); | ||
} | ||
return response.text(); | ||
}) | ||
.then(data => { | ||
const currentUrl = new URL(window.location.href); | ||
const cleanData = data.replace(/^"|"$/g, ''); | ||
if (cleanData) { | ||
currentUrl.searchParams.set('deep_link', cleanData); | ||
} | ||
navigator.clipboard.writeText(currentUrl.toString()); | ||
}) | ||
.catch(error => { | ||
console.error('Error fetching deep link:', error); | ||
return "Error"; | ||
}); | ||
|
||
return "BUTTON_COPIED_VALUE"; | ||
} | ||
""".replace("BUTTON_DEFAULT_VALUE", value).replace( | ||
"BUTTON_COPIED_VALUE", copied_value | ||
) | ||
).replace("ID", self.elem_id) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -366,6 +366,7 @@ class Page(TypedDict): | |
|
||
class BlocksConfigDict(TypedDict): | ||
version: str | ||
deep_link_state: NotRequired[Literal["valid", "invalid", "none"]] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does this parameter do? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the URL is not valid, we return deep_link_state of invalid so that the browser can display an appropriate error message as opposed to just failing silently. |
||
mode: str | ||
app_id: int | ||
dev_mode: bool | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add it to a few demos and link here?