siddhartharya commited on
Commit
817113d
β€’
1 Parent(s): d0c6279

Update README.md

Browse files
Files changed (1) hide show
  1. README.md +1124 -0
README.md CHANGED
@@ -8,7 +8,919 @@ sdk_version: 5.5.0
8
  app_file: app.py
9
  pinned: false
10
  ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  # πŸ“š SmartMarks - AI Browser Bookmarks Manager
13
 
14
  ![License](https://img.shields.io/badge/license-MIT-blue.svg)
@@ -48,3 +960,215 @@ SmartMarks is designed to run seamlessly on [Hugging Face Spaces](https://huggin
48
 
49
  ### Repository Structure
50
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  app_file: app.py
9
  pinned: false
10
  ---
11
+ # πŸ“š SmartMarks - AI Browser Bookmarks Manager
12
+
13
+ ![License](https://img.shields.io/badge/license-MIT-blue.svg)
14
+ ![Gradio](https://img.shields.io/badge/Gradio-v3.38.0-blue)
15
+ ![Python](https://img.shields.io/badge/Python-3.8%2B-blue.svg)
16
+ ![Hugging Face Spaces](https://img.shields.io/badge/Hugging%20Face-Spaces-green.svg)
17
+
18
+ ## 🌟 Overview
19
+
20
+ **SmartMarks** is your intelligent assistant for managing browser bookmarks. Leveraging the power of AI, SmartMarks helps 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.
21
+
22
+ ### Key Features
23
+
24
+ - **πŸ“‚ Upload and Process Bookmarks:** Import your existing bookmarks and let SmartMarks analyze and categorize them for you.
25
+ - **πŸ’¬ Chat with Bookmarks:** Interact with your bookmarks using natural language queries to find relevant links effortlessly.
26
+ - **πŸ› οΈ Manage Bookmarks:** View, edit, delete, and export your bookmarks with ease.
27
+ - **🎨 Dynamic Color Theme:** Automatically adapts to your system or browser's light/dark mode preferences for optimal readability and aesthetics.
28
+
29
+ ## πŸš€ How to Use SmartMarks
30
+
31
+ SmartMarks is divided into three main sections:
32
+
33
+ 1. **πŸ“‚ Upload and Process Bookmarks:** Import and process your bookmarks.
34
+ 2. **πŸ’¬ Chat with Bookmarks:** Ask questions about your bookmarks using natural language.
35
+ 3. **πŸ› οΈ Manage Bookmarks:** Manage your bookmarks by viewing, editing, deleting, and exporting them.
36
+
37
+ Navigate through the tabs to explore each feature in detail.
38
+
39
+ ## πŸ“¦ Deployment on Hugging Face Spaces
40
+
41
+ SmartMarks is designed to run seamlessly on [Hugging Face Spaces](https://huggingface.co/spaces), providing an interactive and user-friendly interface.
42
+
43
+ ### Prerequisites
44
+
45
+ - **Hugging Face Account:** To deploy on Hugging Face Spaces, you need an account. Sign up [here](https://huggingface.co/join).
46
+ - **API Key:** SmartMarks utilizes the Groq Cloud API for its chatbot functionality. Ensure you have a valid `GROQ_API_KEY`.
47
+
48
+ ### Repository Structure
49
+
50
+ smartmarks/ β”‚ β”œβ”€β”€ app.py β”œβ”€β”€ app.css β”œβ”€β”€ requirements.txt β”œβ”€β”€ README.md β”œβ”€β”€ LICENSE └── screenshots/ β”œβ”€β”€ light_mode.png └── dark_mode.png
51
+
52
+
53
+ ### Setting Up Environment Variables
54
+
55
+ 1. **GROQ_API_KEY:**
56
+ SmartMarks requires the `GROQ_API_KEY` to interact with the Groq Cloud API. To set this up:
57
+
58
+ - Navigate to your Space's dashboard on Hugging Face.
59
+ - Go to **Settings** > **Secrets**.
60
+ - Add a new secret with the key `GROQ_API_KEY` and paste your API key as the value.
61
+
62
+ > **Note:** Keep your API keys secure. Never expose them in your code or repository.
63
+
64
+ ### Installing Dependencies
65
+
66
+ Hugging Face Spaces automatically installs dependencies listed in the `requirements.txt` file. Ensure your `requirements.txt` includes all necessary packages:
67
+
68
+ ```plaintext
69
+ gradio
70
+ beautifulsoup4
71
+ sentence-transformers
72
+ faiss-cpu
73
+ aiohttp
74
+ openai
75
+ uuid
76
+
77
+
78
+
79
+ Thought for 8 seconds
80
+ Certainly! Below are the updated app.css and README.md files for your SmartMarks - AI Browser Bookmarks Manager application. Additionally, I've included the necessary updates to your app.py to ensure that the external CSS file is properly integrated. These changes aim to enhance the application's appearance and maintainability by separating styles from the Python code.
81
+
82
+ 1. app.css
83
+ This CSS file contains all the styles previously embedded within your app.py. By moving the styles to an external file, you enhance the maintainability and scalability of your application.
84
+
85
+ css
86
+ Copy code
87
+ /* app.css */
88
+
89
+ /* Card Styling */
90
+ .card {
91
+ box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
92
+ transition: 0.3s;
93
+ padding: 10px;
94
+ margin: 10px;
95
+ border-radius: 5px;
96
+ }
97
+
98
+ .card:hover {
99
+ box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
100
+ }
101
+
102
+ /* Dynamic Theme Styling */
103
+ @media (prefers-color-scheme: dark) {
104
+ body {
105
+ color: white;
106
+ background-color: #121212;
107
+ }
108
+ .card {
109
+ background-color: #1e1e1e;
110
+ }
111
+ a {
112
+ color: #bb86fc;
113
+ }
114
+ h1, h2, h3, p, strong {
115
+ color: inherit;
116
+ }
117
+ }
118
+
119
+ @media (prefers-color-scheme: light) {
120
+ body {
121
+ color: black;
122
+ background-color: white;
123
+ }
124
+ .card {
125
+ background-color: #fff;
126
+ }
127
+ a {
128
+ color: #1a0dab;
129
+ }
130
+ h1, h2, h3, p, strong {
131
+ color: inherit;
132
+ }
133
+ }
134
+ 2. Updated app.py
135
+ To integrate the external app.css file, update your app.py by modifying the gr.Blocks initialization to load the CSS from the app.css file instead of embedding it directly. Ensure that the app.css file is located in the same directory as your app.py or adjust the path accordingly.
136
+
137
+ python
138
+ Copy code
139
+ # app.py
140
+
141
+ import gradio as gr
142
+ from bs4 import BeautifulSoup
143
+ from sentence_transformers import SentenceTransformer
144
+ import faiss
145
+ import numpy as np
146
+ import asyncio
147
+ import aiohttp
148
+ import re
149
+ import base64
150
+ import logging
151
+ import os
152
+ import sys
153
+ import uuid # For unique IDs
154
+
155
+ # Import OpenAI library
156
+ import openai
157
+
158
+ # Set up logging to output to the console
159
+ logger = logging.getLogger(__name__)
160
+ logger.setLevel(logging.INFO)
161
+
162
+ # Create a console handler
163
+ console_handler = logging.StreamHandler(sys.stdout)
164
+ console_handler.setLevel(logging.INFO)
165
+
166
+ # Create a formatter and set it for the handler
167
+ formatter = logging.Formatter('%(asctime)s %(levelname)s %(name)s %(message)s')
168
+ console_handler.setFormatter(formatter)
169
+
170
+ # Add the handler to the logger
171
+ logger.addHandler(console_handler)
172
+
173
+ # Initialize models and variables
174
+ logger.info("Initializing models and variables")
175
+ embedding_model = SentenceTransformer('all-MiniLM-L6-v2')
176
+ faiss_index = faiss.IndexIDMap(faiss.IndexFlatL2(embedding_model.get_sentence_embedding_dimension()))
177
+ bookmarks = []
178
+ fetch_cache = {}
179
+
180
+ # Define the categories
181
+ CATEGORIES = [
182
+ "Social Media",
183
+ "News and Media",
184
+ "Education and Learning",
185
+ "Entertainment",
186
+ "Shopping and E-commerce",
187
+ "Finance and Banking",
188
+ "Technology",
189
+ "Health and Fitness",
190
+ "Travel and Tourism",
191
+ "Food and Recipes",
192
+ "Sports",
193
+ "Arts and Culture",
194
+ "Government and Politics",
195
+ "Business and Economy",
196
+ "Science and Research",
197
+ "Personal Blogs and Journals",
198
+ "Job Search and Careers",
199
+ "Music and Audio",
200
+ "Videos and Movies",
201
+ "Reference and Knowledge Bases",
202
+ "Dead Link",
203
+ "Uncategorized",
204
+ ]
205
+
206
+ # Set up Groq Cloud API key and base URL
207
+ GROQ_API_KEY = os.getenv('GROQ_API_KEY')
208
+
209
+ if not GROQ_API_KEY:
210
+ logger.error("GROQ_API_KEY environment variable not set.")
211
+
212
+ # Set OpenAI API key and base URL to use Groq Cloud API
213
+ openai.api_key = GROQ_API_KEY
214
+ openai.api_base = "https://api.groq.com/openai/v1" # Corrected API base URL
215
+
216
+ # Function to parse bookmarks from HTML
217
+ def parse_bookmarks(file_content):
218
+ logger.info("Parsing bookmarks")
219
+ try:
220
+ soup = BeautifulSoup(file_content, 'html.parser')
221
+ extracted_bookmarks = []
222
+ for link in soup.find_all('a'):
223
+ url = link.get('href')
224
+ title = link.text.strip()
225
+ if url and title:
226
+ extracted_bookmarks.append({'url': url, 'title': title})
227
+ logger.info(f"Extracted {len(extracted_bookmarks)} bookmarks")
228
+ return extracted_bookmarks
229
+ except Exception as e:
230
+ logger.error("Error parsing bookmarks: %s", e)
231
+ raise
232
+
233
+ # Asynchronous function to fetch URL info
234
+ async def fetch_url_info(session, bookmark):
235
+ url = bookmark['url']
236
+ if url in fetch_cache:
237
+ bookmark.update(fetch_cache[url])
238
+ return bookmark
239
+
240
+ try:
241
+ logger.info(f"Fetching URL info for: {url}")
242
+ async with session.get(url, timeout=10) as response:
243
+ bookmark['etag'] = response.headers.get('ETag', 'N/A')
244
+ bookmark['status_code'] = response.status
245
+
246
+ if response.status >= 400:
247
+ bookmark['dead_link'] = True
248
+ bookmark['description'] = ''
249
+ logger.warning(f"Dead link detected: {url} with status {response.status}")
250
+ else:
251
+ bookmark['dead_link'] = False
252
+ content = await response.text()
253
+ soup = BeautifulSoup(content, 'html.parser')
254
+
255
+ # Extract meta description or Open Graph description
256
+ meta_description = soup.find('meta', attrs={'name': 'description'})
257
+ og_description = soup.find('meta', attrs={'property': 'og:description'})
258
+ if og_description and og_description.get('content'):
259
+ description = og_description.get('content')
260
+ elif meta_description and meta_description.get('content'):
261
+ description = meta_description.get('content')
262
+ else:
263
+ # If no description, extract visible text
264
+ texts = soup.stripped_strings
265
+ description = ' '.join(texts[:200]) # Limit to first 200 words
266
+ # Generate summary using LLM
267
+ description = generate_summary_with_llm(description)
268
+
269
+ bookmark['description'] = description
270
+ logger.info(f"Fetched description for {url}")
271
+ except Exception as e:
272
+ bookmark['dead_link'] = True
273
+ bookmark['etag'] = 'N/A'
274
+ bookmark['status_code'] = 'N/A'
275
+ bookmark['description'] = ''
276
+ logger.error(f"Error fetching URL info for {url}: {e}")
277
+ finally:
278
+ fetch_cache[url] = {
279
+ 'etag': bookmark.get('etag'),
280
+ 'status_code': bookmark.get('status_code'),
281
+ 'dead_link': bookmark.get('dead_link'),
282
+ 'description': bookmark.get('description'),
283
+ }
284
+ return bookmark
285
+
286
+ # Asynchronous processing of bookmarks
287
+ async def process_bookmarks_async(bookmarks_list):
288
+ logger.info("Processing bookmarks asynchronously")
289
+ try:
290
+ async with aiohttp.ClientSession() as session:
291
+ tasks = []
292
+ for bookmark in bookmarks_list:
293
+ task = asyncio.ensure_future(fetch_url_info(session, bookmark))
294
+ tasks.append(task)
295
+ await asyncio.gather(*tasks)
296
+ logger.info("Completed processing bookmarks asynchronously")
297
+ except Exception as e:
298
+ logger.error(f"Error in asynchronous processing of bookmarks: {e}")
299
+ raise
300
+
301
+ # Generate summary for a bookmark using LLM
302
+ def generate_summary_with_llm(text):
303
+ logger.info("Generating summary with LLM")
304
+ try:
305
+ prompt = f"Summarize the following content in a concise manner:\n\n{text}"
306
+ response = openai.ChatCompletion.create(
307
+ model='gpt-3.5-turbo', # Use appropriate model
308
+ messages=[
309
+ {"role": "system", "content": "You are a helpful assistant that summarizes text."},
310
+ {"role": "user", "content": prompt}
311
+ ],
312
+ max_tokens=150,
313
+ temperature=0.5,
314
+ )
315
+ summary = response['choices'][0]['message']['content'].strip()
316
+ logger.info("Summary generated successfully")
317
+ return summary
318
+ except Exception as e:
319
+ logger.error(f"Error generating summary with LLM: {e}")
320
+ return "No summary available."
321
+
322
+ # Generate summary for a bookmark
323
+ def generate_summary(bookmark):
324
+ description = bookmark.get('description', '')
325
+ if description:
326
+ bookmark['summary'] = description
327
+ else:
328
+ # Fallback summary generation
329
+ title = bookmark.get('title', '')
330
+ if title:
331
+ bookmark['summary'] = title
332
+ else:
333
+ bookmark['summary'] = 'No summary available.'
334
+ logger.info(f"Generated summary for bookmark: {bookmark.get('url')}")
335
+ return bookmark
336
+
337
+ # Assign category to a bookmark using LLM
338
+ def assign_category_with_llm(summary):
339
+ logger.info("Assigning category with LLM")
340
+ try:
341
+ categories_str = ', '.join(CATEGORIES)
342
+ prompt = f"Assign the most appropriate category to the following summary from the list of categories.\n\nSummary: {summary}\n\nCategories: {categories_str}\n\nCategory:"
343
+ response = openai.ChatCompletion.create(
344
+ model='gpt-3.5-turbo', # Use appropriate model
345
+ messages=[
346
+ {"role": "system", "content": "You are a helpful assistant that assigns categories."},
347
+ {"role": "user", "content": prompt}
348
+ ],
349
+ max_tokens=10,
350
+ temperature=0.3,
351
+ )
352
+ category = response['choices'][0]['message']['content'].strip()
353
+ # Ensure the category is valid
354
+ if category in CATEGORIES:
355
+ logger.info(f"Assigned category '{category}' successfully")
356
+ return category
357
+ else:
358
+ logger.warning(f"Received invalid category '{category}' from LLM. Defaulting to 'Uncategorized'.")
359
+ return "Uncategorized"
360
+ except Exception as e:
361
+ logger.error(f"Error assigning category with LLM: {e}")
362
+ return "Uncategorized"
363
+
364
+ # Assign category to a bookmark
365
+ def assign_category(bookmark):
366
+ if bookmark.get('dead_link'):
367
+ bookmark['category'] = 'Dead Link'
368
+ logger.info(f"Assigned category 'Dead Link' to bookmark: {bookmark.get('url')}")
369
+ return bookmark
370
+
371
+ summary = bookmark.get('summary', '')
372
+ if summary:
373
+ category = assign_category_with_llm(summary)
374
+ bookmark['category'] = category
375
+ else:
376
+ bookmark['category'] = 'Uncategorized'
377
+ logger.info(f"No summary available to assign category for bookmark: {bookmark.get('url')}")
378
+ return bookmark
379
+
380
+ # Vectorize summaries and build FAISS index
381
+ def vectorize_and_index(bookmarks_list):
382
+ global faiss_index
383
+ logger.info("Vectorizing summaries and updating FAISS index")
384
+ try:
385
+ summaries = [bookmark['summary'] for bookmark in bookmarks_list]
386
+ embeddings = embedding_model.encode(summaries).astype('float32')
387
+ ids = [uuid.uuid4().int & (1<<64)-1 for _ in bookmarks_list] # Generate unique 64-bit integer IDs
388
+ faiss_index.add_with_ids(embeddings, np.array(ids))
389
+ logger.info("FAISS index updated successfully")
390
+ return embeddings, ids
391
+ except Exception as e:
392
+ logger.error(f"Error in vectorizing and indexing: {e}")
393
+ raise
394
+
395
+ # Remove vectors from FAISS index by IDs
396
+ def remove_from_faiss(ids_to_remove):
397
+ global faiss_index
398
+ logger.info(f"Removing {len(ids_to_remove)} vectors from FAISS index")
399
+ try:
400
+ faiss_index.remove_ids(np.array(ids_to_remove))
401
+ logger.info("Vectors removed from FAISS index successfully")
402
+ except Exception as e:
403
+ logger.error(f"Error removing vectors from FAISS index: {e}")
404
+
405
+ # Generate HTML display for bookmarks
406
+ def display_bookmarks():
407
+ logger.info("Generating HTML display for bookmarks")
408
+ cards = ''
409
+ for i, bookmark in enumerate(bookmarks):
410
+ index = i + 1 # Start index at 1
411
+ status = "❌ Dead Link" if bookmark.get('dead_link') else "βœ… Active"
412
+ title = bookmark['title']
413
+ url = bookmark['url']
414
+ etag = bookmark.get('etag', 'N/A')
415
+ summary = bookmark.get('summary', '')
416
+ category = bookmark.get('category', 'Uncategorized')
417
+
418
+ # Apply inline styles using CSS variables
419
+ if bookmark.get('dead_link'):
420
+ card_style = "border: 2px solid var(--error-color);"
421
+ text_style = "color: var(--error-color);"
422
+ else:
423
+ card_style = "border: 2px solid var(--success-color);"
424
+ text_style = "color: var(--text-color);"
425
+
426
+ card_html = f'''
427
+ <div class="card" style="{card_style}; padding: 10px; margin: 10px; border-radius: 5px;">
428
+ <div class="card-content">
429
+ <h3 style="{text_style}">{index}. {title} {status}</h3>
430
+ <p style="{text_style}"><strong>Category:</strong> {category}</p>
431
+ <p style="{text_style}"><strong>URL:</strong> <a href="{url}" target="_blank" style="{text_style}">{url}</a></p>
432
+ <p style="{text_style}"><strong>ETag:</strong> {etag}</p>
433
+ <p style="{text_style}"><strong>Summary:</strong> {summary}</p>
434
+ </div>
435
+ </div>
436
+ '''
437
+ cards += card_html
438
+ logger.info("HTML display generated")
439
+ return cards
440
+
441
+ # Process the uploaded file
442
+ def process_uploaded_file(file, state_bookmarks):
443
+ global bookmarks, faiss_index
444
+ logger.info("Processing uploaded file")
445
+ if file is None:
446
+ logger.warning("No file uploaded")
447
+ return (
448
+ "⚠️ Please upload a bookmarks HTML file.",
449
+ "",
450
+ gr.update(choices=[]),
451
+ display_bookmarks(),
452
+ state_bookmarks # Return the unchanged state
453
+ )
454
+
455
+ try:
456
+ file_content = file.decode('utf-8')
457
+ except UnicodeDecodeError as e:
458
+ logger.error(f"Error decoding the file: {e}")
459
+ return (
460
+ "⚠️ Error decoding the file. Please ensure it's a valid HTML file.",
461
+ "",
462
+ gr.update(choices=[]),
463
+ display_bookmarks(),
464
+ state_bookmarks # Return the unchanged state
465
+ )
466
+
467
+ try:
468
+ bookmarks = parse_bookmarks(file_content)
469
+ except Exception as e:
470
+ logger.error(f"Error parsing bookmarks: {e}")
471
+ return (
472
+ "⚠️ Error parsing the bookmarks HTML file.",
473
+ "",
474
+ gr.update(choices=[]),
475
+ display_bookmarks(),
476
+ state_bookmarks # Return the unchanged state
477
+ )
478
+
479
+ if not bookmarks:
480
+ logger.warning("No bookmarks found in the uploaded file")
481
+ return (
482
+ "⚠️ No bookmarks found in the uploaded file.",
483
+ "",
484
+ gr.update(choices=[]),
485
+ display_bookmarks(),
486
+ state_bookmarks # Return the unchanged state
487
+ )
488
+
489
+ # Asynchronously fetch bookmark info
490
+ try:
491
+ asyncio.run(process_bookmarks_async(bookmarks))
492
+ except Exception as e:
493
+ logger.error(f"Error processing bookmarks asynchronously: {e}")
494
+ return (
495
+ "⚠️ Error processing bookmarks.",
496
+ "",
497
+ gr.update(choices=[]),
498
+ display_bookmarks(),
499
+ state_bookmarks # Return the unchanged state
500
+ )
501
+
502
+ # Generate summaries and assign categories
503
+ for bookmark in bookmarks:
504
+ generate_summary(bookmark)
505
+ assign_category(bookmark)
506
+
507
+ try:
508
+ embeddings, ids = vectorize_and_index(bookmarks)
509
+ except Exception as e:
510
+ logger.error(f"Error building FAISS index: {e}")
511
+ return (
512
+ "⚠️ Error building search index.",
513
+ "",
514
+ gr.update(choices=[]),
515
+ display_bookmarks(),
516
+ state_bookmarks # Return the unchanged state
517
+ )
518
+
519
+ # Assign unique IDs to bookmarks
520
+ for bookmark, id_ in zip(bookmarks, ids):
521
+ bookmark['id'] = id_
522
+
523
+ message = f"βœ… Successfully processed {len(bookmarks)} bookmarks."
524
+ logger.info(message)
525
+ bookmark_html = display_bookmarks()
526
+
527
+ # Prepare Manage Bookmarks tab outputs
528
+ choices = [f"{i+1}. {bookmark['title']} (Category: {bookmark['category']})" for i, bookmark in enumerate(bookmarks)]
529
+ bookmarks_html_manage = display_bookmarks()
530
+
531
+ # Update the shared state
532
+ updated_state = bookmarks.copy()
533
+
534
+ return (
535
+ message,
536
+ bookmark_html,
537
+ gr.update(choices=choices, value=[]),
538
+ bookmarks_html_manage,
539
+ updated_state # Return the updated state
540
+ )
541
+
542
+ # Delete selected bookmarks
543
+ def delete_selected_bookmarks(selected_indices, state_bookmarks):
544
+ if not selected_indices:
545
+ return "⚠️ No bookmarks selected.", gr.update(choices=[]), display_bookmarks(), state_bookmarks
546
+
547
+ bookmarks = state_bookmarks.copy()
548
+ ids_to_remove = []
549
+ indices = []
550
+ for s in selected_indices:
551
+ try:
552
+ idx = int(s.split('.')[0]) - 1
553
+ if 0 <= idx < len(bookmarks):
554
+ ids_to_remove.append(bookmarks[idx]['id'])
555
+ indices.append(idx)
556
+ else:
557
+ logger.warning(f"Index out of range: {idx + 1}")
558
+ except ValueError:
559
+ logger.error(f"Invalid selection format: {s}")
560
+
561
+ # Remove bookmarks from the list
562
+ indices = sorted(indices, reverse=True)
563
+ for idx in indices:
564
+ logger.info(f"Deleting bookmark at index {idx + 1}")
565
+ bookmarks.pop(idx)
566
+
567
+ # Remove embeddings from FAISS index
568
+ remove_from_faiss(ids_to_remove)
569
+
570
+ message = "πŸ—‘οΈ Selected bookmarks deleted successfully."
571
+ logger.info(message)
572
+
573
+ # Regenerate HTML display
574
+ bookmarks_html = display_bookmarks()
575
+
576
+ # Update choices for selection
577
+ choices = [f"{i+1}. {bookmark['title']} (Category: {bookmark['category']})" for i, bookmark in enumerate(bookmarks)]
578
+
579
+ # Update the shared state
580
+ updated_state = bookmarks.copy()
581
+
582
+ return message, gr.update(choices=choices, value=[]), bookmarks_html, updated_state
583
+
584
+ # Edit category of selected bookmarks
585
+ def edit_selected_bookmarks_category(selected_indices, new_category, state_bookmarks):
586
+ if not selected_indices:
587
+ return (
588
+ "⚠️ No bookmarks selected.",
589
+ gr.update(choices=[f"{i+1}. {bookmark['title']} (Category: {bookmark['category']})" for i, bookmark in enumerate(state_bookmarks)], value=[]),
590
+ display_bookmarks(),
591
+ state_bookmarks
592
+ )
593
+
594
+ if not new_category:
595
+ return (
596
+ "⚠️ No new category selected.",
597
+ gr.update(choices=[f"{i+1}. {bookmark['title']} (Category: {bookmark['category']})" for i, bookmark in enumerate(state_bookmarks)], value=[]),
598
+ display_bookmarks(),
599
+ state_bookmarks
600
+ )
601
+
602
+ bookmarks = state_bookmarks.copy()
603
+ for s in selected_indices:
604
+ try:
605
+ idx = int(s.split('.')[0]) - 1
606
+ if 0 <= idx < len(bookmarks):
607
+ bookmarks[idx]['category'] = new_category
608
+ logger.info(f"Updated category for bookmark {idx + 1} to {new_category}")
609
+ except ValueError:
610
+ logger.error(f"Invalid selection format: {s}")
611
+
612
+ message = "✏️ Category updated for selected bookmarks."
613
+ logger.info(message)
614
+
615
+ # Regenerate HTML display
616
+ bookmarks_html = display_bookmarks()
617
+
618
+ # Update choices for selection
619
+ choices = [f"{i+1}. {bookmark['title']} (Category: {bookmark['category']})" for i, bookmark in enumerate(bookmarks)]
620
+
621
+ # Update the shared state
622
+ updated_state = bookmarks.copy()
623
 
624
+ return message, gr.update(choices=choices, value=[]), bookmarks_html, updated_state
625
+
626
+ # Export bookmarks to HTML
627
+ def export_bookmarks(state_bookmarks):
628
+ bookmarks = state_bookmarks
629
+ if not bookmarks:
630
+ logger.warning("No bookmarks to export")
631
+ return "⚠️ No bookmarks to export."
632
+ try:
633
+ logger.info("Exporting bookmarks to HTML")
634
+ # Create an HTML content similar to the imported bookmarks file
635
+ soup = BeautifulSoup("<!DOCTYPE NETSCAPE-Bookmark-file-1><Title>Bookmarks</Title><H1>Bookmarks</H1>", 'html.parser')
636
+ dl = soup.new_tag('DL')
637
+ for bookmark in bookmarks:
638
+ dt = soup.new_tag('DT')
639
+ a = soup.new_tag('A', href=bookmark['url'])
640
+ a.string = bookmark['title']
641
+ dt.append(a)
642
+ dl.append(dt)
643
+ soup.append(dl)
644
+ html_content = str(soup)
645
+ # Encode the HTML content to base64 for download
646
+ b64 = base64.b64encode(html_content.encode()).decode()
647
+ href = f'data:text/html;base64,{b64}'
648
+ logger.info("Bookmarks exported successfully")
649
+ return f'<a href="{href}" download="bookmarks.html">πŸ’Ύ Download Exported Bookmarks</a>'
650
+ except Exception as e:
651
+ logger.error(f"Error exporting bookmarks: {e}")
652
+ return "⚠️ Error exporting bookmarks."
653
+
654
+ # Chatbot response using Groq Cloud API with FAISS search
655
+ def chatbot_response(user_query, state_bookmarks):
656
+ if not GROQ_API_KEY:
657
+ logger.warning("GROQ_API_KEY not set.")
658
+ return "⚠️ API key not set. Please set the GROQ_API_KEY environment variable in the Hugging Face Space settings."
659
+
660
+ bookmarks = state_bookmarks
661
+ if not bookmarks:
662
+ logger.warning("No bookmarks available for chatbot")
663
+ return "⚠️ No bookmarks available. Please upload and process your bookmarks first."
664
+
665
+ logger.info(f"Chatbot received query: {user_query}")
666
+
667
+ # Prepare the prompt for the LLM using FAISS search
668
+ try:
669
+ # Encode the user query
670
+ query_embedding = embedding_model.encode([user_query]).astype('float32')
671
+
672
+ # Perform FAISS search
673
+ k = 5 # Number of top results to retrieve
674
+ distances, indices = faiss_index.search(query_embedding, k)
675
+
676
+ # Fetch the corresponding bookmarks
677
+ relevant_bookmarks = []
678
+ for idx in indices[0]:
679
+ if idx == -1:
680
+ continue # No more results
681
+ # Find the bookmark with matching ID
682
+ for bookmark in bookmarks:
683
+ if bookmark.get('id') == idx:
684
+ relevant_bookmarks.append(bookmark)
685
+ break
686
+
687
+ if not relevant_bookmarks:
688
+ return "πŸ” No relevant bookmarks found for your query."
689
+
690
+ # Prepare the summary of relevant bookmarks
691
+ bookmark_data = ""
692
+ for bm in relevant_bookmarks:
693
+ bookmark_data += f"Title: {bm['title']}\nURL: {bm['url']}\nSummary: {bm['summary']}\n\n"
694
+
695
+ # Construct the prompt
696
+ prompt = f"""
697
+ You are an assistant that helps users find relevant bookmarks from their collection based on their queries.
698
+
699
+ User Query:
700
+ {user_query}
701
+
702
+ Relevant Bookmarks:
703
+ {bookmark_data}
704
+
705
+ Please provide a concise summary of the most relevant bookmarks that match the user's query.
706
+ """
707
+
708
+ # Call the Groq Cloud API via the OpenAI client
709
+ response = openai.ChatCompletion.create(
710
+ model='gpt-3.5-turbo', # Use appropriate model
711
+ messages=[
712
+ {"role": "system", "content": "You help users find relevant bookmarks based on their queries."},
713
+ {"role": "user", "content": prompt}
714
+ ],
715
+ max_tokens=500,
716
+ temperature=0.7,
717
+ )
718
+
719
+ # Extract the response text
720
+ answer = response['choices'][0]['message']['content'].strip()
721
+ logger.info("Chatbot response generated using Groq Cloud API")
722
+ return answer
723
+
724
+ except Exception as e:
725
+ error_message = f"⚠️ Error processing your query: {str(e)}"
726
+ logger.error(error_message)
727
+ print(error_message) # Ensure error appears in Hugging Face Spaces logs
728
+ return error_message
729
+
730
+ # Build the Gradio app
731
+ def build_app():
732
+ try:
733
+ logger.info("Building Gradio app")
734
+ with gr.Blocks(css="app.css") as demo: # Load external CSS file
735
+ # Shared states
736
+ state_bookmarks = gr.State([])
737
+ chat_history = gr.State([])
738
+
739
+ # General Overview
740
+ gr.Markdown("""
741
+ # πŸ“š SmartMarks - AI Browser Bookmarks Manager
742
+
743
+ 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.
744
+
745
+ ---
746
+
747
+ ## πŸš€ **How to Use SmartMarks**
748
+
749
+ SmartMarks is divided into three main sections:
750
+
751
+ 1. **πŸ“‚ Upload and Process Bookmarks:** Import your existing bookmarks and let SmartMarks analyze and categorize them for you.
752
+ 2. **πŸ’¬ Chat with Bookmarks:** Interact with your bookmarks using natural language queries to find relevant links effortlessly.
753
+ 3. **πŸ› οΈ Manage Bookmarks:** View, edit, delete, and export your bookmarks with ease.
754
+
755
+ Navigate through the tabs to explore each feature in detail.
756
+ """)
757
+
758
+ # Define Manage Bookmarks components outside the tab for global access
759
+ bookmark_selector = gr.CheckboxGroup(label="βœ… Select Bookmarks", choices=[])
760
+ bookmark_display_manage = gr.HTML(label="πŸ“„ Manage Bookmarks Display")
761
+
762
+ # Upload and Process Bookmarks Tab
763
+ with gr.Tab("Upload and Process Bookmarks"):
764
+ gr.Markdown("""
765
+ ## πŸ“‚ **Upload and Process Bookmarks**
766
+
767
+ ### πŸ“ **Steps to Upload and Process:**
768
+
769
+ 1. **πŸ”½ Upload Bookmarks File:**
770
+ - Click on the **"πŸ“ Upload Bookmarks HTML File"** button.
771
+ - Select your browser's exported bookmarks HTML file from your device.
772
+
773
+ 2. **βš™οΈ Process Bookmarks:**
774
+ - After uploading, click on the **"βš™οΈ Process Bookmarks"** button.
775
+ - SmartMarks will parse your bookmarks, fetch additional information, generate summaries, and categorize each link based on predefined categories.
776
+
777
+ 3. **πŸ“„ View Processed Bookmarks:**
778
+ - Once processing is complete, your bookmarks will be displayed in an organized and visually appealing format below.
779
+ """)
780
+
781
+ upload = gr.File(label="πŸ“ Upload Bookmarks HTML File", type='binary')
782
+ process_button = gr.Button("βš™οΈ Process Bookmarks")
783
+ output_text = gr.Textbox(label="βœ… Output", interactive=False)
784
+ bookmark_display = gr.HTML(label="πŸ“„ Bookmarks")
785
+
786
+ process_button.click(
787
+ process_uploaded_file,
788
+ inputs=[upload, state_bookmarks],
789
+ outputs=[output_text, bookmark_display, bookmark_selector, bookmark_display_manage, state_bookmarks]
790
+ )
791
+
792
+ # Chat with Bookmarks Tab
793
+ with gr.Tab("Chat with Bookmarks"):
794
+ gr.Markdown("""
795
+ ## πŸ’¬ **Chat with Bookmarks**
796
+
797
+ ### πŸ€– **How to Interact:**
798
+
799
+ 1. **✍️ Enter Your Query:**
800
+ - 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?"
801
+
802
+ 2. **πŸ“¨ Submit Your Query:**
803
+ - Click the **"πŸ“¨ Send"** button to submit your query.
804
+
805
+ 3. **πŸ“ˆ Receive AI-Driven Responses:**
806
+ - SmartMarks will analyze your query and provide relevant bookmarks that match your request, making it easier to find specific links without manual searching.
807
+
808
+ 4. **πŸ—‚οΈ View Chat History:**
809
+ - All your queries and the corresponding AI responses are displayed in the chat history for your reference.
810
+ """)
811
+
812
+ with gr.Row():
813
+ chat_history_display = gr.Chatbot(label="πŸ—¨οΈ Chat History")
814
+ with gr.Column(scale=1):
815
+ chat_input = gr.Textbox(
816
+ label="✍️ Ask about your bookmarks",
817
+ placeholder="e.g., Do I have any bookmarks about GenerativeAI?",
818
+ lines=1,
819
+ interactive=True
820
+ )
821
+ chat_button = gr.Button("πŸ“¨ Send")
822
+
823
+ # When user presses Enter in chat_input
824
+ chat_input.submit(
825
+ chatbot_response,
826
+ inputs=[chat_input, state_bookmarks],
827
+ outputs=chat_history_display
828
+ )
829
+
830
+ # When user clicks Send button
831
+ chat_button.click(
832
+ chatbot_response,
833
+ inputs=[chat_input, state_bookmarks],
834
+ outputs=chat_history_display
835
+ )
836
+
837
+ # Manage Bookmarks Tab
838
+ with gr.Tab("Manage Bookmarks"):
839
+ gr.Markdown("""
840
+ ## πŸ› οΈ **Manage Bookmarks**
841
+
842
+ ### πŸ—‚οΈ **Features:**
843
+
844
+ 1. **πŸ‘οΈ View Bookmarks:**
845
+ - All your processed bookmarks are displayed here with their respective categories and summaries.
846
+
847
+ 2. **βœ… Select Bookmarks:**
848
+ - Use the checkboxes next to each bookmark to select one, multiple, or all bookmarks you wish to manage.
849
+
850
+ 3. **πŸ—‘οΈ Delete Selected Bookmarks:**
851
+ - After selecting the desired bookmarks, click the **"πŸ—‘οΈ Delete Selected Bookmarks"** button to remove them from your list.
852
+
853
+ 4. **✏️ Edit Categories:**
854
+ - Select the bookmarks you want to re-categorize.
855
+ - Choose a new category from the dropdown menu labeled **"πŸ†• New Category"**.
856
+ - Click the **"✏️ Edit Category of Selected Bookmarks"** button to update their categories.
857
+
858
+ 5. **πŸ’Ύ Export Bookmarks:**
859
+ - Click the **"πŸ’Ύ Export Bookmarks"** button to download your updated bookmarks as an HTML file.
860
+ - This file can be uploaded back to your browser to reflect the changes made within SmartMarks.
861
+
862
+ 6. **πŸ”„ Refresh Bookmarks:**
863
+ - Click the **"πŸ”„ Refresh Bookmarks"** button to ensure the latest state is reflected in the display.
864
+ """)
865
+
866
+ manage_output = gr.Textbox(label="πŸ”„ Manage Output", interactive=False)
867
+ new_category_input = gr.Dropdown(label="πŸ†• New Category", choices=CATEGORIES, value="Uncategorized")
868
+ with gr.Row():
869
+ delete_button = gr.Button("πŸ—‘οΈ Delete Selected Bookmarks")
870
+ edit_category_button = gr.Button("✏️ Edit Category of Selected Bookmarks")
871
+ export_button = gr.Button("πŸ’Ύ Export Bookmarks")
872
+ download_link = gr.HTML(label="πŸ“₯ Download Exported Bookmarks")
873
+ refresh_button = gr.Button("πŸ”„ Refresh Bookmarks")
874
+
875
+ # Define button actions
876
+ delete_button.click(
877
+ delete_selected_bookmarks,
878
+ inputs=[bookmark_selector, state_bookmarks],
879
+ outputs=[manage_output, bookmark_selector, bookmark_display_manage, state_bookmarks]
880
+ )
881
+
882
+ edit_category_button.click(
883
+ edit_selected_bookmarks_category,
884
+ inputs=[bookmark_selector, new_category_input, state_bookmarks],
885
+ outputs=[manage_output, bookmark_selector, bookmark_display_manage, state_bookmarks]
886
+ )
887
+
888
+ export_button.click(
889
+ export_bookmarks,
890
+ inputs=[state_bookmarks],
891
+ outputs=download_link
892
+ )
893
+
894
+ refresh_button.click(
895
+ lambda bookmarks: (
896
+ [
897
+ f"{i+1}. {bookmark['title']} (Category: {bookmark['category']})" for i, bookmark in enumerate(bookmarks)
898
+ ],
899
+ display_bookmarks()
900
+ ),
901
+ inputs=[state_bookmarks],
902
+ outputs=[bookmark_selector, bookmark_display_manage]
903
+ )
904
+
905
+ logger.info("Launching Gradio app")
906
+ demo.launch(debug=True)
907
+ except Exception as e:
908
+ logger.error(f"Error building the app: {e}")
909
+ print(f"Error building the app: {e}")
910
+
911
+ if __name__ == "__main__":
912
+ build_app()
913
+ Key Changes:
914
+
915
+ External CSS Integration:
916
+ Changed gr.Blocks(css=""" ... """) to gr.Blocks(css="app.css") to load styles from the external app.css file.
917
+ Folder Structure Consideration:
918
+ Ensure that the app.css file is located in the same directory as app.py or adjust the path accordingly.
919
+ 3. Updated README.md
920
+ The updated README.md now reflects the addition of the external app.css file and provides clear instructions on maintaining and organizing your project files.
921
+
922
+ markdown
923
+ Copy code
924
  # πŸ“š SmartMarks - AI Browser Bookmarks Manager
925
 
926
  ![License](https://img.shields.io/badge/license-MIT-blue.svg)
 
960
 
961
  ### Repository Structure
962
 
963
+ smartmarks/ β”‚ β”œβ”€β”€ app.py β”œβ”€β”€ app.css β”œβ”€β”€ requirements.txt β”œβ”€β”€ README.md β”œβ”€β”€ LICENSE └── screenshots/ β”œβ”€β”€ light_mode.png └── dark_mode.png
964
+
965
+ markdown
966
+ Copy code
967
+
968
+ ### Setting Up Environment Variables
969
+
970
+ 1. **GROQ_API_KEY:**
971
+ SmartMarks requires the `GROQ_API_KEY` to interact with the Groq Cloud API. To set this up:
972
+
973
+ - Navigate to your Space's dashboard on Hugging Face.
974
+ - Go to **Settings** > **Secrets**.
975
+ - Add a new secret with the key `GROQ_API_KEY` and paste your API key as the value.
976
+
977
+ > **Note:** Keep your API keys secure. Never expose them in your code or repository.
978
+
979
+ ### Installing Dependencies
980
+
981
+ Hugging Face Spaces automatically installs dependencies listed in the `requirements.txt` file. Ensure your `requirements.txt` includes all necessary packages:
982
+
983
+ ```plaintext
984
+ gradio
985
+ beautifulsoup4
986
+ sentence-transformers
987
+ faiss-cpu
988
+ aiohttp
989
+ openai
990
+ uuid
991
+ Note: The uuid library is part of Python's standard library and doesn't require installation. You can remove it from requirements.txt if preferred.
992
+
993
+ Deploying the Space
994
+ Create a New Space:
995
+
996
+ Navigate to Hugging Face Spaces and click on "Create new Space".
997
+ Choose the Gradio SDK and set the visibility (public or private) as desired.
998
+ Upload Your Code:
999
+
1000
+ Clone your Space repository or use the web interface to upload app.py, app.css, requirements.txt, README.md, and LICENSE.
1001
+ Launch the Space:
1002
+
1003
+ Once all files are uploaded, Hugging Face Spaces will automatically build and deploy your application.
1004
+ Access your Space via the provided URL.
1005
+ 🌈 Dynamic Color Theme
1006
+ SmartMarks automatically detects your system or browser's preferred color scheme and adjusts the application's theme accordingly:
1007
+
1008
+ Dark Mode: White text on a dark background for reduced eye strain in low-light environments.
1009
+ Light Mode: Black text on a light background for bright environments.
1010
+ This dynamic theming ensures optimal readability and aesthetics without any manual intervention.
1011
+
1012
+ How It Works
1013
+ The application uses CSS media queries to detect the user's preferred color scheme and applies appropriate styles by loading them from the external app.css file:
1014
+
1015
+ css
1016
+ Copy code
1017
+ /* app.css */
1018
+
1019
+ /* Card Styling */
1020
+ .card {
1021
+ box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
1022
+ transition: 0.3s;
1023
+ padding: 10px;
1024
+ margin: 10px;
1025
+ border-radius: 5px;
1026
+ }
1027
+
1028
+ .card:hover {
1029
+ box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
1030
+ }
1031
+
1032
+ /* Dynamic Theme Styling */
1033
+ @media (prefers-color-scheme: dark) {
1034
+ body {
1035
+ color: white;
1036
+ background-color: #121212;
1037
+ }
1038
+ .card {
1039
+ background-color: #1e1e1e;
1040
+ }
1041
+ a {
1042
+ color: #bb86fc;
1043
+ }
1044
+ h1, h2, h3, p, strong {
1045
+ color: inherit;
1046
+ }
1047
+ }
1048
+
1049
+ @media (prefers-color-scheme: light) {
1050
+ body {
1051
+ color: black;
1052
+ background-color: white;
1053
+ }
1054
+ .card {
1055
+ background-color: #fff;
1056
+ }
1057
+ a {
1058
+ color: #1a0dab;
1059
+ }
1060
+ h1, h2, h3, p, strong {
1061
+ color: inherit;
1062
+ }
1063
+ }
1064
+ These styles ensure that the text and background colors adapt based on the user's system or browser theme settings.
1065
+
1066
+ πŸ“Έ Screenshots
1067
+ Note: Replace the placeholders below with actual screenshots of your application in both light and dark modes.
1068
+
1069
+ Light Mode
1070
+
1071
+ Dark Mode
1072
+
1073
+ πŸ“‹ Features in Detail
1074
+ πŸ“‚ Upload and Process Bookmarks
1075
+ Upload Bookmarks File:
1076
+
1077
+ Click on the "πŸ“ Upload Bookmarks HTML File" button.
1078
+ Select your browser's exported bookmarks HTML file from your device.
1079
+ Process Bookmarks:
1080
+
1081
+ After uploading, click on the "βš™οΈ Process Bookmarks" button.
1082
+ SmartMarks will parse your bookmarks, fetch additional information, generate summaries, and categorize each link based on predefined categories.
1083
+ View Processed Bookmarks:
1084
+
1085
+ Once processing is complete, your bookmarks will be displayed in an organized and visually appealing format below.
1086
+ πŸ’¬ Chat with Bookmarks
1087
+ Enter Your Query:
1088
+
1089
+ 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?"
1090
+ Submit Your Query:
1091
+
1092
+ Click the "πŸ“¨ Send" button to submit your query.
1093
+ Receive AI-Driven Responses:
1094
+
1095
+ SmartMarks will analyze your query and provide relevant bookmarks that match your request, making it easier to find specific links without manual searching.
1096
+ View Chat History:
1097
+
1098
+ All your queries and the corresponding AI responses are displayed in the chat history for your reference.
1099
+ πŸ› οΈ Manage Bookmarks
1100
+ View Bookmarks:
1101
+
1102
+ All your processed bookmarks are displayed here with their respective categories and summaries.
1103
+ Select Bookmarks:
1104
+
1105
+ Use the checkboxes next to each bookmark to select one, multiple, or all bookmarks you wish to manage.
1106
+ Delete Selected Bookmarks:
1107
+
1108
+ After selecting the desired bookmarks, click the "πŸ—‘οΈ Delete Selected Bookmarks" button to remove them from your list.
1109
+ Edit Categories:
1110
+
1111
+ Select the bookmarks you want to re-categorize.
1112
+ Choose a new category from the dropdown menu labeled "πŸ†• New Category".
1113
+ Click the "✏️ Edit Category of Selected Bookmarks" button to update their categories.
1114
+ Export Bookmarks:
1115
+
1116
+ Click the "πŸ’Ύ Export Bookmarks" button to download your updated bookmarks as an HTML file.
1117
+ This file can be uploaded back to your browser to reflect the changes made within SmartMarks.
1118
+ Refresh Bookmarks:
1119
+
1120
+ Click the "πŸ”„ Refresh Bookmarks" button to ensure the latest state is reflected in the display.
1121
+ πŸ”§ Configuration
1122
+ Ensure that the GROQ_API_KEY environment variable is set correctly, as it's essential for the chatbot functionality to interact with the Groq Cloud API.
1123
+
1124
+ Setting Up Environment Variables
1125
+ Navigate to Space Settings:
1126
+
1127
+ Go to your Space's dashboard on Hugging Face.
1128
+ Add Secret:
1129
+
1130
+ Click on Settings > Secrets.
1131
+ Add a new secret with the key GROQ_API_KEY and paste your API key as the value.
1132
+ Note: Keep your API keys secure. Never expose them in your code or repository.
1133
+
1134
+ 🀝 Contributing
1135
+ Contributions are welcome! Please follow these steps to contribute:
1136
+
1137
+ Fork the Repository:
1138
+
1139
+ Click on the "Fork" button at the top right of the repository page.
1140
+ Clone Your Fork:
1141
+
1142
+ bash
1143
+ Copy code
1144
+ git clone https://github.com/your-username/smartmarks.git
1145
+ cd smartmarks
1146
+ Create a New Branch:
1147
+
1148
+ bash
1149
+ Copy code
1150
+ git checkout -b feature/YourFeature
1151
+ Make Your Changes:
1152
+
1153
+ Implement your feature or bug fix.
1154
+ Commit Your Changes:
1155
+
1156
+ bash
1157
+ Copy code
1158
+ git commit -m "Add some feature"
1159
+ Push to Your Fork:
1160
+
1161
+ bash
1162
+ Copy code
1163
+ git push origin feature/YourFeature
1164
+ Open a Pull Request:
1165
+
1166
+ Navigate to your forked repository on GitHub and click on "Compare & pull request".
1167
+ πŸ“œ License
1168
+ This project is licensed under the MIT License. See the LICENSE file for details.
1169
+
1170
+ πŸ“ž Contact
1171
+ For any questions or feedback, please contact [email protected].
1172
+
1173
+ Happy Bookmarking with SmartMarks! πŸš€πŸ“š
1174
+