elismasilva commited on
Commit
f264e07
·
verified ·
1 Parent(s): 720037c

Upload folder using huggingface_hub

Browse files
.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
- title: Gradio Topbar
3
- emoji: 👀
4
- colorFrom: red
5
- colorTo: red
6
- sdk: gradio
7
- sdk_version: 5.42.0
8
- app_file: app.py
9
- pinned: false
10
- ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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"]