Spaces:
Sleeping
Sleeping
charbelmalo
commited on
Commit
Β·
9811ae6
1
Parent(s):
084d624
revert to old commit but with updated readme
Browse files
app.py
CHANGED
@@ -4,60 +4,24 @@ from datetime import datetime
|
|
4 |
import json
|
5 |
import pandas as pd
|
6 |
import os
|
7 |
-
from watchdog.observers import Observer
|
8 |
-
from watchdog.events import FileSystemEventHandler
|
9 |
-
from functools import wraps
|
10 |
-
from pathlib import Path
|
11 |
-
import shutil
|
12 |
-
import getpass
|
13 |
|
14 |
-
global shortcuts_list
|
15 |
shortcuts_list = []
|
16 |
|
17 |
-
user_dir = Path(__file__).parent / datetime.now().strftime('%Y-%m-%d')
|
18 |
-
|
19 |
-
# Add file change handler
|
20 |
-
class JSONFileHandler(FileSystemEventHandler):
|
21 |
-
def on_modified(self, event):
|
22 |
-
if event.src_path.endswith('.json'):
|
23 |
-
load_shortcuts()
|
24 |
-
|
25 |
-
# Add decorator for file operations
|
26 |
-
def ensure_fresh_data(func):
|
27 |
-
@wraps(func)
|
28 |
-
def wrapper(*args, **kwargs):
|
29 |
-
load_shortcuts() # Reload before each operation
|
30 |
-
result = func(*args, **kwargs)
|
31 |
-
return result
|
32 |
-
return wrapper
|
33 |
-
|
34 |
def save_shortcuts():
|
35 |
-
|
36 |
-
|
37 |
-
with open(path, 'w') as f:
|
38 |
-
json.dump(path, f, default=str)
|
39 |
-
load_shortcuts()
|
40 |
|
41 |
def load_shortcuts():
|
42 |
global shortcuts_list
|
43 |
-
# user_dir: Path = current_dir / datetime.now().strftime('%Y-%m-%d')
|
44 |
-
user_dir.mkdir(exist_ok=True)
|
45 |
-
path = user_dir / f"{getpass.getuser()}.json"
|
46 |
if os.path.exists('shortcuts.json'):
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
tmpshortcuts_list = json.loads(data.decode('utf-8'))
|
51 |
-
for shortcut in tmpshortcuts_list:
|
52 |
shortcut['date_added'] = datetime.fromisoformat(shortcut['date_added'])
|
53 |
-
shortcuts_list = tmpshortcuts_list
|
54 |
-
json.dump(tmpshortcuts_list, open(path, 'w'), default=str)
|
55 |
else:
|
56 |
shortcuts_list = []
|
57 |
|
58 |
-
@ensure_fresh_data
|
59 |
def add_shortcut(name, tags, link, emojis, color_from, color_to, short_description):
|
60 |
-
global shortcuts_list
|
61 |
new_shortcut = {
|
62 |
'name': name.strip(),
|
63 |
'tags': [tag.strip() for tag in tags.split('/') if tag.strip()],
|
@@ -70,18 +34,12 @@ def add_shortcut(name, tags, link, emojis, color_from, color_to, short_descripti
|
|
70 |
'favorited': False,
|
71 |
'date_added': datetime.now().isoformat()
|
72 |
}
|
73 |
-
shortcuts_list
|
74 |
save_shortcuts()
|
75 |
# Return updated HTML
|
76 |
return update_display()
|
77 |
|
78 |
-
def delete_directory():
|
79 |
-
shutil.rmtree(str(user_dir))
|
80 |
-
|
81 |
-
|
82 |
-
@ensure_fresh_data
|
83 |
def get_shortcuts_dataframe(sort_by='Recently Added', search_query='', filter_tags=[]):
|
84 |
-
global shortcuts_list
|
85 |
datafra = pd.DataFrame(shortcuts_list)
|
86 |
if datafra.empty:
|
87 |
return datafra
|
@@ -102,9 +60,7 @@ def get_shortcuts_dataframe(sort_by='Recently Added', search_query='', filter_ta
|
|
102 |
datafra = datafra.reset_index(drop=True)
|
103 |
return datafra
|
104 |
|
105 |
-
@ensure_fresh_data
|
106 |
def generate_cards_html(datafra):
|
107 |
-
global shortcuts_list
|
108 |
if datafra.empty:
|
109 |
return "<p>No shortcuts available.</p>"
|
110 |
cards_html = '<div style="display: flex; flex-wrap: wrap;">'
|
@@ -142,36 +98,33 @@ def generate_cards_html(datafra):
|
|
142 |
</div>
|
143 |
"""
|
144 |
card_html = f"""
|
145 |
-
<div style="{style}"
|
146 |
-
onmouseover="window.handleHover(event, {idx})"
|
147 |
-
onmouseout="window.handleHoverOut(event, {idx})">
|
148 |
{labels_html}
|
149 |
<div style='font-size: 40px; text-align: center;'>{shortcut['emojis']}</div>
|
150 |
<h3 style='text-align: center;'>{shortcut['name']}</h3>
|
151 |
<p style='text-align: center;'>{shortcut['short_description']}</p>
|
152 |
<div style='text-align: center;'>
|
153 |
-
<button style="background: none; border: none; cursor: pointer; {pin_style}" onclick="
|
154 |
-
<button style="background: none; border: none; cursor: pointer;" onclick="
|
155 |
<button onclick="window.open('{shortcut['link']}', '_blank')">π Open</button>
|
156 |
</div>
|
157 |
-
<div id="delete-{idx}" style="display: none; position: absolute; top: 10px; left: 10px; cursor: pointer;" onclick="
|
158 |
ποΈ
|
159 |
</div>
|
160 |
</div>
|
161 |
"""
|
162 |
cards_html += card_html
|
163 |
cards_html += '</div>'
|
|
|
|
|
|
|
164 |
return cards_html
|
165 |
|
166 |
-
@ensure_fresh_data
|
167 |
def update_display(sort_by='Recently Added', search_query='', filter_tags=[]):
|
168 |
-
global shortcuts_list
|
169 |
datafra = get_shortcuts_dataframe(sort_by, search_query, filter_tags)
|
170 |
return generate_cards_html(datafra)
|
171 |
|
172 |
-
@ensure_fresh_data
|
173 |
def toggle_pin(index):
|
174 |
-
global shortcuts_list
|
175 |
index = int(index)
|
176 |
if 0 <= index < len(shortcuts_list):
|
177 |
shortcuts_list[index]['pinned'] = not shortcuts_list[index]['pinned']
|
@@ -179,9 +132,7 @@ def toggle_pin(index):
|
|
179 |
# Return updated HTML
|
180 |
return update_display()
|
181 |
|
182 |
-
@ensure_fresh_data
|
183 |
def toggle_favorite(index):
|
184 |
-
global shortcuts_list
|
185 |
index = int(index)
|
186 |
if 0 <= index < len(shortcuts_list):
|
187 |
shortcuts_list[index]['favorited'] = not shortcuts_list[index]['favorited']
|
@@ -189,21 +140,8 @@ def toggle_favorite(index):
|
|
189 |
# Return updated HTML
|
190 |
return update_display()
|
191 |
|
192 |
-
@ensure_fresh_data
|
193 |
-
def delete_shortcut(index):
|
194 |
-
global shortcuts_list
|
195 |
-
index = int(index)
|
196 |
-
if 0 <= index < len(shortcuts_list):
|
197 |
-
del shortcuts_list[index]
|
198 |
-
save_shortcuts()
|
199 |
-
# Return updated HTML
|
200 |
-
return update_display()
|
201 |
-
|
202 |
load_shortcuts()
|
203 |
|
204 |
-
|
205 |
-
# JavaScript code attached to window object
|
206 |
-
|
207 |
js_code = f"""
|
208 |
function my_func() {{
|
209 |
window.isCmdOrCtrl = false;
|
@@ -246,22 +184,6 @@ window.hideDeleteIcons = function() {{
|
|
246 |
}});
|
247 |
}};
|
248 |
|
249 |
-
window.togglePin = function(idx) {{
|
250 |
-
// Implement the delete functionality, e.g., call an API endpoint
|
251 |
-
fetch('/toggle_pin', {{
|
252 |
-
method: 'POST',
|
253 |
-
headers: {{
|
254 |
-
'Content-Type': 'application/json'
|
255 |
-
}},
|
256 |
-
body: JSON.stringify({{ index: idx }})
|
257 |
-
}})
|
258 |
-
.then(response => response.json())
|
259 |
-
.then(data => {{
|
260 |
-
// Update the grid display
|
261 |
-
document.getElementById('grid_output').innerHTML = data.grid_html;
|
262 |
-
}});
|
263 |
-
}};
|
264 |
-
|
265 |
window.deleteShortcut = function(idx) {{
|
266 |
// Implement the delete functionality, e.g., call an API endpoint
|
267 |
fetch('/delete_shortcut', {{
|
@@ -279,9 +201,8 @@ window.deleteShortcut = function(idx) {{
|
|
279 |
}};
|
280 |
}}
|
281 |
"""
|
282 |
-
|
283 |
# Build the Gradio App
|
284 |
-
with gr.Blocks(theme="charbelgrower/Crystal",
|
285 |
|
286 |
gr.Markdown("## Website Shortcuts")
|
287 |
with gr.Row():
|
@@ -296,9 +217,6 @@ with gr.Blocks(theme="charbelgrower/Crystal", js=js_code) as demo:
|
|
296 |
|
297 |
grid_output = gr.HTML(value=update_display(), elem_id="grid_output")
|
298 |
|
299 |
-
# demo.load(t)
|
300 |
-
load_shortcuts()
|
301 |
-
|
302 |
gr.Markdown("## Add a New Website Shortcut")
|
303 |
with gr.Row():
|
304 |
name = gr.Textbox(label="Name")
|
@@ -310,12 +228,10 @@ with gr.Blocks(theme="charbelgrower/Crystal", js=js_code) as demo:
|
|
310 |
color_to = gr.ColorPicker(label="Gradient Color To")
|
311 |
short_description = gr.Textbox(label="Short Description")
|
312 |
add_button = gr.Button("Add Shortcut")
|
313 |
-
|
314 |
# Update display when filters change
|
315 |
-
def refresh_display(search_query
|
316 |
-
|
317 |
-
filter_tags_options = get_all_tags()
|
318 |
-
return grid_html, gr.update(choices=filter_tags_options)
|
319 |
|
320 |
search_bar.change(fn=refresh_display, inputs=[search_bar, sort_options, filter_tags], outputs=grid_output)
|
321 |
sort_options.change(fn=refresh_display, inputs=[search_bar, sort_options, filter_tags], outputs=grid_output)
|
@@ -327,53 +243,38 @@ with gr.Blocks(theme="charbelgrower/Crystal", js=js_code) as demo:
|
|
327 |
inputs=[name, tags, link, emojis, color_from, color_to, short_description],
|
328 |
outputs=grid_output
|
329 |
)
|
330 |
-
|
331 |
-
load_shortcuts()
|
332 |
-
# demo.load(get_shortcuts_dataframe)
|
333 |
-
# Expose endpoints for custom functions using FastAPI
|
334 |
api = FastAPI()
|
335 |
-
load_shortcuts()
|
336 |
-
|
337 |
|
338 |
-
@api.post(
|
339 |
-
async def
|
340 |
data = await request.json()
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
@api.post('/delete_shortcut')
|
345 |
-
async def delete_shortcut_endpoint(request: Request):
|
346 |
-
data = await request.json()
|
347 |
-
index = data.get('index')
|
348 |
-
grid_html = delete_shortcut(index)
|
349 |
return {'grid_html': grid_html}
|
350 |
|
351 |
-
@api.post(
|
352 |
-
async def
|
353 |
data = await request.json()
|
354 |
-
index = data
|
355 |
-
|
|
|
|
|
|
|
|
|
356 |
return {'grid_html': grid_html}
|
357 |
|
358 |
-
@api.post(
|
359 |
-
async def
|
360 |
data = await request.json()
|
361 |
-
index = data
|
362 |
grid_html = toggle_favorite(index)
|
363 |
return {'grid_html': grid_html}
|
364 |
|
365 |
-
app
|
366 |
-
|
|
|
367 |
if __name__ == "__main__":
|
368 |
import uvicorn
|
369 |
-
|
370 |
-
|
371 |
-
observer.schedule(event_handler, path=str(user_dir / f"{getpass.getuser()}.json"), recursive=False)
|
372 |
-
observer.start()
|
373 |
-
|
374 |
-
try:
|
375 |
-
uvicorn.run(app, host="127.0.0.1", port=7860)
|
376 |
-
finally:
|
377 |
-
demo.unload(delete_directory)
|
378 |
-
observer.stop()
|
379 |
-
observer.join()
|
|
|
4 |
import json
|
5 |
import pandas as pd
|
6 |
import os
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
|
|
|
8 |
shortcuts_list = []
|
9 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
def save_shortcuts():
|
11 |
+
with open('shortcuts.json', 'w') as f:
|
12 |
+
json.dump(shortcuts_list, f, default=str)
|
|
|
|
|
|
|
13 |
|
14 |
def load_shortcuts():
|
15 |
global shortcuts_list
|
|
|
|
|
|
|
16 |
if os.path.exists('shortcuts.json'):
|
17 |
+
with open('shortcuts.json', 'r') as f:
|
18 |
+
shortcuts_list = json.load(f)
|
19 |
+
for shortcut in shortcuts_list:
|
|
|
|
|
20 |
shortcut['date_added'] = datetime.fromisoformat(shortcut['date_added'])
|
|
|
|
|
21 |
else:
|
22 |
shortcuts_list = []
|
23 |
|
|
|
24 |
def add_shortcut(name, tags, link, emojis, color_from, color_to, short_description):
|
|
|
25 |
new_shortcut = {
|
26 |
'name': name.strip(),
|
27 |
'tags': [tag.strip() for tag in tags.split('/') if tag.strip()],
|
|
|
34 |
'favorited': False,
|
35 |
'date_added': datetime.now().isoformat()
|
36 |
}
|
37 |
+
shortcuts_list.append(new_shortcut)
|
38 |
save_shortcuts()
|
39 |
# Return updated HTML
|
40 |
return update_display()
|
41 |
|
|
|
|
|
|
|
|
|
|
|
42 |
def get_shortcuts_dataframe(sort_by='Recently Added', search_query='', filter_tags=[]):
|
|
|
43 |
datafra = pd.DataFrame(shortcuts_list)
|
44 |
if datafra.empty:
|
45 |
return datafra
|
|
|
60 |
datafra = datafra.reset_index(drop=True)
|
61 |
return datafra
|
62 |
|
|
|
63 |
def generate_cards_html(datafra):
|
|
|
64 |
if datafra.empty:
|
65 |
return "<p>No shortcuts available.</p>"
|
66 |
cards_html = '<div style="display: flex; flex-wrap: wrap;">'
|
|
|
98 |
</div>
|
99 |
"""
|
100 |
card_html = f"""
|
101 |
+
<div style="{style}" onmouseover="handleHover({idx})" id="card-{idx}" onmouseout="handleHoverOut({idx})">
|
|
|
|
|
102 |
{labels_html}
|
103 |
<div style='font-size: 40px; text-align: center;'>{shortcut['emojis']}</div>
|
104 |
<h3 style='text-align: center;'>{shortcut['name']}</h3>
|
105 |
<p style='text-align: center;'>{shortcut['short_description']}</p>
|
106 |
<div style='text-align: center;'>
|
107 |
+
<button style="background: none; border: none; cursor: pointer; {pin_style}" onclick="togglePin({idx})">{pin_icon}</button>
|
108 |
+
<button style="background: none; border: none; cursor: pointer;" onclick="toggleFavorite({idx})">{favorite_icon}</button>
|
109 |
<button onclick="window.open('{shortcut['link']}', '_blank')">π Open</button>
|
110 |
</div>
|
111 |
+
<div id="delete-{idx}" style="display: none; position: absolute; top: 10px; left: 10px; cursor: pointer;" onclick="deleteShortcut({idx})">
|
112 |
ποΈ
|
113 |
</div>
|
114 |
</div>
|
115 |
"""
|
116 |
cards_html += card_html
|
117 |
cards_html += '</div>'
|
118 |
+
|
119 |
+
# Add JavaScript for handling hover and command/control key
|
120 |
+
|
121 |
return cards_html
|
122 |
|
|
|
123 |
def update_display(sort_by='Recently Added', search_query='', filter_tags=[]):
|
|
|
124 |
datafra = get_shortcuts_dataframe(sort_by, search_query, filter_tags)
|
125 |
return generate_cards_html(datafra)
|
126 |
|
|
|
127 |
def toggle_pin(index):
|
|
|
128 |
index = int(index)
|
129 |
if 0 <= index < len(shortcuts_list):
|
130 |
shortcuts_list[index]['pinned'] = not shortcuts_list[index]['pinned']
|
|
|
132 |
# Return updated HTML
|
133 |
return update_display()
|
134 |
|
|
|
135 |
def toggle_favorite(index):
|
|
|
136 |
index = int(index)
|
137 |
if 0 <= index < len(shortcuts_list):
|
138 |
shortcuts_list[index]['favorited'] = not shortcuts_list[index]['favorited']
|
|
|
140 |
# Return updated HTML
|
141 |
return update_display()
|
142 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
143 |
load_shortcuts()
|
144 |
|
|
|
|
|
|
|
145 |
js_code = f"""
|
146 |
function my_func() {{
|
147 |
window.isCmdOrCtrl = false;
|
|
|
184 |
}});
|
185 |
}};
|
186 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
187 |
window.deleteShortcut = function(idx) {{
|
188 |
// Implement the delete functionality, e.g., call an API endpoint
|
189 |
fetch('/delete_shortcut', {{
|
|
|
201 |
}};
|
202 |
}}
|
203 |
"""
|
|
|
204 |
# Build the Gradio App
|
205 |
+
with gr.Blocks(theme="charbelgrower/Crystal",js=js_code) as demo:
|
206 |
|
207 |
gr.Markdown("## Website Shortcuts")
|
208 |
with gr.Row():
|
|
|
217 |
|
218 |
grid_output = gr.HTML(value=update_display(), elem_id="grid_output")
|
219 |
|
|
|
|
|
|
|
220 |
gr.Markdown("## Add a New Website Shortcut")
|
221 |
with gr.Row():
|
222 |
name = gr.Textbox(label="Name")
|
|
|
228 |
color_to = gr.ColorPicker(label="Gradient Color To")
|
229 |
short_description = gr.Textbox(label="Short Description")
|
230 |
add_button = gr.Button("Add Shortcut")
|
231 |
+
|
232 |
# Update display when filters change
|
233 |
+
def refresh_display(search_query, sort_by, filter_tags):
|
234 |
+
return update_display(sort_by, search_query, filter_tags)
|
|
|
|
|
235 |
|
236 |
search_bar.change(fn=refresh_display, inputs=[search_bar, sort_options, filter_tags], outputs=grid_output)
|
237 |
sort_options.change(fn=refresh_display, inputs=[search_bar, sort_options, filter_tags], outputs=grid_output)
|
|
|
243 |
inputs=[name, tags, link, emojis, color_from, color_to, short_description],
|
244 |
outputs=grid_output
|
245 |
)
|
246 |
+
# Expose endpoints for toggle functions
|
|
|
|
|
|
|
247 |
api = FastAPI()
|
|
|
|
|
248 |
|
249 |
+
@api.post("/toggle_pin")
|
250 |
+
async def toggle_pin_route(request: Request):
|
251 |
data = await request.json()
|
252 |
+
index = data['index']
|
253 |
+
grid_html = toggle_pin(index)
|
|
|
|
|
|
|
|
|
|
|
|
|
254 |
return {'grid_html': grid_html}
|
255 |
|
256 |
+
@api.post("/delete_shortcut")
|
257 |
+
async def delete_shortcut_route(request: Request):
|
258 |
data = await request.json()
|
259 |
+
index = int(data['index'])
|
260 |
+
if 0 <= index < len(shortcuts_list):
|
261 |
+
del shortcuts_list[index]
|
262 |
+
save_shortcuts()
|
263 |
+
# Return updated HTML
|
264 |
+
grid_html = update_display()
|
265 |
return {'grid_html': grid_html}
|
266 |
|
267 |
+
@api.post("/toggle_favorite")
|
268 |
+
async def toggle_favorite_route(request: Request):
|
269 |
data = await request.json()
|
270 |
+
index = data['index']
|
271 |
grid_html = toggle_favorite(index)
|
272 |
return {'grid_html': grid_html}
|
273 |
|
274 |
+
# Mount the Gradio app onto the FastAPI app
|
275 |
+
app = gr.mount_gradio_app(api, demo, path="/")
|
276 |
+
|
277 |
if __name__ == "__main__":
|
278 |
import uvicorn
|
279 |
+
load_shortcuts()
|
280 |
+
uvicorn.run(app, host="127.0.0.1", port=7860)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|