This commit is contained in:
Ali Abid 2022-07-22 12:49:21 +01:00
parent c2f2fd6d3c
commit 0920710779
46 changed files with 574 additions and 445 deletions

View File

@ -0,0 +1,124 @@
# Quickstart
Docs: examples
**Prerequisite**: Gradio requires Python 3.7 or above, that's it!
## What Problem is Gradio Solving?
One of the *best ways to share* your machine learning model, API, or data science workflow with others is to create an **interactive demo** that allows your users or colleagues to try out the demo in their browsers.
A web-based demo is great as it allows anyone who can use a browser (not just technical people) to intuitively try their own inputs and understand what you've built.
However, creating such web-based demos has traditionally been difficult, as you needed to know web hosting to serve the web app and web development (HTML, CSS, JavaScript) to build a GUI for your demo.
Gradio allows you to **build demos and share them, all in Python.** And usually in just a few lines of code! So let's get started.
## Hello, World
To get Gradio running with a simple "Hello, World" example, follow these three steps:
<span>1.</span> Install Gradio from pip. Note, the minimal supported Python version is 3.7.
```bash
pip install gradio
```
<span>2.</span> Run the code below as a Python script or in a Python notebook (or in a [colab notebook](https://colab.research.google.com/drive/18ODkJvyxHutTN0P5APWyGFO_xwNcgHDZ?usp=sharing)).
$code_hello_world
<span>3.</span> The demo below will appear automatically within the Python notebook, or pop in a browser on [http://localhost:7860](http://localhost:7860/) if running from a script.
$demo_hello_world
## The `Interface` class
You'll notice that in order to create the demo, we defined a `gradio.Interface` class. This `Interface` class can wrap almost any Python function with a user interface. In the example above, we saw a simple text-based function. But the function could be anything from music generator to a tax calculator to (most commonly) the prediction function of a pretrained machine learning model.
The core `Interface` class is initialized with three required parameters:
- `fn`: the function to wrap a UI around
- `inputs`: which component(s) to use for the input, e.g. `"text"` or `"image"` or `"audio"`
- `outputs`: which component(s) to use for the output, e.g. `"text"` or `"image"` `"label"`
Gradio includes more than 20 different components, most of which can be used as inputs or outputs. ([See docs for complete list](https://gradio.app/docs))
## Components Attributes
With these three arguments to `Interface`, you can quickly create user interfaces and `launch()` them. But what if you want to change how the UI components look or behave?
Let's say you want to customize the input text field - for example, you wanted it to be larger and have a text hint. If we use the actual input class for `Textbox` instead of using the string shortcut, you have access to much more customizability through component attributes.
$code_hello_world_2
$demo_hello_world_2
To see a list of all the components Gradio supports and what attributes you can use to customize them, check out the [Docs](https://gradio.app/docs).
## Multiple Inputs and Outputs
Let's say you had a much more complex function, with multiple inputs and outputs. In the example below, we define a function that takes a string, boolean, and number, and returns a string and number. Take a look how you pass a list of input and output components.
$code_hello_world_3
$demo_hello_world_3
You simply wrap the components in a list. Each component in the `inputs` list corresponds to one of the parameters of the function, in order. Each component in the `outputs` list corresponds to one of the values returned by the function, again in order.
## Images
Let's try an image-to-image function! When using the `Image` component, your function will receive a numpy array of your specified size, with the shape `(width, height, 3)`, where the last dimension represents the RGB values. We'll return an image as well in the form of a numpy array.
$code_sepia_filter
$demo_sepia_filter
Additionally, our `Image` input interface comes with an 'edit' button ✏️ which opens tools for cropping and zooming into images. We've found that manipulating images in this way can help reveal biases or hidden flaws in a machine learning model!
In addition to images, Gradio supports other media types, such as audio or video. Read about these in the [Docs](https://gradio.app/docs).
## DataFrames and Graphs
You can use Gradio to support inputs and outputs from your typical data libraries, such as numpy arrays, pandas dataframes, and plotly graphs. Take a look at the demo below (ignore the complicated data manipulation in the function!)
$code_sales_projections
$demo_sales_projections
## Blocks: More Flexibility and Control
Gradio offers two APIs to users: (1) **Interface**, a high level abstraction for creating demos (that we've been discussing so far), and (2) **Blocks**, a low-level API for designing web apps with more flexible layouts and data flows. Blocks allows you to do things like: group together related demos, change where components appear on the page, handle complex data flows (e.g. outputs can serve as inputs to other functions), and update properties/visibility of components based on user interaction -- still all in Python.
As an example, Blocks uses nested `with` statements in Python to lay out components on a page, like this:
$code_blocks_flipper
$demo_blocks_flipper
If you are interested in how Blocks works, [read its dedicated Guide](https://gradio.app/introduction_to_blocks/).
## Sharing Demos
Gradio demos can be easily shared publicly by setting `share=True` in the `launch()` method. Like this:
```python
gr.Interface(classify_image, "image", "label").launch(share=True)
```
This generates a public, shareable link that you can send to anybody! When you send this link, the user on the other side can try out the model in their browser. Because the processing happens on your device (as long as your device stays on!), you don't have to worry about any packaging any dependencies. A share link usually looks something like this: **XXXXX.gradio.app**. Although the link is served through a Gradio URL, we are only a proxy for your local server, and do not store any data sent through the interfaces.
Keep in mind, however, that these links are publicly accessible, meaning that anyone can use your model for prediction! Therefore, make sure not to expose any sensitive information through the functions you write, or allow any critical changes to occur on your device. If you set `share=False` (the default, except in colab notebooks), only a local link is created, which can be shared by [port-forwarding](https://www.ssh.com/ssh/tunneling/example) with specific users.
Share links expire after 72 hours. For permanent hosting, see the next section.
![Sharing diagram](/assets/guides/sharing.svg)
## Hosting Gradio Demo on Spaces
If you'd like to have a permanent link to your Gradio demo on the internet, use Huggingface Spaces. Hugging Face Spaces provides the infrastructure to permanently host your machine learning model for free!
You can either drag and drop a folder containing your Gradio model and all related files, or you can point Spaces to your Git repository and Spaces will pull the Gradio interface from there. See [Huggingface Spaces](http://huggingface.co/spaces/) for more information.
![Hosting Demo](/assets/guides/hf_demo.gif)
## Next Steps
Now that you're familiar with the basics of Gradio, here are some good next steps:

View File

Before

Width:  |  Height:  |  Size: 397 KiB

After

Width:  |  Height:  |  Size: 397 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

Before

Width:  |  Height:  |  Size: 146 KiB

After

Width:  |  Height:  |  Size: 146 KiB

View File

Before

Width:  |  Height:  |  Size: 8.2 MiB

After

Width:  |  Height:  |  Size: 8.2 MiB

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Before

Width:  |  Height:  |  Size: 236 KiB

After

Width:  |  Height:  |  Size: 236 KiB

View File

@ -1,6 +1,5 @@
# Advanced Interface Features
Pinned: 1
Docs: series, parallel
**Prerequisite**: This Guide builds on the Quickstart. Make sure to [read the Quickstart first](/getting_started).

View File

@ -1,6 +1,5 @@
# Quickstart
Pinned: 0
Docs: examples
**Prerequisite**: Gradio requires Python 3.7 or above, that's it!

View File

@ -1,6 +1,5 @@
# Introduction to Gradio Blocks 🧱
Pinned: 2
Docs: update
Gradio is a Python library that allows you to quickly build web-based machine learning demos, data science dashboards, or other kinds of web apps, **entirely in Python**. These web apps can be launched from wherever you use Python (jupyter notebooks, colab notebooks, Python terminal, etc.) and shared with anyone instantly using Gradio's auto-generated share links.

View File

@ -1,4 +1,4 @@
lockfileVersion: 5.4
lockfileVersion: 5.3
importers:
@ -41,7 +41,7 @@ importers:
'@tailwindcss/forms': 0.5.0_tailwindcss@3.0.23
'@testing-library/dom': 8.11.3
'@testing-library/svelte': 3.1.0_svelte@3.47.0
'@testing-library/user-event': 13.5.0_gzufz4q333be4gqfrvipwvqt6a
'@testing-library/user-event': 13.5.0_@testing-library+dom@8.11.3
autoprefixer: 10.4.4_postcss@8.4.6
babylonjs: 4.2.2
babylonjs-loaders: 4.2.2
@ -53,13 +53,13 @@ importers:
postcss: 8.4.6
postcss-nested: 5.0.6_postcss@8.4.6
prettier: 2.6.2
prettier-plugin-svelte: 2.7.0_sqtt6dzjlskmywoml5ykunxlce
prettier-plugin-svelte: 2.7.0_prettier@2.6.2+svelte@3.47.0
sirv: 2.0.2
sirv-cli: 2.0.2
svelte: 3.47.0
svelte-check: 2.7.0_xf35j26wmvzqzzfwect6yhmkcm
svelte-check: 2.7.0_postcss@8.4.6+svelte@3.47.0
svelte-i18n: 3.3.13_svelte@3.47.0
svelte-preprocess: 4.10.6_xf35j26wmvzqzzfwect6yhmkcm
svelte-preprocess: 4.10.6_postcss@8.4.6+svelte@3.47.0
tailwindcss: 3.0.23_autoprefixer@10.4.4
tinyspy: 0.3.0
vite: 2.9.5
@ -127,7 +127,7 @@ importers:
'@gradio/video': link:../video
mime-types: 2.1.34
playwright: 1.22.2
svelte-i18n: 3.3.13
svelte-i18n: 3.3.13_svelte@3.47.0
packages/atoms:
specifiers:
@ -379,13 +379,13 @@ importers:
'@gradio/upload': link:../upload
'@gradio/video': link:../video
devDependencies:
'@sveltejs/adapter-auto': 1.0.0-next.53
'@sveltejs/kit': 1.0.0-next.318
'@sveltejs/adapter-auto': 1.0.0-next.63
'@sveltejs/kit': 1.0.0-next.318_svelte@3.47.0
autoprefixer: 10.4.2_postcss@8.4.6
postcss: 8.4.6
postcss-load-config: 3.1.1
svelte-check: 2.4.1_2y4otvh2n6klv6metqycpfiuzy
svelte-preprocess: 4.10.2_bw7ic75prjd4umr4fb55sbospu
svelte-check: 2.4.1_1fac02bafd0682bb46b2470c9627a189
svelte-preprocess: 4.10.2_7ea8a6d5efea3309cd53b5ce515684ac
tailwindcss: 3.0.23_autoprefixer@10.4.2
tslib: 2.3.1
typescript: 4.5.5
@ -924,6 +924,10 @@ packages:
to-fast-properties: 2.0.0
dev: false
/@cloudflare/workers-types/3.14.1:
resolution: {integrity: sha512-B1/plF62pt+H2IJHvApK8fdOJAVsvojvacuac8x8s+JIyqbropMyqNqHTKLm3YD8ZFLGwYeFTudU+PQ7vGvBdA==}
dev: true
/@formatjs/ecma402-abstract/1.11.3:
resolution: {integrity: sha512-kP/Buv5vVFMAYLHNvvUzr0lwRTU0u2WTy44Tqwku1X3C3lJ5dKqDCYVqA8wL+Y19Bq+MwHgxqd5FZJRCIsLRyQ==}
dependencies:
@ -1076,52 +1080,54 @@ packages:
estree-walker: 2.0.2
picomatch: 2.3.1
/@sveltejs/adapter-auto/1.0.0-next.53:
resolution: {integrity: sha512-LyaeU0rkcymGWvV/3K26AZxqG/+ZQHwa+hrx3xsbmOykjQ2WQPTXRVwmH23zV4A5ABvni76LRMsQOoqWzP3G9Q==}
/@sveltejs/adapter-auto/1.0.0-next.63:
resolution: {integrity: sha512-9KguXwROEJMyyoKrsizAilVSmtfWxEDn2Hbxk44SP8Kj5cgN7tFCxzbL2kmmqyV1CO1tOh5iNC2oWbyTfikXmw==}
dependencies:
'@sveltejs/adapter-cloudflare': 1.0.0-next.24
'@sveltejs/adapter-netlify': 1.0.0-next.66
'@sveltejs/adapter-vercel': 1.0.0-next.59
'@sveltejs/adapter-cloudflare': 1.0.0-next.30
'@sveltejs/adapter-netlify': 1.0.0-next.70
'@sveltejs/adapter-vercel': 1.0.0-next.65
transitivePeerDependencies:
- encoding
- supports-color
dev: true
/@sveltejs/adapter-cloudflare/1.0.0-next.24:
resolution: {integrity: sha512-g1QSrjWYjM6sfJB+pQn52EIfbVFjpk23GYsj5PLt2Gi3zRNfLRbpkFkPeyAOZbAfT4k/9lUqfLW+pkh+W3yxlg==}
/@sveltejs/adapter-cloudflare/1.0.0-next.30:
resolution: {integrity: sha512-jIclgb58n3Uoo8TTudXSa7wmLP7Rn/ESLQS+zOUe0xsti5DG/eDhELTnSvkoSa2lJY21ym5rej/GSERRyeuBVw==}
dependencies:
esbuild: 0.14.42
'@cloudflare/workers-types': 3.14.1
esbuild: 0.14.49
worktop: 0.8.0-next.14
dev: true
/@sveltejs/adapter-netlify/1.0.0-next.66:
resolution: {integrity: sha512-UypTRnTd+R1O6SaDdc8l3A3c9/mQF8xLNoVb3Ay5ipb7uPU5WmjVYjfLVGyeVy67gztFfeFC/9Esu4OI2Ayx1A==}
/@sveltejs/adapter-netlify/1.0.0-next.70:
resolution: {integrity: sha512-lIXY6KIgIFBz4+mdvilx9Ny8oFV7T2iVTKLirJayoI/SqPWGAcxklvWvjGfS4QT8rS9pWKDaKRUQM4M/gl8LlA==}
dependencies:
'@iarna/toml': 2.2.5
esbuild: 0.14.42
esbuild: 0.14.49
set-cookie-parser: 2.4.8
tiny-glob: 0.2.9
dev: true
/@sveltejs/adapter-vercel/1.0.0-next.59:
resolution: {integrity: sha512-1lq5IFLWiLUXmNJVUXjwaInDb07BJg5er43xlMilpFpTA9BZI2hqjYCgtdtk7O6ee5EYJk876b2riM1m+y1M4Q==}
/@sveltejs/adapter-vercel/1.0.0-next.65:
resolution: {integrity: sha512-RV3HL7Ic7pGgIoBSHPwN1pBX96Km1X683oHImPHAKX9h/WOvJZ3bY5+IWNRcR8tx9rPB5gfMRg+msvPSBr3RVw==}
dependencies:
'@vercel/nft': 0.20.1
esbuild: 0.14.42
esbuild: 0.14.49
transitivePeerDependencies:
- encoding
- supports-color
dev: true
/@sveltejs/kit/1.0.0-next.318:
/@sveltejs/kit/1.0.0-next.318_svelte@3.47.0:
resolution: {integrity: sha512-/M/XNvEqK71KCGro1xLuiUuklsMPe+G5DiVMs39tpfFIFhH4oCzAt+YBaIZDKORogGz3QDaYc5BV+eFv9E5cyw==}
engines: {node: '>=14.13'}
hasBin: true
peerDependencies:
svelte: ^3.44.0
dependencies:
'@sveltejs/vite-plugin-svelte': 1.0.0-next.41_vite@2.9.5
'@sveltejs/vite-plugin-svelte': 1.0.0-next.41_svelte@3.47.0+vite@2.9.5
sade: 1.8.1
svelte: 3.47.0
vite: 2.9.5
transitivePeerDependencies:
- diff-match-patch
@ -1131,7 +1137,7 @@ packages:
- supports-color
dev: true
/@sveltejs/vite-plugin-svelte/1.0.0-next.41_vite@2.9.5:
/@sveltejs/vite-plugin-svelte/1.0.0-next.41_svelte@3.47.0+vite@2.9.5:
resolution: {integrity: sha512-2kZ49mpi/YW1PIPvKaJNSSwIFgmw9QUf1+yaNa4U8yJD6AsfSHXAU3goscWbi1jfWnSg2PhvwAf+bvLCdp2F9g==}
engines: {node: ^14.13.1 || >= 16}
peerDependencies:
@ -1146,7 +1152,8 @@ packages:
debug: 4.3.4
kleur: 4.1.4
magic-string: 0.26.1
svelte-hmr: 0.14.11
svelte: 3.47.0
svelte-hmr: 0.14.11_svelte@3.47.0
vite: 2.9.5
transitivePeerDependencies:
- supports-color
@ -1222,7 +1229,7 @@ packages:
svelte: 3.47.0
dev: false
/@testing-library/user-event/13.5.0_gzufz4q333be4gqfrvipwvqt6a:
/@testing-library/user-event/13.5.0_@testing-library+dom@8.11.3:
resolution: {integrity: sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==}
engines: {node: '>=10', npm: '>=6'}
peerDependencies:
@ -2066,8 +2073,8 @@ packages:
requiresBuild: true
optional: true
/esbuild-android-64/0.14.42:
resolution: {integrity: sha512-P4Y36VUtRhK/zivqGVMqhptSrFILAGlYp0Z8r9UQqHJ3iWztRCNWnlBzD9HRx0DbueXikzOiwyOri+ojAFfW6A==}
/esbuild-android-64/0.14.49:
resolution: {integrity: sha512-vYsdOTD+yi+kquhBiFWl3tyxnj2qZJsl4tAqwhT90ktUdnyTizgle7TjNx6Ar1bN7wcwWqZ9QInfdk2WVagSww==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
@ -2083,8 +2090,8 @@ packages:
requiresBuild: true
optional: true
/esbuild-android-arm64/0.14.42:
resolution: {integrity: sha512-0cOqCubq+RWScPqvtQdjXG3Czb3AWI2CaKw3HeXry2eoA2rrPr85HF7IpdU26UWdBXgPYtlTN1LUiuXbboROhg==}
/esbuild-android-arm64/0.14.49:
resolution: {integrity: sha512-g2HGr/hjOXCgSsvQZ1nK4nW/ei8JUx04Li74qub9qWrStlysaVmadRyTVuW32FGIpLQyc5sUjjZopj49eGGM2g==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
@ -2100,8 +2107,8 @@ packages:
requiresBuild: true
optional: true
/esbuild-darwin-64/0.14.42:
resolution: {integrity: sha512-ipiBdCA3ZjYgRfRLdQwP82rTiv/YVMtW36hTvAN5ZKAIfxBOyPXY7Cejp3bMXWgzKD8B6O+zoMzh01GZsCuEIA==}
/esbuild-darwin-64/0.14.49:
resolution: {integrity: sha512-3rvqnBCtX9ywso5fCHixt2GBCUsogNp9DjGmvbBohh31Ces34BVzFltMSxJpacNki96+WIcX5s/vum+ckXiLYg==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
@ -2117,8 +2124,8 @@ packages:
requiresBuild: true
optional: true
/esbuild-darwin-arm64/0.14.42:
resolution: {integrity: sha512-bU2tHRqTPOaoH/4m0zYHbFWpiYDmaA0gt90/3BMEFaM0PqVK/a6MA2V/ypV5PO0v8QxN6gH5hBPY4YJ2lopXgA==}
/esbuild-darwin-arm64/0.14.49:
resolution: {integrity: sha512-XMaqDxO846srnGlUSJnwbijV29MTKUATmOLyQSfswbK/2X5Uv28M9tTLUJcKKxzoo9lnkYPsx2o8EJcTYwCs/A==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
@ -2134,8 +2141,8 @@ packages:
requiresBuild: true
optional: true
/esbuild-freebsd-64/0.14.42:
resolution: {integrity: sha512-75h1+22Ivy07+QvxHyhVqOdekupiTZVLN1PMwCDonAqyXd8TVNJfIRFrdL8QmSJrOJJ5h8H1I9ETyl2L8LQDaw==}
/esbuild-freebsd-64/0.14.49:
resolution: {integrity: sha512-NJ5Q6AjV879mOHFri+5lZLTp5XsO2hQ+KSJYLbfY9DgCu8s6/Zl2prWXVANYTeCDLlrIlNNYw8y34xqyLDKOmQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
@ -2151,8 +2158,8 @@ packages:
requiresBuild: true
optional: true
/esbuild-freebsd-arm64/0.14.42:
resolution: {integrity: sha512-W6Jebeu5TTDQMJUJVarEzRU9LlKpNkPBbjqSu+GUPTHDCly5zZEQq9uHkmHHl7OKm+mQ2zFySN83nmfCeZCyNA==}
/esbuild-freebsd-arm64/0.14.49:
resolution: {integrity: sha512-lFLtgXnAc3eXYqj5koPlBZvEbBSOSUbWO3gyY/0+4lBdRqELyz4bAuamHvmvHW5swJYL7kngzIZw6kdu25KGOA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
@ -2168,8 +2175,8 @@ packages:
requiresBuild: true
optional: true
/esbuild-linux-32/0.14.42:
resolution: {integrity: sha512-Ooy/Bj+mJ1z4jlWcK5Dl6SlPlCgQB9zg1UrTCeY8XagvuWZ4qGPyYEWGkT94HUsRi2hKsXvcs6ThTOjBaJSMfg==}
/esbuild-linux-32/0.14.49:
resolution: {integrity: sha512-zTTH4gr2Kb8u4QcOpTDVn7Z8q7QEIvFl/+vHrI3cF6XOJS7iEI1FWslTo3uofB2+mn6sIJEQD9PrNZKoAAMDiA==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
@ -2185,8 +2192,8 @@ packages:
requiresBuild: true
optional: true
/esbuild-linux-64/0.14.42:
resolution: {integrity: sha512-2L0HbzQfbTuemUWfVqNIjOfaTRt9zsvjnme6lnr7/MO9toz/MJ5tZhjqrG6uDWDxhsaHI2/nsDgrv8uEEN2eoA==}
/esbuild-linux-64/0.14.49:
resolution: {integrity: sha512-hYmzRIDzFfLrB5c1SknkxzM8LdEUOusp6M2TnuQZJLRtxTgyPnZZVtyMeCLki0wKgYPXkFsAVhi8vzo2mBNeTg==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
@ -2202,8 +2209,8 @@ packages:
requiresBuild: true
optional: true
/esbuild-linux-arm/0.14.42:
resolution: {integrity: sha512-STq69yzCMhdRaWnh29UYrLSr/qaWMm/KqwaRF1pMEK7kDiagaXhSL1zQGXbYv94GuGY/zAwzK98+6idCMUOOCg==}
/esbuild-linux-arm/0.14.49:
resolution: {integrity: sha512-iE3e+ZVv1Qz1Sy0gifIsarJMQ89Rpm9mtLSRtG3AH0FPgAzQ5Z5oU6vYzhc/3gSPi2UxdCOfRhw2onXuFw/0lg==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
@ -2219,8 +2226,8 @@ packages:
requiresBuild: true
optional: true
/esbuild-linux-arm64/0.14.42:
resolution: {integrity: sha512-c3Ug3e9JpVr8jAcfbhirtpBauLxzYPpycjWulD71CF6ZSY26tvzmXMJYooQ2YKqDY4e/fPu5K8bm7MiXMnyxuA==}
/esbuild-linux-arm64/0.14.49:
resolution: {integrity: sha512-KLQ+WpeuY+7bxukxLz5VgkAAVQxUv67Ft4DmHIPIW+2w3ObBPQhqNoeQUHxopoW/aiOn3m99NSmSV+bs4BSsdA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
@ -2236,8 +2243,8 @@ packages:
requiresBuild: true
optional: true
/esbuild-linux-mips64le/0.14.42:
resolution: {integrity: sha512-QuvpHGbYlkyXWf2cGm51LBCHx6eUakjaSrRpUqhPwjh/uvNUYvLmz2LgPTTPwCqaKt0iwL+OGVL0tXA5aDbAbg==}
/esbuild-linux-mips64le/0.14.49:
resolution: {integrity: sha512-n+rGODfm8RSum5pFIqFQVQpYBw+AztL8s6o9kfx7tjfK0yIGF6tm5HlG6aRjodiiKkH2xAiIM+U4xtQVZYU4rA==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
@ -2253,8 +2260,8 @@ packages:
requiresBuild: true
optional: true
/esbuild-linux-ppc64le/0.14.42:
resolution: {integrity: sha512-8ohIVIWDbDT+i7lCx44YCyIRrOW1MYlks9fxTo0ME2LS/fxxdoJBwHWzaDYhjvf8kNpA+MInZvyOEAGoVDrMHg==}
/esbuild-linux-ppc64le/0.14.49:
resolution: {integrity: sha512-WP9zR4HX6iCBmMFH+XHHng2LmdoIeUmBpL4aL2TR8ruzXyT4dWrJ5BSbT8iNo6THN8lod6GOmYDLq/dgZLalGw==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
@ -2270,8 +2277,8 @@ packages:
requiresBuild: true
optional: true
/esbuild-linux-riscv64/0.14.42:
resolution: {integrity: sha512-DzDqK3TuoXktPyG1Lwx7vhaF49Onv3eR61KwQyxYo4y5UKTpL3NmuarHSIaSVlTFDDpcIajCDwz5/uwKLLgKiQ==}
/esbuild-linux-riscv64/0.14.49:
resolution: {integrity: sha512-h66ORBz+Dg+1KgLvzTVQEA1LX4XBd1SK0Fgbhhw4akpG/YkN8pS6OzYI/7SGENiN6ao5hETRDSkVcvU9NRtkMQ==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
@ -2287,8 +2294,8 @@ packages:
requiresBuild: true
optional: true
/esbuild-linux-s390x/0.14.42:
resolution: {integrity: sha512-YFRhPCxl8nb//Wn6SiS5pmtplBi4z9yC2gLrYoYI/tvwuB1jldir9r7JwAGy1Ck4D7sE7wBN9GFtUUX/DLdcEQ==}
/esbuild-linux-s390x/0.14.49:
resolution: {integrity: sha512-DhrUoFVWD+XmKO1y7e4kNCqQHPs6twz6VV6Uezl/XHYGzM60rBewBF5jlZjG0nCk5W/Xy6y1xWeopkrhFFM0sQ==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
@ -2304,8 +2311,8 @@ packages:
requiresBuild: true
optional: true
/esbuild-netbsd-64/0.14.42:
resolution: {integrity: sha512-QYSD2k+oT9dqB/4eEM9c+7KyNYsIPgzYOSrmfNGDIyJrbT1d+CFVKvnKahDKNJLfOYj8N4MgyFaU9/Ytc6w5Vw==}
/esbuild-netbsd-64/0.14.49:
resolution: {integrity: sha512-BXaUwFOfCy2T+hABtiPUIpWjAeWK9P8O41gR4Pg73hpzoygVGnj0nI3YK4SJhe52ELgtdgWP/ckIkbn2XaTxjQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
@ -2321,8 +2328,8 @@ packages:
requiresBuild: true
optional: true
/esbuild-openbsd-64/0.14.42:
resolution: {integrity: sha512-M2meNVIKWsm2HMY7+TU9AxM7ZVwI9havdsw6m/6EzdXysyCFFSoaTQ/Jg03izjCsK17FsVRHqRe26Llj6x0MNA==}
/esbuild-openbsd-64/0.14.49:
resolution: {integrity: sha512-lP06UQeLDGmVPw9Rg437Btu6J9/BmyhdoefnQ4gDEJTtJvKtQaUcOQrhjTq455ouZN4EHFH1h28WOJVANK41kA==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
@ -2338,8 +2345,8 @@ packages:
requiresBuild: true
optional: true
/esbuild-sunos-64/0.14.42:
resolution: {integrity: sha512-uXV8TAZEw36DkgW8Ak3MpSJs1ofBb3Smkc/6pZ29sCAN1KzCAQzsje4sUwugf+FVicrHvlamCOlFZIXgct+iqQ==}
/esbuild-sunos-64/0.14.49:
resolution: {integrity: sha512-4c8Zowp+V3zIWje329BeLbGh6XI9c/rqARNaj5yPHdC61pHI9UNdDxT3rePPJeWcEZVKjkiAS6AP6kiITp7FSw==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
@ -2355,8 +2362,8 @@ packages:
requiresBuild: true
optional: true
/esbuild-windows-32/0.14.42:
resolution: {integrity: sha512-4iw/8qWmRICWi9ZOnJJf9sYt6wmtp3hsN4TdI5NqgjfOkBVMxNdM9Vt3626G1Rda9ya2Q0hjQRD9W1o+m6Lz6g==}
/esbuild-windows-32/0.14.49:
resolution: {integrity: sha512-q7Rb+J9yHTeKr9QTPDYkqfkEj8/kcKz9lOabDuvEXpXuIcosWCJgo5Z7h/L4r7rbtTH4a8U2FGKb6s1eeOHmJA==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
@ -2372,8 +2379,8 @@ packages:
requiresBuild: true
optional: true
/esbuild-windows-64/0.14.42:
resolution: {integrity: sha512-j3cdK+Y3+a5H0wHKmLGTJcq0+/2mMBHPWkItR3vytp/aUGD/ua/t2BLdfBIzbNN9nLCRL9sywCRpOpFMx3CxzA==}
/esbuild-windows-64/0.14.49:
resolution: {integrity: sha512-+Cme7Ongv0UIUTniPqfTX6mJ8Deo7VXw9xN0yJEN1lQMHDppTNmKwAM3oGbD/Vqff+07K2gN0WfNkMohmG+dVw==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
@ -2389,8 +2396,8 @@ packages:
requiresBuild: true
optional: true
/esbuild-windows-arm64/0.14.42:
resolution: {integrity: sha512-+lRAARnF+hf8J0mN27ujO+VbhPbDqJ8rCcJKye4y7YZLV6C4n3pTRThAb388k/zqF5uM0lS5O201u0OqoWSicw==}
/esbuild-windows-arm64/0.14.49:
resolution: {integrity: sha512-v+HYNAXzuANrCbbLFJ5nmO3m5y2PGZWLe3uloAkLt87aXiO2mZr3BTmacZdjwNkNEHuH3bNtN8cak+mzVjVPfA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
@ -2425,32 +2432,32 @@ packages:
esbuild-windows-64: 0.14.31
esbuild-windows-arm64: 0.14.31
/esbuild/0.14.42:
resolution: {integrity: sha512-V0uPZotCEHokJdNqyozH6qsaQXqmZEOiZWrXnds/zaH/0SyrIayRXWRB98CENO73MIZ9T3HBIOsmds5twWtmgw==}
/esbuild/0.14.49:
resolution: {integrity: sha512-/TlVHhOaq7Yz8N1OJrjqM3Auzo5wjvHFLk+T8pIue+fhnhIMpfAzsG6PLVMbFveVxqD2WOp3QHei+52IMUNmCw==}
engines: {node: '>=12'}
hasBin: true
requiresBuild: true
optionalDependencies:
esbuild-android-64: 0.14.42
esbuild-android-arm64: 0.14.42
esbuild-darwin-64: 0.14.42
esbuild-darwin-arm64: 0.14.42
esbuild-freebsd-64: 0.14.42
esbuild-freebsd-arm64: 0.14.42
esbuild-linux-32: 0.14.42
esbuild-linux-64: 0.14.42
esbuild-linux-arm: 0.14.42
esbuild-linux-arm64: 0.14.42
esbuild-linux-mips64le: 0.14.42
esbuild-linux-ppc64le: 0.14.42
esbuild-linux-riscv64: 0.14.42
esbuild-linux-s390x: 0.14.42
esbuild-netbsd-64: 0.14.42
esbuild-openbsd-64: 0.14.42
esbuild-sunos-64: 0.14.42
esbuild-windows-32: 0.14.42
esbuild-windows-64: 0.14.42
esbuild-windows-arm64: 0.14.42
esbuild-android-64: 0.14.49
esbuild-android-arm64: 0.14.49
esbuild-darwin-64: 0.14.49
esbuild-darwin-arm64: 0.14.49
esbuild-freebsd-64: 0.14.49
esbuild-freebsd-arm64: 0.14.49
esbuild-linux-32: 0.14.49
esbuild-linux-64: 0.14.49
esbuild-linux-arm: 0.14.49
esbuild-linux-arm64: 0.14.49
esbuild-linux-mips64le: 0.14.49
esbuild-linux-ppc64le: 0.14.49
esbuild-linux-riscv64: 0.14.49
esbuild-linux-s390x: 0.14.49
esbuild-netbsd-64: 0.14.49
esbuild-openbsd-64: 0.14.49
esbuild-sunos-64: 0.14.49
esbuild-windows-32: 0.14.49
esbuild-windows-64: 0.14.49
esbuild-windows-arm64: 0.14.49
dev: true
/escalade/3.1.1:
@ -3521,7 +3528,7 @@ packages:
picocolors: 1.0.0
source-map-js: 1.0.2
/prettier-plugin-svelte/2.7.0_sqtt6dzjlskmywoml5ykunxlce:
/prettier-plugin-svelte/2.7.0_prettier@2.6.2+svelte@3.47.0:
resolution: {integrity: sha512-fQhhZICprZot2IqEyoiUYLTRdumULGRvw0o4dzl5jt0jfzVWdGqeYW27QTWAeXhoupEZJULmNoH3ueJwUWFLIA==}
peerDependencies:
prettier: ^1.16.4 || ^2.0.0
@ -3982,7 +3989,7 @@ packages:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
/svelte-check/2.4.1_2y4otvh2n6klv6metqycpfiuzy:
/svelte-check/2.4.1_1fac02bafd0682bb46b2470c9627a189:
resolution: {integrity: sha512-xhf3ShP5rnRwBokrgTBJ/0cO9QIc1DAVu1NWNRTfCDsDBNjGmkS3HgitgUadRuoMKj1+irZR/yHJ+Uqobnkbrw==}
hasBin: true
peerDependencies:
@ -3995,7 +4002,8 @@ packages:
picocolors: 1.0.0
sade: 1.8.1
source-map: 0.7.3
svelte-preprocess: 4.10.2_bw7ic75prjd4umr4fb55sbospu
svelte: 3.47.0
svelte-preprocess: 4.10.2_7ea8a6d5efea3309cd53b5ce515684ac
typescript: 4.5.5
transitivePeerDependencies:
- '@babel/core'
@ -4010,7 +4018,7 @@ packages:
- sugarss
dev: true
/svelte-check/2.7.0_xf35j26wmvzqzzfwect6yhmkcm:
/svelte-check/2.7.0_postcss@8.4.6+svelte@3.47.0:
resolution: {integrity: sha512-GrvG24j0+i8AOm0k0KyJ6Dqc+TAR2yzB7rtS4nljHStunVxCTr/1KYlv4EsOeoqtHLzeWMOd5D2O6nDdP/yw4A==}
hasBin: true
peerDependencies:
@ -4023,7 +4031,7 @@ packages:
sade: 1.8.1
source-map: 0.7.3
svelte: 3.47.0
svelte-preprocess: 4.10.6_bwx7acail2u6vixfjbtw6klhdi
svelte-preprocess: 4.10.6_0daff008085ea9eaa2e548676f29671a
typescript: 4.5.5
transitivePeerDependencies:
- '@babel/core'
@ -4038,13 +4046,6 @@ packages:
- sugarss
dev: false
/svelte-hmr/0.14.11:
resolution: {integrity: sha512-R9CVfX6DXxW1Kn45Jtmx+yUe+sPhrbYSUp7TkzbW0jI5fVPn6lsNG9NEs5dFg5qRhFNAoVdRw5qQDLALNKhwbQ==}
engines: {node: ^12.20 || ^14.13.1 || >= 16}
peerDependencies:
svelte: '>=3.19.0'
dev: true
/svelte-hmr/0.14.11_svelte@3.47.0:
resolution: {integrity: sha512-R9CVfX6DXxW1Kn45Jtmx+yUe+sPhrbYSUp7TkzbW0jI5fVPn6lsNG9NEs5dFg5qRhFNAoVdRw5qQDLALNKhwbQ==}
engines: {node: ^12.20 || ^14.13.1 || >= 16}
@ -4052,21 +4053,6 @@ packages:
svelte: '>=3.19.0'
dependencies:
svelte: 3.47.0
dev: false
/svelte-i18n/3.3.13:
resolution: {integrity: sha512-RQM+ys4+Y9ztH//tX22H1UL2cniLNmIR+N4xmYygV6QpQ6EyQvloZiENRew8XrVzfvJ8HaE8NU6/yurLkl7z3g==}
engines: {node: '>= 11.15.0'}
hasBin: true
peerDependencies:
svelte: ^3.25.1
dependencies:
deepmerge: 4.2.2
estree-walker: 2.0.2
intl-messageformat: 9.11.4
sade: 1.8.1
tiny-glob: 0.2.9
dev: false
/svelte-i18n/3.3.13_svelte@3.47.0:
resolution: {integrity: sha512-RQM+ys4+Y9ztH//tX22H1UL2cniLNmIR+N4xmYygV6QpQ6EyQvloZiENRew8XrVzfvJ8HaE8NU6/yurLkl7z3g==}
@ -4083,7 +4069,7 @@ packages:
tiny-glob: 0.2.9
dev: false
/svelte-preprocess/4.10.2_bw7ic75prjd4umr4fb55sbospu:
/svelte-preprocess/4.10.2_7ea8a6d5efea3309cd53b5ce515684ac:
resolution: {integrity: sha512-aPpkCreSo8EL/y8kJSa1trhiX0oyAtTjlNNM7BNjRAsMJ8Yy2LtqHt0zyd4pQPXt+D4PzbO3qTjjio3kwOxDlA==}
engines: {node: '>= 9.11.2'}
requiresBuild: true
@ -4132,10 +4118,11 @@ packages:
postcss-load-config: 3.1.1
sorcery: 0.10.0
strip-indent: 3.0.0
svelte: 3.47.0
typescript: 4.5.5
dev: true
/svelte-preprocess/4.10.6_bwx7acail2u6vixfjbtw6klhdi:
/svelte-preprocess/4.10.6_0daff008085ea9eaa2e548676f29671a:
resolution: {integrity: sha512-I2SV1w/AveMvgIQlUF/ZOO3PYVnhxfcpNyGt8pxpUVhPfyfL/CZBkkw/KPfuFix5FJ9TnnNYMhACK3DtSaYVVQ==}
engines: {node: '>= 9.11.2'}
requiresBuild: true
@ -4187,7 +4174,7 @@ packages:
typescript: 4.5.5
dev: false
/svelte-preprocess/4.10.6_xf35j26wmvzqzzfwect6yhmkcm:
/svelte-preprocess/4.10.6_postcss@8.4.6+svelte@3.47.0:
resolution: {integrity: sha512-I2SV1w/AveMvgIQlUF/ZOO3PYVnhxfcpNyGt8pxpUVhPfyfL/CZBkkw/KPfuFix5FJ9TnnNYMhACK3DtSaYVVQ==}
engines: {node: '>= 9.11.2'}
requiresBuild: true

View File

@ -10,7 +10,7 @@ module.exports = {
preset: 'default'
}),
purgecss({
content: ['./src/**/*.html'],
content: ['./src/**/*.html', './src/**/*.js'],
defaultExtractor: content => content.match(/[\w-/:]+(?<!:)/g) || []
}),
postcss_hash({

View File

@ -134,6 +134,12 @@
<script type="module" src="/assets/index.js"></script>
{% include 'templates/footer.html' %}
<script>
{% include 'templates/links.js' %}
</script>
<script>
{% include 'templates/guide-color.js' %}
</script>
<script>
const show_demo = (component, demo) => {
document.querySelectorAll(`#${component} .demo-btn.selected-demo`).forEach(n => n.classList.remove('selected-demo'));
document.querySelectorAll(`#${component} .demo-content`).forEach(n => n.classList.add('hidden'));
@ -146,116 +152,6 @@
}
}
let mainNavLinks = document.querySelectorAll(".navigation a");
window.addEventListener("scroll", event => {
let fromTop = window.scrollY;
mainNavLinks.forEach(link => {
let section = document.querySelector(link.hash);
if (
section.offsetTop <= fromTop*1.01 &&
section.offsetTop + section.offsetHeight > fromTop*1.01
) {
link.classList.add("current-nav-link");
} else {
link.classList.remove("current-nav-link");
}
});
});
// adds anchor button when hovering over headers, except on touch devices where instead the header becomes a link
function createAnchorTag(link) {
let a = document.createElement('a');
a.href = link;
a.classList.add("invisible", "group-hover-visible");
let img = document.createElement('img');
img.classList.add("anchor-img")
img.src = "/assets/img/anchor.svg";
a.appendChild(img);
return a;
}
function createMobileAnchorTag(link) {
let a = document.createElement('a');
a.href = link;
a.classList.add("no-underline")
return a;
}
var headers = document.querySelectorAll("h2, h3");
function isTouchDevice() {
return (('ontouchstart' in window) ||
(navigator.maxTouchPoints > 0) ||
(navigator.msMaxTouchPoints > 0));
}
if (isTouchDevice()) {
for (let i = 0; i < headers.length; i++) {
let link = '#' + headers[i].id;
var parent = headers[i].parentNode;
var wrapper = createMobileAnchorTag(link);
parent.replaceChild(wrapper, headers[i]);
wrapper.appendChild(headers[i]);
}
} else {
for (let i = 0; i < headers.length; i++) {
headers[i].classList.add("group")
let link = '#' + headers[i].id;
var anchorTag = createAnchorTag(link);
headers[i].appendChild(createAnchorTag(link));
}
}
const COLOR_SETS = [
["from-green-100", "to-green-50"],
["from-yellow-100", "to-yellow-50"],
["from-red-100", "to-red-50"],
["from-blue-100", "to-blue-50"],
["from-pink-100", "to-pink-50"],
["from-purple-100", "to-purple-50"],
]
document.querySelectorAll(".guide-box").forEach(guide => {
const [start_color, end_color] = COLOR_SETS[Math.floor(Math.random() * COLOR_SETS.length)]
guide.classList.add(start_color);
guide.classList.add(end_color);
})
// add copy buttons to all codeblocks
const svgCopy =
'<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true"><path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path></svg>';
const svgCheck =
'<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true"><path fill-rule="evenodd" fill="rgb(255, 124, 1)" d="M13.78 4.22a.75.75 0 010 1.06l-7.25 7.25a.75.75 0 01-1.06 0L2.22 9.28a.75.75 0 011.06-1.06L6 10.94l6.72-6.72a.75.75 0 011.06 0z"></path></svg>';
const addCopyButtons = (clipboard) => {
document.querySelectorAll("pre > code").forEach((codeBlock) => {
const button = document.createElement("button");
button.classList.add("clipboard-button");
button.type = "button";
button.innerHTML = svgCopy;
button.addEventListener("click", () => {
clipboard.writeText(codeBlock.innerText).then(
() => {
button.blur();
button.innerHTML = svgCheck;
setTimeout(() => (button.innerHTML = svgCopy), 2000);
},
(error) => (button.innerHTML = "Error")
);
});
const pre = codeBlock.parentNode;
pre.parentNode.insertBefore(button, pre);
});
};
if (navigator && navigator.clipboard) {
addCopyButtons(navigator.clipboard);
};
// Remove built-with-gradio footers and extra space from embedded components
window.addEventListener('load', function () {
document.querySelectorAll('.embedded-component gradio-app').forEach(g => {

View File

@ -8,7 +8,7 @@ TEMPLATE_FILE = os.path.join(DIR, "template.html")
GRADIO_DIR = "../../"
GUIDES_DIR = os.path.join(GRADIO_DIR, "guides")
GUIDE_ASSETS_DIR = os.path.join(GUIDES_DIR, "assets", "guides")
GUIDE_ASSETS_DIR = os.path.join(GUIDES_DIR, "assets")
DEMOS_DIR = os.path.join(GRADIO_DIR, "demo")
TEMP_TEMPLATE = os.path.join(DIR, "temporary_template.html")
@ -23,70 +23,87 @@ for demo_folder in os.listdir(DEMOS_DIR):
'if __name__ == "__main__":\n demo.launch()', "demo.launch()"
)
guide_list = os.listdir(GUIDES_DIR)
guide_list.remove("CONTRIBUTING.md")
guide_list.remove("assets")
def format_name(guide_name):
index = None
if re.match("^[0-9]+\)", guide_name):
index = int(guide_name[: guide_name.index(")")])
guide_name = guide_name[guide_name.index(")") + 1 :]
if guide_name.lower().endswith(".md"):
guide_name = guide_name[:-3]
pretty_guide_name = " ".join([word[0].upper() + word[1:] for word in guide_name.split("_")])
return index, guide_name, pretty_guide_name
guide_folders = sorted(os.listdir(GUIDES_DIR))
guide_folders.remove("CONTRIBUTING.md")
guide_folders.remove("etc")
guide_folders.remove("assets")
guides = []
for guide in guide_list:
guide_name = guide[:-3]
if guide_name == "getting_started":
pretty_guide_name = "Quickstart"
else:
pretty_guide_name = " ".join(
[word.capitalize() for word in guide_name.split("_")]
guides_by_category = []
absolute_index = 0
for guide_folder in guide_folders:
guide_list = sorted(os.listdir(os.path.join(GUIDES_DIR, guide_folder)))
_, guide_category, pretty_guide_category = format_name(guide_folder)
guides_by_category.append({"category": pretty_guide_category, "guides": []})
for guide_file in guide_list:
guide_index, guide_name, pretty_guide_name = format_name(guide_file)
with open(os.path.join(GUIDES_DIR, guide_folder, guide_file), "r") as f:
guide_content = f.read()
title = guide_content.split("\n")[0]
metadata_labels = []
def get_labeled_metadata(label, is_list=True):
metadata_labels.append(label)
full_label = label + " "
metadata = [] if is_list else None
if full_label in guide_content:
metadata = guide_content.split(full_label)[1].split("\n")[0]
if is_list:
metadata = metadata.split(", ")
return metadata
tags = get_labeled_metadata("Tags:")
spaces = get_labeled_metadata("Related spaces:")
contributor = get_labeled_metadata("Contributed by", is_list=False)
docs = get_labeled_metadata("Docs:")
pinned = get_labeled_metadata("Pinned:", is_list=False)
url = f"/{guide_name}/"
guide_content = "\n".join(
[
line
for i, line in enumerate(guide_content.split("\n"))
if not any([line.startswith(label) for label in metadata_labels])
]
)
guide_content = re.sub(
r"```([a-z]+)\n",
lambda x: f"<div class='codeblock'><pre><code class='lang-{x.group(1)}'>",
guide_content,
)
guide_content = re.sub(r"```", "</code></pre></div>", guide_content)
guide_content = re.sub(
r"\$code_([a-z _\-0-9]+)",
lambda x: f"<div class='codeblock'><pre><code class='lang-python'>{demos[x.group(1)]}</code></pre></div>",
guide_content,
)
guide_content = re.sub(
r"\$demo_([a-z _\-0-9]+)",
lambda x: f"<gradio-app src='/demo/{x.group(1)}' />",
guide_content,
)
with open(os.path.join(GUIDES_DIR, guide), "r") as f:
guide_content = f.read()
title = guide_content.split("\n")[0]
def get_labeled_metadata(label, is_list=True):
full_label = label + " "
metadata = [] if is_list else None
if full_label in guide_content:
metadata = guide_content.split(full_label)[1].split("\n")[0]
if is_list:
metadata = metadata.split(", ")
return metadata
tags = get_labeled_metadata("Tags:")
spaces = get_labeled_metadata("Related spaces:")
contributor = get_labeled_metadata("Contributed by", is_list=False)
docs = get_labeled_metadata("Docs:")
pinned = get_labeled_metadata("Pinned:", is_list=False)
url = f"https://gradio.app/{guide_name}/"
guide_content = "\n".join(
[
line
for i, line in enumerate(guide_content.split("\n"))
if not (
line.startswith("Tags: ")
or line.startswith("Related spaces: ")
or line.startswith("Contributed by ")
or line.startswith("Docs: ")
or line.startswith("Pinned: ")
)
]
)
guide_content = re.sub(r"```([a-z]+)\n", lambda x: f"<div class='codeblock'><pre><code class='lang-{x.group(1)}'>", guide_content)
guide_content = re.sub(r"```", "</code></pre></div>", guide_content)
guide_content = re.sub(
r"\$code_([a-z _\-0-9]+)",
lambda x: f"<div class='codeblock'><pre><code class='lang-python'>{demos[x.group(1)]}</code></pre></div>",
guide_content
)
guide_content = re.sub(
r"\$demo_([a-z _\-0-9]+)",
lambda x: f"<gradio-app src='/demo/{x.group(1)}' />",
guide_content
)
guides.append(
{
guide_data = {
"name": guide_name,
"category": guide_category,
"pretty_category": pretty_guide_category,
"guide_index": guide_index,
"absolute_index": absolute_index,
"pretty_name": pretty_guide_name,
"content": guide_content,
"tags": tags,
@ -94,11 +111,12 @@ for guide in guide_list:
"url": url,
"contributor": contributor,
"docs": docs,
"pinned": pinned
"pinned": pinned,
}
)
guides.append(guide_data)
guides_by_category[-1]["guides"].append(guide_data)
absolute_index += 1
guides = sorted(guides, key=lambda x: float('inf') if x["pinned"] is None else int(x["pinned"]))
def build_guides(output_dir, jinja_env):
shutil.copytree(GUIDE_ASSETS_DIR, os.path.join(output_dir, "assets", "guides"))
@ -107,20 +125,44 @@ def build_guides(output_dir, jinja_env):
temp_html.write(
markdown2.markdown(
guide["content"],
extras=["target-blank-links", "header-ids", "tables", "fenced-code-blocks"],
extras=[
"target-blank-links",
"header-ids",
"tables",
"fenced-code-blocks",
],
)
)
template = jinja_env.get_template("guides/template.html")
output_folder = os.path.join(output_dir, guide["name"])
os.makedirs(output_folder)
output_file = os.path.join(output_folder, "index.html")
output = template.render(code={}, demos={}, spaces=guide["spaces"], name=guide["name"], pretty_name=guide["pretty_name"])
prev_guide = list(filter(lambda g: g["absolute_index"] == guide["absolute_index"] - 1, guides))
next_guide = list(filter(lambda g: g["absolute_index"] == guide["absolute_index"] + 1, guides))
output = template.render(
code={},
demos={},
spaces=guide["spaces"],
category=guide["pretty_category"],
name=guide["name"],
pretty_name=guide["pretty_name"],
guides_by_category=guides_by_category,
prev_guide=prev_guide[0] if len(prev_guide) else None,
next_guide=next_guide[0] if len(next_guide) else None,
)
with open(output_file, "w") as index_html:
index_html.write(output)
if guide["guide_index"] == 1:
output_folder = os.path.join(output_dir, guide["category"])
os.makedirs(output_folder)
category_file = os.path.join(output_folder, "index.html")
with open(category_file, "w") as index_html:
index_html.write(output)
def build_gallery(output_dir, jinja_env):
template = jinja_env.get_template("guides/gallery_template.html")
output = template.render(guides=guides)
output = template.render(guides=guides, guides_by_category=guides_by_category)
output_folder = os.path.join(output_dir, "guides")
os.makedirs(output_folder)
output_file = os.path.join(output_folder, "index.html")
@ -130,6 +172,7 @@ def build_gallery(output_dir, jinja_env):
with open(output_file, "w") as index_html:
index_html.write(output)
def build(output_dir, jinja_env):
build_guides(output_dir, jinja_env)
build_gallery(output_dir, jinja_env)

View File

@ -16,33 +16,38 @@
placeholder="What do you want to build?"
autocomplete="off"
onkeyup="search(this.value);"/>
<h2 class="text-gray-600 mb-6 mx-auto w-fit text-sm">
<div class="text-gray-600 mb-6 mx-auto w-fit text-sm">
Search through
<span id="counter ">{{ guides|length }}</span>
<span id="counter">{{ guides|length }}</span>
Guides. <a class="link text-gray-600"
href="https://github.com/gradio-app/gradio/tree/main/guides">Contribute here</a>
</h2>
<div id="guide-list" class="grid grid-cols-1 lg:grid-cols-3 gap-12">
{% for guide in guides %}
<a class="guide-box flex lg:col-span-1 flex-col group overflow-hidden relative rounded-xl shadow-sm hover:shadow-alternate transition-shadow bg-gradient-to-r"
name="{{ guide.name }}"
href="/{{ guide.name }}">
<div class="flex flex-col p-4 h-min">
<h2 class="font-semibold group-hover:underline text-xl">{{ guide.pretty_name }}</h2>
<div class="tags-holder">
{% if guide.tags is not none %}
<p class="text-gray-600">
{% for tag in guide.tags %}
{{ tag }}
{% if not loop.last %},{% endif %}
{% endfor %}
</p>
{% endif %}
</div>
</div>
</a>
{% endfor %}
</div>
{% for category_guides in guides_by_category %}
<div class="category mb-8">
<h2 class="mb-4 text-2xl font-thin block">{{ category_guides["category"] }}</h2>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
{% for guide in category_guides["guides"] %}
<a class="guide-box flex lg:col-span-1 flex-col group overflow-hidden relative rounded-xl shadow-sm hover:shadow-alternate transition-shadow bg-gradient-to-r"
name="{{ guide.name }}"
href="/{{ guide.name }}">
<div class="flex flex-col p-4 h-min">
<h2 class="group-hover:underline text-lg">{{ guide.pretty_name }}</h2>
<div class="tags-holder">
{% if guide.tags is not none %}
<p class="text-gray-600">
{% for tag in guide.tags %}
{{ tag }}
{% if not loop.last %},{% endif %}
{% endfor %}
</p>
{% endif %}
</div>
</div>
</a>
{% endfor %}
</div>
</div>
{% endfor %}
<div class="no-guides hidden text-center text-xl text-gray-500">
<p class="mb-4">Sorry, we couldn't find a guide :(</p>
<p>
@ -52,27 +57,20 @@
</div>
{% include 'templates/footer.html' %}
<script>
const COLOR_SETS = [
["from-green-100", "to-green-50"],
["from-yellow-100", "to-yellow-50"],
["from-red-100", "to-red-50"],
["from-blue-100", "to-blue-50"],
["from-pink-100", "to-pink-50"],
["from-purple-100", "to-purple-50"],
]
document.querySelectorAll(".guide-box").forEach(guide => {
const [start_color, end_color] = COLOR_SETS[Math.floor(Math.random() * COLOR_SETS.length)]
guide.classList.add(start_color);
guide.classList.add(end_color);
})
{% include 'templates/guide-color.js' %}
</script>
<script>
const guides = {{ guides|tojson }};
const search = query => {
query = query.toLowerCase();
let filtered_guides = guides
.filter(guide =>
guide.name.toLowerCase().includes(query) || guide.content.toLowerCase().includes(query))
.map(guide => guide.name)
if (query.length > 0) {
query = query.toLowerCase();
var filtered_guides = guides
.filter(guide =>
guide.name.toLowerCase().includes(query) || guide.content.toLowerCase().includes(query))
.map(guide => guide.name)
} else {
var filtered_guides = guides.map(guide => guide.name);
}
if (filtered_guides.length === 0) {
document.querySelector(".no-guides").classList.remove("hidden");
} else {
@ -85,6 +83,13 @@
guide.classList.add("hidden");
}
})
document.querySelectorAll(".category").forEach(category => {
if (category.querySelectorAll(".guide-box:not(.hidden)").length === 0) {
category.classList.add("hidden");
} else {
category.classList.remove("hidden");
}
})
}
</script>
</body>

View File

@ -9,119 +9,87 @@
</head>
<body>
{% include "templates/navbar.html" %}
<div class="container relative mx-auto px-4 pt-8 pb-12">
{% if spaces %}
<div id='spaces-holder' class="mb-4">
<a href='https://hf.co/spaces' target='_blank'>
<img class="inline-block my-0 mx-auto w-5 max-w-full pb-1" src='/assets/img/spaces-logo.svg'>
<div class="container mx-auto px-4 flex gap-4 relative">
<div class="side-navigation h-screen leading-relaxed sticky top-0 text-md overflow-y-auto overflow-x-hidden hidden lg:block rounded-t-xl bg-gradient-to-r from-white to-gray-50"
style="min-width: 18%">
{% for category_guides in guides_by_category %}
<a class="category-link link {% if loop.first %} mb-2 {% else %} my-2 {% endif %} font-semibold px-4 pt-2 text-ellipsis whitespace-nowrap block"
style="max-width: 12rem"
href="{{ category_guides['guides'][0]['url'] }}">
{{ category_guides["category"] }}
</a>
<p class="m-0 inline text-lg font-normal">Related Spaces: </p>
{% for space in spaces %}
<div class='space-link inline-block m-1 px-1 rounded-md'>
<a href='{{space}}' target='_blank' class="no-underline">{{space[30:]}}</a>
</div>
{% for guide in category_guides["guides"] %}
<a class="guide-link {% if name == guide['name'] %} current-nav-link pb-1{% endif %} -indent-2 ml-2 thin-link px-4 block overflow-hidden"
style="max-width: 12rem"
href="{{ guide['url'] }}">{{ guide['pretty_name'] }}</a>
{% if name == guide['name'] %}
<div class="navigation max-w-full bg-gradient-to-r from-orange-50 to-orange-100 p-2 mx-2 border-l-2 border-orange-500 mb-2">
</div>
{% endif %}
{% endfor %}
{% endfor %}
</div>
<div class="w-full">
{% if spaces %}
<div id='spaces-holder' class="mb-4">
<a href='https://hf.co/spaces' target='_blank'>
<img class="inline-block my-0 mx-auto w-5 max-w-full pb-1"
src='/assets/img/spaces-logo.svg'>
</a>
<p class="m-0 inline text-lg font-normal">Related Spaces:</p>
{% for space in spaces %}
<div class='space-link inline-block m-1 px-1 rounded-md'>
<a href='{{ space }}' target='_blank' class="no-underline">{{ space[30:] }}</a>
</div>
{% endfor %}
</div>
{% endif %}
<div class="prose max-w-full">
{% include "guides/temporary_template.html" %}
{% endif %}
<div class="prose max-w-full">{% include "guides/temporary_template.html" %}</div>
<div class="flex justify-between mt-4">
{% if prev_guide is not none %}
<a href="{{ prev_guide['url'] }}"
class="text-left p-4 bg-gray-50 hover:underline">
<div class="text-gray-600 block">&lt; Previous</div>
<div class="text-lg font-semibold">{{ prev_guide['pretty_name'] }}</div>
</a>
{% else %}
<div></div>
{% endif %}
{% if next_guide is not none %}
<a href="{{ next_guide['url'] }}"
class="text-right p-4 bg-gray-50 hover:underline">
<div class="text-gray-600 block">Next &gt;</div>
<div class="text-lg font-semibold">{{ next_guide['pretty_name'] }}</div>
</a>
{% else %}
<div></div>
{% endif %}
</div>
</div>
</div>
<script src="/assets/prism.js"></script>
<script>
window.__gradio_mode__ = "website";
</script>
<script>window.__gradio_mode__ = "website";</script>
<script type="module" src="/assets/index.js"></script>
{% include 'templates/footer.html' %}
<script>
// adds anchor button when hovering over headers, except on touch devices where instead the header becomes a link
function createAnchorTag(link) {
let a = document.createElement('a');
a.href = link;
a.classList.add("invisible", "group-hover-visible");
let img = document.createElement('img');
img.classList.add("anchor-img")
img.src = "/assets/img/anchor.svg";
a.appendChild(img);
return a;
}
function createMobileAnchorTag(link) {
let a = document.createElement('a');
a.href = link;
a.classList.add("no-underline")
return a;
}
var headers = document.querySelectorAll("h2, h3");
function isTouchDevice() {
return (('ontouchstart' in window) ||
(navigator.maxTouchPoints > 0) ||
(navigator.msMaxTouchPoints > 0));
}
if (isTouchDevice()) {
for (let i = 0; i < headers.length; i++) {
let link = '#' + headers[i].id;
var parent = headers[i].parentNode;
var wrapper = createMobileAnchorTag(link);
parent.replaceChild(wrapper, headers[i]);
wrapper.appendChild(headers[i]);
}
} else {
for (let i = 0; i < headers.length; i++) {
headers[i].classList.add("group")
let link = '#' + headers[i].id;
var anchorTag = createAnchorTag(link);
headers[i].appendChild(createAnchorTag(link));
}
let sidebar = document.querySelector(".side-navigation")
let target_link = document.querySelector(".current-nav-link");
if (target_link.previousElementSibling.classList.contains("category-link")) {
target_link = target_link.previousElementSibling;
}
// color related spaces
sidebar.scrollTop = target_link.offsetTop
document.querySelectorAll(".prose h2").forEach(subheader => {
document.querySelector(".navigation").innerHTML += `
<a class='subheading block thin-link -indent-2 ml-4 mr-2' href='#${subheader.id}'>${subheader.innerText}</a>
`
})
const COLORS = ["bg-green-50", "bg-yellow-50", "bg-red-50", "bg-pink-50", "bg-purple-50"];
document.querySelectorAll(".space-link").forEach(guide => {
const color = COLORS[Math.floor(Math.random() * COLORS.length)]
guide.classList.add(color);
})
// add copy buttons to all codeblocks
const svgCopy =
'<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true"><path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path></svg>';
const svgCheck =
'<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true"><path fill-rule="evenodd" fill="rgb(255, 124, 1)" d="M13.78 4.22a.75.75 0 010 1.06l-7.25 7.25a.75.75 0 01-1.06 0L2.22 9.28a.75.75 0 011.06-1.06L6 10.94l6.72-6.72a.75.75 0 011.06 0z"></path></svg>';
const addCopyButtons = (clipboard) => {
document.querySelectorAll("pre > code").forEach((codeBlock) => {
const button = document.createElement("button");
button.classList.add("clipboard-button");
button.type = "button";
button.innerHTML = svgCopy;
button.addEventListener("click", () => {
clipboard.writeText(codeBlock.innerText).then(
() => {
button.blur();
button.innerHTML = svgCheck;
setTimeout(() => (button.innerHTML = svgCopy), 2000);
},
(error) => (button.innerHTML = "Error")
);
});
const pre = codeBlock.parentNode;
pre.parentNode.insertBefore(button, pre);
});
};
if (navigator && navigator.clipboard) {
addCopyButtons(navigator.clipboard);
};
</script>
<script>{% include 'templates/links.js' %}</script>
</body>
</html>

View File

@ -72,16 +72,21 @@
display: none;
}
/* docs */
.selected-demo {
@apply font-semibold bg-gray-50 rounded text-orange-500;
}
/* docs & guides */
.thin-link.current-nav-link {
@apply border-orange-500 text-orange-500 md:border-l-2 pl-4;
@apply text-orange-500;
}
.thin-link.current-nav-link:not(.subheading) {
@apply border-orange-500 md:border-l-2 pl-4;
}
.link.current-nav-link {
@apply border-orange-500 text-orange-500;
}
/* docs */
.selected-demo {
@apply font-semibold bg-gray-50 rounded text-orange-500;
}
code.language-python {
@apply !leading-7 !whitespace-pre-wrap !break-all;
}

View File

@ -0,0 +1,13 @@
const COLOR_SETS = [
["from-green-100", "to-green-50"],
["from-yellow-100", "to-yellow-50"],
["from-red-100", "to-red-50"],
["from-blue-100", "to-blue-50"],
["from-pink-100", "to-pink-50"],
["from-purple-100", "to-purple-50"],
]
document.querySelectorAll(".guide-box").forEach(guide => {
const [start_color, end_color] = COLOR_SETS[Math.floor(Math.random() * COLOR_SETS.length)]
guide.classList.add(start_color);
guide.classList.add(end_color);
})

View File

@ -0,0 +1,91 @@
let mainNavLinks = document.querySelectorAll(".navigation a");
window.addEventListener("scroll", event => {
let fromTop = window.scrollY;
let lowest_link = null;
mainNavLinks.forEach(link => {
let section = document.querySelector(link.hash);
if (section.offsetTop <= fromTop * 1.01) {
lowest_link = link;
}
link.classList.remove("current-nav-link");
});
lowest_link.classList.add("current-nav-link");
});
// adds anchor button when hovering over headers, except on touch devices where instead the header becomes a link
function createAnchorTag(link) {
let a = document.createElement('a');
a.href = link;
a.classList.add("invisible", "group-hover-visible");
let img = document.createElement('img');
img.classList.add("anchor-img")
img.src = "/assets/img/anchor.svg";
a.appendChild(img);
return a;
}
function createMobileAnchorTag(link) {
let a = document.createElement('a');
a.href = link;
a.classList.add("no-underline")
return a;
}
var headers = document.querySelectorAll("h2, h3");
function isTouchDevice() {
return (('ontouchstart' in window) ||
(navigator.maxTouchPoints > 0) ||
(navigator.msMaxTouchPoints > 0));
}
if (isTouchDevice()) {
for (let i = 0; i < headers.length; i++) {
let link = '#' + headers[i].id;
var parent = headers[i].parentNode;
var wrapper = createMobileAnchorTag(link);
parent.replaceChild(wrapper, headers[i]);
wrapper.appendChild(headers[i]);
}
} else {
for (let i = 0; i < headers.length; i++) {
headers[i].classList.add("group")
let link = '#' + headers[i].id;
var anchorTag = createAnchorTag(link);
headers[i].appendChild(createAnchorTag(link));
}
}
// add copy buttons to all codeblocks
const svgCopy =
'<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true"><path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path></svg>';
const svgCheck =
'<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true"><path fill-rule="evenodd" fill="rgb(255, 124, 1)" d="M13.78 4.22a.75.75 0 010 1.06l-7.25 7.25a.75.75 0 01-1.06 0L2.22 9.28a.75.75 0 011.06-1.06L6 10.94l6.72-6.72a.75.75 0 011.06 0z"></path></svg>';
const addCopyButtons = (clipboard) => {
document.querySelectorAll("pre > code").forEach((codeBlock) => {
const button = document.createElement("button");
button.classList.add("clipboard-button");
button.type = "button";
button.innerHTML = svgCopy;
button.addEventListener("click", () => {
clipboard.writeText(codeBlock.innerText).then(
() => {
button.blur();
button.innerHTML = svgCheck;
setTimeout(() => (button.innerHTML = svgCopy), 2000);
},
(error) => (button.innerHTML = "Error")
);
});
const pre = codeBlock.parentNode;
pre.parentNode.insertBefore(button, pre);
});
};
if (navigator && navigator.clipboard) {
addCopyButtons(navigator.clipboard);
};