Guides Section and Redesign Parts of the Website (#490)
* added emojis to navbar; added guides main page * Added guides list * chatbot tutorial * chatbot tutorial almost complete * embedding chatbot * fixed html rendering inside pre tags issue * finished guide * basic search * search guides content * design changes * reading author and date * sketchpad tutorial * font change * added version badge and spaces links * sketch app * auto meta images, removed date and author * removed empty guides * navbar separate; updated readme * added navbar; fixed guide * added flagging guide * added related spaces to flagging guide * added tags, small fixes * footer design Co-authored-by: Abubakar Abid <aaabid93@gmail.com> Co-authored-by: Abubakar Abid <a12d@stanford.edu> Co-authored-by: aliabd <ali.si3luwa@gmail.com>
@ -1,4 +1,4 @@
|
||||
Metadata-Version: 2.1
|
||||
Metadata-Version: 1.0
|
||||
Name: gradio
|
||||
Version: 2.7.5.2
|
||||
Summary: Python library for easily interacting with trained machine learning models
|
||||
@ -6,9 +6,7 @@ Home-page: https://github.com/gradio-app/gradio-UI
|
||||
Author: Abubakar Abid, Ali Abid, Ali Abdalla, Dawood Khan, Ahsen Khaliq
|
||||
Author-email: team@gradio.app
|
||||
License: Apache License 2.0
|
||||
Description: UNKNOWN
|
||||
Keywords: machine learning,visualization,reproducibility
|
||||
Platform: UNKNOWN
|
||||
License-File: LICENSE
|
||||
|
||||
UNKNOWN
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
analytics-python
|
||||
aiohttp
|
||||
analytics-python
|
||||
fastapi
|
||||
ffmpy
|
||||
markdown-it-py[linkify,plugins]
|
||||
@ -9,7 +9,7 @@ pandas
|
||||
paramiko
|
||||
pillow
|
||||
pycryptodome
|
||||
python-multipart
|
||||
pydub
|
||||
python-multipart
|
||||
requests
|
||||
uvicorn
|
||||
|
6
gradio/package-lock.json
generated
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "gradio",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
103
guides/building_a_pictionary_app.md
Normal file
@ -0,0 +1,103 @@
|
||||
# Building a Pictionary App
|
||||
|
||||
related_spaces: https://huggingface.co/spaces/nateraw/quickdraw
|
||||
tags: SKETCHPAD, LABELS, LIVE
|
||||
|
||||
## Introduction
|
||||
|
||||
How well can an algorithm guess what you're drawing? A few years ago, Google released the **Quick Draw** dataset, which contains drawings made by humans of a variety of every objects. Researchers have used this dataset to train models to guess Pictionary-style drawings. Such models are perfect to use with Gradio's *sketchpad* input, so in this tutorial we will build a Pictionary web application using Gradio. We will be able to build the whole web application in Python, and will look like this (try drawing something!):
|
||||
|
||||
<iframe src="https://hf.space/gradioiframe/abidlabs/draw2/+" frameBorder="0" height="450" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>
|
||||
|
||||
Let's get started!
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Make sure you have the `gradio` Python package already [installed](/getting_started). To use the pretrained sketchpad model, also install `torch`.
|
||||
|
||||
## Step 1 — Setting up the Sketch Recognition Model
|
||||
|
||||
First, you will need a sketch recognition model. Since many researchers have already trained their own models on the Quick Draw dataset, we will use a pretrained model in this tutorial. Our model is a light 1.5 MB model trained by Nate Raw, that [you can download here](https://huggingface.co/spaces/nateraw/quickdraw/blob/main/pytorch_model.bin).
|
||||
|
||||
If you are interested, here [is the code](https://github.com/nateraw/quickdraw-pytorch) that was used to train the model. We will simply load the pretrained model in PyTorch, as follows:
|
||||
|
||||
```python
|
||||
import torch
|
||||
from torch import nn
|
||||
|
||||
model = nn.Sequential(
|
||||
nn.Conv2d(1, 32, 3, padding='same'),
|
||||
nn.ReLU(),
|
||||
nn.MaxPool2d(2),
|
||||
nn.Conv2d(32, 64, 3, padding='same'),
|
||||
nn.ReLU(),
|
||||
nn.MaxPool2d(2),
|
||||
nn.Conv2d(64, 128, 3, padding='same'),
|
||||
nn.ReLU(),
|
||||
nn.MaxPool2d(2),
|
||||
nn.Flatten(),
|
||||
nn.Linear(1152, 256),
|
||||
nn.ReLU(),
|
||||
nn.Linear(256, len(LABELS)),
|
||||
)
|
||||
state_dict = torch.load('pytorch_model.bin', map_location='cpu')
|
||||
model.load_state_dict(state_dict, strict=False)
|
||||
model.eval()
|
||||
```
|
||||
|
||||
## Step 2 — Defining a `predict` function
|
||||
|
||||
Next, you will need to define a function that takes in the *user input*, which in this case is a sketched image, and returns the prediction. The prediction should be returned as a dictionary whose keys are class name and values are confidence probabilities. We will load the class names from this [text file](https://huggingface.co/spaces/nateraw/quickdraw/blob/main/class_names.txt).
|
||||
|
||||
In the case of our pretrained model, it will look like this:
|
||||
|
||||
```python
|
||||
from pathlib import Path
|
||||
|
||||
LABELS = Path('class_names.txt').read_text().splitlines()
|
||||
|
||||
def predict(img):
|
||||
x = torch.tensor(img, dtype=torch.float32).unsqueeze(0).unsqueeze(0) / 255.
|
||||
with torch.no_grad():
|
||||
out = model(x)
|
||||
probabilities = torch.nn.functional.softmax(out[0], dim=0)
|
||||
values, indices = torch.topk(probabilities, 5)
|
||||
confidences = {LABELS[i]: v.item() for i, v in zip(indices, values)}
|
||||
return confidences
|
||||
```
|
||||
|
||||
Let's break this down. The function takes one parameters:
|
||||
|
||||
* `img`: the input image as a `numpy` array
|
||||
|
||||
Then, the function converts the image to a PyTorch `tensor`, passes it through the model, and returns:
|
||||
|
||||
* `confidences`: the top five predictions, as a dictionary whose keys are class labels and whose values are confidence probabilities
|
||||
|
||||
## Step 3 — Creating a Gradio Interface
|
||||
|
||||
Now that we have our predictive function set up, we can create a Gradio Interface around it.
|
||||
|
||||
In this case, the input component is a sketchpad. To create a sketchpad input, we can use the convenient string shortcut, `"sketchpad"` which creates a canvas for a user to draw on and handles the preprocessing to convert that to a numpy array.
|
||||
|
||||
The output component will be a `"label"`, which displays the top labels in a nice form.
|
||||
|
||||
Finally, we'll add one more parameter, setting `live=True`, which allows our interface to run in real time, adjusting its predictions every time a user draws on the sketchpad. The code for Gradio looks like this:
|
||||
|
||||
```python
|
||||
import gradio as gr
|
||||
|
||||
gr.Interface(fn=predict,
|
||||
inputs="sketchpad",
|
||||
outputs="label",
|
||||
live=True).launch()
|
||||
```
|
||||
|
||||
This produces the following interface, which you can try right here in your browser (try drawing something, like a "snake" or a "laptop"):
|
||||
|
||||
<iframe src="https://hf.space/gradioiframe/abidlabs/draw2/+" frameBorder="0" height="450" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>
|
||||
|
||||
----------
|
||||
|
||||
And you're done! That's all the code you need to build a Pictionary-style guessing app. Have fun and try to find some edge cases 🧐
|
||||
|
145
guides/creating_a_chatbot.md
Normal file
@ -0,0 +1,145 @@
|
||||
# How to Create a Chatbot
|
||||
|
||||
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
|
||||
## Introduction
|
||||
|
||||
Chatbots are widely studied in natural language processing (NLP) research and are a common use case of NLP in industry. Because chatbots are designed to be used directly by customers and end users, it is important to validate that chatbots are behaving as expected when confronted with a wide variety of input prompts. Using `gradio`, you can easily build a demo of your chatbot model and share that with a testing team, or test it yourself using an intuitive chatbot GUI.
|
||||
|
||||
This tutorial will show how to take a pretrained chatbot model and deploy it with a Gradio interface in 4 steps. The live chatbot interface that we create will look something like this (try it!):
|
||||
|
||||
<iframe src="https://hf.space/gradioiframe/abidlabs/chatbot-stylized/+" frameBorder="0" height="350" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>
|
||||
|
||||
Chatbots are *stateful*, meaning that the model's prediction can change depending on how the user has previously interacted with the model. So, in this tutorial, we will also cover how to use **state** with Gradio demos.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Make sure you have the `gradio` Python package already [installed](/getting_started). To use a pretrained chatbot model, also install `transformers` and `torch`.
|
||||
|
||||
## Step 1 — Setting up the Chatbot Model
|
||||
|
||||
First, you will need to have a chatbot model that you have either trained yourself or you will need to download a pretrained model. In this tutorial, we will use a pretrained chatbot model, `DialoGPT`, and its tokenizer from the [Hugging Face Hub](https://huggingface.co/microsoft/DialoGPT-medium), but you can replace this with your own model.
|
||||
|
||||
Here is the code to load `DialoGPT` from Hugging Face `transformers`.
|
||||
|
||||
```python
|
||||
from transformers import AutoModelForCausalLM, AutoTokenizer
|
||||
import torch
|
||||
|
||||
tokenizer = AutoTokenizer.from_pretrained("microsoft/DialoGPT-medium")
|
||||
model = AutoModelForCausalLM.from_pretrained("microsoft/DialoGPT-medium")
|
||||
```
|
||||
|
||||
## Step 2 — Defining a `predict` function
|
||||
|
||||
Next, you will need to define a function that takes in the *user input* as well as the previous *chat history* to generate a response.
|
||||
|
||||
In the case of our pretrained model, it will look like this:
|
||||
|
||||
```python
|
||||
def predict(input, history=[]):
|
||||
# tokenize the new input sentence
|
||||
new_user_input_ids = tokenizer.encode(input + tokenizer.eos_token, return_tensors='pt')
|
||||
|
||||
# append the new user input tokens to the chat history
|
||||
bot_input_ids = torch.cat([torch.LongTensor(history), new_user_input_ids], dim=-1)
|
||||
|
||||
# generate a response
|
||||
history = model.generate(bot_input_ids, max_length=1000, pad_token_id=tokenizer.eos_token_id).tolist()
|
||||
# convert the tokens to text, and then split the responses into lines
|
||||
response = tokenizer.decode(history[0]).replace("<|endoftext|>", "\n")
|
||||
|
||||
return response, history
|
||||
```
|
||||
|
||||
Let's break this down. The function takes two parameters:
|
||||
|
||||
* `input`: which is what the user enters (through the Gradio GUI) in a particular step of the conversation.
|
||||
* `history`: which represents the **state**, consisting of the list of user and bot responses. To create a stateful Gradio demo, we *must* pass in a parameter to represent the state, and we set the default value of this parameter to be the initial value of the state (in this case, the empty list since this is what we would like the chat history to be at the start).
|
||||
|
||||
Then, the function tokenizes the input and concatenates it with the tokens corresponding to the previous user and bot responses. Then, this is fed into the pretrained model to get a prediction. Finally, we do some cleaning up so that we can return two values from our function:
|
||||
|
||||
* `response`: which is a list of strings corresponding to all of the user and bot responses. This will be rendered as the output in the Gradio demo.
|
||||
* `history` variable, which is the token representation of all of the user and bot responses. In stateful Gradio demos, we *must* return the updated state at the end of the function.
|
||||
|
||||
## Step 3 — Creating a Gradio Interface
|
||||
|
||||
Now that we have our predictive function set up, we can create a Gradio Interface around it.
|
||||
|
||||
In this case, our function takes in two values, a text input and a state input. The corresponding input components in `gradio` are `"text"` and `"state"`.
|
||||
|
||||
The function also returns two values. For now, we will display the list of responses as `"text"` and use the `"state"` output component type for the second return value.
|
||||
|
||||
Note that the `"state"` input and output components are not displayed.
|
||||
|
||||
```python
|
||||
import gradio as gr
|
||||
|
||||
gr.Interface(fn=predict,
|
||||
inputs=["text", "state"],
|
||||
outputs=["text", "state"]).launch()
|
||||
```
|
||||
|
||||
This produces the following interface, which you can try right here in your browser (try typing in some simple greetings like "Hi!" to get started):
|
||||
|
||||
<iframe src="https://hf.space/gradioiframe/abidlabs/chatbot-minimal/+" frameBorder="0" height="350" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>
|
||||
|
||||
## Step 4 — Styling Your Interface
|
||||
|
||||
The problem is that the output of the chatbot looks pretty ugly. No problem, we can make it prettier by using a little bit of CSS. First, we modify our function to return a string of HTML components, instead of just text:
|
||||
|
||||
```python
|
||||
def predict(input, history=[]):
|
||||
# tokenize the new input sentence
|
||||
new_user_input_ids = tokenizer.encode(input + tokenizer.eos_token, return_tensors='pt')
|
||||
|
||||
# append the new user input tokens to the chat history
|
||||
bot_input_ids = torch.cat([torch.LongTensor(history), new_user_input_ids], dim=-1)
|
||||
|
||||
# generate a response
|
||||
history = model.generate(bot_input_ids, max_length=1000, pad_token_id=tokenizer.eos_token_id).tolist()
|
||||
|
||||
# convert the tokens to text, and then split the responses into lines
|
||||
response = tokenizer.decode(history[0]).split("<|endoftext|>")
|
||||
response.remove("")
|
||||
|
||||
# write some HTML
|
||||
html = "<div class='chatbot'>"
|
||||
for m, msg in enumerate(response):
|
||||
cls = "user" if m%2 == 0 else "bot"
|
||||
html += "<div class='msg {}'> {}</div>".format(cls, msg)
|
||||
html += "</div>"
|
||||
|
||||
return html, history
|
||||
```
|
||||
|
||||
Now, we change the first output component to be `"html"` instead, since now we are returning a string of HTML code. We also include some custom css to make the output prettier using the `css` parameter.
|
||||
|
||||
```python
|
||||
import gradio as gr
|
||||
|
||||
css = """
|
||||
.chatbox {display:flex;flex-direction:column}
|
||||
.msg {padding:4px;margin-bottom:4px;border-radius:4px;width:80%}
|
||||
.msg.user {background-color:cornflowerblue;color:white}
|
||||
.msg.bot {background-color:lightgray;align-self:self-end}
|
||||
"""
|
||||
|
||||
gr.Interface(fn=predict,
|
||||
inputs=[gr.inputs.Textbox(placeholder="How are you?"), "state"],
|
||||
outputs=["html", "state"],
|
||||
css=css).launch()
|
||||
```
|
||||
|
||||
Notice that we have also added a placeholder to the input `text` component by instantiating the `gr.inputs.Textbox()` class and passing in a `placeholder` value, and now we are good to go! Try it out below:
|
||||
|
||||
<iframe src="https://hf.space/gradioiframe/abidlabs/chatbot-stylized/+" frameBorder="0" height="350" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>
|
||||
|
||||
----------
|
||||
|
||||
And you're done! That's all the code you need to build an interface for your chatbot model. Here are some references that you may find useful:
|
||||
|
||||
* Gradio's ["Getting Started" guide]()
|
||||
* The [chatbot demo]() and [complete code]() (on Hugging Face Spaces)
|
||||
|
||||
|
@ -1,8 +1,10 @@
|
||||
## Getting Started
|
||||
|
||||
**Prerequisite**: Python 3.7+ and that's it!
|
||||
|
||||
### Quick Start
|
||||
|
||||
To get Gradio running with a simple example, follow these three steps:
|
||||
To get Gradio running with a simple "Hello, World" example, follow these three steps:
|
||||
|
||||
<span>1.</span> Install Gradio from pip.
|
||||
|
||||
@ -18,15 +20,15 @@ pip install gradio
|
||||
|
||||
{{ demos["hello_world"] }}
|
||||
|
||||
### The Interface
|
||||
### Understanding the `Interface` class
|
||||
|
||||
Gradio can wrap almost any Python function with an easy-to-use user interface. That function could be anything from a simple tax calculator to a pretrained machine learning model.
|
||||
Gradio can wrap almost any Python function with an easy-to-use user interface. In the example above, we saw a simple text-based function. But the function could be anything from image enhancer to a tax calculator to (most commonly) the prediction function of a pretrained machine learning model.
|
||||
|
||||
The core `Interface` class is initialized with three parameters:
|
||||
|
||||
- `fn`: the function to wrap
|
||||
- `inputs`: the input component type(s)
|
||||
- `outputs`: the output component type(s)
|
||||
- `inputs`: the input component type(s), e.g. `"image"` or `"audio"` ([see docs for complete list](/docs))
|
||||
- `outputs`: the output component type(s) e.g. `"image"` or `"label"` ([see docs for complete list](/docs))
|
||||
|
||||
With these three arguments, we can quickly create interfaces and `launch()` them. But what if you want to change how the UI components look or behave?
|
||||
|
||||
@ -84,14 +86,14 @@ Note there is no submit button, because the interface resubmits automatically on
|
||||
|
||||
### Using State
|
||||
|
||||
Your function may use data that persists beyond a single function call. If the data is something accessible to all function calls, you can create a global variable outside the function call and access it inside the function. For example, you may load a large model outside the function and use it inside the function so that every function call does not need to reload the model.
|
||||
Your function may use data that persists beyond a single function call. If the data is something accessible to all function calls and all users, you can create a global variable outside the function call and access it inside the function. For example, you may load a large model outside the function and use it inside the function so that every function call does not need to reload the model.
|
||||
|
||||
Another type of data persistence Gradio supports is session state, where data persists across multiple submits within a page load. To store data with this permanence, use `gr.get_state` and `gr.set_state` methods.
|
||||
Another type of data persistence Gradio supports is session **state**, where data persists across multiple submits within a page load. However, data is *not* shared between different users of your model. To store data in a session state, you need to do three things: (1) Pass in an extra parameter into your function, which represents the state of the interface. (2) At the end of the function, return the updated value of the state as an extra return value (3) Add the `'state'` input and `'state'` output components when creating your `Interface`. See the chatbot example below:
|
||||
|
||||
{{ code["chatbot"] }}
|
||||
{{ demos["chatbot"] }}
|
||||
|
||||
Notice how the state persists across submits within each page, but the state is not shared between the two pages.
|
||||
Notice how the state persists across submits within each page, but the state is not shared between the two pages. Some more points to note: you can pass in a default value to the state parameter, which is used as the initial value of the state. The state must be a something that can be serialized to a JSON format (e.g. a dictionary, a list, or a single value. Typically, objects will not work).
|
||||
|
||||
### Flagging
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
# Welcome to Gradio
|
||||
|
||||
Quickly create customizable UI components around your models. Gradio makes it easy for you to "play around" with your model in your browser by dragging-and-dropping in your own images, pasting your own text, recording your own voice, etc. and seeing what the model outputs.
|
||||
Quickly create beautiful user interfaces around your machine learning models. Gradio (pronounced GRAY-dee-oh) makes it easy for you to demo your model in your browser or let people "try it out" by dragging-and-dropping in their own images, pasting text, recording their own voice, etc. and seeing what the model outputs.
|
||||
|
||||

|
||||
|
||||
@ -12,7 +12,7 @@ Gradio is useful for:
|
||||
|
||||
* **Deploying** your models quickly with automatic shareable links and getting feedback on model performance
|
||||
|
||||
* **Debugging** your model interactively during development using built-in interpretation visualizations for any model
|
||||
* **Debugging** your model interactively during development using built-in manipulation and interpretation tools
|
||||
|
||||
**You can find an interactive version of the following Getting Started at [https://gradio.app/getting_started](https://gradio.app/getting_started).**
|
||||
|
||||
|
157
guides/using_flagging.md
Normal file
@ -0,0 +1,157 @@
|
||||
# Using Flagging
|
||||
|
||||
related_spaces: https://huggingface.co/spaces/aliabd/calculator-flagging-crowdsourced, https://huggingface.co/spaces/aliabd/calculator-flagging-options, https://huggingface.co/spaces/aliabd/calculator-flag-basic
|
||||
tags: FLAGGING, DATA
|
||||
|
||||
## The `flag` button
|
||||
|
||||
Underneath the output interfaces, there is a button marked `flag`. When a user testing your model sees input with interesting output, such as erroneous or unexpected model behaviour, they can flag the input for the interface creator to review.
|
||||
|
||||

|
||||
|
||||
There are four parameters `gr.Interface` that control how flagging works. We will go over them in greater detail.
|
||||
|
||||
* `allow_flagging`:
|
||||
* This parameter can be set to either `"manual"`, `"auto"`, or `"never"`.
|
||||
* `manual`: users will see a button to flag, and events are only flagged when it's clicked.
|
||||
* `auto`: users will not see a button to flag, but every event will be flagged automatically.
|
||||
* `never`: users will not see a button to flag, and no event will be flagged.
|
||||
* `flagging_options`:
|
||||
* This parameter takes a list of strings.
|
||||
* If provided, allows user to select from a list of options when flagging. Only applies if `allow_flagging` is `"manual"`.
|
||||
* The chosen option is then piped along with the input and output.
|
||||
-----> INSERT VIDEO FOR FLAGGING_OPTIONS
|
||||
* `flagging_dir`:
|
||||
* This parameter takes a string.
|
||||
* What to name the directory where flagged data is stored.
|
||||
* `flagging_callback`:
|
||||
* Using this parameter allows you to write custom code that gets run when the flag button is clicked
|
||||
* One example is setting it to `gr.HuggingFaceDatasetSaver` which can allow you to pipe any flagged data into a HuggingFace Dataset.
|
||||
|
||||
## The data:
|
||||
|
||||
Within the directory provided by the `flagging_dir` argument, a CSV file will log the flagged data.
|
||||
|
||||
Here's an example: The code below creates the calculator interface embedded below it:
|
||||
|
||||
```python
|
||||
import gradio as gr
|
||||
|
||||
|
||||
def calculator(num1, operation, num2):
|
||||
if operation == "add":
|
||||
return num1 + num2
|
||||
elif operation == "subtract":
|
||||
return num1 - num2
|
||||
elif operation == "multiply":
|
||||
return num1 * num2
|
||||
elif operation == "divide":
|
||||
return num1 / num2
|
||||
|
||||
|
||||
iface = gr.Interface(
|
||||
calculator,
|
||||
["number", gr.inputs.Radio(["add", "subtract", "multiply", "divide"]), "number"],
|
||||
"number",
|
||||
allow_flagging="manual"
|
||||
)
|
||||
|
||||
iface.launch()
|
||||
```
|
||||
|
||||
<iframe src="https://hf.space/gradioiframe/aliabd/calculator-flag-basic/+" frameBorder="0" height="500" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>
|
||||
|
||||
|
||||
When you click the flag button above, the directory where the interface was launched will include a new flagged subfolder, with a csv file inside it. This csv file includes all the data that was flagged.
|
||||
|
||||
```directory
|
||||
+-- flagged/
|
||||
| +-- logs.csv
|
||||
```
|
||||
_flagged/logs.csv_
|
||||
```csv
|
||||
num1,operation,num2,Output,timestamp
|
||||
5,add,7,12,2022-01-31 11:40:51.093412
|
||||
6,subtract,1.5,4.5,2022-01-31 03:25:32.023542
|
||||
```
|
||||
|
||||
If the interface involves file data, such as for Image and Audio components, folders will be created to store those flagged data as well. For example an `image` input to `image` output interface will create the following structure.
|
||||
|
||||
```directory
|
||||
+-- flagged/
|
||||
| +-- logs.csv
|
||||
| +-- image/
|
||||
| | +-- 0.png
|
||||
| | +-- 1.png
|
||||
| +-- Output/
|
||||
| | +-- 0.png
|
||||
| | +-- 1.png
|
||||
```
|
||||
_flagged/logs.csv_
|
||||
```csv
|
||||
im,Output timestamp
|
||||
im/0.png,Output/0.png,2022-02-04 19:49:58.026963
|
||||
im/1.png,Output/1.png,2022-02-02 10:40:51.093412
|
||||
```
|
||||
|
||||
If you wish for the user to provide a reason for flagging, you can pass a list of strings to the `flagging_options` argument of Interface. Users will have to select one of the strings when flagging, which will be saved as an additional column to the CSV.
|
||||
|
||||
If we go back to the calculator example, the following code will create the interface embedded below it.
|
||||
```python
|
||||
iface = gr.Interface(
|
||||
calculator,
|
||||
["number", gr.inputs.Radio(["add", "subtract", "multiply", "divide"]), "number"],
|
||||
"number",
|
||||
allow_flagging="manual",
|
||||
flagging_options=["wrong sign", "off by one", "other"]
|
||||
)
|
||||
|
||||
iface.launch()
|
||||
```
|
||||
<iframe src="https://hf.space/gradioiframe/aliabd/calculator-flagging-options/+" frameBorder="0" height="500" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>
|
||||
|
||||
When users click the flag button, the csv file will now include a column indicating the selected option.
|
||||
|
||||
_flagged/logs.csv_
|
||||
```csv
|
||||
num1,operation,num2,Output,flag,timestamp
|
||||
5,add,7,-12,wrong sign,2022-02-04 11:40:51.093412
|
||||
6,subtract,1.5,3.5,off by one,2022-02-04 11:42:32.062512
|
||||
```
|
||||
|
||||
## Doing more with the data
|
||||
|
||||
Suppose you want to take some action on the flagged data, instead of just saving it. Perhaps you want to trigger your model to retrain, or even just share it with others in a cloud dataset. We've made this super easy with the `flagging_callback` parameter.
|
||||
|
||||
For example, below we're going to pipe flagged data from our calculator example into a crowd-sourced Hugging Face Dataset.
|
||||
|
||||
```python
|
||||
import os
|
||||
|
||||
HF_TOKEN = os.getenv('HF_TOKEN')
|
||||
hf_writer = gr.HuggingFaceDatasetSaver(HF_TOKEN, "crowdsourced-calculator-demo")
|
||||
|
||||
iface = gr.Interface(
|
||||
calculator,
|
||||
["number", gr.inputs.Radio(["add", "subtract", "multiply", "divide"]), "number"],
|
||||
"number",
|
||||
allow_flagging="manual",
|
||||
flagging_options=["wrong sign", "off by one", "other"],
|
||||
flagging_callback=hf_writer
|
||||
)
|
||||
|
||||
iface.launch()
|
||||
```
|
||||
<iframe src="https://hf.space/gradioiframe/aliabd/calculator-flagging-crowdsourced/+" frameBorder="0" height="500" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>
|
||||
|
||||
You can now see all the examples flagged above in this [public HF dataset](https://huggingface.co/datasets/aliabd/crowdsourced-calculator-demo/blob/main/data.csv).
|
||||
|
||||

|
||||
|
||||
We created the `gr.HuggingFaceDatasetSaver` class, but you can pass your own custom class as long as it inherits from `FLaggingCallback` defined in [this file](https://github.com/gradio-app/gradio/blob/master/gradio/flagging.py). If you create a cool callback, please contribute it to the repo!
|
||||
|
||||
## Privacy
|
||||
|
||||
Please make sure your users understand when the data they submit is being saved, and what you plan on doing with it. This is especially important when you use `allow_flagging=auto`. We suggest including this info in the description so that it's read before the interface.
|
||||
|
||||
### That's all! Happy building :)
|
2857
website/homepage/package-lock.json
generated
@ -1,3 +1,4 @@
|
||||
import html
|
||||
import inspect
|
||||
import json
|
||||
import os
|
||||
@ -11,11 +12,14 @@ from gradio.inputs import InputComponent
|
||||
from gradio.interface import Interface
|
||||
from gradio.outputs import OutputComponent
|
||||
|
||||
import cairo
|
||||
|
||||
GRADIO_DIR = "../../"
|
||||
GRADIO_GUIDES_DIR = os.path.join(GRADIO_DIR, "guides")
|
||||
GRADIO_DEMO_DIR = os.path.join(GRADIO_DIR, "demo")
|
||||
|
||||
guide_names = [] # used for dropdown in navbar
|
||||
|
||||
guides = []
|
||||
for guide in sorted(os.listdir(GRADIO_GUIDES_DIR)):
|
||||
if "template" in guide or "getting_started" in guide:
|
||||
continue
|
||||
@ -23,8 +27,25 @@ for guide in sorted(os.listdir(GRADIO_GUIDES_DIR)):
|
||||
pretty_guide_name = " ".join(
|
||||
[word.capitalize().replace("Ml", "ML") for word in guide_name.split("_")]
|
||||
)
|
||||
guide_names.append((guide_name, pretty_guide_name))
|
||||
with open(os.path.join(GRADIO_GUIDES_DIR, guide), "r") as f:
|
||||
guide_content = f.read()
|
||||
|
||||
tags = []
|
||||
if "tags: " in guide_content:
|
||||
tags = guide_content.split("tags: ")[1].split("\n")[0].split(", ")
|
||||
tags = tags[:3]
|
||||
tags = " • ".join(tags)
|
||||
|
||||
guide_dict = {
|
||||
"guide_name": guide_name,
|
||||
"pretty_guide_name": pretty_guide_name,
|
||||
"guide_content": guide_content,
|
||||
"tags": tags
|
||||
}
|
||||
guides.append(guide_dict)
|
||||
|
||||
with open("src/navbar.html", encoding="utf-8") as navbar_file:
|
||||
navbar_html = navbar_file.read()
|
||||
|
||||
def render_index():
|
||||
os.makedirs("generated", exist_ok=True)
|
||||
@ -34,20 +55,89 @@ def render_index():
|
||||
requests.get("https://api.github.com/repos/gradio-app/gradio").json()[
|
||||
"stargazers_count"
|
||||
]
|
||||
)
|
||||
)
|
||||
with open("src/index_template.html", encoding="utf-8") as template_file:
|
||||
template = Template(template_file.read())
|
||||
output_html = template.render(
|
||||
tweets=tweets, star_count=star_count, guide_names=guide_names
|
||||
)
|
||||
with open(
|
||||
os.path.join("generated", "index.html"), "w", encoding="utf-8"
|
||||
) as generated_template:
|
||||
output_html = template.render(tweets=tweets, star_count=star_count,
|
||||
navbar_html=navbar_html)
|
||||
with open(os.path.join("generated", "index.html"), "w", encoding='utf-8') as generated_template:
|
||||
generated_template.write(output_html)
|
||||
|
||||
|
||||
def render_guides_main():
|
||||
with open("src/guides_main_template.html", encoding='utf-8') as template_file:
|
||||
template = Template(template_file.read())
|
||||
output_html = template.render(guides=guides, navbar_html=navbar_html)
|
||||
with open(os.path.join("generated", "guides.html"), "w", encoding='utf-8') as generated_template:
|
||||
generated_template.write(output_html)
|
||||
|
||||
|
||||
def add_line_breaks(text, num_char):
|
||||
if len(text) > num_char:
|
||||
text_list = text.split()
|
||||
text = ""
|
||||
total_count = 0
|
||||
count = 0
|
||||
for word in text_list:
|
||||
if total_count > num_char*5:
|
||||
text = text[:-1]
|
||||
text += "..."
|
||||
break
|
||||
count += len(word)
|
||||
if count > num_char:
|
||||
text += word + "\n"
|
||||
total_count += count
|
||||
count = 0
|
||||
else:
|
||||
text += word + " "
|
||||
total_count += len(word+" ")
|
||||
return text
|
||||
return text
|
||||
|
||||
|
||||
def generate_guide_meta_tags(title, tags, url, guide_path_name):
|
||||
surface = cairo.ImageSurface.create_from_png("src/assets/img/guides/base-image.png")
|
||||
ctx = cairo.Context(surface)
|
||||
ctx.scale(500, 500)
|
||||
ctx.set_source_rgba(0.611764706,0.639215686,0.6862745098,1)
|
||||
ctx.select_font_face("Arial", cairo.FONT_SLANT_NORMAL,
|
||||
cairo.FONT_WEIGHT_NORMAL)
|
||||
ctx.set_font_size(0.15)
|
||||
ctx.move_to(0.3, 0.55)
|
||||
ctx.show_text("gradio.app/guides")
|
||||
if len(tags) > 5:
|
||||
tags = tags[:5]
|
||||
tags = " | ".join(tags)
|
||||
ctx.move_to(0.3, 2.2)
|
||||
ctx.show_text(tags)
|
||||
ctx.set_source_rgba(0.352941176,0.352941176,0.352941176,1)
|
||||
ctx.set_font_size(0.28)
|
||||
title_breaked = add_line_breaks(title, 10)
|
||||
|
||||
if "\n" in title_breaked:
|
||||
for i, t in enumerate(title_breaked.split("\n")):
|
||||
ctx.move_to(0.3, 0.9+i*0.4)
|
||||
ctx.show_text(t)
|
||||
else:
|
||||
ctx.move_to(0.3, 1.0)
|
||||
ctx.show_text(title_breaked)
|
||||
image_path = f"src/assets/img/guides/{guide_path_name}.png"
|
||||
surface.write_to_png(image_path)
|
||||
load_path = f"/assets/img/guides/{guide_path_name}.png"
|
||||
meta_tags = f"""
|
||||
<title>{title}</title>
|
||||
<meta property="og:title" content="{title}" />
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="og:url" content="{url}" />
|
||||
<meta property="og:image" content="{load_path}" />
|
||||
<meta name="twitter:title" content="{title}">
|
||||
<meta name="twitter:image" content="{load_path}">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
"""
|
||||
return meta_tags
|
||||
|
||||
|
||||
def render_guides():
|
||||
guides = []
|
||||
for guide in os.listdir(GRADIO_GUIDES_DIR):
|
||||
if "template" in guide:
|
||||
continue
|
||||
@ -55,6 +145,26 @@ def render_guides():
|
||||
os.path.join(GRADIO_GUIDES_DIR, guide), encoding="utf-8"
|
||||
) as guide_file:
|
||||
guide_text = guide_file.read()
|
||||
|
||||
if "related_spaces: " in guide_text:
|
||||
spaces = guide_text.split("related_spaces: ")[1].split("\n")[0].split(", ")
|
||||
spaces_html = "<div id='spaces-holder'><a href='https://hf.co/spaces' target='_blank'><img src='/assets/img/spaces-logo.svg'></a><p style='margin: 0;display: inline;font-size: large;font-weight: 400;'>Related Spaces: </p>"
|
||||
for space in spaces:
|
||||
spaces_html += f"<div class='space-link'><a href='{space}' target='_blank'>{space[30:]}</a></div>"
|
||||
spaces_html += "</div>"
|
||||
guide_text = guide_text.split("related_spaces: ")[0] + spaces_html + "\n".join(guide_text.split("related_spaces: ")[1].split("\n")[1:])
|
||||
|
||||
tags = ""
|
||||
if "tags: " in guide_text:
|
||||
tags = guide_text.split("tags: ")[1].split("\n")[0].split(", ")
|
||||
guide_text = guide_text.split("tags: ")[0] + "\n" + "\n".join(guide_text.split("tags: ")[1].split("\n")[1:])
|
||||
|
||||
title = " ".join(
|
||||
[word.capitalize().replace("Ml", "ML").replace("Gan", "GAN") for word in guide[:-3].split("_")]
|
||||
)
|
||||
url = f"https://gradio.app/{guide[:-3]}/"
|
||||
meta_tags = generate_guide_meta_tags(title, tags, url, guide[:-3])
|
||||
|
||||
code_tags = re.findall(r'\{\{ code\["([^\s]*)"\] \}\}', guide_text)
|
||||
demo_names = re.findall(r'\{\{ demos\["([^\s]*)"\] \}\}', guide_text)
|
||||
code, demos = {}, {}
|
||||
@ -66,6 +176,7 @@ def render_guides():
|
||||
.replace("```csv\n", "<pre><code class='lang-bash'>")
|
||||
.replace("```", "</code></pre>")
|
||||
)
|
||||
|
||||
for code_src in code_tags:
|
||||
with open(os.path.join(GRADIO_DEMO_DIR, code_src, "run.py")) as code_file:
|
||||
python_code = code_file.read().replace(
|
||||
@ -74,17 +185,26 @@ def render_guides():
|
||||
code[code_src] = (
|
||||
"<pre><code class='lang-python'>" + python_code + "</code></pre>"
|
||||
)
|
||||
|
||||
|
||||
for demo_name in demo_names:
|
||||
demos[demo_name] = "<div id='interface_" + demo_name + "'></div>"
|
||||
guide_template = Template(guide_text)
|
||||
guide_output = guide_template.render(code=code, demos=demos)
|
||||
|
||||
# Escape HTML tags inside python code blocks so they show up properly
|
||||
pattern = "<code class='lang-python'>\n?((.|\n)*?)\n?</code>"
|
||||
guide_output = re.sub(pattern, lambda x: "<code class='lang-python'>" + html.escape(x.group(1)) + "</code>", guide_output)
|
||||
|
||||
output_html = markdown2.markdown(guide_output)
|
||||
output_html = output_html.replace("<a ", "<a target='blank' ")
|
||||
|
||||
for match in re.findall(r"<h3>([A-Za-z0-9 ]*)<\/h3>", output_html):
|
||||
output_html = output_html.replace(
|
||||
f"<h3>{match}</h3>",
|
||||
f"<h3 id={match.lower().replace(' ', '_')}>{match}</h3>",
|
||||
)
|
||||
|
||||
os.makedirs("generated", exist_ok=True)
|
||||
guide = guide[:-3]
|
||||
os.makedirs(os.path.join("generated", guide), exist_ok=True)
|
||||
@ -92,14 +212,10 @@ def render_guides():
|
||||
"src/guides_template.html", encoding="utf-8"
|
||||
) as general_template_file:
|
||||
general_template = Template(general_template_file.read())
|
||||
with open(
|
||||
os.path.join("generated", guide, "index.html"), "w", encoding="utf-8"
|
||||
) as generated_template:
|
||||
with open(os.path.join("generated", guide, "index.html"), "w", encoding='utf-8') as generated_template:
|
||||
output_html = general_template.render(
|
||||
template_html=output_html,
|
||||
demo_names=demo_names,
|
||||
guide_names=guide_names,
|
||||
)
|
||||
template_html=output_html, demo_names=demo_names,
|
||||
meta_tags=meta_tags, navbar_html=navbar_html)
|
||||
generated_template.write(output_html)
|
||||
|
||||
|
||||
@ -217,9 +333,8 @@ def render_docs():
|
||||
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, guide_names=guide_names
|
||||
)
|
||||
output_html = template.render(docs=docs, demo_links=demo_links,
|
||||
navbar_html=navbar_html)
|
||||
os.makedirs(os.path.join("generated", "docs"), exist_ok=True)
|
||||
with open(
|
||||
os.path.join("generated", "docs", "index.html"), "w"
|
||||
@ -234,7 +349,7 @@ def render_other():
|
||||
os.path.join("src/other_templates", template_filename)
|
||||
) as template_file:
|
||||
template = Template(template_file.read())
|
||||
output_html = template.render(guide_names=guide_names)
|
||||
output_html = template.render()
|
||||
folder_name = template_filename[:-14]
|
||||
os.makedirs(os.path.join("generated", folder_name), exist_ok=True)
|
||||
with open(
|
||||
@ -245,6 +360,7 @@ def render_other():
|
||||
|
||||
if __name__ == "__main__":
|
||||
render_index()
|
||||
render_guides_main()
|
||||
render_guides()
|
||||
render_docs()
|
||||
render_other()
|
||||
|
@ -1,4 +1,5 @@
|
||||
markdown2
|
||||
jinja2
|
||||
requests
|
||||
pydrive
|
||||
pydrive
|
||||
pycairo
|
BIN
website/homepage/src/assets/img/guides/base-image.png
Normal file
After Width: | Height: | Size: 87 KiB |
After Width: | Height: | Size: 89 KiB |
BIN
website/homepage/src/assets/img/guides/creating_a_chatbot.png
Normal file
After Width: | Height: | Size: 75 KiB |
BIN
website/homepage/src/assets/img/guides/getting_started.png
Normal file
After Width: | Height: | Size: 68 KiB |
BIN
website/homepage/src/assets/img/guides/guides-meta.png
Normal file
After Width: | Height: | Size: 197 KiB |
BIN
website/homepage/src/assets/img/guides/using_flagging.png
Normal file
After Width: | Height: | Size: 67 KiB |
After Width: | Height: | Size: 1.3 MiB |
After Width: | Height: | Size: 146 KiB |
1
website/homepage/src/assets/img/just-logo.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="482.122" height="170.306" viewBox="0 0 127.561 45.06"><path style="fill:#f1bc8a;fill-opacity:1;stroke-width:.0943025" transform="matrix(-.8768 -.48087 0 1 -16.026 -117.419)" d="M-65.857 110.343h20.722v5.806h-20.722zM-65.857 100.267h20.722v5.806h-20.722z"/><path style="fill:#eba059;fill-opacity:1;stroke-width:.0942989" transform="matrix(.87677 -.4809 0 1 -16.026 -117.419)" d="M45.06 163.607h20.798v5.843H45.06z"/><path style="fill:#f1bc8a;fill-opacity:1;stroke-width:.0943025" transform="matrix(-.8768 -.48087 0 1 -16.026 -117.419)" d="M-45.06 120.27h20.8v5.843h-20.8z"/><path style="fill:#eba059;fill-opacity:1;stroke-width:.0944389" transform="matrix(.87755 -.47948 0 1 -16.026 -117.419)" d="M24.237 143.556h20.86v5.843h-20.86z"/><path style="fill:#eba059;fill-opacity:1;stroke-width:.0942989" transform="matrix(.87677 -.4809 0 1 -16.026 -117.419)" d="M45.06 173.683h20.798v5.843H45.06z"/><path style="fill:#f1bc8a;fill-opacity:1;stroke-width:.0943025" transform="matrix(-.8768 -.48087 0 1 -16.026 -117.419)" d="M-45.06 130.346h20.8v5.843h-20.8z"/><path style="fill:#eba059;fill-opacity:1;stroke-width:.0944389" transform="matrix(.87755 -.47948 0 1 -16.026 -117.419)" d="M24.237 153.633h20.86v5.843h-20.86z"/></svg>
|
After Width: | Height: | Size: 1.2 KiB |
1
website/homepage/src/assets/img/spaces-logo.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg class="mr-1 text-gray-400" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" viewBox="0 0 32 32"><path d="M7.81 18.746v5.445h5.444v-5.445H7.809Z" fill="#FF3270"/><path d="M18.746 18.746v5.445h5.444v-5.445h-5.444Z" fill="#861FFF"/><path d="M7.81 7.81v5.444h5.444V7.81H7.809Z" fill="#097EFF"/><path fill-rule="evenodd" clip-rule="evenodd" d="M4 6.418A2.418 2.418 0 0 1 6.418 4h8.228c1.117 0 2.057.757 2.334 1.786a6.532 6.532 0 0 1 9.234 9.234A2.419 2.419 0 0 1 28 17.355v8.227A2.418 2.418 0 0 1 25.582 28H6.417A2.418 2.418 0 0 1 4 25.582V6.417ZM7.81 7.81v5.444h5.444V7.81H7.81Zm0 16.38v-5.444h5.444v5.445H7.81Zm10.936 0v-5.444h5.445v5.445h-5.445Zm0-13.658a2.722 2.722 0 1 1 5.445 0 2.722 2.722 0 0 1-5.445 0Z"/><path d="M21.468 7.81a2.722 2.722 0 1 0 0 5.444 2.722 2.722 0 0 0 0-5.444Z" fill="#FFD702"/></svg>
|
After Width: | Height: | Size: 816 B |
@ -49,41 +49,7 @@
|
||||
</head>
|
||||
|
||||
<body class="bg-white text-gray-900 text-md sm:text-lg">
|
||||
<div class="w-full bg-yellow-200 text-center p-3">
|
||||
🎉 We are joining Hugging Face!
|
||||
<a class="font-semibold underline" href="/joining-huggingface/">Read our announcement here.</a> 🤗
|
||||
</div>
|
||||
<header class="container mx-auto p-4 flex justify-between items-center gap-4 flex-col sm:flex-row">
|
||||
<a href="/">
|
||||
<img src="/assets/img/logo.svg" class="h-10">
|
||||
</a>
|
||||
<nav class="flex gap-12 sm:gap-16">
|
||||
<div class="group relative cursor-pointer font-semibold flex items-center gap-1" onClick="document.querySelector('.guide-menu').classList.toggle('flex'); document.querySelector('.guide-menu').classList.toggle('hidden');">
|
||||
Guides
|
||||
<svg class="h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z" />
|
||||
</svg>
|
||||
<div class="hidden group-hover:flex group-active:flex flex-col absolute top-6 left-0 bg-white shadow w-52">
|
||||
{% for guide_name, pretty_guide_name in guide_names %}
|
||||
<a class="link px-4 py-2 inline-block hover:bg-gray-100" href="/{{ guide_name }}">{{ pretty_guide_name }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<a class="link" href="/docs">Docs</a>
|
||||
<div class="group relative cursor-pointer font-semibold flex items-center gap-1" onClick="document.querySelector('.help-menu').classList.toggle('flex'); document.querySelector('.help-menu').classList.toggle('hidden');">
|
||||
Help
|
||||
<svg class="h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z" />
|
||||
</svg>
|
||||
<div class="help-menu hidden group-hover:flex flex-col absolute top-6 right-0 bg-white shadow w-52">
|
||||
<a class="link px-4 py-2 inline-block hover:bg-gray-100"
|
||||
href="https://github.com/gradio-app/gradio/issues/new/choose">File an Issue</a>
|
||||
<a class="link px-4 py-2 inline-block hover:bg-gray-100"
|
||||
href="https://github.com/gradio-app/gradio/discussions">Discussions</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
{{navbar_html|safe}}
|
||||
<div class="container mx-auto px-4">
|
||||
<main class="container flex gap-4">
|
||||
<div class="h-screen leading-relaxed sticky top-0 bg-gray-50 p-4 text-md overflow-y-auto hidden md:block" style="width: 64rem">
|
||||
|
274
website/homepage/src/guides_main_template.html
Normal file
@ -0,0 +1,274 @@
|
||||
<!doctype html>
|
||||
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>Gradio Guides</title>
|
||||
<meta name="description" content="How to use Gradio">
|
||||
<meta name="author" content="How to use Gradio">
|
||||
<meta property="og:title" content="Gradio Guides">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://gradio.app/guides">
|
||||
<meta property="og:description" content="How to use Gradio">
|
||||
<meta property="og:image" content="/assets/img/guides/guides-meta.png">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:creator" content="@teamGradio">
|
||||
<meta name="twitter:title" content="Gradio Guides">
|
||||
<meta name="twitter:description" content="How to use Gradio">
|
||||
<meta name="twitter:image" content="/assets/img/guides/guides-meta.png">
|
||||
|
||||
<link rel="icon" type="image/png" href="/assets/img/logo.png">
|
||||
<link href="/gradio_static/static/bundle.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<link rel="stylesheet" href="/assets/prism.css">
|
||||
<style>
|
||||
html {
|
||||
font-size: 16px !important;
|
||||
}
|
||||
header {
|
||||
flex
|
||||
}
|
||||
.prose p > img {
|
||||
margin: 0 auto;
|
||||
width: 600px;
|
||||
max-width: 100%;
|
||||
}
|
||||
.prose {
|
||||
max-width: none !important;
|
||||
}
|
||||
.gradio_page .footer {
|
||||
display: none !important;
|
||||
}
|
||||
.prose .code, .prose pre {
|
||||
background-color: whitesmoke;
|
||||
color: black;
|
||||
}
|
||||
.prose table {
|
||||
width: auto;
|
||||
}
|
||||
h3 a {
|
||||
display: inline-block;
|
||||
}
|
||||
h3 a img {
|
||||
height: 14px;
|
||||
margin: 0 4px !important;
|
||||
}
|
||||
|
||||
ul {
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
.search-for-tags {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.searchTerm {
|
||||
width: 100%;
|
||||
border: 1px solid #bebfc3;
|
||||
padding: 5px;
|
||||
height: 50px;
|
||||
border-radius: 25px;
|
||||
outline: none;
|
||||
color: #9ca3af;
|
||||
text-align: center;
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
.searchTerm:focus{
|
||||
color: #101827;
|
||||
}
|
||||
|
||||
.wrap-for-search{
|
||||
padding-top: 20px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
#no-guides-found {
|
||||
text-align: center;
|
||||
color: #9ca3af;
|
||||
font-size: larger;
|
||||
}
|
||||
|
||||
.counter-box {
|
||||
width: 100%;
|
||||
color: #ec8f14;
|
||||
}
|
||||
|
||||
.counter{
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.counter-parent {
|
||||
text-align: center;
|
||||
font-size: smaller;
|
||||
}
|
||||
.counter-child {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.tags-holder {
|
||||
text-align: left;
|
||||
font-size: small;
|
||||
font-weight: 300;
|
||||
color: #5a5a5a;
|
||||
}
|
||||
|
||||
</style>
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-156449732-1"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag() {
|
||||
dataLayer.push(arguments);
|
||||
}
|
||||
gtag('js', new Date());
|
||||
gtag('config', 'UA-156449732-1');
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body class="bg-white text-gray-900 text-md sm:text-lg">
|
||||
{{navbar_html|safe}}
|
||||
<div class="container mx-auto px-4 mb-12" style="width: 80%;">
|
||||
|
||||
<div class="wrap-for-search">
|
||||
<div class="search-for-tag">
|
||||
<input id="search-by-tag" type="text" class="searchTerm" placeholder="What do you want to build..." onfocus="this.placeholder = ''"
|
||||
onblur="this.placeholder = 'What do you want to build...'" autocomplete="off" onkeyup="search(this.value);">
|
||||
</div>
|
||||
</div>
|
||||
<div class="counter-parent">
|
||||
<div class="counter-child">
|
||||
<h2 style="font-weight: 100; color: #444343; padding-bottom: 20px;">Search through </h2>
|
||||
</div>
|
||||
<div class="counter-child">
|
||||
<div class="counter-box">
|
||||
<div id="counter" class="counter"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="counter-child">
|
||||
<h2 style="font-weight: 100; color: #444343; padding-bottom: 20px;"> Guides. <a class="link" href="https://github.com/gradio-app/gradio/tree/master/guides" style="color: grey;">Contribute here</a></h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="guide-list" class="grid grid-cols-1 lg:grid-cols-3 gap-12 pt-12">
|
||||
{% for guide in guides %}
|
||||
<a class="flex lg:col-span-1 flex-col group overflow-hidden relative rounded-xl shadow-sm hover:shadow-alternate transition-shadow" href="/{{ guide.guide_name }}">
|
||||
<div class="flex flex-col p-4" style="
|
||||
height: min-content;">
|
||||
<h2 class="font-semibold group-hover:underline text-xl" style="color: #5a5a5a;">{{ guide.pretty_guide_name }}
|
||||
</h2>
|
||||
<div class="tags-holder">
|
||||
<p>
|
||||
{{ guide.tags }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div id="no-guides-found" style="display: none">
|
||||
<p>
|
||||
Sorry, we couldn't find a guide :(
|
||||
</p>
|
||||
<p>
|
||||
Try a different term, or <a class="link" href="/docs">see the docs</a>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<footer class="container mx-auto p-4 flex justify-between items-center">
|
||||
<div class="h-10"></div>
|
||||
<div class="flex gap-4">
|
||||
<a class="hover:opacity-75 transition" href="https://twitter.com/Gradio">
|
||||
<img src="/assets/img/twitter.svg" class="h-8">
|
||||
</a>
|
||||
<a class="hover:opacity-75 transition" href="https://github.com/gradio-app/gradio">
|
||||
<img src="/assets/img/github.svg" class="h-8">
|
||||
</a>
|
||||
</div>
|
||||
<div class="h-10"></div>
|
||||
</footer>
|
||||
<script>
|
||||
var div, li, a;
|
||||
div = document.getElementById("guide-list");
|
||||
a = div.getElementsByTagName('a');
|
||||
|
||||
var backgrounds = ['linear-gradient(90deg, rgba(255,254,188,0.3) 0%, rgba(255,255,240,0.3) 100%)',
|
||||
'linear-gradient(90deg, rgba(255,205,188,0.3) 0%, rgba(255,253,253,0.3) 100%)',
|
||||
'linear-gradient(90deg, rgba(255,188,188,0.3) 0%, rgba(255,238,238,0.3) 100%)',
|
||||
'linear-gradient(90deg, rgba(194,255,169,0.3) 0%, rgba(243,255,238,0.3) 100%)',
|
||||
'linear-gradient(90deg, rgba(169,255,237,0.3) 0%, rgba(230,255,250,0.3) 100%)',
|
||||
'linear-gradient(90deg, rgba(182,169,255,0.3) 0%, rgba(237,234,255,0.3) 100%)',
|
||||
'linear-gradient(90deg, rgba(255,183,245,0.3) 0%, rgba(255,234,252,0.3) 100%)']
|
||||
|
||||
function shuffleBackgrounds(array) {
|
||||
for (let i = array.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[array[i], array[j]] = [array[j], array[i]];
|
||||
}
|
||||
}
|
||||
|
||||
shuffleBackgrounds(backgrounds);
|
||||
|
||||
color_counter = 0
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
a[i].style.background = backgrounds[color_counter];
|
||||
color_counter += 1
|
||||
if (color_counter == backgrounds.length) {
|
||||
color_counter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function animateValue(obj, start, end, duration) {
|
||||
let startTimestamp = null;
|
||||
const step = (timestamp) => {
|
||||
if (!startTimestamp) startTimestamp = timestamp;
|
||||
const progress = Math.min((timestamp - startTimestamp) / duration, 1);
|
||||
obj.innerHTML = Math.floor(progress * (end - start) + start);
|
||||
if (progress < 1) {
|
||||
window.requestAnimationFrame(step);
|
||||
}
|
||||
};
|
||||
window.requestAnimationFrame(step);
|
||||
}
|
||||
|
||||
const obj = document.getElementById("counter");
|
||||
animateValue(obj, 0, a.length, 1000);
|
||||
|
||||
function search(term) {
|
||||
var filter, i, txtValue;
|
||||
filter = term.toUpperCase();
|
||||
var counter = 0
|
||||
{% for guide in guides %}
|
||||
txtValue = a[{{ loop.index - 1 }}].innerText;
|
||||
guideContent = {{ guide.guide_content|tojson|safe }}
|
||||
if (txtValue.toUpperCase().indexOf(filter) > -1 || guideContent.toUpperCase().indexOf(filter) > -1) {
|
||||
a[{{ loop.index - 1}}].style.display = "";
|
||||
} else {
|
||||
a[{{ loop.index - 1 }}].style.display = "none";
|
||||
counter++;
|
||||
}
|
||||
{% endfor %}
|
||||
ngf = document.getElementById("no-guides-found");
|
||||
if (counter == a.length) {
|
||||
ngf.style.display = "";
|
||||
} else {
|
||||
ngf.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
<script src="/assets/prism.js"></script>
|
||||
<script>
|
||||
window.gradio_mode = "website";
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
@ -5,20 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>Gradio</title>
|
||||
<meta name="description" content="Start building machine learning web apps in 5 lines of pure Python!">
|
||||
<meta name="author" content="Getting Started with Gradio">
|
||||
<meta property="og:title" content="Gradio">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://gradio.app/">
|
||||
<meta property="og:description" content="Start building machine learning web apps in 5 lines of pure Python!">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:creator" content="@teamGradio">
|
||||
<meta name="twitter:title" content="Getting Started with Gradio">
|
||||
<meta name="twitter:description" content="Start building machine learning web apps in 5 lines of pure Python!">
|
||||
<meta name="twitter:image" content="https://gradio.app/static/home/img/social-cheetah.jpg">
|
||||
|
||||
{{ meta_tags|safe }}
|
||||
<link rel="icon" type="image/png" href="/assets/img/logo.png">
|
||||
<link href="/gradio_static/build/themes.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
@ -57,6 +44,37 @@
|
||||
height: 14px;
|
||||
margin: 0 4px !important;
|
||||
}
|
||||
#guide-template{
|
||||
margin-top:30px;
|
||||
}
|
||||
.prose p > img {
|
||||
margin: 0;
|
||||
width: fit-content;
|
||||
}
|
||||
.prose h1 {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
|
||||
.space-link {
|
||||
display: inline-block;
|
||||
margin: 4px;
|
||||
padding-left: 6px;
|
||||
padding-right: 6px;
|
||||
border-radius: 7px;
|
||||
}
|
||||
|
||||
.space-link a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#spaces-holder img {
|
||||
display: inherit;
|
||||
width: 2.5%;
|
||||
margin: 0;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-156449732-1"></script>
|
||||
<script>
|
||||
@ -70,45 +88,11 @@
|
||||
</head>
|
||||
|
||||
<body class="bg-white text-gray-900 text-md sm:text-lg">
|
||||
<div class="w-full bg-yellow-200 text-center p-3">
|
||||
🎉 We are joining Hugging Face!
|
||||
<a class="font-semibold underline" href="/joining-huggingface/">Read our announcement here.</a> 🤗
|
||||
</div>
|
||||
<header class="container mx-auto p-4 flex justify-between items-center gap-4 flex-col sm:flex-row">
|
||||
<a href="/">
|
||||
<img src="/assets/img/logo.svg" class="h-10">
|
||||
</a>
|
||||
<nav class="flex gap-12 sm:gap-16">
|
||||
<div class="group relative cursor-pointer font-semibold flex items-center gap-1" onClick="document.querySelector('.guide-menu').classList.toggle('flex'); document.querySelector('.guide-menu').classList.toggle('hidden');">
|
||||
Guides
|
||||
<svg class="h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z" />
|
||||
</svg>
|
||||
<div class="hidden group-hover:flex flex-col absolute top-6 left-0 bg-white shadow w-52">
|
||||
{% for guide_name, pretty_guide_name in guide_names %}
|
||||
<a class="link px-4 py-2 inline-block hover:bg-gray-100" href="/{{ guide_name }}">{{ pretty_guide_name }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<a class="link" href="/docs">Docs</a>
|
||||
<div class="group relative cursor-pointer font-semibold flex items-center gap-1" onClick="document.querySelector('.help-menu').classList.toggle('flex'); document.querySelector('.help-menu').classList.toggle('hidden');">
|
||||
Help
|
||||
<svg class="h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z" />
|
||||
</svg>
|
||||
<div class="hidden group-hover:flex group-active:flex flex-col absolute top-6 right-0 bg-white shadow w-52">
|
||||
<a class="link px-4 py-2 inline-block hover:bg-gray-100"
|
||||
href="https://github.com/gradio-app/gradio/issues/new/choose">File an Issue</a>
|
||||
<a class="link px-4 py-2 inline-block hover:bg-gray-100"
|
||||
href="https://github.com/gradio-app/gradio/discussions">Discussions</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
<div class="container mx-auto px-4 mb-12">
|
||||
<div class="prose prose-lg max-w-none">
|
||||
{{navbar_html|safe}}
|
||||
<div class="container mx-auto max-w-4xl px-4 mb-12" id="guide-template">
|
||||
<div class="prose max-w-none">
|
||||
{{ template_html|safe }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="container mx-auto p-4 flex justify-between items-center">
|
||||
<img src="/assets/img/logo.svg" class="h-10">
|
||||
@ -139,6 +123,36 @@
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
var spacesHolder, spaces;
|
||||
spacesHolder = document.getElementById("spaces-holder");
|
||||
spaces = spacesHolder.getElementsByTagName('div');
|
||||
|
||||
var backgrounds = ['rgba(255,254,188,0.3)',
|
||||
'rgba(255,205,188,0.3)',
|
||||
'rgba(255,188,188,0.3)',
|
||||
'rgba(194,255,169,0.3)',
|
||||
'rgba(169,255,237,0.3)',
|
||||
'rgba(182,169,255,0.3)',
|
||||
'rgba(255,183,245,0.3)']
|
||||
|
||||
function shuffleBackgrounds(array) {
|
||||
for (let i = array.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[array[i], array[j]] = [array[j], array[i]];
|
||||
}
|
||||
}
|
||||
|
||||
shuffleBackgrounds(backgrounds);
|
||||
|
||||
color_counter = 0
|
||||
for (let i = 0; i < spaces.length; i++) {
|
||||
spaces[i].style.background = backgrounds[color_counter];
|
||||
color_counter += 1
|
||||
if (color_counter == backgrounds.length) {
|
||||
color_counter = 0;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
@ -35,54 +35,11 @@
|
||||
</head>
|
||||
|
||||
<body class="bg-white text-gray-900 text-md sm:text-lg">
|
||||
<div class="w-full bg-yellow-200 text-center p-3">
|
||||
<!-- <div class="w-full bg-yellow-200 text-center p-3">
|
||||
🎉 We are joining Hugging Face!
|
||||
<a class="font-semibold underline" href="/joining-huggingface/">Read our announcement here.</a> 🤗
|
||||
</div>
|
||||
<header class="container mx-auto p-4 flex justify-between items-center gap-4 flex-col sm:flex-row">
|
||||
<a href="/">
|
||||
<img src="/assets/img/logo.svg" class="h-10">
|
||||
</a>
|
||||
<nav class="flex gap-12 sm:gap-16">
|
||||
<!-- <div class="group relative cursor-pointer font-semibold flex items-center gap-1" onClick="document.querySelector('.guide-menu').classList.toggle('flex'); document.querySelector('.guide-menu').classList.toggle('hidden');">
|
||||
Guides
|
||||
<svg class="h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z" />
|
||||
</svg>
|
||||
<div class="guide-menu hidden group-hover:flex group-active:flex flex-col absolute top-6 left-0 bg-white shadow w-52">
|
||||
{% for guide_name, pretty_guide_name in guide_names %}
|
||||
<a class="link px-4 py-2 inline-block hover:bg-gray-100" href="/{{ guide_name }}">{{ pretty_guide_name }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div> -->
|
||||
<a class="link" href="/getting_started">Getting Started</a>
|
||||
<a class="link" href="/docs">Docs</a>
|
||||
<div class="group relative cursor-pointer font-semibold flex items-center gap-1" onClick="document.querySelector('.guide-menu').classList.toggle('flex'); document.querySelector('.guide-menu').classList.toggle('hidden');">
|
||||
Tutorials
|
||||
<svg class="h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z" />
|
||||
</svg>
|
||||
<div class="guide-menu hidden group-hover:flex group-active:flex flex-col absolute top-6 left-0 bg-white shadow w-52">
|
||||
{% for guide_name, pretty_guide_name in guide_names %}
|
||||
<a class="link px-4 py-2 inline-block hover:bg-gray-100" href="/{{ guide_name }}">{{ pretty_guide_name }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="group relative cursor-pointer font-semibold flex items-center gap-1" onClick="document.querySelector('.help-menu').classList.toggle('flex'); document.querySelector('.help-menu').classList.toggle('hidden');">
|
||||
Help
|
||||
<svg class="h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z" />
|
||||
</svg>
|
||||
<div class="help-menu hidden group-hover:flex flex-col absolute top-6 right-0 bg-white shadow w-52">
|
||||
<a class="link px-4 py-2 inline-block hover:bg-gray-100"
|
||||
href="https://github.com/gradio-app/gradio/issues/new/choose">File an Issue</a>
|
||||
<a class="link px-4 py-2 inline-block hover:bg-gray-100"
|
||||
href="https://github.com/gradio-app/gradio/discussions">Discussions</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
</div> -->
|
||||
{{navbar_html|safe}}
|
||||
<section
|
||||
class="flex flex-col gap-8 items-center justify-center p-4 bg-opacity-20 bg-cover bg-center"
|
||||
style="background-image: url('/assets/img/gallery.png');">
|
||||
|
26
website/homepage/src/navbar.html
Normal file
@ -0,0 +1,26 @@
|
||||
<header class="container mx-auto p-4 flex justify-between items-center gap-4 flex-col sm:flex-row">
|
||||
<a href="/">
|
||||
<img src="/assets/img/logo.svg" class="h-10">
|
||||
</a>
|
||||
<nav class="flex gap-12 sm:gap-16">
|
||||
<a class="link" href="/getting_started">⚡ Getting Started</a>
|
||||
<a class="link" href="/docs">✍️ Docs</a>
|
||||
<a class="link" href="/guides.html">💡 Guides</a>
|
||||
<div class="group relative cursor-pointer font-semibold flex items-center gap-1" onClick="document.querySelector('.help-menu').classList.toggle('flex'); document.querySelector('.help-menu').classList.toggle('hidden');">
|
||||
🖐 Community
|
||||
<svg class="h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z" />
|
||||
</svg>
|
||||
<div class="help-menu hidden group-hover:flex flex-col absolute top-6 right-0 bg-white shadow w-52">
|
||||
<a class="link px-4 py-2 inline-block hover:bg-gray-100"
|
||||
href="https://github.com/gradio-app/gradio/issues/new/choose" target="_blank">File an Issue</a>
|
||||
<a class="link px-4 py-2 inline-block hover:bg-gray-100"
|
||||
href="https://discuss.huggingface.co/c/gradio/26" target="_blank">Discuss</a>
|
||||
<a class="link px-4 py-2 inline-block hover:bg-gray-100" target="_blank"
|
||||
href="https://discord.gg/Vuujh8wt">Discord</a>
|
||||
<a class="link px-4 py-2 inline-block hover:bg-gray-100" target="_blank"
|
||||
href="https://gradio.curated.co/">Newsletter</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|