siddhartharya commited on
Commit
e985ab1
Β·
verified Β·
1 Parent(s): c6d370d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +233 -263
app.py CHANGED
@@ -2,6 +2,10 @@
2
 
3
  import gradio as gr
4
  from bs4 import BeautifulSoup
 
 
 
 
5
  import asyncio
6
  import aiohttp
7
  import re
@@ -9,9 +13,6 @@ import base64
9
  import logging
10
  import os
11
  import sys
12
- from sentence_transformers import SentenceTransformer
13
- import faiss
14
- import numpy as np
15
 
16
  # Import OpenAI library
17
  import openai
@@ -35,6 +36,7 @@ logger.addHandler(console_handler)
35
  logger.info("Initializing models and variables")
36
  embedding_model = SentenceTransformer('all-MiniLM-L6-v2')
37
  faiss_index = None
 
38
  fetch_cache = {}
39
 
40
  # Define the categories
@@ -232,10 +234,10 @@ def vectorize_and_index(bookmarks):
232
  raise
233
 
234
  # Generate HTML display for bookmarks
235
- def display_bookmarks(bookmarks_list):
236
  logger.info("Generating HTML display for bookmarks")
237
  cards = ''
238
- for i, bookmark in enumerate(bookmarks_list):
239
  index = i + 1 # Start index at 1
240
  status = "❌ Dead Link" if bookmark.get('dead_link') else "βœ… Active"
241
  title = bookmark['title']
@@ -244,20 +246,22 @@ def display_bookmarks(bookmarks_list):
244
  summary = bookmark.get('summary', '')
245
  category = bookmark.get('category', 'Uncategorized')
246
 
247
- # Assign CSS classes based on bookmark status
248
  if bookmark.get('dead_link'):
249
- card_classes = "card dead-link"
 
250
  else:
251
- card_classes = "card active-link"
 
252
 
253
  card_html = f'''
254
- <div class="{card_classes}">
255
  <div class="card-content">
256
- <h3>{index}. {title} {status}</h3>
257
- <p><strong>Category:</strong> {category}</p>
258
- <p><strong>URL:</strong> <a href="{url}" target="_blank">{url}</a></p>
259
- <p><strong>ETag:</strong> {etag}</p>
260
- <p><strong>Summary:</strong> {summary}</p>
261
  </div>
262
  </div>
263
  '''
@@ -266,64 +270,34 @@ def display_bookmarks(bookmarks_list):
266
  return cards
267
 
268
  # Process the uploaded file
269
- def process_uploaded_file(file, state_bookmarks):
 
270
  logger.info("Processing uploaded file")
271
  if file is None:
272
  logger.warning("No file uploaded")
273
- return (
274
- "⚠️ Please upload a bookmarks HTML file.",
275
- "",
276
- [],
277
- "",
278
- state_bookmarks # Return the unchanged state
279
- )
280
-
281
  try:
282
  file_content = file.decode('utf-8')
283
  except UnicodeDecodeError as e:
284
  logger.error(f"Error decoding the file: {e}")
285
- return (
286
- "⚠️ Error decoding the file. Please ensure it's a valid HTML file.",
287
- "",
288
- [],
289
- "",
290
- state_bookmarks # Return the unchanged state
291
- )
292
 
293
  try:
294
  bookmarks = parse_bookmarks(file_content)
295
  except Exception as e:
296
  logger.error(f"Error parsing bookmarks: {e}")
297
- return (
298
- "⚠️ Error parsing the bookmarks HTML file.",
299
- "",
300
- [],
301
- "",
302
- state_bookmarks # Return the unchanged state
303
- )
304
 
305
  if not bookmarks:
306
  logger.warning("No bookmarks found in the uploaded file")
307
- return (
308
- "⚠️ No bookmarks found in the uploaded file.",
309
- "",
310
- [],
311
- "",
312
- state_bookmarks # Return the unchanged state
313
- )
314
 
315
  # Asynchronously fetch bookmark info
316
  try:
317
  asyncio.run(process_bookmarks_async(bookmarks))
318
  except Exception as e:
319
  logger.error(f"Error processing bookmarks asynchronously: {e}")
320
- return (
321
- "⚠️ Error processing bookmarks.",
322
- "",
323
- [],
324
- "",
325
- state_bookmarks # Return the unchanged state
326
- )
327
 
328
  # Generate summaries and assign categories
329
  for bookmark in bookmarks:
@@ -334,123 +308,67 @@ def process_uploaded_file(file, state_bookmarks):
334
  faiss_index, embeddings = vectorize_and_index(bookmarks)
335
  except Exception as e:
336
  logger.error(f"Error building FAISS index: {e}")
337
- return (
338
- "⚠️ Error building search index.",
339
- "",
340
- [],
341
- "",
342
- state_bookmarks # Return the unchanged state
343
- )
344
 
345
  message = f"βœ… Successfully processed {len(bookmarks)} bookmarks."
346
  logger.info(message)
347
- bookmark_html = display_bookmarks(bookmarks)
348
 
349
- # Prepare Manage Bookmarks tab outputs
350
  choices = [f"{i+1}. {bookmark['title']} (Category: {bookmark['category']})" for i, bookmark in enumerate(bookmarks)]
351
- bookmarks_html_manage = display_bookmarks(bookmarks)
352
 
353
- # Update the shared state
354
- updated_state = bookmarks.copy()
355
 
356
- return (
357
- message,
358
- bookmark_html,
359
- choices,
360
- bookmarks_html_manage,
361
- updated_state # Return the updated state
362
- )
363
 
364
  # Delete selected bookmarks
365
- def delete_selected_bookmarks(selected_indices, state_bookmarks):
 
366
  if not selected_indices:
367
- return "⚠️ No bookmarks selected.", gr.update(choices=[]), ""
368
-
369
- bookmarks = state_bookmarks.copy()
370
- indices = []
371
- for s in selected_indices:
372
- try:
373
- idx = int(s.split('.')[0]) - 1
374
- if 0 <= idx < len(bookmarks):
375
- indices.append(idx)
376
- else:
377
- logger.warning(f"Index out of range: {idx + 1}")
378
- except ValueError:
379
- logger.error(f"Invalid selection format: {s}")
380
-
381
  indices = sorted(indices, reverse=True)
382
  for idx in indices:
383
- logger.info(f"Deleting bookmark at index {idx + 1}")
384
- bookmarks.pop(idx)
385
-
386
  if bookmarks:
387
  faiss_index, embeddings = vectorize_and_index(bookmarks)
388
  else:
389
  faiss_index = None
390
-
391
  message = "πŸ—‘οΈ Selected bookmarks deleted successfully."
392
  logger.info(message)
393
-
394
- # Regenerate HTML display
395
- bookmarks_html = display_bookmarks(bookmarks)
396
-
397
- # Update choices for selection
398
  choices = [f"{i+1}. {bookmark['title']} (Category: {bookmark['category']})" for i, bookmark in enumerate(bookmarks)]
399
-
400
- # Update the shared state
401
- updated_state = bookmarks.copy()
402
-
403
- return message, gr.update(choices=choices), bookmarks_html
404
 
405
  # Edit category of selected bookmarks
406
- def edit_selected_bookmarks_category(selected_indices, new_category, state_bookmarks):
407
  if not selected_indices:
408
- return (
409
- "⚠️ No bookmarks selected.",
410
- gr.update(choices=[f"{i+1}. {bookmark['title']} (Category: {bookmark['category']})" for i, bookmark in enumerate(state_bookmarks)]),
411
- display_bookmarks(state_bookmarks)
412
- )
413
-
414
  if not new_category:
415
- return (
416
- "⚠️ No new category selected.",
417
- gr.update(choices=[f"{i+1}. {bookmark['title']} (Category: {bookmark['category']})" for i, bookmark in enumerate(state_bookmarks)]),
418
- display_bookmarks(state_bookmarks)
419
- )
420
-
421
- bookmarks = state_bookmarks.copy()
422
- indices = []
423
- for s in selected_indices:
424
- try:
425
- idx = int(s.split('.')[0]) - 1
426
- if 0 <= idx < len(bookmarks):
427
- indices.append(idx)
428
- else:
429
- logger.warning(f"Index out of range: {idx + 1}")
430
- except ValueError:
431
- logger.error(f"Invalid selection format: {s}")
432
-
433
  for idx in indices:
434
- bookmarks[idx]['category'] = new_category
435
- logger.info(f"Updated category for bookmark {idx + 1} to {new_category}")
436
-
437
  message = "✏️ Category updated for selected bookmarks."
438
  logger.info(message)
439
-
440
- # Regenerate HTML display
441
- bookmarks_html = display_bookmarks(bookmarks)
442
-
443
- # Update choices for selection
444
  choices = [f"{i+1}. {bookmark['title']} (Category: {bookmark['category']})" for i, bookmark in enumerate(bookmarks)]
445
-
446
- # Update the shared state
447
- updated_state = bookmarks.copy()
448
-
449
- return message, gr.update(choices=choices), bookmarks_html
450
 
451
  # Export bookmarks to HTML
452
- def export_bookmarks(state_bookmarks):
453
- bookmarks = state_bookmarks
454
  if not bookmarks:
455
  logger.warning("No bookmarks to export")
456
  return "⚠️ No bookmarks to export."
@@ -477,12 +395,11 @@ def export_bookmarks(state_bookmarks):
477
  return "⚠️ Error exporting bookmarks."
478
 
479
  # Chatbot response using Groq Cloud API
480
- def chatbot_response(user_query, state_bookmarks):
481
  if not GROQ_API_KEY:
482
  logger.warning("GROQ_API_KEY not set.")
483
  return "⚠️ API key not set. Please set the GROQ_API_KEY environment variable in the Hugging Face Space settings."
484
 
485
- bookmarks = state_bookmarks
486
  if not bookmarks:
487
  logger.warning("No bookmarks available for chatbot")
488
  return "⚠️ No bookmarks available. Please upload and process your bookmarks first."
@@ -537,212 +454,265 @@ def build_app():
537
  try:
538
  logger.info("Building Gradio app")
539
  with gr.Blocks(css="""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
540
  .card {
541
- box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
542
  transition: 0.3s;
 
543
  }
544
  .card:hover {
545
- box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
546
  }
547
 
548
- /* Dynamic Theme Styling */
549
- @media (prefers-color-scheme: dark) {
550
- body {
551
- color: white;
552
- background-color: #121212;
553
- }
554
- .card {
555
- background-color: #1e1e1e;
556
- }
557
- a {
558
- color: #bb86fc;
559
- }
560
- h1, h2, h3, p, strong {
561
- color: inherit;
562
- }
563
  }
564
 
565
- @media (prefers-color-scheme: light) {
566
- body {
567
- color: black;
568
- background-color: white;
569
- }
570
- .card {
571
- background-color: #fff;
572
- }
573
- a {
574
- color: #1a0dab;
575
- }
576
- h1, h2, h3, p, strong {
577
- color: inherit;
578
- }
579
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
580
  """) as demo:
581
- # Shared states
582
- state_bookmarks = gr.State([])
583
- chat_history = gr.State([])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
584
 
585
  # General Overview
586
  gr.Markdown("""
587
- # πŸ“š SmartMarks - AI Browser Bookmarks Manager
588
 
589
- Welcome to **SmartMarks**, your intelligent assistant for managing browser bookmarks. SmartMarks leverages AI to help you organize, search, and interact with your bookmarks seamlessly. Whether you're looking to categorize your links, retrieve information quickly, or maintain an updated list, SmartMarks has you covered.
590
 
591
- ---
592
 
593
- ## πŸš€ **How to Use SmartMarks**
594
 
595
- SmartMarks is divided into three main sections:
596
 
597
- 1. **πŸ“‚ Upload and Process Bookmarks:** Import your existing bookmarks and let SmartMarks analyze and categorize them for you.
598
- 2. **πŸ’¬ Chat with Bookmarks:** Interact with your bookmarks using natural language queries to find relevant links effortlessly.
599
- 3. **πŸ› οΈ Manage Bookmarks:** View, edit, delete, and export your bookmarks with ease.
600
 
601
- Navigate through the tabs to explore each feature in detail.
602
- """)
603
-
604
- # Define Manage Bookmarks components outside the tab for global access
605
- bookmark_selector = gr.CheckboxGroup(label="βœ… Select Bookmarks", choices=[])
606
- bookmark_display_manage = gr.HTML(label="πŸ“„ Manage Bookmarks Display")
607
 
608
  # Upload and Process Bookmarks Tab
609
  with gr.Tab("Upload and Process Bookmarks"):
610
  gr.Markdown("""
611
- ## πŸ“‚ **Upload and Process Bookmarks**
612
 
613
- ### πŸ“ **Steps to Upload and Process:**
614
 
615
- 1. **πŸ”½ Upload Bookmarks File:**
616
- - Click on the **"πŸ“ Upload Bookmarks HTML File"** button.
617
- - Select your browser's exported bookmarks HTML file from your device.
618
 
619
- 2. **βš™οΈ Process Bookmarks:**
620
- - After uploading, click on the **"βš™οΈ Process Bookmarks"** button.
621
- - SmartMarks will parse your bookmarks, fetch additional information, generate summaries, and categorize each link based on predefined categories.
622
 
623
- 3. **πŸ“„ View Processed Bookmarks:**
624
- - Once processing is complete, your bookmarks will be displayed in an organized and visually appealing format below.
625
- """)
626
 
627
  upload = gr.File(label="πŸ“ Upload Bookmarks HTML File", type='binary')
628
  process_button = gr.Button("βš™οΈ Process Bookmarks")
629
  output_text = gr.Textbox(label="βœ… Output", interactive=False)
630
  bookmark_display = gr.HTML(label="πŸ“„ Bookmarks")
631
 
 
 
 
 
632
  process_button.click(
633
  process_uploaded_file,
634
- inputs=[upload, state_bookmarks],
635
- outputs=[output_text, bookmark_display, bookmark_selector, bookmark_display_manage, state_bookmarks]
636
  )
637
 
638
  # Chat with Bookmarks Tab
639
  with gr.Tab("Chat with Bookmarks"):
640
  gr.Markdown("""
641
- ## πŸ’¬ **Chat with Bookmarks**
642
 
643
- ### πŸ€– **How to Interact:**
644
 
645
- 1. **✍️ Enter Your Query:**
646
- - In the **"✍️ Ask about your bookmarks"** textbox, type your question or keyword related to your bookmarks. For example, "Do I have any bookmarks about GenerativeAI?"
647
 
648
- 2. **πŸ“¨ Submit Your Query:**
649
- - Click the **"πŸ“¨ Send"** button to submit your query.
650
 
651
- 3. **πŸ“ˆ Receive AI-Driven Responses:**
652
- - SmartMarks will analyze your query and provide relevant bookmarks that match your request, making it easier to find specific links without manual searching.
 
653
 
654
- 4. **πŸ—‚οΈ View Chat History:**
655
- - All your queries and the corresponding AI responses are displayed in the chat history for your reference.
656
- """)
657
 
658
- with gr.Row():
659
- chat_history_display = gr.Chatbot(label="πŸ—¨οΈ Chat History")
660
- with gr.Column(scale=1):
661
- chat_input = gr.Textbox(
662
- label="✍️ Ask about your bookmarks",
663
- placeholder="e.g., Do I have any bookmarks about GenerativeAI?",
664
- lines=1,
665
- interactive=True
666
- )
667
- chat_button = gr.Button("πŸ“¨ Send")
668
-
669
- # When user presses Enter in chat_input
670
- chat_input.submit(
671
- chatbot_response,
672
- inputs=[chat_input, state_bookmarks],
673
- outputs=chat_history_display
674
- )
675
-
676
- # When user clicks Send button
677
  chat_button.click(
678
  chatbot_response,
679
- inputs=[chat_input, state_bookmarks],
680
- outputs=chat_history_display
681
  )
682
 
683
  # Manage Bookmarks Tab
684
  with gr.Tab("Manage Bookmarks"):
685
  gr.Markdown("""
686
- ## πŸ› οΈ **Manage Bookmarks**
687
 
688
- ### πŸ—‚οΈ **Features:**
689
 
690
- 1. **πŸ‘οΈ View Bookmarks:**
691
- - All your processed bookmarks are displayed here with their respective categories and summaries.
692
 
693
- 2. **βœ… Select Bookmarks:**
694
- - Use the checkboxes next to each bookmark to select one, multiple, or all bookmarks you wish to manage.
695
 
696
- 3. **πŸ—‘οΈ Delete Selected Bookmarks:**
697
- - After selecting the desired bookmarks, click the **"πŸ—‘οΈ Delete Selected Bookmarks"** button to remove them from your list.
698
 
699
- 4. **✏️ Edit Categories:**
700
- - Select the bookmarks you want to re-categorize.
701
- - Choose a new category from the dropdown menu labeled **"πŸ†• New Category"**.
702
- - Click the **"✏️ Edit Category of Selected Bookmarks"** button to update their categories.
703
 
704
- 5. **πŸ’Ύ Export Bookmarks:**
705
- - Click the **"πŸ’Ύ Export Bookmarks"** button to download your updated bookmarks as an HTML file.
706
- - This file can be uploaded back to your browser to reflect the changes made within SmartMarks.
707
- """)
708
 
709
  manage_output = gr.Textbox(label="πŸ”„ Manage Output", interactive=False)
 
 
 
710
  new_category_input = gr.Dropdown(label="πŸ†• New Category", choices=CATEGORIES, value="Uncategorized")
711
  with gr.Row():
712
  delete_button = gr.Button("πŸ—‘οΈ Delete Selected Bookmarks")
713
  edit_category_button = gr.Button("✏️ Edit Category of Selected Bookmarks")
714
  export_button = gr.Button("πŸ’Ύ Export Bookmarks")
715
  download_link = gr.HTML(label="πŸ“₯ Download Exported Bookmarks")
716
- refresh_button = gr.Button("πŸ”„ Refresh Bookmarks")
717
 
718
  # Define button actions
719
  delete_button.click(
720
  delete_selected_bookmarks,
721
- inputs=[bookmark_selector, state_bookmarks],
722
  outputs=[manage_output, bookmark_selector, bookmark_display_manage]
723
  )
724
 
725
  edit_category_button.click(
726
  edit_selected_bookmarks_category,
727
- inputs=[bookmark_selector, new_category_input, state_bookmarks],
728
  outputs=[manage_output, bookmark_selector, bookmark_display_manage]
729
  )
730
 
731
  export_button.click(
732
  export_bookmarks,
733
- inputs=[state_bookmarks],
734
  outputs=download_link
735
  )
736
 
737
- refresh_button.click(
738
- lambda bookmarks: (
739
- [
740
- f"{i+1}. {bookmark['title']} (Category: {bookmark['category']})" for i, bookmark in enumerate(bookmarks)
741
- ],
742
- display_bookmarks(bookmarks)
743
- ),
744
- inputs=[state_bookmarks],
745
- outputs=[bookmark_selector, bookmark_display_manage]
746
  )
747
 
748
  logger.info("Launching Gradio app")
 
2
 
3
  import gradio as gr
4
  from bs4 import BeautifulSoup
5
+ import requests
6
+ from sentence_transformers import SentenceTransformer
7
+ import faiss
8
+ import numpy as np
9
  import asyncio
10
  import aiohttp
11
  import re
 
13
  import logging
14
  import os
15
  import sys
 
 
 
16
 
17
  # Import OpenAI library
18
  import openai
 
36
  logger.info("Initializing models and variables")
37
  embedding_model = SentenceTransformer('all-MiniLM-L6-v2')
38
  faiss_index = None
39
+ bookmarks = []
40
  fetch_cache = {}
41
 
42
  # Define the categories
 
234
  raise
235
 
236
  # Generate HTML display for bookmarks
237
+ def display_bookmarks():
238
  logger.info("Generating HTML display for bookmarks")
239
  cards = ''
240
+ for i, bookmark in enumerate(bookmarks):
241
  index = i + 1 # Start index at 1
242
  status = "❌ Dead Link" if bookmark.get('dead_link') else "βœ… Active"
243
  title = bookmark['title']
 
246
  summary = bookmark.get('summary', '')
247
  category = bookmark.get('category', 'Uncategorized')
248
 
249
+ # Apply inline styles for dead links
250
  if bookmark.get('dead_link'):
251
+ card_style = "border: 2px solid var(--error-color);"
252
+ text_style = "color: var(--error-color);"
253
  else:
254
+ card_style = "border: 2px solid var(--success-color);"
255
+ text_style = "color: var(--text-color);"
256
 
257
  card_html = f'''
258
+ <div class="card" style="{card_style}; padding: 10px; margin: 10px; border-radius: 5px;">
259
  <div class="card-content">
260
+ <h3 style="{text_style}">{index}. {title} {status}</h3>
261
+ <p style="{text_style}"><strong>Category:</strong> {category}</p>
262
+ <p style="{text_style}"><strong>URL:</strong> <a href="{url}" target="_blank" style="{text_style}">{url}</a></p>
263
+ <p style="{text_style}"><strong>ETag:</strong> {etag}</p>
264
+ <p style="{text_style}"><strong>Summary:</strong> {summary}</p>
265
  </div>
266
  </div>
267
  '''
 
270
  return cards
271
 
272
  # Process the uploaded file
273
+ def process_uploaded_file(file):
274
+ global bookmarks, faiss_index
275
  logger.info("Processing uploaded file")
276
  if file is None:
277
  logger.warning("No file uploaded")
278
+ return "Please upload a bookmarks HTML file.", '', gr.update(choices=[]), display_bookmarks()
 
 
 
 
 
 
 
279
  try:
280
  file_content = file.decode('utf-8')
281
  except UnicodeDecodeError as e:
282
  logger.error(f"Error decoding the file: {e}")
283
+ return "Error decoding the file. Please ensure it's a valid HTML file.", '', gr.update(choices=[]), display_bookmarks()
 
 
 
 
 
 
284
 
285
  try:
286
  bookmarks = parse_bookmarks(file_content)
287
  except Exception as e:
288
  logger.error(f"Error parsing bookmarks: {e}")
289
+ return "Error parsing the bookmarks HTML file.", '', gr.update(choices=[]), display_bookmarks()
 
 
 
 
 
 
290
 
291
  if not bookmarks:
292
  logger.warning("No bookmarks found in the uploaded file")
293
+ return "No bookmarks found in the uploaded file.", '', gr.update(choices=[]), display_bookmarks()
 
 
 
 
 
 
294
 
295
  # Asynchronously fetch bookmark info
296
  try:
297
  asyncio.run(process_bookmarks_async(bookmarks))
298
  except Exception as e:
299
  logger.error(f"Error processing bookmarks asynchronously: {e}")
300
+ return "Error processing bookmarks.", '', gr.update(choices=[]), display_bookmarks()
 
 
 
 
 
 
301
 
302
  # Generate summaries and assign categories
303
  for bookmark in bookmarks:
 
308
  faiss_index, embeddings = vectorize_and_index(bookmarks)
309
  except Exception as e:
310
  logger.error(f"Error building FAISS index: {e}")
311
+ return "Error building search index.", '', gr.update(choices=[]), display_bookmarks()
 
 
 
 
 
 
312
 
313
  message = f"βœ… Successfully processed {len(bookmarks)} bookmarks."
314
  logger.info(message)
315
+ bookmark_html = display_bookmarks()
316
 
317
+ # Update bookmark_selector choices
318
  choices = [f"{i+1}. {bookmark['title']} (Category: {bookmark['category']})" for i, bookmark in enumerate(bookmarks)]
319
+ bookmark_selector_update = gr.update(choices=choices, value=[])
320
 
321
+ # Update bookmark_display_manage
322
+ bookmark_display_manage_update = display_bookmarks()
323
 
324
+ return message, bookmark_html, bookmark_selector_update, bookmark_display_manage_update
 
 
 
 
 
 
325
 
326
  # Delete selected bookmarks
327
+ def delete_selected_bookmarks(selected_indices):
328
+ global bookmarks, faiss_index
329
  if not selected_indices:
330
+ return "⚠️ No bookmarks selected.", gr.update(choices=[]), display_bookmarks()
331
+ indices = [int(s.split('.')[0])-1 for s in selected_indices]
 
 
 
 
 
 
 
 
 
 
 
 
332
  indices = sorted(indices, reverse=True)
333
  for idx in indices:
334
+ if 0 <= idx < len(bookmarks):
335
+ logger.info(f"Deleting bookmark at index {idx + 1}")
336
+ bookmarks.pop(idx)
337
  if bookmarks:
338
  faiss_index, embeddings = vectorize_and_index(bookmarks)
339
  else:
340
  faiss_index = None
 
341
  message = "πŸ—‘οΈ Selected bookmarks deleted successfully."
342
  logger.info(message)
343
+ # Update bookmark_selector choices
 
 
 
 
344
  choices = [f"{i+1}. {bookmark['title']} (Category: {bookmark['category']})" for i, bookmark in enumerate(bookmarks)]
345
+ bookmark_selector_update = gr.update(choices=choices, value=[])
346
+ # Update bookmarks display
347
+ bookmarks_html = display_bookmarks()
348
+ return message, bookmark_selector_update, bookmarks_html
 
349
 
350
  # Edit category of selected bookmarks
351
+ def edit_selected_bookmarks_category(selected_indices, new_category):
352
  if not selected_indices:
353
+ return "⚠️ No bookmarks selected.", '', gr.update()
 
 
 
 
 
354
  if not new_category:
355
+ return "⚠️ No new category selected.", '', gr.update()
356
+ indices = [int(s.split('.')[0])-1 for s in selected_indices]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
357
  for idx in indices:
358
+ if 0 <= idx < len(bookmarks):
359
+ bookmarks[idx]['category'] = new_category
360
+ logger.info(f"Updated category for bookmark {idx + 1} to {new_category}")
361
  message = "✏️ Category updated for selected bookmarks."
362
  logger.info(message)
363
+ # Update bookmark_selector choices
 
 
 
 
364
  choices = [f"{i+1}. {bookmark['title']} (Category: {bookmark['category']})" for i, bookmark in enumerate(bookmarks)]
365
+ bookmark_selector_update = gr.update(choices=choices, value=[])
366
+ # Update bookmarks display
367
+ bookmarks_html = display_bookmarks()
368
+ return message, bookmark_selector_update, bookmarks_html
 
369
 
370
  # Export bookmarks to HTML
371
+ def export_bookmarks():
 
372
  if not bookmarks:
373
  logger.warning("No bookmarks to export")
374
  return "⚠️ No bookmarks to export."
 
395
  return "⚠️ Error exporting bookmarks."
396
 
397
  # Chatbot response using Groq Cloud API
398
+ def chatbot_response(user_query):
399
  if not GROQ_API_KEY:
400
  logger.warning("GROQ_API_KEY not set.")
401
  return "⚠️ API key not set. Please set the GROQ_API_KEY environment variable in the Hugging Face Space settings."
402
 
 
403
  if not bookmarks:
404
  logger.warning("No bookmarks available for chatbot")
405
  return "⚠️ No bookmarks available. Please upload and process your bookmarks first."
 
454
  try:
455
  logger.info("Building Gradio app")
456
  with gr.Blocks(css="""
457
+ /* Define CSS Variables for Themes */
458
+ :root {
459
+ --background-color: #FFFFFF;
460
+ --text-color: #000000;
461
+ --success-color: #4CAF50;
462
+ --error-color: #D32F2F;
463
+ --card-shadow: rgba(0, 0, 0, 0.2);
464
+ }
465
+
466
+ .dark-theme {
467
+ --background-color: #121212;
468
+ --text-color: #FFFFFF;
469
+ --success-color: #81C784;
470
+ --error-color: #E57373;
471
+ --card-shadow: rgba(255, 255, 255, 0.2);
472
+ }
473
+
474
+ body {
475
+ background-color: var(--background-color);
476
+ color: var(--text-color);
477
+ }
478
+
479
  .card {
480
+ box-shadow: 0 4px 8px 0 var(--card-shadow);
481
  transition: 0.3s;
482
+ background-color: var(--background-color);
483
  }
484
  .card:hover {
485
+ box-shadow: 0 8px 16px 0 var(--card-shadow);
486
+ }
487
+
488
+ /* Toggle Switch Styles */
489
+ .theme-toggle {
490
+ display: flex;
491
+ align-items: center;
492
+ justify-content: flex-end;
493
+ padding: 10px;
494
+ }
495
+
496
+ .switch {
497
+ position: relative;
498
+ display: inline-block;
499
+ width: 60px;
500
+ height: 34px;
501
+ }
502
+
503
+ .switch input {
504
+ opacity: 0;
505
+ width: 0;
506
+ height: 0;
507
+ }
508
+
509
+ .slider {
510
+ position: absolute;
511
+ cursor: pointer;
512
+ top: 0;
513
+ left: 0;
514
+ right: 0;
515
+ bottom: 0;
516
+ background-color: #ccc;
517
+ transition: .4s;
518
+ border-radius: 34px;
519
  }
520
 
521
+ .slider:before {
522
+ position: absolute;
523
+ content: "";
524
+ height: 26px;
525
+ width: 26px;
526
+ left: 4px;
527
+ bottom: 4px;
528
+ background-color: white;
529
+ transition: .4s;
530
+ border-radius: 50%;
 
 
 
 
 
531
  }
532
 
533
+ input:checked + .slider {
534
+ background-color: #2196F3;
 
 
 
 
 
 
 
 
 
 
 
 
535
  }
536
+
537
+ input:checked + .slider:before {
538
+ transform: translateX(26px);
539
+ }
540
+
541
+ /* Gradio Components Styling */
542
+ .gradio-container {
543
+ background-color: var(--background-color);
544
+ color: var(--text-color);
545
+ }
546
+
547
+ a {
548
+ color: var(--text-color);
549
+ }
550
+
551
  """) as demo:
552
+ # Add Theme Toggle Switch
553
+ gr.HTML(
554
+ """
555
+ <div class="theme-toggle">
556
+ <label class="switch">
557
+ <input type="checkbox" id="theme-checkbox">
558
+ <span class="slider"></span>
559
+ </label>
560
+ <span style="margin-left: 10px;">Dark Mode</span>
561
+ </div>
562
+ <script>
563
+ const toggleSwitch = document.getElementById('theme-checkbox');
564
+ toggleSwitch.addEventListener('change', function() {
565
+ if(this.checked) {
566
+ document.body.classList.add('dark-theme');
567
+ } else {
568
+ document.body.classList.remove('dark-theme');
569
+ }
570
+ });
571
+ </script>
572
+ """
573
+ )
574
 
575
  # General Overview
576
  gr.Markdown("""
577
+ # πŸ“š SmartMarks - AI Browser Bookmarks Manager
578
 
579
+ Welcome to **SmartMarks**, your intelligent assistant for managing browser bookmarks. SmartMarks leverages AI to help you organize, search, and interact with your bookmarks seamlessly. Whether you're looking to categorize your links, retrieve information quickly, or maintain an updated list, SmartMarks has you covered.
580
 
581
+ ---
582
 
583
+ ## πŸš€ **How to Use SmartMarks**
584
 
585
+ SmartMarks is divided into three main sections:
586
 
587
+ 1. **πŸ“‚ Upload and Process Bookmarks:** Import your existing bookmarks and let SmartMarks analyze and categorize them for you.
588
+ 2. **πŸ’¬ Chat with Bookmarks:** Interact with your bookmarks using natural language queries to find relevant links effortlessly.
589
+ 3. **πŸ› οΈ Manage Bookmarks:** View, edit, delete, and export your bookmarks with ease.
590
 
591
+ Navigate through the tabs to explore each feature in detail.
592
+ """)
 
 
 
 
593
 
594
  # Upload and Process Bookmarks Tab
595
  with gr.Tab("Upload and Process Bookmarks"):
596
  gr.Markdown("""
597
+ ## πŸ“‚ **Upload and Process Bookmarks**
598
 
599
+ ### πŸ“ **Steps to Upload and Process:**
600
 
601
+ 1. **πŸ”½ Upload Bookmarks File:**
602
+ - Click on the **"Upload Bookmarks HTML File"** button.
603
+ - Select your browser's exported bookmarks HTML file from your device.
604
 
605
+ 2. **βš™οΈ Process Bookmarks:**
606
+ - After uploading, click on the **"Process Bookmarks"** button.
607
+ - SmartMarks will parse your bookmarks, fetch additional information, generate summaries, and categorize each link based on predefined categories.
608
 
609
+ 3. **πŸ“„ View Processed Bookmarks:**
610
+ - Once processing is complete, your bookmarks will be displayed in an organized and visually appealing format below.
611
+ """)
612
 
613
  upload = gr.File(label="πŸ“ Upload Bookmarks HTML File", type='binary')
614
  process_button = gr.Button("βš™οΈ Process Bookmarks")
615
  output_text = gr.Textbox(label="βœ… Output", interactive=False)
616
  bookmark_display = gr.HTML(label="πŸ“„ Bookmarks")
617
 
618
+ # Initialize Manage Bookmarks components
619
+ bookmark_selector = gr.CheckboxGroup(label="βœ… Select Bookmarks", choices=[])
620
+ bookmark_display_manage = gr.HTML(label="πŸ“„ Manage Bookmarks Display")
621
+
622
  process_button.click(
623
  process_uploaded_file,
624
+ inputs=upload,
625
+ outputs=[output_text, bookmark_display, bookmark_selector, bookmark_display_manage]
626
  )
627
 
628
  # Chat with Bookmarks Tab
629
  with gr.Tab("Chat with Bookmarks"):
630
  gr.Markdown("""
631
+ ## πŸ’¬ **Chat with Bookmarks**
632
 
633
+ ### πŸ€– **How to Interact:**
634
 
635
+ 1. **✍️ Enter Your Query:**
636
+ - In the **"Ask about your bookmarks"** textbox, type your question or keyword related to your bookmarks. For example, "Do I have any bookmarks about GenerativeAI?"
637
 
638
+ 2. **πŸ“¨ Submit Your Query:**
639
+ - Click the **"Send"** button to submit your query.
640
 
641
+ 3. **πŸ“ˆ Receive AI-Driven Responses:**
642
+ - SmartMarks will analyze your query and provide relevant bookmarks that match your request, making it easier to find specific links without manual searching.
643
+ """)
644
 
645
+ user_input = gr.Textbox(label="✍️ Ask about your bookmarks", placeholder="e.g., Do I have any bookmarks about GenerativeAI?")
646
+ chat_output = gr.Textbox(label="πŸ’¬ Chatbot Response", interactive=False)
647
+ chat_button = gr.Button("πŸ“¨ Send")
648
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
649
  chat_button.click(
650
  chatbot_response,
651
+ inputs=user_input,
652
+ outputs=chat_output
653
  )
654
 
655
  # Manage Bookmarks Tab
656
  with gr.Tab("Manage Bookmarks"):
657
  gr.Markdown("""
658
+ ## πŸ› οΈ **Manage Bookmarks**
659
 
660
+ ### πŸ—‚οΈ **Features:**
661
 
662
+ 1. **πŸ‘οΈ View Bookmarks:**
663
+ - All your processed bookmarks are displayed here with their respective categories and summaries.
664
 
665
+ 2. **βœ… Select Bookmarks:**
666
+ - Use the checkboxes next to each bookmark to select one, multiple, or all bookmarks you wish to manage.
667
 
668
+ 3. **πŸ—‘οΈ Delete Selected Bookmarks:**
669
+ - After selecting the desired bookmarks, click the **"Delete Selected Bookmarks"** button to remove them from your list.
670
 
671
+ 4. **✏️ Edit Categories:**
672
+ - Select the bookmarks you want to re-categorize.
673
+ - Choose a new category from the dropdown menu labeled **"New Category"**.
674
+ - Click the **"Edit Category of Selected Bookmarks"** button to update their categories.
675
 
676
+ 5. **πŸ’Ύ Export Bookmarks:**
677
+ - Click the **"Export Bookmarks"** button to download your updated bookmarks as an HTML file.
678
+ - This file can be uploaded back to your browser to reflect the changes made within SmartMarks.
679
+ """)
680
 
681
  manage_output = gr.Textbox(label="πŸ”„ Manage Output", interactive=False)
682
+ bookmark_display_manage = gr.HTML(label="πŸ“„ Manage Bookmarks Display")
683
+ bookmark_selector = gr.CheckboxGroup(label="βœ… Select Bookmarks", choices=[])
684
+
685
  new_category_input = gr.Dropdown(label="πŸ†• New Category", choices=CATEGORIES, value="Uncategorized")
686
  with gr.Row():
687
  delete_button = gr.Button("πŸ—‘οΈ Delete Selected Bookmarks")
688
  edit_category_button = gr.Button("✏️ Edit Category of Selected Bookmarks")
689
  export_button = gr.Button("πŸ’Ύ Export Bookmarks")
690
  download_link = gr.HTML(label="πŸ“₯ Download Exported Bookmarks")
 
691
 
692
  # Define button actions
693
  delete_button.click(
694
  delete_selected_bookmarks,
695
+ inputs=bookmark_selector,
696
  outputs=[manage_output, bookmark_selector, bookmark_display_manage]
697
  )
698
 
699
  edit_category_button.click(
700
  edit_selected_bookmarks_category,
701
+ inputs=[bookmark_selector, new_category_input],
702
  outputs=[manage_output, bookmark_selector, bookmark_display_manage]
703
  )
704
 
705
  export_button.click(
706
  export_bookmarks,
707
+ inputs=None,
708
  outputs=download_link
709
  )
710
 
711
+ # Initialize display after processing bookmarks
712
+ process_button.click(
713
+ process_uploaded_file,
714
+ inputs=upload,
715
+ outputs=[output_text, bookmark_display, bookmark_selector, bookmark_display_manage]
 
 
 
 
716
  )
717
 
718
  logger.info("Launching Gradio app")