Skip to content

Commit

Permalink
Sharing themes (gradio-app#3428)
Browse files Browse the repository at this point in the history
* Rebase

* Remove build hooks

* Working implementation

* Add semver + unit tests

* CHANGELOG

* Add to docs

* Rename push_to_hub and fix typos

* Fix gallery

* Fix typo

* Address comments + tests

* Update gradio/themes/app.py

Co-authored-by: Abubakar Abid <[email protected]>

* Import Base as Theme. Use DefaultTheme() as fallback

* Fix types

* Make version and token truly optional

* Add version dropdown + tests

* trigger

* Support private themes and org_names

* Fix org_name typo

* Update wheel

* Fix font loading and dumping

* fixing tests

* fix tests

* formatting

* version

* remove requirements

* remove requirements

* formatting

* fix tests

---------

Co-authored-by: Abubakar Abid <[email protected]>
  • Loading branch information
freddyaboulton and abidlabs authored Mar 19, 2023
1 parent 27be008 commit 8ec2b0b
Show file tree
Hide file tree
Showing 19 changed files with 1,129 additions and 12 deletions.
12 changes: 11 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,15 @@
// Use Pollen's inbuilt variable ordering
"cssvar.disableSort": true,
// Add support for autocomplete in other file types
"cssvar.extensions": ["js", "css", "html", "jsx", "tsx", "svelte"]
"cssvar.extensions": [
"js",
"css",
"html",
"jsx",
"tsx",
"svelte"
],
"python.analysis.extraPaths": [
"./gradio/themes/utils"
]
}
68 changes: 68 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,74 @@ No changes to highlight.

## New Features:

### Theme Sharing 🎨 🤝

You can now share your gradio themes with the world!

After creating a theme, you can upload it to the HuggingFace Hub to let others view it, use it, and build off of it!

### Uploading
There are two ways to upload a theme, via the theme class instance or the command line.

1. Via the class instance

```python
my_theme.push_to_hub(repo_name="my_theme",
version="0.2.0",
hf_token="...")
```

2. Via the command line

First save the theme to disk
```python
my_theme.dump(filename="my_theme.json")
```

Then use the `upload_theme` command:

```bash
upload_theme\
"my_theme.json"\
"my_theme"\
"0.2.0"\
"<hf-token>"
```

The `version` must be a valid [semantic version](https://www.geeksforgeeks.org/introduction-semantic-versioning/) string.

This creates a space on the huggingface hub to host the theme files and show potential users a preview of your theme.

An example theme space is here: https://huggingface.co/spaces/freddyaboulton/dracula_revamped

### Downloading
To use a theme from the hub, use the `from_hub` method on the `ThemeClass` and pass it to your app:

```python
my_theme = gr.Theme.from_hub("freddyaboulton/my_theme")

with gr.Blocks(theme=my_theme) as demo:
....
```

You can also pass the theme string directly to `Blocks` or `Interface` (`gr.Blocks(theme="freddyaboulton/my_theme")`)

You can pin your app to an upstream theme version by using semantic versioning expressions.

For example, the following would ensure the theme we load from the `my_theme` repo was between versions `0.1.0` and `0.2.0`:

```python
with gr.Blocks(theme="freddyaboulton/my_theme@>=0.1.0,<0.2.0") as demo:
....
```

by [@freddyaboulton](https://github.com/freddyaboulton) in [PR 3428](https://github.com/gradio-app/gradio/pull/3428)


### Code component 🦾

New code component allows you to enter, edit and display code with full syntax highlighting by [@pngwn](https://github.com/pngwn) in [PR 3421](https://github.com/gradio-app/gradio/pull/3421)

### The `Chatbot` component now supports audio, video, and images

The `Chatbot` component now supports audio, video, and images with a simple syntax: simply
Expand Down
1 change: 1 addition & 0 deletions gradio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
TextArea,
Webcam,
)
from gradio.themes import Base as Theme

current_pkg_version = (
(pkgutil.get_data(__name__, "version.txt") or b"").decode("ascii").strip()
Expand Down
10 changes: 8 additions & 2 deletions gradio/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ def update(name):

def __init__(
self,
theme: Theme | None = None,
theme: Theme | str | None = None,
analytics_enabled: bool | None = None,
mode: str = "blocks",
title: str = "Gradio",
Expand All @@ -496,7 +496,13 @@ def __init__(
self.save_to = None
if theme is None:
theme = DefaultTheme()
elif not isinstance(theme, Theme):
elif isinstance(theme, str):
try:
theme = Theme.from_hub(theme)
except Exception as e:
warnings.warn(f"Cannot load {theme}. Caught Exception: {str(e)}")
theme = DefaultTheme()
if not isinstance(theme, Theme):
warnings.warn("Theme should be a class loaded from gradio.themes")
theme = DefaultTheme()
self.theme = theme
Expand Down
4 changes: 2 additions & 2 deletions gradio/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -3705,7 +3705,7 @@ class JSON(Changeable, IOComponent, JSONSerializable):

def __init__(
self,
value: str | Callable | None = None,
value: str | Dict | List | Callable | None = None,
*,
label: str | None = None,
every: float | None = None,
Expand Down Expand Up @@ -3866,7 +3866,7 @@ class Gallery(IOComponent, TempFileManager, FileSerializable, Selectable):

def __init__(
self,
value: List[np.ndarray | _Image.Image | str] | Callable | None = None,
value: List[np.ndarray | _Image.Image | str | Tuple] | Callable | None = None,
*,
label: str | None = None,
every: float | None = None,
Expand Down
147 changes: 147 additions & 0 deletions gradio/themes/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import time

from theme_dropdown import create_theme_dropdown # noqa: F401

import gradio as gr

dropdown, js = create_theme_dropdown()

with gr.Blocks(theme=gr.themes.Default()) as demo:
with gr.Row().style(equal_height=True):
with gr.Column(scale=10):
gr.Markdown(
"""
# Theme preview: `{THEME}`
To use this theme, set `theme='{AUTHOR}/{SPACE_NAME}'` in `gr.Blocks()` or `gr.Interface()`.
You can append an `@` and a semantic version expression, e.g. @>=1.0.0,<2.0.0 to pin to a given version
of this theme.
"""
)
with gr.Column(scale=3):
with gr.Box():
dropdown.render()
toggle_dark = gr.Button(value="Toggle Dark").style(full_width=True)

dropdown.change(None, dropdown, None, _js=js)
toggle_dark.click(
None,
_js="""
() => {
document.body.classList.toggle('dark');
document.querySelector('gradio-app').style.backgroundColor = 'var(--color-background-primary)'
}
""",
)

name = gr.Textbox(
label="Name",
info="Full name, including middle name. No special characters.",
placeholder="John Doe",
value="John Doe",
interactive=True,
)

with gr.Row():
slider1 = gr.Slider(label="Slider 1")
slider2 = gr.Slider(label="Slider 2")
gr.CheckboxGroup(["A", "B", "C"], label="Checkbox Group")

with gr.Row():
with gr.Column(variant="panel", scale=1):
gr.Markdown("## Panel 1")
radio = gr.Radio(
["A", "B", "C"],
label="Radio",
info="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
)
drop = gr.Dropdown(["Option 1", "Option 2", "Option 3"], show_label=False)
drop_2 = gr.Dropdown(
["Option A", "Option B", "Option C"],
multiselect=True,
value=["Option A"],
label="Dropdown",
interactive=True,
)
check = gr.Checkbox(label="Go")
with gr.Column(variant="panel", scale=2):
img = gr.Image(
"https://gradio.app/assets/img/header-image.jpg", label="Image"
).style(height=320)
with gr.Row():
go_btn = gr.Button("Go", label="Primary Button", variant="primary")
clear_btn = gr.Button(
"Clear", label="Secondary Button", variant="secondary"
)

def go(*args):
time.sleep(3)
return "https://gradio.app/assets/img/header-image.jpg"

go_btn.click(go, [radio, drop, drop_2, check, name], img, api_name="go")

def clear():
time.sleep(0.2)
return None

clear_btn.click(clear, None, img)

with gr.Row():
btn1 = gr.Button("Button 1").style(size="sm")
btn2 = gr.UploadButton().style(size="sm")
stop_btn = gr.Button("Stop", label="Stop Button", variant="stop").style(
size="sm"
)

with gr.Row():
gr.Dataframe(value=[[1, 2, 3], [4, 5, 6], [7, 8, 9]], label="Dataframe")
gr.JSON(
value={"a": 1, "b": 2, "c": {"test": "a", "test2": [1, 2, 3]}}, label="JSON"
)
gr.Label(value={"cat": 0.7, "dog": 0.2, "fish": 0.1})
gr.File()
with gr.Row():
gr.ColorPicker()
gr.Video("https://gradio-static-files.s3.us-west-2.amazonaws.com/world.mp4")
gr.Gallery(
[
(
"https://gradio-static-files.s3.us-west-2.amazonaws.com/lion.jpg",
"lion",
),
(
"https://gradio-static-files.s3.us-west-2.amazonaws.com/logo.png",
"logo",
),
(
"https://gradio-static-files.s3.us-west-2.amazonaws.com/tower.jpg",
"tower",
),
]
).style(height="200px", grid=2)

with gr.Row():
with gr.Column(scale=2):
chatbot = gr.Chatbot([("Hello", "Hi")], label="Chatbot")
chat_btn = gr.Button("Add messages")

def chat(history):
time.sleep(2)
yield [["How are you?", "I am good."]]

chat_btn.click(
lambda history: history
+ [["How are you?", "I am good."]]
+ (time.sleep(2) or []),
chatbot,
chatbot,
)
with gr.Column(scale=1):
with gr.Accordion("Advanced Settings"):
gr.Markdown("Hello")
gr.Number(label="Chatbot control 1")
gr.Number(label="Chatbot control 2")
gr.Number(label="Chatbot control 3")


if __name__ == "__main__":
demo.queue().launch()
Loading

0 comments on commit 8ec2b0b

Please sign in to comment.