charbel-malo commited on
Commit
22741e9
β€’
1 Parent(s): 4b666fa

Upload folder using huggingface_hub

Browse files
Files changed (2) hide show
  1. app.py +85 -46
  2. shortcuts.json +1 -1
app.py CHANGED
@@ -4,26 +4,46 @@ from datetime import datetime
4
  import json
5
  import pandas as pd
6
  import os
 
 
 
7
 
8
  shortcuts_list = []
9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  def save_shortcuts():
11
- global shortcuts_list
12
  with open('shortcuts.json', 'w') as f:
13
  json.dump(shortcuts_list, f, default=str)
 
14
 
15
  def load_shortcuts():
16
  global shortcuts_list
17
  if os.path.exists('shortcuts.json'):
18
- with open('shortcuts.json', 'r') as f:
19
- shortcuts_list = json.load(f)
 
 
20
  for shortcut in shortcuts_list:
21
  shortcut['date_added'] = datetime.fromisoformat(shortcut['date_added'])
22
  else:
23
  shortcuts_list = []
24
 
 
25
  def add_shortcut(name, tags, link, emojis, color_from, color_to, short_description):
26
- global shortcuts_list
27
  new_shortcut = {
28
  'name': name.strip(),
29
  'tags': [tag.strip() for tag in tags.split('/') if tag.strip()],
@@ -41,8 +61,8 @@ def add_shortcut(name, tags, link, emojis, color_from, color_to, short_descripti
41
  # Return updated HTML
42
  return update_display()
43
 
 
44
  def get_shortcuts_dataframe(sort_by='Recently Added', search_query='', filter_tags=[]):
45
- global shortcuts_list
46
  datafra = pd.DataFrame(shortcuts_list)
47
  if datafra.empty:
48
  return datafra
@@ -63,8 +83,8 @@ def get_shortcuts_dataframe(sort_by='Recently Added', search_query='', filter_ta
63
  datafra = datafra.reset_index(drop=True)
64
  return datafra
65
 
 
66
  def generate_cards_html(datafra):
67
- global shortcuts_list
68
  if datafra.empty:
69
  return "<p>No shortcuts available.</p>"
70
  cards_html = '<div style="display: flex; flex-wrap: wrap;">'
@@ -102,51 +122,63 @@ def generate_cards_html(datafra):
102
  </div>
103
  """
104
  card_html = f"""
105
- <div style="{style}" onmouseover="handleHover({idx})" onmouseout="handleHoverOut({idx})" id="card-{idx}">
 
 
106
  {labels_html}
107
  <div style='font-size: 40px; text-align: center;'>{shortcut['emojis']}</div>
108
  <h3 style='text-align: center;'>{shortcut['name']}</h3>
109
  <p style='text-align: center;'>{shortcut['short_description']}</p>
110
  <div style='text-align: center;'>
111
- <button style="background: none; border: none; cursor: pointer; {pin_style}" onclick="togglePin({idx})">{pin_icon}</button>
112
- <button style="background: none; border: none; cursor: pointer;" onclick="toggleFavorite({idx})">{favorite_icon}</button>
113
  <button onclick="window.open('{shortcut['link']}', '_blank')">πŸ”— Open</button>
114
  </div>
115
- <div id="delete-{idx}" style="display: none; position: absolute; top: 10px; left: 10px; cursor: pointer;" onclick="deleteShortcut({idx})">
116
  πŸ—‘οΈ
117
  </div>
118
  </div>
119
  """
120
  cards_html += card_html
121
  cards_html += '</div>'
122
-
123
- # Add JavaScript for handling hover and command/control key
124
-
125
  return cards_html
126
 
 
127
  def update_display(sort_by='Recently Added', search_query='', filter_tags=[]):
128
  datafra = get_shortcuts_dataframe(sort_by, search_query, filter_tags)
129
  return generate_cards_html(datafra)
130
 
 
131
  def toggle_pin(index):
132
- global shortcuts_list
133
- index = int(index) + 1
134
- shortcuts_list[index]['pinned'] = not shortcuts_list[index]['pinned']
135
- save_shortcuts()
136
  # Return updated HTML
137
  return update_display()
138
 
 
139
  def toggle_favorite(index):
140
- global shortcuts_list
141
- index = int(index) - 1
142
  if 0 <= index < len(shortcuts_list):
143
  shortcuts_list[index]['favorited'] = not shortcuts_list[index]['favorited']
144
  save_shortcuts()
145
  # Return updated HTML
146
  return update_display()
147
 
 
 
 
 
 
 
 
 
 
148
  load_shortcuts()
149
 
 
 
150
  js_code = f"""
151
  function my_func() {{
152
  window.isCmdOrCtrl = false;
@@ -172,7 +204,7 @@ window.handleHover = function(event, idx) {{
172
  }};
173
 
174
  window.handleHoverOut = function(event, idx) {{
175
- document.getElementById('card-' + idx).style.display = 'none';
176
  }};
177
 
178
  window.showDeleteIcons = function() {{
@@ -189,7 +221,6 @@ window.hideDeleteIcons = function() {{
189
  }});
190
  }};
191
 
192
-
193
  window.togglePin = function(idx) {{
194
  // Implement the delete functionality, e.g., call an API endpoint
195
  fetch('/toggle_pin', {{
@@ -199,6 +230,7 @@ window.togglePin = function(idx) {{
199
  }},
200
  body: JSON.stringify({{ index: idx }})
201
  }})
 
202
  .then(data => {{
203
  // Update the grid display
204
  document.getElementById('grid_output').innerHTML = data.grid_html;
@@ -222,8 +254,9 @@ window.deleteShortcut = function(idx) {{
222
  }};
223
  }}
224
  """
 
225
  # Build the Gradio App
226
- with gr.Blocks(theme="charbelgrower/Crystal",js=js_code) as demo:
227
 
228
  gr.Markdown("## Website Shortcuts")
229
  with gr.Row():
@@ -251,8 +284,10 @@ with gr.Blocks(theme="charbelgrower/Crystal",js=js_code) as demo:
251
  add_button = gr.Button("Add Shortcut")
252
 
253
  # Update display when filters change
254
- def refresh_display(search_query, sort_by, filter_tags):
255
- return update_display(sort_by, search_query, filter_tags)
 
 
256
 
257
  search_bar.change(fn=refresh_display, inputs=[search_bar, sort_options, filter_tags], outputs=grid_output)
258
  sort_options.change(fn=refresh_display, inputs=[search_bar, sort_options, filter_tags], outputs=grid_output)
@@ -264,39 +299,43 @@ with gr.Blocks(theme="charbelgrower/Crystal",js=js_code) as demo:
264
  inputs=[name, tags, link, emojis, color_from, color_to, short_description],
265
  outputs=grid_output
266
  )
267
- # Expose endpoints for toggle functions
 
268
  api = FastAPI()
269
 
270
- @api.post("/toggle_pin")
271
- async def toggle_pin_route(request: Request):
272
  data = await request.json()
273
- index = data['index']
274
- grid_html = toggle_pin(index)
275
  return {'grid_html': grid_html}
276
 
277
- @api.post("/delete_shortcut")
278
- async def delete_shortcut_route(request: Request):
279
- global shortcuts_list
280
  data = await request.json()
281
- index = int(data['index']) - 1
282
- if 0 <= index < len(shortcuts_list):
283
- del shortcuts_list[index]
284
- save_shortcuts()
285
- # Return updated HTML
286
- grid_html = update_display()
287
  return {'grid_html': grid_html}
288
 
289
- @api.post("/toggle_favorite")
290
- async def toggle_favorite_route(request: Request):
291
  data = await request.json()
292
- index = data['index']
293
  grid_html = toggle_favorite(index)
294
  return {'grid_html': grid_html}
295
 
296
- # Mount the Gradio app onto the FastAPI app
297
- app = gr.mount_gradio_app(api, demo, path="/")
298
- demo.launch(ssr_mode=False)
299
  if __name__ == "__main__":
300
  import uvicorn
301
- load_shortcuts()
302
- uvicorn.run(app, host="https://api.hf.space/v1/charbel-malo/LinkMaster-XS", port=443)
 
 
 
 
 
 
 
 
 
 
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
 
11
  shortcuts_list = []
12
 
13
+ # Add file change handler
14
+ class JSONFileHandler(FileSystemEventHandler):
15
+ def on_modified(self, event):
16
+ if event.src_path.endswith('shortcuts.json'):
17
+ load_shortcuts()
18
+
19
+ # Add decorator for file operations
20
+ def ensure_fresh_data(func):
21
+ @wraps(func)
22
+ def wrapper(*args, **kwargs):
23
+ load_shortcuts() # Reload before each operation
24
+ result = func(*args, **kwargs)
25
+ return result
26
+ return wrapper
27
+
28
  def save_shortcuts():
 
29
  with open('shortcuts.json', 'w') as f:
30
  json.dump(shortcuts_list, f, default=str)
31
+ load_shortcuts()
32
 
33
  def load_shortcuts():
34
  global shortcuts_list
35
  if os.path.exists('shortcuts.json'):
36
+ # Open the file in binary mode with no buffering to avoid cached data
37
+ with open('shortcuts.json', 'rb', buffering=0) as f:
38
+ data = f.read()
39
+ shortcuts_list = json.loads(data.decode('utf-8'))
40
  for shortcut in shortcuts_list:
41
  shortcut['date_added'] = datetime.fromisoformat(shortcut['date_added'])
42
  else:
43
  shortcuts_list = []
44
 
45
+ @ensure_fresh_data
46
  def add_shortcut(name, tags, link, emojis, color_from, color_to, short_description):
 
47
  new_shortcut = {
48
  'name': name.strip(),
49
  'tags': [tag.strip() for tag in tags.split('/') if tag.strip()],
 
61
  # Return updated HTML
62
  return update_display()
63
 
64
+ @ensure_fresh_data
65
  def get_shortcuts_dataframe(sort_by='Recently Added', search_query='', filter_tags=[]):
 
66
  datafra = pd.DataFrame(shortcuts_list)
67
  if datafra.empty:
68
  return datafra
 
83
  datafra = datafra.reset_index(drop=True)
84
  return datafra
85
 
86
+ @ensure_fresh_data
87
  def generate_cards_html(datafra):
 
88
  if datafra.empty:
89
  return "<p>No shortcuts available.</p>"
90
  cards_html = '<div style="display: flex; flex-wrap: wrap;">'
 
122
  </div>
123
  """
124
  card_html = f"""
125
+ <div style="{style}"
126
+ onmouseover="window.handleHover(event, {idx})"
127
+ onmouseout="window.handleHoverOut(event, {idx})">
128
  {labels_html}
129
  <div style='font-size: 40px; text-align: center;'>{shortcut['emojis']}</div>
130
  <h3 style='text-align: center;'>{shortcut['name']}</h3>
131
  <p style='text-align: center;'>{shortcut['short_description']}</p>
132
  <div style='text-align: center;'>
133
+ <button style="background: none; border: none; cursor: pointer; {pin_style}" onclick="window.togglePin({idx})">{pin_icon}</button>
134
+ <button style="background: none; border: none; cursor: pointer;" onclick="window.toggleFavorite({idx})">{favorite_icon}</button>
135
  <button onclick="window.open('{shortcut['link']}', '_blank')">πŸ”— Open</button>
136
  </div>
137
+ <div id="delete-{idx}" style="display: none; position: absolute; top: 10px; left: 10px; cursor: pointer;" onclick="window.deleteShortcut({idx})">
138
  πŸ—‘οΈ
139
  </div>
140
  </div>
141
  """
142
  cards_html += card_html
143
  cards_html += '</div>'
 
 
 
144
  return cards_html
145
 
146
+ @ensure_fresh_data
147
  def update_display(sort_by='Recently Added', search_query='', filter_tags=[]):
148
  datafra = get_shortcuts_dataframe(sort_by, search_query, filter_tags)
149
  return generate_cards_html(datafra)
150
 
151
+ @ensure_fresh_data
152
  def toggle_pin(index):
153
+ index = int(index)
154
+ if 0 <= index < len(shortcuts_list):
155
+ shortcuts_list[index]['pinned'] = not shortcuts_list[index]['pinned']
156
+ save_shortcuts()
157
  # Return updated HTML
158
  return update_display()
159
 
160
+ @ensure_fresh_data
161
  def toggle_favorite(index):
162
+ index = int(index)
 
163
  if 0 <= index < len(shortcuts_list):
164
  shortcuts_list[index]['favorited'] = not shortcuts_list[index]['favorited']
165
  save_shortcuts()
166
  # Return updated HTML
167
  return update_display()
168
 
169
+ @ensure_fresh_data
170
+ def delete_shortcut(index):
171
+ index = int(index)
172
+ if 0 <= index < len(shortcuts_list):
173
+ del shortcuts_list[index]
174
+ save_shortcuts()
175
+ # Return updated HTML
176
+ return update_display()
177
+
178
  load_shortcuts()
179
 
180
+ # JavaScript code attached to window object
181
+
182
  js_code = f"""
183
  function my_func() {{
184
  window.isCmdOrCtrl = false;
 
204
  }};
205
 
206
  window.handleHoverOut = function(event, idx) {{
207
+ document.getElementById('card-'+ idx).style.display = 'none';
208
  }};
209
 
210
  window.showDeleteIcons = function() {{
 
221
  }});
222
  }};
223
 
 
224
  window.togglePin = function(idx) {{
225
  // Implement the delete functionality, e.g., call an API endpoint
226
  fetch('/toggle_pin', {{
 
230
  }},
231
  body: JSON.stringify({{ index: idx }})
232
  }})
233
+ .then(response => response.json())
234
  .then(data => {{
235
  // Update the grid display
236
  document.getElementById('grid_output').innerHTML = data.grid_html;
 
254
  }};
255
  }}
256
  """
257
+
258
  # Build the Gradio App
259
+ with gr.Blocks(theme="charbelgrower/Crystal", js=js_code) as demo:
260
 
261
  gr.Markdown("## Website Shortcuts")
262
  with gr.Row():
 
284
  add_button = gr.Button("Add Shortcut")
285
 
286
  # Update display when filters change
287
+ def refresh_display(search_query='', sort_by='Recently Added', filter_tags=[]):
288
+ grid_html = update_display(sort_by, search_query, filter_tags)
289
+ filter_tags_options = get_all_tags()
290
+ return grid_html, gr.update(choices=filter_tags_options)
291
 
292
  search_bar.change(fn=refresh_display, inputs=[search_bar, sort_options, filter_tags], outputs=grid_output)
293
  sort_options.change(fn=refresh_display, inputs=[search_bar, sort_options, filter_tags], outputs=grid_output)
 
299
  inputs=[name, tags, link, emojis, color_from, color_to, short_description],
300
  outputs=grid_output
301
  )
302
+
303
+ # Expose endpoints for custom functions using FastAPI
304
  api = FastAPI()
305
 
306
+ @api.post('/delete_shortcut')
307
+ async def delete_shortcut_endpoint(request: Request):
308
  data = await request.json()
309
+ index = data.get('index')
310
+ grid_html = delete_shortcut(index)
311
  return {'grid_html': grid_html}
312
 
313
+ @api.post('/toggle_pin')
314
+ async def toggle_pin_endpoint(request: Request):
 
315
  data = await request.json()
316
+ index = data.get('index')
317
+ grid_html = toggle_pin(index)
 
 
 
 
318
  return {'grid_html': grid_html}
319
 
320
+ @api.post('/toggle_favorite')
321
+ async def toggle_favorite_endpoint(request: Request):
322
  data = await request.json()
323
+ index = data.get('index')
324
  grid_html = toggle_favorite(index)
325
  return {'grid_html': grid_html}
326
 
327
+ app = gr.mount_gradio_app(api, demo,"/")
328
+ # Initialize file watcher
 
329
  if __name__ == "__main__":
330
  import uvicorn
331
+ demo.unload(fn=load_shortcuts)
332
+ event_handler = JSONFileHandler()
333
+ observer = Observer()
334
+ observer.schedule(event_handler, path='shortcuts.json', recursive=False)
335
+ observer.start()
336
+
337
+ try:
338
+ uvicorn.run(app, host="127.0.0.1", port=7860)
339
+ finally:
340
+ observer.stop()
341
+ observer.join()
shortcuts.json CHANGED
@@ -1 +1 @@
1
- [{"name": "Charbel", "tags": ["Personal"], "link": "example.com", "emojis": "\ud83e\udd70", "color_from": "rgba(157.78125, 125.2561677631579, 125.2561677631579, 1)", "color_to": "rgba(255, 0, 0, 1)", "short_description": "ssadasd", "pinned": false, "favorited": false, "date_added": "2024-11-13T16:25:36.058089"}]
 
1
+ [{"name": "Charbel", "tags": ["Personal"], "link": "example.com", "emojis": "\ud83e\udd70", "color_from": "rgba(157.78125, 125.2561677631579, 125.2561677631579, 1)", "color_to": "rgba(255, 0, 0, 1)", "short_description": "ssadasd", "pinned": true, "favorited": false, "date_added": "2024-11-13 16:25:36.058089"}]