Spaces:
Running
Running
Upload folder using huggingface_hub
Browse files- .gitignore +14 -0
- README.md +256 -12
- __init__.py +0 -0
- app.py +61 -0
- css.css +157 -0
- requirements.txt +1 -0
- space.py +159 -0
- src/.gitignore +14 -0
- src/README.md +256 -0
- src/backend/gradio_topbar/__init__.py +4 -0
- src/backend/gradio_topbar/templates/component/index.js +0 -0
- src/backend/gradio_topbar/templates/component/style.css +1 -0
- src/backend/gradio_topbar/topbar.py +71 -0
- src/demo/__init__.py +0 -0
- src/demo/app.py +61 -0
- src/demo/css.css +157 -0
- src/demo/requirements.txt +1 -0
- src/demo/space.py +159 -0
- src/frontend/Index.svelte +42 -0
- src/frontend/gradio.config.js +9 -0
- src/frontend/package-lock.json +0 -0
- src/frontend/package.json +34 -0
- src/frontend/shared/TopBarPanel.svelte +149 -0
- src/frontend/tsconfig.json +14 -0
- src/pyproject.toml +51 -0
.gitignore
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.eggs/
|
| 2 |
+
dist/
|
| 3 |
+
.vscode/
|
| 4 |
+
*.pyc
|
| 5 |
+
__pycache__/
|
| 6 |
+
*.py[cod]
|
| 7 |
+
*$py.class
|
| 8 |
+
__tmp/*
|
| 9 |
+
*.pyi
|
| 10 |
+
.mypycache
|
| 11 |
+
.ruff_cache
|
| 12 |
+
node_modules
|
| 13 |
+
backend/**/templates/
|
| 14 |
+
README_TEMPLATE.md
|
README.md
CHANGED
|
@@ -1,12 +1,256 @@
|
|
| 1 |
-
---
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
---
|
| 11 |
-
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
tags: [gradio-custom-component, SideBar]
|
| 3 |
+
title: gradio_topbar
|
| 4 |
+
short_description: A TopBar for Gradio Interface
|
| 5 |
+
colorFrom: blue
|
| 6 |
+
colorTo: yellow
|
| 7 |
+
sdk: gradio
|
| 8 |
+
pinned: false
|
| 9 |
+
app_file: space.py
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
# `gradio_topbar`
|
| 13 |
+
<img alt="Static Badge" src="https://img.shields.io/badge/version%20-%200.0.1%20-%20orange">
|
| 14 |
+
|
| 15 |
+
A TopBar for Gradio Interface
|
| 16 |
+
|
| 17 |
+
## Installation
|
| 18 |
+
|
| 19 |
+
```bash
|
| 20 |
+
pip install gradio_topbar
|
| 21 |
+
```
|
| 22 |
+
|
| 23 |
+
## Usage
|
| 24 |
+
|
| 25 |
+
```python
|
| 26 |
+
# demo/app.py
|
| 27 |
+
|
| 28 |
+
import gradio as gr
|
| 29 |
+
import time
|
| 30 |
+
from gradio_topbar import TopBar
|
| 31 |
+
|
| 32 |
+
def chat_response(message, history):
|
| 33 |
+
history = history or ""
|
| 34 |
+
history += f"You: {message}\n"
|
| 35 |
+
time.sleep(1) # Simulate thinking
|
| 36 |
+
history += f"Bot: Thanks for the message! You said: '{message}'\n"
|
| 37 |
+
return history, ""
|
| 38 |
+
|
| 39 |
+
def update_label(value):
|
| 40 |
+
return f"Current temperature is: {value}"
|
| 41 |
+
|
| 42 |
+
with gr.Blocks(theme=gr.themes.Ocean(), title="Full Layout Demo") as demo:
|
| 43 |
+
gr.Markdown(
|
| 44 |
+
"""
|
| 45 |
+
# TopBar Demo
|
| 46 |
+
This demo shows the `TopBar` with `width="50%"` and `bring_to_front=True`.
|
| 47 |
+
Notice how the bar is now horizontally centered and no longer full-width.
|
| 48 |
+
"""
|
| 49 |
+
)
|
| 50 |
+
|
| 51 |
+
with TopBar(open=True, height=180, width="50%", bring_to_front=True):
|
| 52 |
+
with gr.Row():
|
| 53 |
+
message_box = gr.Textbox(
|
| 54 |
+
show_label=False,
|
| 55 |
+
placeholder="Type your message here...",
|
| 56 |
+
elem_id="message-input",
|
| 57 |
+
scale=7
|
| 58 |
+
)
|
| 59 |
+
send_button = gr.Button("Send", variant="primary", scale=1)
|
| 60 |
+
with gr.Row():
|
| 61 |
+
gr.Button("Upload File")
|
| 62 |
+
gr.Button("Record Audio")
|
| 63 |
+
gr.ClearButton([message_box])
|
| 64 |
+
|
| 65 |
+
with gr.Row(equal_height=True):
|
| 66 |
+
with gr.Column(scale=3):
|
| 67 |
+
gr.Markdown("### 🤖 Chat Interface")
|
| 68 |
+
chatbot_display = gr.Textbox(
|
| 69 |
+
label="Chat History",
|
| 70 |
+
lines=25,
|
| 71 |
+
interactive=False
|
| 72 |
+
)
|
| 73 |
+
|
| 74 |
+
send_button.click(
|
| 75 |
+
fn=chat_response,
|
| 76 |
+
inputs=[message_box, chatbot_display],
|
| 77 |
+
outputs=[chatbot_display, message_box]
|
| 78 |
+
)
|
| 79 |
+
message_box.submit(
|
| 80 |
+
fn=chat_response,
|
| 81 |
+
inputs=[message_box, chatbot_display],
|
| 82 |
+
outputs=[chatbot_display, message_box]
|
| 83 |
+
)
|
| 84 |
+
|
| 85 |
+
if __name__ == "__main__":
|
| 86 |
+
demo.launch(debug=True)
|
| 87 |
+
```
|
| 88 |
+
|
| 89 |
+
## `TopBar`
|
| 90 |
+
|
| 91 |
+
### Initialization
|
| 92 |
+
|
| 93 |
+
<table>
|
| 94 |
+
<thead>
|
| 95 |
+
<tr>
|
| 96 |
+
<th align="left">name</th>
|
| 97 |
+
<th align="left" style="width: 25%;">type</th>
|
| 98 |
+
<th align="left">default</th>
|
| 99 |
+
<th align="left">description</th>
|
| 100 |
+
</tr>
|
| 101 |
+
</thead>
|
| 102 |
+
<tbody>
|
| 103 |
+
<tr>
|
| 104 |
+
<td align="left"><code>label</code></td>
|
| 105 |
+
<td align="left" style="width: 25%;">
|
| 106 |
+
|
| 107 |
+
```python
|
| 108 |
+
str | gradio.i18n.I18nData | None
|
| 109 |
+
```
|
| 110 |
+
|
| 111 |
+
</td>
|
| 112 |
+
<td align="left"><code>None</code></td>
|
| 113 |
+
<td align="left">name of the top bar. Not displayed to the user.</td>
|
| 114 |
+
</tr>
|
| 115 |
+
|
| 116 |
+
<tr>
|
| 117 |
+
<td align="left"><code>open</code></td>
|
| 118 |
+
<td align="left" style="width: 25%;">
|
| 119 |
+
|
| 120 |
+
```python
|
| 121 |
+
bool
|
| 122 |
+
```
|
| 123 |
+
|
| 124 |
+
</td>
|
| 125 |
+
<td align="left"><code>True</code></td>
|
| 126 |
+
<td align="left">if True, top bar is open by default.</td>
|
| 127 |
+
</tr>
|
| 128 |
+
|
| 129 |
+
<tr>
|
| 130 |
+
<td align="left"><code>visible</code></td>
|
| 131 |
+
<td align="left" style="width: 25%;">
|
| 132 |
+
|
| 133 |
+
```python
|
| 134 |
+
bool
|
| 135 |
+
```
|
| 136 |
+
|
| 137 |
+
</td>
|
| 138 |
+
<td align="left"><code>True</code></td>
|
| 139 |
+
<td align="left">If False, the component will be hidden.</td>
|
| 140 |
+
</tr>
|
| 141 |
+
|
| 142 |
+
<tr>
|
| 143 |
+
<td align="left"><code>elem_id</code></td>
|
| 144 |
+
<td align="left" style="width: 25%;">
|
| 145 |
+
|
| 146 |
+
```python
|
| 147 |
+
str | None
|
| 148 |
+
```
|
| 149 |
+
|
| 150 |
+
</td>
|
| 151 |
+
<td align="left"><code>None</code></td>
|
| 152 |
+
<td align="left">An optional string that is assigned as the id of this component in the HTML DOM. Can be used for targeting CSS styles.</td>
|
| 153 |
+
</tr>
|
| 154 |
+
|
| 155 |
+
<tr>
|
| 156 |
+
<td align="left"><code>elem_classes</code></td>
|
| 157 |
+
<td align="left" style="width: 25%;">
|
| 158 |
+
|
| 159 |
+
```python
|
| 160 |
+
list[str] | str | None
|
| 161 |
+
```
|
| 162 |
+
|
| 163 |
+
</td>
|
| 164 |
+
<td align="left"><code>None</code></td>
|
| 165 |
+
<td align="left">An optional string or list of strings that are assigned as the class of this component in the HTML DOM. Can be used for targeting CSS styles.</td>
|
| 166 |
+
</tr>
|
| 167 |
+
|
| 168 |
+
<tr>
|
| 169 |
+
<td align="left"><code>render</code></td>
|
| 170 |
+
<td align="left" style="width: 25%;">
|
| 171 |
+
|
| 172 |
+
```python
|
| 173 |
+
bool
|
| 174 |
+
```
|
| 175 |
+
|
| 176 |
+
</td>
|
| 177 |
+
<td align="left"><code>True</code></td>
|
| 178 |
+
<td align="left">If False, this layout will not be rendered in the Blocks context. Should be used if the intention is to assign event listeners now but render the component later.</td>
|
| 179 |
+
</tr>
|
| 180 |
+
|
| 181 |
+
<tr>
|
| 182 |
+
<td align="left"><code>height</code></td>
|
| 183 |
+
<td align="left" style="width: 25%;">
|
| 184 |
+
|
| 185 |
+
```python
|
| 186 |
+
int | str
|
| 187 |
+
```
|
| 188 |
+
|
| 189 |
+
</td>
|
| 190 |
+
<td align="left"><code>320</code></td>
|
| 191 |
+
<td align="left">The height of the top bar, specified in pixels if a number is passed, or in CSS units if a string is passed.</td>
|
| 192 |
+
</tr>
|
| 193 |
+
|
| 194 |
+
<tr>
|
| 195 |
+
<td align="left"><code>width</code></td>
|
| 196 |
+
<td align="left" style="width: 25%;">
|
| 197 |
+
|
| 198 |
+
```python
|
| 199 |
+
int | str
|
| 200 |
+
```
|
| 201 |
+
|
| 202 |
+
</td>
|
| 203 |
+
<td align="left"><code>"100%"</code></td>
|
| 204 |
+
<td align="left">The width of the top bar, specified in pixels if a number is passed, or in CSS units (like "80%") if a string is passed. The bar will be horizontally centered.</td>
|
| 205 |
+
</tr>
|
| 206 |
+
|
| 207 |
+
<tr>
|
| 208 |
+
<td align="left"><code>bring_to_front</code></td>
|
| 209 |
+
<td align="left" style="width: 25%;">
|
| 210 |
+
|
| 211 |
+
```python
|
| 212 |
+
bool
|
| 213 |
+
```
|
| 214 |
+
|
| 215 |
+
</td>
|
| 216 |
+
<td align="left"><code>False</code></td>
|
| 217 |
+
<td align="left">If True, the TopBar will be rendered on top of all other elements with a higher z-index. Defaults to False.</td>
|
| 218 |
+
</tr>
|
| 219 |
+
|
| 220 |
+
<tr>
|
| 221 |
+
<td align="left"><code>key</code></td>
|
| 222 |
+
<td align="left" style="width: 25%;">
|
| 223 |
+
|
| 224 |
+
```python
|
| 225 |
+
int | str | tuple[int | str, Ellipsis] | None
|
| 226 |
+
```
|
| 227 |
+
|
| 228 |
+
</td>
|
| 229 |
+
<td align="left"><code>None</code></td>
|
| 230 |
+
<td align="left">in a gr.render, Components with the same key across re-renders are treated as the same component, not a new component. Properties set in 'preserved_by_key' are not reset across a re-render.</td>
|
| 231 |
+
</tr>
|
| 232 |
+
|
| 233 |
+
<tr>
|
| 234 |
+
<td align="left"><code>preserved_by_key</code></td>
|
| 235 |
+
<td align="left" style="width: 25%;">
|
| 236 |
+
|
| 237 |
+
```python
|
| 238 |
+
list[str] | str | None
|
| 239 |
+
```
|
| 240 |
+
|
| 241 |
+
</td>
|
| 242 |
+
<td align="left"><code>None</code></td>
|
| 243 |
+
<td align="left">A list of parameters from this component's constructor. Inside a gr.render() function, if a component is re-rendered with the same key, these (and only these) parameters will be preserved in the UI (if they have been changed by the user or an event listener) instead of re-rendered based on the values provided during constructor.</td>
|
| 244 |
+
</tr>
|
| 245 |
+
</tbody></table>
|
| 246 |
+
|
| 247 |
+
|
| 248 |
+
### Events
|
| 249 |
+
|
| 250 |
+
| name | description |
|
| 251 |
+
|:-----|:------------|
|
| 252 |
+
| `expand` | This listener is triggered when the TopBar is expanded. |
|
| 253 |
+
| `collapse` | This listener is triggered when the TopBar is collapsed. |
|
| 254 |
+
|
| 255 |
+
|
| 256 |
+
|
__init__.py
ADDED
|
File without changes
|
app.py
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# demo/app.py
|
| 2 |
+
|
| 3 |
+
import gradio as gr
|
| 4 |
+
import time
|
| 5 |
+
from gradio_topbar import TopBar
|
| 6 |
+
|
| 7 |
+
def chat_response(message, history):
|
| 8 |
+
history = history or ""
|
| 9 |
+
history += f"You: {message}\n"
|
| 10 |
+
time.sleep(1) # Simulate thinking
|
| 11 |
+
history += f"Bot: Thanks for the message! You said: '{message}'\n"
|
| 12 |
+
return history, ""
|
| 13 |
+
|
| 14 |
+
def update_label(value):
|
| 15 |
+
return f"Current temperature is: {value}"
|
| 16 |
+
|
| 17 |
+
with gr.Blocks(theme=gr.themes.Ocean(), title="Full Layout Demo") as demo:
|
| 18 |
+
gr.Markdown(
|
| 19 |
+
"""
|
| 20 |
+
# TopBar Demo
|
| 21 |
+
This demo shows the `TopBar` with `width="50%"` and `bring_to_front=True`.
|
| 22 |
+
Notice how the bar is now horizontally centered and no longer full-width.
|
| 23 |
+
"""
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
with TopBar(open=True, height=180, width="50%", bring_to_front=True):
|
| 27 |
+
with gr.Row():
|
| 28 |
+
message_box = gr.Textbox(
|
| 29 |
+
show_label=False,
|
| 30 |
+
placeholder="Type your message here...",
|
| 31 |
+
elem_id="message-input",
|
| 32 |
+
scale=7
|
| 33 |
+
)
|
| 34 |
+
send_button = gr.Button("Send", variant="primary", scale=1)
|
| 35 |
+
with gr.Row():
|
| 36 |
+
gr.Button("Upload File")
|
| 37 |
+
gr.Button("Record Audio")
|
| 38 |
+
gr.ClearButton([message_box])
|
| 39 |
+
|
| 40 |
+
with gr.Row(equal_height=True):
|
| 41 |
+
with gr.Column(scale=3):
|
| 42 |
+
gr.Markdown("### 🤖 Chat Interface")
|
| 43 |
+
chatbot_display = gr.Textbox(
|
| 44 |
+
label="Chat History",
|
| 45 |
+
lines=25,
|
| 46 |
+
interactive=False
|
| 47 |
+
)
|
| 48 |
+
|
| 49 |
+
send_button.click(
|
| 50 |
+
fn=chat_response,
|
| 51 |
+
inputs=[message_box, chatbot_display],
|
| 52 |
+
outputs=[chatbot_display, message_box]
|
| 53 |
+
)
|
| 54 |
+
message_box.submit(
|
| 55 |
+
fn=chat_response,
|
| 56 |
+
inputs=[message_box, chatbot_display],
|
| 57 |
+
outputs=[chatbot_display, message_box]
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
if __name__ == "__main__":
|
| 61 |
+
demo.launch(debug=True)
|
css.css
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
html {
|
| 2 |
+
font-family: Inter;
|
| 3 |
+
font-size: 16px;
|
| 4 |
+
font-weight: 400;
|
| 5 |
+
line-height: 1.5;
|
| 6 |
+
-webkit-text-size-adjust: 100%;
|
| 7 |
+
background: #fff;
|
| 8 |
+
color: #323232;
|
| 9 |
+
-webkit-font-smoothing: antialiased;
|
| 10 |
+
-moz-osx-font-smoothing: grayscale;
|
| 11 |
+
text-rendering: optimizeLegibility;
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
:root {
|
| 15 |
+
--space: 1;
|
| 16 |
+
--vspace: calc(var(--space) * 1rem);
|
| 17 |
+
--vspace-0: calc(3 * var(--space) * 1rem);
|
| 18 |
+
--vspace-1: calc(2 * var(--space) * 1rem);
|
| 19 |
+
--vspace-2: calc(1.5 * var(--space) * 1rem);
|
| 20 |
+
--vspace-3: calc(0.5 * var(--space) * 1rem);
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
.app {
|
| 24 |
+
max-width: 748px !important;
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
.prose p {
|
| 28 |
+
margin: var(--vspace) 0;
|
| 29 |
+
line-height: var(--vspace * 2);
|
| 30 |
+
font-size: 1rem;
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
code {
|
| 34 |
+
font-family: "Inconsolata", sans-serif;
|
| 35 |
+
font-size: 16px;
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
h1,
|
| 39 |
+
h1 code {
|
| 40 |
+
font-weight: 400;
|
| 41 |
+
line-height: calc(2.5 / var(--space) * var(--vspace));
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
h1 code {
|
| 45 |
+
background: none;
|
| 46 |
+
border: none;
|
| 47 |
+
letter-spacing: 0.05em;
|
| 48 |
+
padding-bottom: 5px;
|
| 49 |
+
position: relative;
|
| 50 |
+
padding: 0;
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
h2 {
|
| 54 |
+
margin: var(--vspace-1) 0 var(--vspace-2) 0;
|
| 55 |
+
line-height: 1em;
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
h3,
|
| 59 |
+
h3 code {
|
| 60 |
+
margin: var(--vspace-1) 0 var(--vspace-2) 0;
|
| 61 |
+
line-height: 1em;
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
h4,
|
| 65 |
+
h5,
|
| 66 |
+
h6 {
|
| 67 |
+
margin: var(--vspace-3) 0 var(--vspace-3) 0;
|
| 68 |
+
line-height: var(--vspace);
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
.bigtitle,
|
| 72 |
+
h1,
|
| 73 |
+
h1 code {
|
| 74 |
+
font-size: calc(8px * 4.5);
|
| 75 |
+
word-break: break-word;
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
.title,
|
| 79 |
+
h2,
|
| 80 |
+
h2 code {
|
| 81 |
+
font-size: calc(8px * 3.375);
|
| 82 |
+
font-weight: lighter;
|
| 83 |
+
word-break: break-word;
|
| 84 |
+
border: none;
|
| 85 |
+
background: none;
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
.subheading1,
|
| 89 |
+
h3,
|
| 90 |
+
h3 code {
|
| 91 |
+
font-size: calc(8px * 1.8);
|
| 92 |
+
font-weight: 600;
|
| 93 |
+
border: none;
|
| 94 |
+
background: none;
|
| 95 |
+
letter-spacing: 0.1em;
|
| 96 |
+
text-transform: uppercase;
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
h2 code {
|
| 100 |
+
padding: 0;
|
| 101 |
+
position: relative;
|
| 102 |
+
letter-spacing: 0.05em;
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
blockquote {
|
| 106 |
+
font-size: calc(8px * 1.1667);
|
| 107 |
+
font-style: italic;
|
| 108 |
+
line-height: calc(1.1667 * var(--vspace));
|
| 109 |
+
margin: var(--vspace-2) var(--vspace-2);
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
.subheading2,
|
| 113 |
+
h4 {
|
| 114 |
+
font-size: calc(8px * 1.4292);
|
| 115 |
+
text-transform: uppercase;
|
| 116 |
+
font-weight: 600;
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
.subheading3,
|
| 120 |
+
h5 {
|
| 121 |
+
font-size: calc(8px * 1.2917);
|
| 122 |
+
line-height: calc(1.2917 * var(--vspace));
|
| 123 |
+
|
| 124 |
+
font-weight: lighter;
|
| 125 |
+
text-transform: uppercase;
|
| 126 |
+
letter-spacing: 0.15em;
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
h6 {
|
| 130 |
+
font-size: calc(8px * 1.1667);
|
| 131 |
+
font-size: 1.1667em;
|
| 132 |
+
font-weight: normal;
|
| 133 |
+
font-style: italic;
|
| 134 |
+
font-family: "le-monde-livre-classic-byol", serif !important;
|
| 135 |
+
letter-spacing: 0px !important;
|
| 136 |
+
}
|
| 137 |
+
|
| 138 |
+
#start .md > *:first-child {
|
| 139 |
+
margin-top: 0;
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
h2 + h3 {
|
| 143 |
+
margin-top: 0;
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
.md hr {
|
| 147 |
+
border: none;
|
| 148 |
+
border-top: 1px solid var(--block-border-color);
|
| 149 |
+
margin: var(--vspace-2) 0 var(--vspace-2) 0;
|
| 150 |
+
}
|
| 151 |
+
.prose ul {
|
| 152 |
+
margin: var(--vspace-2) 0 var(--vspace-1) 0;
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
.gap {
|
| 156 |
+
gap: 0;
|
| 157 |
+
}
|
requirements.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
gradio_topbar
|
space.py
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
import gradio as gr
|
| 3 |
+
from app import demo as app
|
| 4 |
+
import os
|
| 5 |
+
|
| 6 |
+
_docs = {'TopBar': {'description': 'TopBar is a collapsible panel that renders child components in a fixed bar at the top of the page.\n with gr.Blocks() as demo:\n with gr.TopBar(height=200, width="80%"):\n gr.Textbox(label="Enter your text here")\n gr.Button("Submit")', 'members': {'__init__': {'label': {'type': 'str | gradio.i18n.I18nData | None', 'default': 'None', 'description': 'name of the top bar. Not displayed to the user.'}, 'open': {'type': 'bool', 'default': 'True', 'description': 'if True, top bar is open by default.'}, 'visible': {'type': 'bool', 'default': 'True', 'description': 'If False, the component will be hidden.'}, 'elem_id': {'type': 'str | None', 'default': 'None', 'description': 'An optional string that is assigned as the id of this component in the HTML DOM. Can be used for targeting CSS styles.'}, 'elem_classes': {'type': 'list[str] | str | None', 'default': 'None', 'description': 'An optional string or list of strings that are assigned as the class of this component in the HTML DOM. Can be used for targeting CSS styles.'}, 'render': {'type': 'bool', 'default': 'True', 'description': 'If False, this layout will not be rendered in the Blocks context. Should be used if the intention is to assign event listeners now but render the component later.'}, 'height': {'type': 'int | str', 'default': '320', 'description': 'The height of the top bar, specified in pixels if a number is passed, or in CSS units if a string is passed.'}, 'width': {'type': 'int | str', 'default': '"100%"', 'description': 'The width of the top bar, specified in pixels if a number is passed, or in CSS units (like "80%") if a string is passed. The bar will be horizontally centered.'}, 'bring_to_front': {'type': 'bool', 'default': 'False', 'description': 'If True, the TopBar will be rendered on top of all other elements with a higher z-index. Defaults to False.'}, 'key': {'type': 'int | str | tuple[int | str, Ellipsis] | None', 'default': 'None', 'description': "in a gr.render, Components with the same key across re-renders are treated as the same component, not a new component. Properties set in 'preserved_by_key' are not reset across a re-render."}, 'preserved_by_key': {'type': 'list[str] | str | None', 'default': 'None', 'description': "A list of parameters from this component's constructor. Inside a gr.render() function, if a component is re-rendered with the same key, these (and only these) parameters will be preserved in the UI (if they have been changed by the user or an event listener) instead of re-rendered based on the values provided during constructor."}}, 'postprocess': {}}, 'events': {'expand': {'type': None, 'default': None, 'description': 'This listener is triggered when the TopBar is expanded.'}, 'collapse': {'type': None, 'default': None, 'description': 'This listener is triggered when the TopBar is collapsed.'}}}, '__meta__': {'additional_interfaces': {}}}
|
| 7 |
+
|
| 8 |
+
abs_path = os.path.join(os.path.dirname(__file__), "css.css")
|
| 9 |
+
|
| 10 |
+
with gr.Blocks(
|
| 11 |
+
css=abs_path,
|
| 12 |
+
theme=gr.themes.Ocean(
|
| 13 |
+
font_mono=[
|
| 14 |
+
gr.themes.GoogleFont("Inconsolata"),
|
| 15 |
+
"monospace",
|
| 16 |
+
],
|
| 17 |
+
),
|
| 18 |
+
) as demo:
|
| 19 |
+
gr.Markdown(
|
| 20 |
+
"""
|
| 21 |
+
# `gradio_topbar`
|
| 22 |
+
|
| 23 |
+
<div style="display: flex; gap: 7px;">
|
| 24 |
+
<img alt="Static Badge" src="https://img.shields.io/badge/version%20-%200.0.1%20-%20orange">
|
| 25 |
+
</div>
|
| 26 |
+
|
| 27 |
+
A TopBar for Gradio Interface
|
| 28 |
+
""", elem_classes=["md-custom"], header_links=True)
|
| 29 |
+
app.render()
|
| 30 |
+
gr.Markdown(
|
| 31 |
+
"""
|
| 32 |
+
## Installation
|
| 33 |
+
|
| 34 |
+
```bash
|
| 35 |
+
pip install gradio_topbar
|
| 36 |
+
```
|
| 37 |
+
|
| 38 |
+
## Usage
|
| 39 |
+
|
| 40 |
+
```python
|
| 41 |
+
# demo/app.py
|
| 42 |
+
|
| 43 |
+
import gradio as gr
|
| 44 |
+
import time
|
| 45 |
+
from gradio_topbar import TopBar
|
| 46 |
+
|
| 47 |
+
def chat_response(message, history):
|
| 48 |
+
history = history or ""
|
| 49 |
+
history += f"You: {message}\n"
|
| 50 |
+
time.sleep(1) # Simulate thinking
|
| 51 |
+
history += f"Bot: Thanks for the message! You said: '{message}'\n"
|
| 52 |
+
return history, ""
|
| 53 |
+
|
| 54 |
+
def update_label(value):
|
| 55 |
+
return f"Current temperature is: {value}"
|
| 56 |
+
|
| 57 |
+
with gr.Blocks(theme=gr.themes.Ocean(), title="Full Layout Demo") as demo:
|
| 58 |
+
gr.Markdown(
|
| 59 |
+
\"\"\"
|
| 60 |
+
# TopBar Demo
|
| 61 |
+
This demo shows the `TopBar` with `width="50%"` and `bring_to_front=True`.
|
| 62 |
+
Notice how the bar is now horizontally centered and no longer full-width.
|
| 63 |
+
\"\"\"
|
| 64 |
+
)
|
| 65 |
+
|
| 66 |
+
with TopBar(open=True, height=180, width="50%", bring_to_front=True):
|
| 67 |
+
with gr.Row():
|
| 68 |
+
message_box = gr.Textbox(
|
| 69 |
+
show_label=False,
|
| 70 |
+
placeholder="Type your message here...",
|
| 71 |
+
elem_id="message-input",
|
| 72 |
+
scale=7
|
| 73 |
+
)
|
| 74 |
+
send_button = gr.Button("Send", variant="primary", scale=1)
|
| 75 |
+
with gr.Row():
|
| 76 |
+
gr.Button("Upload File")
|
| 77 |
+
gr.Button("Record Audio")
|
| 78 |
+
gr.ClearButton([message_box])
|
| 79 |
+
|
| 80 |
+
with gr.Row(equal_height=True):
|
| 81 |
+
with gr.Column(scale=3):
|
| 82 |
+
gr.Markdown("### 🤖 Chat Interface")
|
| 83 |
+
chatbot_display = gr.Textbox(
|
| 84 |
+
label="Chat History",
|
| 85 |
+
lines=25,
|
| 86 |
+
interactive=False
|
| 87 |
+
)
|
| 88 |
+
|
| 89 |
+
send_button.click(
|
| 90 |
+
fn=chat_response,
|
| 91 |
+
inputs=[message_box, chatbot_display],
|
| 92 |
+
outputs=[chatbot_display, message_box]
|
| 93 |
+
)
|
| 94 |
+
message_box.submit(
|
| 95 |
+
fn=chat_response,
|
| 96 |
+
inputs=[message_box, chatbot_display],
|
| 97 |
+
outputs=[chatbot_display, message_box]
|
| 98 |
+
)
|
| 99 |
+
|
| 100 |
+
if __name__ == "__main__":
|
| 101 |
+
demo.launch(debug=True)
|
| 102 |
+
```
|
| 103 |
+
""", elem_classes=["md-custom"], header_links=True)
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
gr.Markdown("""
|
| 107 |
+
## `TopBar`
|
| 108 |
+
|
| 109 |
+
### Initialization
|
| 110 |
+
""", elem_classes=["md-custom"], header_links=True)
|
| 111 |
+
|
| 112 |
+
gr.ParamViewer(value=_docs["TopBar"]["members"]["__init__"], linkify=[])
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
gr.Markdown("### Events")
|
| 116 |
+
gr.ParamViewer(value=_docs["TopBar"]["events"], linkify=['Event'])
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
demo.load(None, js=r"""function() {
|
| 125 |
+
const refs = {};
|
| 126 |
+
const user_fn_refs = {};
|
| 127 |
+
requestAnimationFrame(() => {
|
| 128 |
+
|
| 129 |
+
Object.entries(user_fn_refs).forEach(([key, refs]) => {
|
| 130 |
+
if (refs.length > 0) {
|
| 131 |
+
const el = document.querySelector(`.${key}-user-fn`);
|
| 132 |
+
if (!el) return;
|
| 133 |
+
refs.forEach(ref => {
|
| 134 |
+
el.innerHTML = el.innerHTML.replace(
|
| 135 |
+
new RegExp("\\b"+ref+"\\b", "g"),
|
| 136 |
+
`<a href="#h-${ref.toLowerCase()}">${ref}</a>`
|
| 137 |
+
);
|
| 138 |
+
})
|
| 139 |
+
}
|
| 140 |
+
})
|
| 141 |
+
|
| 142 |
+
Object.entries(refs).forEach(([key, refs]) => {
|
| 143 |
+
if (refs.length > 0) {
|
| 144 |
+
const el = document.querySelector(`.${key}`);
|
| 145 |
+
if (!el) return;
|
| 146 |
+
refs.forEach(ref => {
|
| 147 |
+
el.innerHTML = el.innerHTML.replace(
|
| 148 |
+
new RegExp("\\b"+ref+"\\b", "g"),
|
| 149 |
+
`<a href="#h-${ref.toLowerCase()}">${ref}</a>`
|
| 150 |
+
);
|
| 151 |
+
})
|
| 152 |
+
}
|
| 153 |
+
})
|
| 154 |
+
})
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
""")
|
| 158 |
+
|
| 159 |
+
demo.launch()
|
src/.gitignore
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.eggs/
|
| 2 |
+
dist/
|
| 3 |
+
.vscode/
|
| 4 |
+
*.pyc
|
| 5 |
+
__pycache__/
|
| 6 |
+
*.py[cod]
|
| 7 |
+
*$py.class
|
| 8 |
+
__tmp/*
|
| 9 |
+
*.pyi
|
| 10 |
+
.mypycache
|
| 11 |
+
.ruff_cache
|
| 12 |
+
node_modules
|
| 13 |
+
backend/**/templates/
|
| 14 |
+
README_TEMPLATE.md
|
src/README.md
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
tags: [gradio-custom-component, SideBar]
|
| 3 |
+
title: gradio_topbar
|
| 4 |
+
short_description: A TopBar for Gradio Interface
|
| 5 |
+
colorFrom: blue
|
| 6 |
+
colorTo: yellow
|
| 7 |
+
sdk: gradio
|
| 8 |
+
pinned: false
|
| 9 |
+
app_file: space.py
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
# `gradio_topbar`
|
| 13 |
+
<img alt="Static Badge" src="https://img.shields.io/badge/version%20-%200.0.1%20-%20orange">
|
| 14 |
+
|
| 15 |
+
A TopBar for Gradio Interface
|
| 16 |
+
|
| 17 |
+
## Installation
|
| 18 |
+
|
| 19 |
+
```bash
|
| 20 |
+
pip install gradio_topbar
|
| 21 |
+
```
|
| 22 |
+
|
| 23 |
+
## Usage
|
| 24 |
+
|
| 25 |
+
```python
|
| 26 |
+
# demo/app.py
|
| 27 |
+
|
| 28 |
+
import gradio as gr
|
| 29 |
+
import time
|
| 30 |
+
from gradio_topbar import TopBar
|
| 31 |
+
|
| 32 |
+
def chat_response(message, history):
|
| 33 |
+
history = history or ""
|
| 34 |
+
history += f"You: {message}\n"
|
| 35 |
+
time.sleep(1) # Simulate thinking
|
| 36 |
+
history += f"Bot: Thanks for the message! You said: '{message}'\n"
|
| 37 |
+
return history, ""
|
| 38 |
+
|
| 39 |
+
def update_label(value):
|
| 40 |
+
return f"Current temperature is: {value}"
|
| 41 |
+
|
| 42 |
+
with gr.Blocks(theme=gr.themes.Ocean(), title="Full Layout Demo") as demo:
|
| 43 |
+
gr.Markdown(
|
| 44 |
+
"""
|
| 45 |
+
# TopBar Demo
|
| 46 |
+
This demo shows the `TopBar` with `width="50%"` and `bring_to_front=True`.
|
| 47 |
+
Notice how the bar is now horizontally centered and no longer full-width.
|
| 48 |
+
"""
|
| 49 |
+
)
|
| 50 |
+
|
| 51 |
+
with TopBar(open=True, height=180, width="50%", bring_to_front=True):
|
| 52 |
+
with gr.Row():
|
| 53 |
+
message_box = gr.Textbox(
|
| 54 |
+
show_label=False,
|
| 55 |
+
placeholder="Type your message here...",
|
| 56 |
+
elem_id="message-input",
|
| 57 |
+
scale=7
|
| 58 |
+
)
|
| 59 |
+
send_button = gr.Button("Send", variant="primary", scale=1)
|
| 60 |
+
with gr.Row():
|
| 61 |
+
gr.Button("Upload File")
|
| 62 |
+
gr.Button("Record Audio")
|
| 63 |
+
gr.ClearButton([message_box])
|
| 64 |
+
|
| 65 |
+
with gr.Row(equal_height=True):
|
| 66 |
+
with gr.Column(scale=3):
|
| 67 |
+
gr.Markdown("### 🤖 Chat Interface")
|
| 68 |
+
chatbot_display = gr.Textbox(
|
| 69 |
+
label="Chat History",
|
| 70 |
+
lines=25,
|
| 71 |
+
interactive=False
|
| 72 |
+
)
|
| 73 |
+
|
| 74 |
+
send_button.click(
|
| 75 |
+
fn=chat_response,
|
| 76 |
+
inputs=[message_box, chatbot_display],
|
| 77 |
+
outputs=[chatbot_display, message_box]
|
| 78 |
+
)
|
| 79 |
+
message_box.submit(
|
| 80 |
+
fn=chat_response,
|
| 81 |
+
inputs=[message_box, chatbot_display],
|
| 82 |
+
outputs=[chatbot_display, message_box]
|
| 83 |
+
)
|
| 84 |
+
|
| 85 |
+
if __name__ == "__main__":
|
| 86 |
+
demo.launch(debug=True)
|
| 87 |
+
```
|
| 88 |
+
|
| 89 |
+
## `TopBar`
|
| 90 |
+
|
| 91 |
+
### Initialization
|
| 92 |
+
|
| 93 |
+
<table>
|
| 94 |
+
<thead>
|
| 95 |
+
<tr>
|
| 96 |
+
<th align="left">name</th>
|
| 97 |
+
<th align="left" style="width: 25%;">type</th>
|
| 98 |
+
<th align="left">default</th>
|
| 99 |
+
<th align="left">description</th>
|
| 100 |
+
</tr>
|
| 101 |
+
</thead>
|
| 102 |
+
<tbody>
|
| 103 |
+
<tr>
|
| 104 |
+
<td align="left"><code>label</code></td>
|
| 105 |
+
<td align="left" style="width: 25%;">
|
| 106 |
+
|
| 107 |
+
```python
|
| 108 |
+
str | gradio.i18n.I18nData | None
|
| 109 |
+
```
|
| 110 |
+
|
| 111 |
+
</td>
|
| 112 |
+
<td align="left"><code>None</code></td>
|
| 113 |
+
<td align="left">name of the top bar. Not displayed to the user.</td>
|
| 114 |
+
</tr>
|
| 115 |
+
|
| 116 |
+
<tr>
|
| 117 |
+
<td align="left"><code>open</code></td>
|
| 118 |
+
<td align="left" style="width: 25%;">
|
| 119 |
+
|
| 120 |
+
```python
|
| 121 |
+
bool
|
| 122 |
+
```
|
| 123 |
+
|
| 124 |
+
</td>
|
| 125 |
+
<td align="left"><code>True</code></td>
|
| 126 |
+
<td align="left">if True, top bar is open by default.</td>
|
| 127 |
+
</tr>
|
| 128 |
+
|
| 129 |
+
<tr>
|
| 130 |
+
<td align="left"><code>visible</code></td>
|
| 131 |
+
<td align="left" style="width: 25%;">
|
| 132 |
+
|
| 133 |
+
```python
|
| 134 |
+
bool
|
| 135 |
+
```
|
| 136 |
+
|
| 137 |
+
</td>
|
| 138 |
+
<td align="left"><code>True</code></td>
|
| 139 |
+
<td align="left">If False, the component will be hidden.</td>
|
| 140 |
+
</tr>
|
| 141 |
+
|
| 142 |
+
<tr>
|
| 143 |
+
<td align="left"><code>elem_id</code></td>
|
| 144 |
+
<td align="left" style="width: 25%;">
|
| 145 |
+
|
| 146 |
+
```python
|
| 147 |
+
str | None
|
| 148 |
+
```
|
| 149 |
+
|
| 150 |
+
</td>
|
| 151 |
+
<td align="left"><code>None</code></td>
|
| 152 |
+
<td align="left">An optional string that is assigned as the id of this component in the HTML DOM. Can be used for targeting CSS styles.</td>
|
| 153 |
+
</tr>
|
| 154 |
+
|
| 155 |
+
<tr>
|
| 156 |
+
<td align="left"><code>elem_classes</code></td>
|
| 157 |
+
<td align="left" style="width: 25%;">
|
| 158 |
+
|
| 159 |
+
```python
|
| 160 |
+
list[str] | str | None
|
| 161 |
+
```
|
| 162 |
+
|
| 163 |
+
</td>
|
| 164 |
+
<td align="left"><code>None</code></td>
|
| 165 |
+
<td align="left">An optional string or list of strings that are assigned as the class of this component in the HTML DOM. Can be used for targeting CSS styles.</td>
|
| 166 |
+
</tr>
|
| 167 |
+
|
| 168 |
+
<tr>
|
| 169 |
+
<td align="left"><code>render</code></td>
|
| 170 |
+
<td align="left" style="width: 25%;">
|
| 171 |
+
|
| 172 |
+
```python
|
| 173 |
+
bool
|
| 174 |
+
```
|
| 175 |
+
|
| 176 |
+
</td>
|
| 177 |
+
<td align="left"><code>True</code></td>
|
| 178 |
+
<td align="left">If False, this layout will not be rendered in the Blocks context. Should be used if the intention is to assign event listeners now but render the component later.</td>
|
| 179 |
+
</tr>
|
| 180 |
+
|
| 181 |
+
<tr>
|
| 182 |
+
<td align="left"><code>height</code></td>
|
| 183 |
+
<td align="left" style="width: 25%;">
|
| 184 |
+
|
| 185 |
+
```python
|
| 186 |
+
int | str
|
| 187 |
+
```
|
| 188 |
+
|
| 189 |
+
</td>
|
| 190 |
+
<td align="left"><code>320</code></td>
|
| 191 |
+
<td align="left">The height of the top bar, specified in pixels if a number is passed, or in CSS units if a string is passed.</td>
|
| 192 |
+
</tr>
|
| 193 |
+
|
| 194 |
+
<tr>
|
| 195 |
+
<td align="left"><code>width</code></td>
|
| 196 |
+
<td align="left" style="width: 25%;">
|
| 197 |
+
|
| 198 |
+
```python
|
| 199 |
+
int | str
|
| 200 |
+
```
|
| 201 |
+
|
| 202 |
+
</td>
|
| 203 |
+
<td align="left"><code>"100%"</code></td>
|
| 204 |
+
<td align="left">The width of the top bar, specified in pixels if a number is passed, or in CSS units (like "80%") if a string is passed. The bar will be horizontally centered.</td>
|
| 205 |
+
</tr>
|
| 206 |
+
|
| 207 |
+
<tr>
|
| 208 |
+
<td align="left"><code>bring_to_front</code></td>
|
| 209 |
+
<td align="left" style="width: 25%;">
|
| 210 |
+
|
| 211 |
+
```python
|
| 212 |
+
bool
|
| 213 |
+
```
|
| 214 |
+
|
| 215 |
+
</td>
|
| 216 |
+
<td align="left"><code>False</code></td>
|
| 217 |
+
<td align="left">If True, the TopBar will be rendered on top of all other elements with a higher z-index. Defaults to False.</td>
|
| 218 |
+
</tr>
|
| 219 |
+
|
| 220 |
+
<tr>
|
| 221 |
+
<td align="left"><code>key</code></td>
|
| 222 |
+
<td align="left" style="width: 25%;">
|
| 223 |
+
|
| 224 |
+
```python
|
| 225 |
+
int | str | tuple[int | str, Ellipsis] | None
|
| 226 |
+
```
|
| 227 |
+
|
| 228 |
+
</td>
|
| 229 |
+
<td align="left"><code>None</code></td>
|
| 230 |
+
<td align="left">in a gr.render, Components with the same key across re-renders are treated as the same component, not a new component. Properties set in 'preserved_by_key' are not reset across a re-render.</td>
|
| 231 |
+
</tr>
|
| 232 |
+
|
| 233 |
+
<tr>
|
| 234 |
+
<td align="left"><code>preserved_by_key</code></td>
|
| 235 |
+
<td align="left" style="width: 25%;">
|
| 236 |
+
|
| 237 |
+
```python
|
| 238 |
+
list[str] | str | None
|
| 239 |
+
```
|
| 240 |
+
|
| 241 |
+
</td>
|
| 242 |
+
<td align="left"><code>None</code></td>
|
| 243 |
+
<td align="left">A list of parameters from this component's constructor. Inside a gr.render() function, if a component is re-rendered with the same key, these (and only these) parameters will be preserved in the UI (if they have been changed by the user or an event listener) instead of re-rendered based on the values provided during constructor.</td>
|
| 244 |
+
</tr>
|
| 245 |
+
</tbody></table>
|
| 246 |
+
|
| 247 |
+
|
| 248 |
+
### Events
|
| 249 |
+
|
| 250 |
+
| name | description |
|
| 251 |
+
|:-----|:------------|
|
| 252 |
+
| `expand` | This listener is triggered when the TopBar is expanded. |
|
| 253 |
+
| `collapse` | This listener is triggered when the TopBar is collapsed. |
|
| 254 |
+
|
| 255 |
+
|
| 256 |
+
|
src/backend/gradio_topbar/__init__.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
from .topbar import TopBar
|
| 3 |
+
|
| 4 |
+
__all__ = ['TopBar']
|
src/backend/gradio_topbar/templates/component/index.js
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
src/backend/gradio_topbar/templates/component/style.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
.top-bar.svelte-isy6s1.svelte-isy6s1{display:flex;flex-direction:column;position:fixed;top:0;left:50%;background-color:var(--background-fill-secondary);transform:translate(-50%) translateY(-100%);z-index:1000;border-bottom:1px solid var(--border-color-primary);box-shadow:0 2px 5px #0000000d}.top-bar.on-top.svelte-isy6s1.svelte-isy6s1{z-index:2000}.top-bar.svelte-isy6s1.svelte-isy6s1:not(.reduce-motion){transition:transform .3s ease-in-out}.top-bar.open.svelte-isy6s1.svelte-isy6s1{transform:translate(-50%) translateY(0)}.toggle-top-button.svelte-isy6s1.svelte-isy6s1{position:absolute;bottom:0;left:50%;transform:translate(-50%,100%);background:var(--background-fill-secondary);border:1px solid var(--border-color-primary);border-top:none;cursor:pointer;padding:var(--size-2);display:flex;align-items:center;justify-content:center;width:var(--size-10);height:var(--size-6);z-index:inherit;border-radius:0 0 var(--radius-lg) var(--radius-lg)}.toggle-top-button.svelte-isy6s1.svelte-isy6s1:not(.reduce-motion){transition:all .3s ease-in-out}.chevron.svelte-isy6s1.svelte-isy6s1{position:relative;display:block;width:var(--size-3);height:var(--size-3)}.chevron-arrow.svelte-isy6s1.svelte-isy6s1{position:absolute;box-sizing:border-box;top:10%;left:18%;width:80%;height:80%;border-bottom:var(--size-0-5) solid var(--body-text-color);border-right:var(--size-0-5) solid var(--body-text-color);transform:rotate(45deg)}.top-bar.svelte-isy6s1:not(.reduce-motion) .chevron-arrow.svelte-isy6s1{transition:transform .3s ease-in-out}.top-bar.open.svelte-isy6s1 .chevron-arrow.svelte-isy6s1{transform:rotate(-135deg)}.bar-content.svelte-isy6s1.svelte-isy6s1{padding:var(--size-4);overflow-y:auto;flex-grow:1}svg.svelte-43sxxs.svelte-43sxxs{width:var(--size-20);height:var(--size-20)}svg.svelte-43sxxs path.svelte-43sxxs{fill:var(--loader-color)}div.svelte-43sxxs.svelte-43sxxs{z-index:var(--layer-2)}.margin.svelte-43sxxs.svelte-43sxxs{margin:var(--size-4)}.block.svelte-239wnu{position:relative;margin:0;box-shadow:var(--block-shadow);border-width:var(--block-border-width);border-color:var(--block-border-color);border-radius:var(--block-radius);background:var(--block-background-fill);width:100%;line-height:var(--line-sm)}.block.fullscreen.svelte-239wnu{border-radius:0}.auto-margin.svelte-239wnu{margin-left:auto;margin-right:auto}.block.border_focus.svelte-239wnu{border-color:var(--color-accent)}.block.border_contrast.svelte-239wnu{border-color:var(--body-text-color)}.padded.svelte-239wnu{padding:var(--block-padding)}.hidden.svelte-239wnu{display:none}.flex.svelte-239wnu{display:flex;flex-direction:column}.hide-container.svelte-239wnu:not(.fullscreen){margin:0;box-shadow:none;--block-border-width:0;background:transparent;padding:0;overflow:visible}.resize-handle.svelte-239wnu{position:absolute;bottom:0;right:0;width:10px;height:10px;fill:var(--block-border-color);cursor:nwse-resize}.fullscreen.svelte-239wnu{position:fixed;top:0;left:0;width:100vw;height:100vh;z-index:1000;overflow:auto}.animating.svelte-239wnu{animation:svelte-239wnu-pop-out .1s ease-out forwards}@keyframes svelte-239wnu-pop-out{0%{position:fixed;top:var(--start-top);left:var(--start-left);width:var(--start-width);height:var(--start-height);z-index:100}to{position:fixed;top:0vh;left:0vw;width:100vw;height:100vh;z-index:1000}}.placeholder.svelte-239wnu{border-radius:var(--block-radius);border-width:var(--block-border-width);border-color:var(--block-border-color);border-style:dashed}Tables */ table,tr,td,th{margin-top:var(--spacing-sm);margin-bottom:var(--spacing-sm);padding:var(--spacing-xl)}.md code,.md pre{background:none;font-family:var(--font-mono);font-size:var(--text-sm);text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:2;tab-size:2;-webkit-hyphens:none;hyphens:none}.md pre[class*=language-]::selection,.md pre[class*=language-] ::selection,.md code[class*=language-]::selection,.md code[class*=language-] ::selection{text-shadow:none;background:#b3d4fc}.md pre{padding:1em;margin:.5em 0;overflow:auto;position:relative;margin-top:var(--spacing-sm);margin-bottom:var(--spacing-sm);box-shadow:none;border:none;border-radius:var(--radius-md);background:var(--code-background-fill);padding:var(--spacing-xxl);font-family:var(--font-mono);text-shadow:none;border-radius:var(--radius-sm);white-space:nowrap;display:block;white-space:pre}.md :not(pre)>code{padding:.1em;border-radius:var(--radius-xs);white-space:normal;background:var(--code-background-fill);border:1px solid var(--panel-border-color);padding:var(--spacing-xxs) var(--spacing-xs)}.md .token.comment,.md .token.prolog,.md .token.doctype,.md .token.cdata{color:#708090}.md .token.punctuation{color:#999}.md .token.namespace{opacity:.7}.md .token.property,.md .token.tag,.md .token.boolean,.md .token.number,.md .token.constant,.md .token.symbol,.md .token.deleted{color:#905}.md .token.selector,.md .token.attr-name,.md .token.string,.md .token.char,.md .token.builtin,.md .token.inserted{color:#690}.md .token.atrule,.md .token.attr-value,.md .token.keyword{color:#07a}.md .token.function,.md .token.class-name{color:#dd4a68}.md .token.regex,.md .token.important,.md .token.variable{color:#e90}.md .token.important,.md .token.bold{font-weight:700}.md .token.italic{font-style:italic}.md .token.entity{cursor:help}.dark .md .token.comment,.dark .md .token.prolog,.dark .md .token.cdata{color:#5c6370}.dark .md .token.doctype,.dark .md .token.punctuation,.dark .md .token.entity{color:#abb2bf}.dark .md .token.attr-name,.dark .md .token.class-name,.dark .md .token.boolean,.dark .md .token.constant,.dark .md .token.number,.dark .md .token.atrule{color:#d19a66}.dark .md .token.keyword{color:#c678dd}.dark .md .token.property,.dark .md .token.tag,.dark .md .token.symbol,.dark .md .token.deleted,.dark .md .token.important{color:#e06c75}.dark .md .token.selector,.dark .md .token.string,.dark .md .token.char,.dark .md .token.builtin,.dark .md .token.inserted,.dark .md .token.regex,.dark .md .token.attr-value,.dark .md .token.attr-value>.token.punctuation{color:#98c379}.dark .md .token.variable,.dark .md .token.operator,.dark .md .token.function{color:#61afef}.dark .md .token.url{color:#56b6c2}span.svelte-1m32c2s div[class*=code_wrap]{position:relative}span.svelte-1m32c2s span.katex{font-size:var(--text-lg);direction:ltr}span.svelte-1m32c2s div[class*=code_wrap]>button{z-index:1;cursor:pointer;border-bottom-left-radius:var(--radius-sm);padding:var(--spacing-md);width:25px;height:25px;position:absolute;right:0}span.svelte-1m32c2s .check{opacity:0;z-index:var(--layer-top);transition:opacity .2s;background:var(--code-background-fill);color:var(--body-text-color);position:absolute;top:var(--size-1-5);left:var(--size-1-5)}span.svelte-1m32c2s p:not(:first-child){margin-top:var(--spacing-xxl)}span.svelte-1m32c2s .md-header-anchor{margin-left:-25px;padding-right:8px;line-height:1;color:var(--body-text-color-subdued);opacity:0}span.svelte-1m32c2s h1:hover .md-header-anchor,span.svelte-1m32c2s h2:hover .md-header-anchor,span.svelte-1m32c2s h3:hover .md-header-anchor,span.svelte-1m32c2s h4:hover .md-header-anchor,span.svelte-1m32c2s h5:hover .md-header-anchor,span.svelte-1m32c2s h6:hover .md-header-anchor{opacity:1}span.md.svelte-1m32c2s .md-header-anchor>svg{color:var(--body-text-color-subdued)}span.svelte-1m32c2s table{word-break:break-word}div.svelte-17qq50w>.md.prose{font-weight:var(--block-info-text-weight);font-size:var(--block-info-text-size);line-height:var(--line-sm)}div.svelte-17qq50w>.md.prose *{color:var(--block-info-text-color)}div.svelte-17qq50w{margin-bottom:var(--spacing-md)}span.has-info.svelte-zgrq3{margin-bottom:var(--spacing-xs)}span.svelte-zgrq3:not(.has-info){margin-bottom:var(--spacing-lg)}span.svelte-zgrq3{display:inline-block;position:relative;z-index:var(--layer-4);border:solid var(--block-title-border-width) var(--block-title-border-color);border-radius:var(--block-title-radius);background:var(--block-title-background-fill);padding:var(--block-title-padding);color:var(--block-title-text-color);font-weight:var(--block-title-text-weight);font-size:var(--block-title-text-size);line-height:var(--line-sm)}span[dir=rtl].svelte-zgrq3{display:block}.hide.svelte-zgrq3{margin:0;height:0}label.svelte-13ao5pu.svelte-13ao5pu{display:inline-flex;align-items:center;z-index:var(--layer-2);box-shadow:var(--block-label-shadow);border:var(--block-label-border-width) solid var(--block-label-border-color);border-top:none;border-left:none;border-radius:var(--block-label-radius);background:var(--block-label-background-fill);padding:var(--block-label-padding);pointer-events:none;color:var(--block-label-text-color);font-weight:var(--block-label-text-weight);font-size:var(--block-label-text-size);line-height:var(--line-sm)}.gr-group label.svelte-13ao5pu.svelte-13ao5pu{border-top-left-radius:0}label.float.svelte-13ao5pu.svelte-13ao5pu{position:absolute;top:var(--block-label-margin);left:var(--block-label-margin)}label.svelte-13ao5pu.svelte-13ao5pu:not(.float){position:static;margin-top:var(--block-label-margin);margin-left:var(--block-label-margin)}.hide.svelte-13ao5pu.svelte-13ao5pu{height:0}span.svelte-13ao5pu.svelte-13ao5pu{opacity:.8;margin-right:var(--size-2);width:calc(var(--block-label-text-size) - 1px);height:calc(var(--block-label-text-size) - 1px)}.hide-label.svelte-13ao5pu.svelte-13ao5pu{box-shadow:none;border-width:0;background:transparent;overflow:visible}label[dir=rtl].svelte-13ao5pu.svelte-13ao5pu{border:var(--block-label-border-width) solid var(--block-label-border-color);border-top:none;border-right:none;border-bottom-left-radius:var(--block-radius);border-bottom-right-radius:var(--block-label-radius);border-top-left-radius:var(--block-label-radius)}label[dir=rtl].svelte-13ao5pu span.svelte-13ao5pu{margin-left:var(--size-2);margin-right:0}button.svelte-qgco6m{display:flex;justify-content:center;align-items:center;gap:1px;z-index:var(--layer-2);border-radius:var(--radius-xs);color:var(--block-label-text-color);border:1px solid transparent;padding:var(--spacing-xxs)}button.svelte-qgco6m:hover{background-color:var(--background-fill-secondary)}button[disabled].svelte-qgco6m{opacity:.5;box-shadow:none}button[disabled].svelte-qgco6m:hover{cursor:not-allowed}.padded.svelte-qgco6m{background:var(--bg-color)}button.svelte-qgco6m:hover,button.highlight.svelte-qgco6m{cursor:pointer;color:var(--color-accent)}.padded.svelte-qgco6m:hover{color:var(--block-label-text-color)}span.svelte-qgco6m{padding:0 1px;font-size:10px}div.svelte-qgco6m{display:flex;align-items:center;justify-content:center;transition:filter .2s ease-in-out}.x-small.svelte-qgco6m{width:10px;height:10px}.small.svelte-qgco6m{width:14px;height:14px}.medium.svelte-qgco6m{width:20px;height:20px}.large.svelte-qgco6m{width:22px;height:22px}.pending.svelte-qgco6m{animation:svelte-qgco6m-flash .5s infinite}@keyframes svelte-qgco6m-flash{0%{opacity:.5}50%{opacity:1}to{opacity:.5}}.transparent.svelte-qgco6m{background:transparent;border:none;box-shadow:none}.empty.svelte-3w3rth{display:flex;justify-content:center;align-items:center;margin-top:calc(0px - var(--size-6));height:var(--size-full)}.icon.svelte-3w3rth{opacity:.5;height:var(--size-5);color:var(--body-text-color)}.small.svelte-3w3rth{min-height:calc(var(--size-32) - 20px)}.large.svelte-3w3rth{min-height:calc(var(--size-64) - 20px)}.unpadded_box.svelte-3w3rth{margin-top:0}.small_parent.svelte-3w3rth{min-height:100%!important}.dropdown-arrow.svelte-145leq6,.dropdown-arrow.svelte-ihhdbf{fill:currentColor}.circle.svelte-ihhdbf{fill:currentColor;opacity:.1}svg.svelte-pb9pol{animation:svelte-pb9pol-spin 1.5s linear infinite}@keyframes svelte-pb9pol-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}h2.svelte-1xg7h5n{font-size:var(--text-xl)!important}p.svelte-1xg7h5n,h2.svelte-1xg7h5n{white-space:pre-line}.wrap.svelte-1xg7h5n{display:flex;flex-direction:column;justify-content:center;align-items:center;min-height:var(--size-60);color:var(--block-label-text-color);line-height:var(--line-md);height:100%;padding-top:var(--size-3);text-align:center;margin:auto var(--spacing-lg)}.or.svelte-1xg7h5n{color:var(--body-text-color-subdued);display:flex}.icon-wrap.svelte-1xg7h5n{width:30px;margin-bottom:var(--spacing-lg)}@media (--screen-md){.wrap.svelte-1xg7h5n{font-size:var(--text-lg)}}.hovered.svelte-1xg7h5n{color:var(--color-accent)}div.svelte-q32hvf{border-top:1px solid transparent;display:flex;max-height:100%;justify-content:center;align-items:center;gap:var(--spacing-sm);height:auto;align-items:flex-end;color:var(--block-label-text-color);flex-shrink:0}.show_border.svelte-q32hvf{border-top:1px solid var(--block-border-color);margin-top:var(--spacing-xxl);box-shadow:var(--shadow-drop)}.source-selection.svelte-15ls1gu{display:flex;align-items:center;justify-content:center;border-top:1px solid var(--border-color-primary);width:100%;margin-left:auto;margin-right:auto;height:var(--size-10)}.icon.svelte-15ls1gu{width:22px;height:22px;margin:var(--spacing-lg) var(--spacing-xs);padding:var(--spacing-xs);color:var(--neutral-400);border-radius:var(--radius-md)}.selected.svelte-15ls1gu{color:var(--color-accent)}.icon.svelte-15ls1gu:hover,.icon.svelte-15ls1gu:focus{color:var(--color-accent)}.icon-button-wrapper.svelte-1h0hs6p{display:flex;flex-direction:row;align-items:center;justify-content:center;z-index:var(--layer-2);gap:var(--spacing-sm);box-shadow:var(--shadow-drop);border:1px solid var(--border-color-primary);background:var(--block-background-fill);padding:var(--spacing-xxs)}.icon-button-wrapper.hide-top-corner.svelte-1h0hs6p{border-top:none;border-right:none;border-radius:var(--block-label-right-radius)}.icon-button-wrapper.display-top-corner.svelte-1h0hs6p{border-radius:var(--radius-sm) 0 0 var(--radius-sm);top:var(--spacing-sm);right:-1px}.icon-button-wrapper.svelte-1h0hs6p:not(.top-panel){border:1px solid var(--border-color-primary);border-radius:var(--radius-sm)}.top-panel.svelte-1h0hs6p{position:absolute;top:var(--block-label-margin);right:var(--block-label-margin);margin:0}.icon-button-wrapper.svelte-1h0hs6p button{margin:var(--spacing-xxs);border-radius:var(--radius-xs);position:relative}.icon-button-wrapper.svelte-1h0hs6p a.download-link:not(:last-child),.icon-button-wrapper.svelte-1h0hs6p button:not(:last-child){margin-right:var(--spacing-xxs)}.icon-button-wrapper.svelte-1h0hs6p a.download-link:not(:last-child):not(.no-border *):after,.icon-button-wrapper.svelte-1h0hs6p button:not(:last-child):not(.no-border *):after{content:"";position:absolute;right:-4.5px;top:15%;height:70%;width:1px;background-color:var(--border-color-primary)}.icon-button-wrapper.svelte-1h0hs6p>*{height:100%}.wrap.svelte-17v219f.svelte-17v219f{display:flex;flex-direction:column;justify-content:center;align-items:center;z-index:var(--layer-2);transition:opacity .1s ease-in-out;border-radius:var(--block-radius);background:var(--block-background-fill);padding:0 var(--size-6);max-height:var(--size-screen-h);overflow:hidden}.wrap.center.svelte-17v219f.svelte-17v219f{top:0;right:0;left:0}.wrap.default.svelte-17v219f.svelte-17v219f{top:0;right:0;bottom:0;left:0}.hide.svelte-17v219f.svelte-17v219f{opacity:0;pointer-events:none}.generating.svelte-17v219f.svelte-17v219f{animation:svelte-17v219f-pulseStart 1s cubic-bezier(.4,0,.6,1),svelte-17v219f-pulse 2s cubic-bezier(.4,0,.6,1) 1s infinite;border:2px solid var(--color-accent);background:transparent;z-index:var(--layer-1);pointer-events:none}.translucent.svelte-17v219f.svelte-17v219f{background:none}@keyframes svelte-17v219f-pulseStart{0%{opacity:0}to{opacity:1}}@keyframes svelte-17v219f-pulse{0%,to{opacity:1}50%{opacity:.5}}.loading.svelte-17v219f.svelte-17v219f{z-index:var(--layer-2);color:var(--body-text-color)}.eta-bar.svelte-17v219f.svelte-17v219f{position:absolute;top:0;right:0;bottom:0;left:0;transform-origin:left;opacity:.8;z-index:var(--layer-1);transition:10ms;background:var(--background-fill-secondary)}.progress-bar-wrap.svelte-17v219f.svelte-17v219f{border:1px solid var(--border-color-primary);background:var(--background-fill-primary);width:55.5%;height:var(--size-4)}.progress-bar.svelte-17v219f.svelte-17v219f{transform-origin:left;background-color:var(--loader-color);width:var(--size-full);height:var(--size-full)}.progress-level.svelte-17v219f.svelte-17v219f{display:flex;flex-direction:column;align-items:center;gap:1;z-index:var(--layer-2);width:var(--size-full)}.progress-level-inner.svelte-17v219f.svelte-17v219f{margin:var(--size-2) auto;color:var(--body-text-color);font-size:var(--text-sm);font-family:var(--font-mono)}.meta-text.svelte-17v219f.svelte-17v219f{position:absolute;bottom:0;right:0;z-index:var(--layer-2);padding:var(--size-1) var(--size-2);font-size:var(--text-sm);font-family:var(--font-mono)}.meta-text-center.svelte-17v219f.svelte-17v219f{display:flex;position:absolute;top:0;right:0;justify-content:center;align-items:center;transform:translateY(var(--size-6));z-index:var(--layer-2);padding:var(--size-1) var(--size-2);font-size:var(--text-sm);font-family:var(--font-mono);text-align:center}.error.svelte-17v219f.svelte-17v219f{box-shadow:var(--shadow-drop);border:solid 1px var(--error-border-color);border-radius:var(--radius-full);background:var(--error-background-fill);padding-right:var(--size-4);padding-left:var(--size-4);color:var(--error-text-color);font-weight:var(--weight-semibold);font-size:var(--text-lg);line-height:var(--line-lg);font-family:var(--font)}.minimal.svelte-17v219f.svelte-17v219f{pointer-events:none}.minimal.svelte-17v219f .progress-text.svelte-17v219f{background:var(--block-background-fill)}.border.svelte-17v219f.svelte-17v219f{border:1px solid var(--border-color-primary)}.clear-status.svelte-17v219f.svelte-17v219f{position:absolute;display:flex;top:var(--size-2);right:var(--size-2);justify-content:flex-end;gap:var(--spacing-sm);z-index:var(--layer-1)}.toast-body.svelte-syezpc{display:flex;position:relative;right:0;left:0;align-items:center;margin:var(--size-6) var(--size-4);margin:auto;border-radius:var(--container-radius);overflow:hidden;pointer-events:auto}.toast-body.error.svelte-syezpc{border:1px solid var(--color-red-700);background:var(--color-red-50)}.dark .toast-body.error.svelte-syezpc{border:1px solid var(--color-red-500);background-color:var(--color-grey-950)}.toast-body.warning.svelte-syezpc{border:1px solid var(--color-yellow-700);background:var(--color-yellow-50)}.dark .toast-body.warning.svelte-syezpc{border:1px solid var(--color-yellow-500);background-color:var(--color-grey-950)}.toast-body.info.svelte-syezpc{border:1px solid var(--color-grey-700);background:var(--color-grey-50)}.dark .toast-body.info.svelte-syezpc{border:1px solid var(--color-grey-500);background-color:var(--color-grey-950)}.toast-body.success.svelte-syezpc{border:1px solid var(--color-green-700);background:var(--color-green-50)}.dark .toast-body.success.svelte-syezpc{border:1px solid var(--color-green-500);background-color:var(--color-grey-950)}.toast-title.svelte-syezpc{display:flex;align-items:center;font-weight:var(--weight-bold);font-size:var(--text-lg);line-height:var(--line-sm)}.toast-title.error.svelte-syezpc{color:var(--color-red-700)}.dark .toast-title.error.svelte-syezpc{color:var(--color-red-50)}.toast-title.warning.svelte-syezpc{color:var(--color-yellow-700)}.dark .toast-title.warning.svelte-syezpc{color:var(--color-yellow-50)}.toast-title.info.svelte-syezpc{color:var(--color-grey-700)}.dark .toast-title.info.svelte-syezpc{color:var(--color-grey-50)}.toast-title.success.svelte-syezpc{color:var(--color-green-700)}.dark .toast-title.success.svelte-syezpc{color:var(--color-green-50)}.toast-close.svelte-syezpc{margin:0 var(--size-3);border-radius:var(--size-3);padding:0px var(--size-1-5);font-size:var(--size-5);line-height:var(--size-5)}.toast-close.error.svelte-syezpc{color:var(--color-red-700)}.dark .toast-close.error.svelte-syezpc{color:var(--color-red-500)}.toast-close.warning.svelte-syezpc{color:var(--color-yellow-700)}.dark .toast-close.warning.svelte-syezpc{color:var(--color-yellow-500)}.toast-close.info.svelte-syezpc{color:var(--color-grey-700)}.dark .toast-close.info.svelte-syezpc{color:var(--color-grey-500)}.toast-close.success.svelte-syezpc{color:var(--color-green-700)}.dark .toast-close.success.svelte-syezpc{color:var(--color-green-500)}.toast-text.svelte-syezpc{font-size:var(--text-lg);word-wrap:break-word;overflow-wrap:break-word;word-break:break-word}.toast-text.error.svelte-syezpc{color:var(--color-red-700)}.dark .toast-text.error.svelte-syezpc{color:var(--color-red-50)}.toast-text.warning.svelte-syezpc{color:var(--color-yellow-700)}.dark .toast-text.warning.svelte-syezpc{color:var(--color-yellow-50)}.toast-text.info.svelte-syezpc{color:var(--color-grey-700)}.dark .toast-text.info.svelte-syezpc{color:var(--color-grey-50)}.toast-text.success.svelte-syezpc{color:var(--color-green-700)}.dark .toast-text.success.svelte-syezpc{color:var(--color-green-50)}.toast-details.svelte-syezpc{margin:var(--size-3) var(--size-3) var(--size-3) 0;width:100%}.toast-icon.svelte-syezpc{display:flex;position:absolute;position:relative;flex-shrink:0;justify-content:center;align-items:center;margin:var(--size-2);border-radius:var(--radius-full);padding:var(--size-1);padding-left:calc(var(--size-1) - 1px);width:35px;height:35px}.toast-icon.error.svelte-syezpc{color:var(--color-red-700)}.dark .toast-icon.error.svelte-syezpc{color:var(--color-red-500)}.toast-icon.warning.svelte-syezpc{color:var(--color-yellow-700)}.dark .toast-icon.warning.svelte-syezpc{color:var(--color-yellow-500)}.toast-icon.info.svelte-syezpc{color:var(--color-grey-700)}.dark .toast-icon.info.svelte-syezpc{color:var(--color-grey-500)}.toast-icon.success.svelte-syezpc{color:var(--color-green-700)}.dark .toast-icon.success.svelte-syezpc{color:var(--color-green-500)}@keyframes svelte-syezpc-countdown{0%{transform:scaleX(1)}to{transform:scaleX(0)}}.timer.svelte-syezpc{position:absolute;bottom:0;left:0;transform-origin:0 0;animation:svelte-syezpc-countdown 10s linear forwards;width:100%;height:var(--size-1)}.timer.error.svelte-syezpc{background:var(--color-red-700)}.dark .timer.error.svelte-syezpc{background:var(--color-red-500)}.timer.warning.svelte-syezpc{background:var(--color-yellow-700)}.dark .timer.warning.svelte-syezpc{background:var(--color-yellow-500)}.timer.info.svelte-syezpc{background:var(--color-grey-700)}.dark .timer.info.svelte-syezpc{background:var(--color-grey-500)}.timer.success.svelte-syezpc{background:var(--color-green-700)}.dark .timer.success.svelte-syezpc{background:var(--color-green-500)}.hidden.svelte-syezpc{display:none}.toast-text.svelte-syezpc a{text-decoration:underline}.toast-wrap.svelte-gatr8h{display:flex;position:fixed;top:var(--size-4);right:var(--size-4);flex-direction:column;align-items:end;gap:var(--size-2);z-index:var(--layer-top);width:calc(100% - var(--size-8))}@media (--screen-sm){.toast-wrap.svelte-gatr8h{width:calc(var(--size-96) + var(--size-10))}}.streaming-bar.svelte-ga0jj6{position:absolute;bottom:0;left:0;right:0;height:4px;background-color:var(--primary-600);animation:svelte-ga0jj6-countdown linear forwards;z-index:1}@keyframes svelte-ga0jj6-countdown{0%{transform:translate(0)}to{transform:translate(-100%)}}div.svelte-y3tjew{display:flex;position:relative;flex-direction:column;gap:var(--layout-gap)}div.svelte-y3tjew>*,div.svelte-y3tjew>.form>*{width:var(--size-full)}.hide.svelte-y3tjew{display:none}.compact.svelte-y3tjew>*,.compact.svelte-y3tjew .box{border-radius:0}.compact.svelte-y3tjew,.panel.svelte-y3tjew{border:solid var(--panel-border-width) var(--panel-border-color);border-radius:var(--container-radius);background:var(--panel-background-fill);padding:var(--spacing-lg)}
|
src/backend/gradio_topbar/topbar.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# gradio_topbar/topbar.py
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from typing import Literal
|
| 6 |
+
|
| 7 |
+
from gradio_client.documentation import document
|
| 8 |
+
|
| 9 |
+
from gradio.blocks import BlockContext
|
| 10 |
+
from gradio.component_meta import ComponentMeta
|
| 11 |
+
from gradio.events import Events
|
| 12 |
+
from gradio.i18n import I18nData
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
@document()
|
| 16 |
+
class TopBar(BlockContext, metaclass=ComponentMeta):
|
| 17 |
+
"""
|
| 18 |
+
TopBar is a collapsible panel that renders child components in a fixed bar at the top of the page.
|
| 19 |
+
Example:
|
| 20 |
+
with gr.Blocks() as demo:
|
| 21 |
+
with gr.TopBar(height=200, width="80%"):
|
| 22 |
+
gr.Textbox(label="Enter your text here")
|
| 23 |
+
gr.Button("Submit")
|
| 24 |
+
Guides: controlling-layout
|
| 25 |
+
"""
|
| 26 |
+
|
| 27 |
+
EVENTS = [Events.expand, Events.collapse]
|
| 28 |
+
|
| 29 |
+
def __init__(
|
| 30 |
+
self,
|
| 31 |
+
label: str | I18nData | None = None,
|
| 32 |
+
*,
|
| 33 |
+
open: bool = True,
|
| 34 |
+
visible: bool = True,
|
| 35 |
+
elem_id: str | None = None,
|
| 36 |
+
elem_classes: list[str] | str | None = None,
|
| 37 |
+
render: bool = True,
|
| 38 |
+
height: int | str = 320,
|
| 39 |
+
width: int | str = "100%",
|
| 40 |
+
bring_to_front: bool = False,
|
| 41 |
+
key: int | str | tuple[int | str, ...] | None = None,
|
| 42 |
+
preserved_by_key: list[str] | str | None = None,
|
| 43 |
+
):
|
| 44 |
+
"""
|
| 45 |
+
Parameters:
|
| 46 |
+
label: name of the top bar. Not displayed to the user.
|
| 47 |
+
open: if True, top bar is open by default.
|
| 48 |
+
visible: If False, the component will be hidden.
|
| 49 |
+
elem_id: An optional string that is assigned as the id of this component in the HTML DOM. Can be used for targeting CSS styles.
|
| 50 |
+
elem_classes: An optional string or list of strings that are assigned as the class of this component in the HTML DOM. Can be used for targeting CSS styles.
|
| 51 |
+
render: If False, this layout will not be rendered in the Blocks context. Should be used if the intention is to assign event listeners now but render the component later.
|
| 52 |
+
height: The height of the top bar, specified in pixels if a number is passed, or in CSS units if a string is passed.
|
| 53 |
+
width: The width of the top bar, specified in pixels if a number is passed, or in CSS units (like "80%") if a string is passed. The bar will be horizontally centered.
|
| 54 |
+
bring_to_front: If True, the TopBar will be rendered on top of all other elements with a higher z-index. Defaults to False.
|
| 55 |
+
key: in a gr.render, Components with the same key across re-renders are treated as the same component, not a new component. Properties set in 'preserved_by_key' are not reset across a re-render.
|
| 56 |
+
preserved_by_key: A list of parameters from this component's constructor. Inside a gr.render() function, if a component is re-rendered with the same key, these (and only these) parameters will be preserved in the UI (if they have been changed by the user or an event listener) instead of re-rendered based on the values provided during constructor.
|
| 57 |
+
"""
|
| 58 |
+
self.label = label
|
| 59 |
+
self.open = open
|
| 60 |
+
self.height = height
|
| 61 |
+
self.width = width
|
| 62 |
+
self.bring_to_front = bring_to_front
|
| 63 |
+
BlockContext.__init__(
|
| 64 |
+
self,
|
| 65 |
+
visible=visible,
|
| 66 |
+
elem_id=elem_id,
|
| 67 |
+
elem_classes=elem_classes,
|
| 68 |
+
render=render,
|
| 69 |
+
key=key,
|
| 70 |
+
preserved_by_key=preserved_by_key,
|
| 71 |
+
)
|
src/demo/__init__.py
ADDED
|
File without changes
|
src/demo/app.py
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# demo/app.py
|
| 2 |
+
|
| 3 |
+
import gradio as gr
|
| 4 |
+
import time
|
| 5 |
+
from gradio_topbar import TopBar
|
| 6 |
+
|
| 7 |
+
def chat_response(message, history):
|
| 8 |
+
history = history or ""
|
| 9 |
+
history += f"You: {message}\n"
|
| 10 |
+
time.sleep(1) # Simulate thinking
|
| 11 |
+
history += f"Bot: Thanks for the message! You said: '{message}'\n"
|
| 12 |
+
return history, ""
|
| 13 |
+
|
| 14 |
+
def update_label(value):
|
| 15 |
+
return f"Current temperature is: {value}"
|
| 16 |
+
|
| 17 |
+
with gr.Blocks(theme=gr.themes.Ocean(), title="Full Layout Demo") as demo:
|
| 18 |
+
gr.Markdown(
|
| 19 |
+
"""
|
| 20 |
+
# TopBar Demo
|
| 21 |
+
This demo shows the `TopBar` with `width="50%"` and `bring_to_front=True`.
|
| 22 |
+
Notice how the bar is now horizontally centered and no longer full-width.
|
| 23 |
+
"""
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
with TopBar(open=True, height=180, width="50%", bring_to_front=True):
|
| 27 |
+
with gr.Row():
|
| 28 |
+
message_box = gr.Textbox(
|
| 29 |
+
show_label=False,
|
| 30 |
+
placeholder="Type your message here...",
|
| 31 |
+
elem_id="message-input",
|
| 32 |
+
scale=7
|
| 33 |
+
)
|
| 34 |
+
send_button = gr.Button("Send", variant="primary", scale=1)
|
| 35 |
+
with gr.Row():
|
| 36 |
+
gr.Button("Upload File")
|
| 37 |
+
gr.Button("Record Audio")
|
| 38 |
+
gr.ClearButton([message_box])
|
| 39 |
+
|
| 40 |
+
with gr.Row(equal_height=True):
|
| 41 |
+
with gr.Column(scale=3):
|
| 42 |
+
gr.Markdown("### 🤖 Chat Interface")
|
| 43 |
+
chatbot_display = gr.Textbox(
|
| 44 |
+
label="Chat History",
|
| 45 |
+
lines=25,
|
| 46 |
+
interactive=False
|
| 47 |
+
)
|
| 48 |
+
|
| 49 |
+
send_button.click(
|
| 50 |
+
fn=chat_response,
|
| 51 |
+
inputs=[message_box, chatbot_display],
|
| 52 |
+
outputs=[chatbot_display, message_box]
|
| 53 |
+
)
|
| 54 |
+
message_box.submit(
|
| 55 |
+
fn=chat_response,
|
| 56 |
+
inputs=[message_box, chatbot_display],
|
| 57 |
+
outputs=[chatbot_display, message_box]
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
if __name__ == "__main__":
|
| 61 |
+
demo.launch(debug=True)
|
src/demo/css.css
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
html {
|
| 2 |
+
font-family: Inter;
|
| 3 |
+
font-size: 16px;
|
| 4 |
+
font-weight: 400;
|
| 5 |
+
line-height: 1.5;
|
| 6 |
+
-webkit-text-size-adjust: 100%;
|
| 7 |
+
background: #fff;
|
| 8 |
+
color: #323232;
|
| 9 |
+
-webkit-font-smoothing: antialiased;
|
| 10 |
+
-moz-osx-font-smoothing: grayscale;
|
| 11 |
+
text-rendering: optimizeLegibility;
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
:root {
|
| 15 |
+
--space: 1;
|
| 16 |
+
--vspace: calc(var(--space) * 1rem);
|
| 17 |
+
--vspace-0: calc(3 * var(--space) * 1rem);
|
| 18 |
+
--vspace-1: calc(2 * var(--space) * 1rem);
|
| 19 |
+
--vspace-2: calc(1.5 * var(--space) * 1rem);
|
| 20 |
+
--vspace-3: calc(0.5 * var(--space) * 1rem);
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
.app {
|
| 24 |
+
max-width: 748px !important;
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
.prose p {
|
| 28 |
+
margin: var(--vspace) 0;
|
| 29 |
+
line-height: var(--vspace * 2);
|
| 30 |
+
font-size: 1rem;
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
code {
|
| 34 |
+
font-family: "Inconsolata", sans-serif;
|
| 35 |
+
font-size: 16px;
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
h1,
|
| 39 |
+
h1 code {
|
| 40 |
+
font-weight: 400;
|
| 41 |
+
line-height: calc(2.5 / var(--space) * var(--vspace));
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
h1 code {
|
| 45 |
+
background: none;
|
| 46 |
+
border: none;
|
| 47 |
+
letter-spacing: 0.05em;
|
| 48 |
+
padding-bottom: 5px;
|
| 49 |
+
position: relative;
|
| 50 |
+
padding: 0;
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
h2 {
|
| 54 |
+
margin: var(--vspace-1) 0 var(--vspace-2) 0;
|
| 55 |
+
line-height: 1em;
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
h3,
|
| 59 |
+
h3 code {
|
| 60 |
+
margin: var(--vspace-1) 0 var(--vspace-2) 0;
|
| 61 |
+
line-height: 1em;
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
h4,
|
| 65 |
+
h5,
|
| 66 |
+
h6 {
|
| 67 |
+
margin: var(--vspace-3) 0 var(--vspace-3) 0;
|
| 68 |
+
line-height: var(--vspace);
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
.bigtitle,
|
| 72 |
+
h1,
|
| 73 |
+
h1 code {
|
| 74 |
+
font-size: calc(8px * 4.5);
|
| 75 |
+
word-break: break-word;
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
.title,
|
| 79 |
+
h2,
|
| 80 |
+
h2 code {
|
| 81 |
+
font-size: calc(8px * 3.375);
|
| 82 |
+
font-weight: lighter;
|
| 83 |
+
word-break: break-word;
|
| 84 |
+
border: none;
|
| 85 |
+
background: none;
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
.subheading1,
|
| 89 |
+
h3,
|
| 90 |
+
h3 code {
|
| 91 |
+
font-size: calc(8px * 1.8);
|
| 92 |
+
font-weight: 600;
|
| 93 |
+
border: none;
|
| 94 |
+
background: none;
|
| 95 |
+
letter-spacing: 0.1em;
|
| 96 |
+
text-transform: uppercase;
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
h2 code {
|
| 100 |
+
padding: 0;
|
| 101 |
+
position: relative;
|
| 102 |
+
letter-spacing: 0.05em;
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
blockquote {
|
| 106 |
+
font-size: calc(8px * 1.1667);
|
| 107 |
+
font-style: italic;
|
| 108 |
+
line-height: calc(1.1667 * var(--vspace));
|
| 109 |
+
margin: var(--vspace-2) var(--vspace-2);
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
.subheading2,
|
| 113 |
+
h4 {
|
| 114 |
+
font-size: calc(8px * 1.4292);
|
| 115 |
+
text-transform: uppercase;
|
| 116 |
+
font-weight: 600;
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
.subheading3,
|
| 120 |
+
h5 {
|
| 121 |
+
font-size: calc(8px * 1.2917);
|
| 122 |
+
line-height: calc(1.2917 * var(--vspace));
|
| 123 |
+
|
| 124 |
+
font-weight: lighter;
|
| 125 |
+
text-transform: uppercase;
|
| 126 |
+
letter-spacing: 0.15em;
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
h6 {
|
| 130 |
+
font-size: calc(8px * 1.1667);
|
| 131 |
+
font-size: 1.1667em;
|
| 132 |
+
font-weight: normal;
|
| 133 |
+
font-style: italic;
|
| 134 |
+
font-family: "le-monde-livre-classic-byol", serif !important;
|
| 135 |
+
letter-spacing: 0px !important;
|
| 136 |
+
}
|
| 137 |
+
|
| 138 |
+
#start .md > *:first-child {
|
| 139 |
+
margin-top: 0;
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
h2 + h3 {
|
| 143 |
+
margin-top: 0;
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
.md hr {
|
| 147 |
+
border: none;
|
| 148 |
+
border-top: 1px solid var(--block-border-color);
|
| 149 |
+
margin: var(--vspace-2) 0 var(--vspace-2) 0;
|
| 150 |
+
}
|
| 151 |
+
.prose ul {
|
| 152 |
+
margin: var(--vspace-2) 0 var(--vspace-1) 0;
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
.gap {
|
| 156 |
+
gap: 0;
|
| 157 |
+
}
|
src/demo/requirements.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
gradio_topbar
|
src/demo/space.py
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
import gradio as gr
|
| 3 |
+
from app import demo as app
|
| 4 |
+
import os
|
| 5 |
+
|
| 6 |
+
_docs = {'TopBar': {'description': 'TopBar is a collapsible panel that renders child components in a fixed bar at the top of the page.\n with gr.Blocks() as demo:\n with gr.TopBar(height=200, width="80%"):\n gr.Textbox(label="Enter your text here")\n gr.Button("Submit")', 'members': {'__init__': {'label': {'type': 'str | gradio.i18n.I18nData | None', 'default': 'None', 'description': 'name of the top bar. Not displayed to the user.'}, 'open': {'type': 'bool', 'default': 'True', 'description': 'if True, top bar is open by default.'}, 'visible': {'type': 'bool', 'default': 'True', 'description': 'If False, the component will be hidden.'}, 'elem_id': {'type': 'str | None', 'default': 'None', 'description': 'An optional string that is assigned as the id of this component in the HTML DOM. Can be used for targeting CSS styles.'}, 'elem_classes': {'type': 'list[str] | str | None', 'default': 'None', 'description': 'An optional string or list of strings that are assigned as the class of this component in the HTML DOM. Can be used for targeting CSS styles.'}, 'render': {'type': 'bool', 'default': 'True', 'description': 'If False, this layout will not be rendered in the Blocks context. Should be used if the intention is to assign event listeners now but render the component later.'}, 'height': {'type': 'int | str', 'default': '320', 'description': 'The height of the top bar, specified in pixels if a number is passed, or in CSS units if a string is passed.'}, 'width': {'type': 'int | str', 'default': '"100%"', 'description': 'The width of the top bar, specified in pixels if a number is passed, or in CSS units (like "80%") if a string is passed. The bar will be horizontally centered.'}, 'bring_to_front': {'type': 'bool', 'default': 'False', 'description': 'If True, the TopBar will be rendered on top of all other elements with a higher z-index. Defaults to False.'}, 'key': {'type': 'int | str | tuple[int | str, Ellipsis] | None', 'default': 'None', 'description': "in a gr.render, Components with the same key across re-renders are treated as the same component, not a new component. Properties set in 'preserved_by_key' are not reset across a re-render."}, 'preserved_by_key': {'type': 'list[str] | str | None', 'default': 'None', 'description': "A list of parameters from this component's constructor. Inside a gr.render() function, if a component is re-rendered with the same key, these (and only these) parameters will be preserved in the UI (if they have been changed by the user or an event listener) instead of re-rendered based on the values provided during constructor."}}, 'postprocess': {}}, 'events': {'expand': {'type': None, 'default': None, 'description': 'This listener is triggered when the TopBar is expanded.'}, 'collapse': {'type': None, 'default': None, 'description': 'This listener is triggered when the TopBar is collapsed.'}}}, '__meta__': {'additional_interfaces': {}}}
|
| 7 |
+
|
| 8 |
+
abs_path = os.path.join(os.path.dirname(__file__), "css.css")
|
| 9 |
+
|
| 10 |
+
with gr.Blocks(
|
| 11 |
+
css=abs_path,
|
| 12 |
+
theme=gr.themes.Ocean(
|
| 13 |
+
font_mono=[
|
| 14 |
+
gr.themes.GoogleFont("Inconsolata"),
|
| 15 |
+
"monospace",
|
| 16 |
+
],
|
| 17 |
+
),
|
| 18 |
+
) as demo:
|
| 19 |
+
gr.Markdown(
|
| 20 |
+
"""
|
| 21 |
+
# `gradio_topbar`
|
| 22 |
+
|
| 23 |
+
<div style="display: flex; gap: 7px;">
|
| 24 |
+
<img alt="Static Badge" src="https://img.shields.io/badge/version%20-%200.0.1%20-%20orange">
|
| 25 |
+
</div>
|
| 26 |
+
|
| 27 |
+
A TopBar for Gradio Interface
|
| 28 |
+
""", elem_classes=["md-custom"], header_links=True)
|
| 29 |
+
app.render()
|
| 30 |
+
gr.Markdown(
|
| 31 |
+
"""
|
| 32 |
+
## Installation
|
| 33 |
+
|
| 34 |
+
```bash
|
| 35 |
+
pip install gradio_topbar
|
| 36 |
+
```
|
| 37 |
+
|
| 38 |
+
## Usage
|
| 39 |
+
|
| 40 |
+
```python
|
| 41 |
+
# demo/app.py
|
| 42 |
+
|
| 43 |
+
import gradio as gr
|
| 44 |
+
import time
|
| 45 |
+
from gradio_topbar import TopBar
|
| 46 |
+
|
| 47 |
+
def chat_response(message, history):
|
| 48 |
+
history = history or ""
|
| 49 |
+
history += f"You: {message}\n"
|
| 50 |
+
time.sleep(1) # Simulate thinking
|
| 51 |
+
history += f"Bot: Thanks for the message! You said: '{message}'\n"
|
| 52 |
+
return history, ""
|
| 53 |
+
|
| 54 |
+
def update_label(value):
|
| 55 |
+
return f"Current temperature is: {value}"
|
| 56 |
+
|
| 57 |
+
with gr.Blocks(theme=gr.themes.Ocean(), title="Full Layout Demo") as demo:
|
| 58 |
+
gr.Markdown(
|
| 59 |
+
\"\"\"
|
| 60 |
+
# TopBar Demo
|
| 61 |
+
This demo shows the `TopBar` with `width="50%"` and `bring_to_front=True`.
|
| 62 |
+
Notice how the bar is now horizontally centered and no longer full-width.
|
| 63 |
+
\"\"\"
|
| 64 |
+
)
|
| 65 |
+
|
| 66 |
+
with TopBar(open=True, height=180, width="50%", bring_to_front=True):
|
| 67 |
+
with gr.Row():
|
| 68 |
+
message_box = gr.Textbox(
|
| 69 |
+
show_label=False,
|
| 70 |
+
placeholder="Type your message here...",
|
| 71 |
+
elem_id="message-input",
|
| 72 |
+
scale=7
|
| 73 |
+
)
|
| 74 |
+
send_button = gr.Button("Send", variant="primary", scale=1)
|
| 75 |
+
with gr.Row():
|
| 76 |
+
gr.Button("Upload File")
|
| 77 |
+
gr.Button("Record Audio")
|
| 78 |
+
gr.ClearButton([message_box])
|
| 79 |
+
|
| 80 |
+
with gr.Row(equal_height=True):
|
| 81 |
+
with gr.Column(scale=3):
|
| 82 |
+
gr.Markdown("### 🤖 Chat Interface")
|
| 83 |
+
chatbot_display = gr.Textbox(
|
| 84 |
+
label="Chat History",
|
| 85 |
+
lines=25,
|
| 86 |
+
interactive=False
|
| 87 |
+
)
|
| 88 |
+
|
| 89 |
+
send_button.click(
|
| 90 |
+
fn=chat_response,
|
| 91 |
+
inputs=[message_box, chatbot_display],
|
| 92 |
+
outputs=[chatbot_display, message_box]
|
| 93 |
+
)
|
| 94 |
+
message_box.submit(
|
| 95 |
+
fn=chat_response,
|
| 96 |
+
inputs=[message_box, chatbot_display],
|
| 97 |
+
outputs=[chatbot_display, message_box]
|
| 98 |
+
)
|
| 99 |
+
|
| 100 |
+
if __name__ == "__main__":
|
| 101 |
+
demo.launch(debug=True)
|
| 102 |
+
```
|
| 103 |
+
""", elem_classes=["md-custom"], header_links=True)
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
gr.Markdown("""
|
| 107 |
+
## `TopBar`
|
| 108 |
+
|
| 109 |
+
### Initialization
|
| 110 |
+
""", elem_classes=["md-custom"], header_links=True)
|
| 111 |
+
|
| 112 |
+
gr.ParamViewer(value=_docs["TopBar"]["members"]["__init__"], linkify=[])
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
gr.Markdown("### Events")
|
| 116 |
+
gr.ParamViewer(value=_docs["TopBar"]["events"], linkify=['Event'])
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
demo.load(None, js=r"""function() {
|
| 125 |
+
const refs = {};
|
| 126 |
+
const user_fn_refs = {};
|
| 127 |
+
requestAnimationFrame(() => {
|
| 128 |
+
|
| 129 |
+
Object.entries(user_fn_refs).forEach(([key, refs]) => {
|
| 130 |
+
if (refs.length > 0) {
|
| 131 |
+
const el = document.querySelector(`.${key}-user-fn`);
|
| 132 |
+
if (!el) return;
|
| 133 |
+
refs.forEach(ref => {
|
| 134 |
+
el.innerHTML = el.innerHTML.replace(
|
| 135 |
+
new RegExp("\\b"+ref+"\\b", "g"),
|
| 136 |
+
`<a href="#h-${ref.toLowerCase()}">${ref}</a>`
|
| 137 |
+
);
|
| 138 |
+
})
|
| 139 |
+
}
|
| 140 |
+
})
|
| 141 |
+
|
| 142 |
+
Object.entries(refs).forEach(([key, refs]) => {
|
| 143 |
+
if (refs.length > 0) {
|
| 144 |
+
const el = document.querySelector(`.${key}`);
|
| 145 |
+
if (!el) return;
|
| 146 |
+
refs.forEach(ref => {
|
| 147 |
+
el.innerHTML = el.innerHTML.replace(
|
| 148 |
+
new RegExp("\\b"+ref+"\\b", "g"),
|
| 149 |
+
`<a href="#h-${ref.toLowerCase()}">${ref}</a>`
|
| 150 |
+
);
|
| 151 |
+
})
|
| 152 |
+
}
|
| 153 |
+
})
|
| 154 |
+
})
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
""")
|
| 158 |
+
|
| 159 |
+
demo.launch()
|
src/frontend/Index.svelte
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!-- frontend/index.svelte -->
|
| 2 |
+
|
| 3 |
+
<script lang="ts">
|
| 4 |
+
// This component acts as a bridge between the Gradio Python backend and the Svelte UI.
|
| 5 |
+
import TopBarPanel from "./shared/TopBarPanel.svelte";
|
| 6 |
+
import { StatusTracker } from "@gradio/statustracker";
|
| 7 |
+
import type { LoadingStatus } from "@gradio/statustracker";
|
| 8 |
+
import type { Gradio } from "@gradio/utils";
|
| 9 |
+
import Column from "@gradio/column";
|
| 10 |
+
|
| 11 |
+
export let open = true;
|
| 12 |
+
export let loading_status: LoadingStatus;
|
| 13 |
+
export let gradio: Gradio<{
|
| 14 |
+
expand: never;
|
| 15 |
+
collapse: never;
|
| 16 |
+
}>;
|
| 17 |
+
export let height: number | string;
|
| 18 |
+
export let width: number | string;
|
| 19 |
+
export let visible = true;
|
| 20 |
+
export let bring_to_front = false;
|
| 21 |
+
</script>
|
| 22 |
+
|
| 23 |
+
<StatusTracker
|
| 24 |
+
autoscroll={gradio.autoscroll}
|
| 25 |
+
i18n={gradio.i18n}
|
| 26 |
+
{...loading_status}
|
| 27 |
+
/>
|
| 28 |
+
|
| 29 |
+
{#if visible}
|
| 30 |
+
<TopBarPanel
|
| 31 |
+
bind:open
|
| 32 |
+
{height}
|
| 33 |
+
{width}
|
| 34 |
+
{bring_to_front}
|
| 35 |
+
on:expand={() => gradio.dispatch("expand")}
|
| 36 |
+
on:collapse={() => gradio.dispatch("collapse")}
|
| 37 |
+
>
|
| 38 |
+
<Column>
|
| 39 |
+
<slot />
|
| 40 |
+
</Column>
|
| 41 |
+
</TopBarPanel>
|
| 42 |
+
{/if}
|
src/frontend/gradio.config.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export default {
|
| 2 |
+
plugins: [],
|
| 3 |
+
svelte: {
|
| 4 |
+
preprocess: [],
|
| 5 |
+
},
|
| 6 |
+
build: {
|
| 7 |
+
target: "modules",
|
| 8 |
+
},
|
| 9 |
+
};
|
src/frontend/package-lock.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
src/frontend/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "gradio_topbar",
|
| 3 |
+
"version": "0.1.19",
|
| 4 |
+
"description": "Gradio UI packages",
|
| 5 |
+
"type": "module",
|
| 6 |
+
"author": "",
|
| 7 |
+
"license": "ISC",
|
| 8 |
+
"main_changeset": true,
|
| 9 |
+
"dependencies": {
|
| 10 |
+
"@gradio/atoms": "0.16.4",
|
| 11 |
+
"@gradio/column": "0.2.1",
|
| 12 |
+
"@gradio/statustracker": "0.10.16",
|
| 13 |
+
"@gradio/utils": "0.10.2"
|
| 14 |
+
},
|
| 15 |
+
"peerDependencies": {
|
| 16 |
+
"svelte": "^4.0.0"
|
| 17 |
+
},
|
| 18 |
+
"devDependencies": {
|
| 19 |
+
"@gradio/preview": "0.14.0"
|
| 20 |
+
},
|
| 21 |
+
"exports": {
|
| 22 |
+
".": {
|
| 23 |
+
"gradio": "./Index.svelte",
|
| 24 |
+
"svelte": "./dist/Index.svelte",
|
| 25 |
+
"types": "./dist/Index.svelte.d.ts"
|
| 26 |
+
},
|
| 27 |
+
"./package.json": "./package.json"
|
| 28 |
+
},
|
| 29 |
+
"repository": {
|
| 30 |
+
"type": "git",
|
| 31 |
+
"url": "git+https://github.com/gradio-app/gradio.git",
|
| 32 |
+
"directory": "js/sidebar"
|
| 33 |
+
}
|
| 34 |
+
}
|
src/frontend/shared/TopBarPanel.svelte
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!-- frontend/shared/TopBarPanel.svelte -->
|
| 2 |
+
|
| 3 |
+
<script lang="ts">
|
| 4 |
+
import { createEventDispatcher, onMount } from "svelte";
|
| 5 |
+
const dispatch = createEventDispatcher<{
|
| 6 |
+
expand: void;
|
| 7 |
+
collapse: void;
|
| 8 |
+
}>();
|
| 9 |
+
|
| 10 |
+
export let open = true;
|
| 11 |
+
export let height: number | string;
|
| 12 |
+
export let width: number | string;
|
| 13 |
+
export let bring_to_front = false;
|
| 14 |
+
|
| 15 |
+
let mounted = false;
|
| 16 |
+
let _open = false;
|
| 17 |
+
|
| 18 |
+
let width_css = typeof width === "number" ? `${width}px` : width;
|
| 19 |
+
let height_css = typeof height === "number" ? `${height}px` : height;
|
| 20 |
+
let prefersReducedMotion: boolean;
|
| 21 |
+
|
| 22 |
+
onMount(() => {
|
| 23 |
+
mounted = true;
|
| 24 |
+
const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
|
| 25 |
+
prefersReducedMotion = mediaQuery.matches;
|
| 26 |
+
|
| 27 |
+
const updateMotionPreference = (e: MediaQueryListEvent): void => {
|
| 28 |
+
prefersReducedMotion = e.matches;
|
| 29 |
+
};
|
| 30 |
+
mediaQuery.addEventListener("change", updateMotionPreference);
|
| 31 |
+
return () => {
|
| 32 |
+
mediaQuery.removeEventListener("change", updateMotionPreference);
|
| 33 |
+
};
|
| 34 |
+
});
|
| 35 |
+
|
| 36 |
+
$: if (mounted) _open = open;
|
| 37 |
+
</script>
|
| 38 |
+
|
| 39 |
+
<div
|
| 40 |
+
class="top-bar"
|
| 41 |
+
class:open={_open}
|
| 42 |
+
class:reduce-motion={prefersReducedMotion}
|
| 43 |
+
class:on-top={bring_to_front}
|
| 44 |
+
style="height: {height_css}; width: {width_css};"
|
| 45 |
+
>
|
| 46 |
+
<button
|
| 47 |
+
on:click={() => {
|
| 48 |
+
_open = !_open;
|
| 49 |
+
open = _open;
|
| 50 |
+
if (_open) {
|
| 51 |
+
dispatch("expand");
|
| 52 |
+
} else {
|
| 53 |
+
dispatch("collapse");
|
| 54 |
+
}
|
| 55 |
+
}}
|
| 56 |
+
class="toggle-top-button"
|
| 57 |
+
aria-label="Toggle Top Bar"
|
| 58 |
+
>
|
| 59 |
+
<div class="chevron">
|
| 60 |
+
<span class="chevron-arrow"></span>
|
| 61 |
+
</div>
|
| 62 |
+
</button>
|
| 63 |
+
<div class="bar-content">
|
| 64 |
+
<slot />
|
| 65 |
+
</div>
|
| 66 |
+
</div>
|
| 67 |
+
|
| 68 |
+
<style>
|
| 69 |
+
.top-bar {
|
| 70 |
+
display: flex;
|
| 71 |
+
flex-direction: column;
|
| 72 |
+
position: fixed;
|
| 73 |
+
top: 0;
|
| 74 |
+
left: 50%;
|
| 75 |
+
background-color: var(--background-fill-secondary);
|
| 76 |
+
transform: translateX(-50%) translateY(-100%);
|
| 77 |
+
z-index: 1000;
|
| 78 |
+
border-bottom: 1px solid var(--border-color-primary);
|
| 79 |
+
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
.top-bar.on-top {
|
| 83 |
+
z-index: 2000;
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
.top-bar:not(.reduce-motion) {
|
| 87 |
+
transition: transform 0.3s ease-in-out;
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
.top-bar.open {
|
| 91 |
+
transform: translateX(-50%) translateY(0);
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
.toggle-top-button {
|
| 95 |
+
position: absolute;
|
| 96 |
+
bottom: 0;
|
| 97 |
+
left: 50%;
|
| 98 |
+
transform: translate(-50%, 100%);
|
| 99 |
+
background: var(--background-fill-secondary);
|
| 100 |
+
border: 1px solid var(--border-color-primary);
|
| 101 |
+
border-top: none;
|
| 102 |
+
cursor: pointer;
|
| 103 |
+
padding: var(--size-2);
|
| 104 |
+
display: flex;
|
| 105 |
+
align-items: center;
|
| 106 |
+
justify-content: center;
|
| 107 |
+
width: var(--size-10);
|
| 108 |
+
height: var(--size-6);
|
| 109 |
+
z-index: inherit;
|
| 110 |
+
border-radius: 0 0 var(--radius-lg) var(--radius-lg);
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
.toggle-top-button:not(.reduce-motion) {
|
| 114 |
+
transition: all 0.3s ease-in-out;
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
.chevron {
|
| 118 |
+
position: relative;
|
| 119 |
+
display: block;
|
| 120 |
+
width: var(--size-3);
|
| 121 |
+
height: var(--size-3);
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
.chevron-arrow {
|
| 125 |
+
position: absolute;
|
| 126 |
+
box-sizing: border-box;
|
| 127 |
+
top: 10%;
|
| 128 |
+
left: 18%;
|
| 129 |
+
width: 80%;
|
| 130 |
+
height: 80%;
|
| 131 |
+
border-bottom: var(--size-0-5) solid var(--body-text-color);
|
| 132 |
+
border-right: var(--size-0-5) solid var(--body-text-color);
|
| 133 |
+
transform: rotate(45deg);
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
.top-bar:not(.reduce-motion) .chevron-arrow {
|
| 137 |
+
transition: transform 0.3s ease-in-out;
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
.top-bar.open .chevron-arrow {
|
| 141 |
+
transform: rotate(-135deg);
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
.bar-content {
|
| 145 |
+
padding: var(--size-4);
|
| 146 |
+
overflow-y: auto;
|
| 147 |
+
flex-grow: 1;
|
| 148 |
+
}
|
| 149 |
+
</style>
|
src/frontend/tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"compilerOptions": {
|
| 3 |
+
"allowJs": true,
|
| 4 |
+
"checkJs": true,
|
| 5 |
+
"esModuleInterop": true,
|
| 6 |
+
"forceConsistentCasingInFileNames": true,
|
| 7 |
+
"resolveJsonModule": true,
|
| 8 |
+
"skipLibCheck": true,
|
| 9 |
+
"sourceMap": true,
|
| 10 |
+
"strict": true,
|
| 11 |
+
"verbatimModuleSyntax": true
|
| 12 |
+
},
|
| 13 |
+
"exclude": ["node_modules", "dist", "./gradio.config.js"]
|
| 14 |
+
}
|
src/pyproject.toml
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[build-system]
|
| 2 |
+
requires = [
|
| 3 |
+
"hatchling",
|
| 4 |
+
"hatch-requirements-txt",
|
| 5 |
+
"hatch-fancy-pypi-readme>=22.5.0",
|
| 6 |
+
]
|
| 7 |
+
build-backend = "hatchling.build"
|
| 8 |
+
|
| 9 |
+
[project]
|
| 10 |
+
name = "gradio_topbar"
|
| 11 |
+
version = "0.0.1"
|
| 12 |
+
description = "A TopBar for Gradio Interface"
|
| 13 |
+
readme = "README.md"
|
| 14 |
+
license = "apache-2.0"
|
| 15 |
+
requires-python = ">=3.10"
|
| 16 |
+
authors = [{ name = "Eliseu Silva", email = "[email protected]" }]
|
| 17 |
+
keywords = ["gradio-custom-component", "gradio-template-Sidebar"]
|
| 18 |
+
# Add dependencies here
|
| 19 |
+
dependencies = ["gradio>=4.0,<6.0"]
|
| 20 |
+
classifiers = [
|
| 21 |
+
'Development Status :: 3 - Alpha',
|
| 22 |
+
'Operating System :: OS Independent',
|
| 23 |
+
'Programming Language :: Python :: 3',
|
| 24 |
+
'Programming Language :: Python :: 3 :: Only',
|
| 25 |
+
'Programming Language :: Python :: 3.8',
|
| 26 |
+
'Programming Language :: Python :: 3.9',
|
| 27 |
+
'Programming Language :: Python :: 3.10',
|
| 28 |
+
'Programming Language :: Python :: 3.11',
|
| 29 |
+
'Topic :: Scientific/Engineering',
|
| 30 |
+
'Topic :: Scientific/Engineering :: Artificial Intelligence',
|
| 31 |
+
'Topic :: Scientific/Engineering :: Visualization',
|
| 32 |
+
]
|
| 33 |
+
|
| 34 |
+
# The repository and space URLs are optional, but recommended.
|
| 35 |
+
# Adding a repository URL will create a badge in the auto-generated README that links to the repository.
|
| 36 |
+
# Adding a space URL will create a badge in the auto-generated README that links to the space.
|
| 37 |
+
# This will make it easy for people to find your deployed demo or source code when they
|
| 38 |
+
# encounter your project in the wild.
|
| 39 |
+
|
| 40 |
+
# [project.urls]
|
| 41 |
+
# repository = "https://github.com/DEVAIEXP/gradio_component_topbar"
|
| 42 |
+
# space = "https://huggingface.co/spaces/elismasilva/gradio_topbar"
|
| 43 |
+
|
| 44 |
+
[project.optional-dependencies]
|
| 45 |
+
dev = ["build", "twine"]
|
| 46 |
+
|
| 47 |
+
[tool.hatch.build]
|
| 48 |
+
artifacts = ["/backend/gradio_topbar/templates", "*.pyi", "/\\backend\\gradio_topbar\\templates"]
|
| 49 |
+
|
| 50 |
+
[tool.hatch.build.targets.wheel]
|
| 51 |
+
packages = ["/backend/gradio_topbar"]
|