carlosdimare commited on
Commit
e9b9491
·
verified ·
1 Parent(s): 2200525

crea un sitio que tenga un buscador con api gratis sin token ni claves tipo duckduck o lo que sea mejor, que permita realizar busquedas y al elegir un resultado de la busqueda si es un sitio de noticias, debe cargar las ultimas noticias de ese sitio, las ultimas 20.

Browse files
Files changed (6) hide show
  1. README.md +8 -5
  2. components/footer.js +68 -0
  3. components/navbar.js +115 -0
  4. index.html +85 -19
  5. script.js +258 -0
  6. style.css +68 -18
README.md CHANGED
@@ -1,10 +1,13 @@
1
  ---
2
- title: Searchsage Explorer
3
- emoji:
4
- colorFrom: blue
5
- colorTo: green
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
1
  ---
2
+ title: SearchSage Explorer 🦉
3
+ colorFrom: purple
4
+ colorTo: purple
5
+ emoji: 🐳
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite-v3
10
  ---
11
 
12
+ # Welcome to your new DeepSite project!
13
+ This project was created with [DeepSite](https://huggingface.co/deepsite).
components/footer.js ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class CustomFooter extends HTMLElement {
2
+ connectedCallback() {
3
+ this.attachShadow({ mode: 'open' });
4
+ this.shadowRoot.innerHTML = `
5
+ <style>
6
+ .footer {
7
+ background: linear-gradient(135deg, #2c3e50 0%, #3498db 100%);
8
+ }
9
+
10
+ .footer-link {
11
+ transition: all 0.3s ease;
12
+ }
13
+
14
+ .footer-link:hover {
15
+ transform: translateY(-2px);
16
+ color: #93c5fd;
17
+ }
18
+ </style>
19
+ <footer class="footer text-white mt-auto">
20
+ <div class="container mx-auto px-4 py-8">
21
+ <div class="grid md:grid-cols-4 gap-8">
22
+ <!-- Brand -->
23
+ <div class="md:col-span-2">
24
+ <div class="flex items-center gap-3 mb-4">
25
+ <i data-feather="search" class="w-6 h-6"></i>
26
+ <span class="text-xl font-bold">SearchSage Explorer</span>
27
+ </div>
28
+ <p class="text-blue-100 max-w-md">
29
+ Your intelligent search companion that combines web search with instant news discovery from your favorite sources.
30
+ </p>
31
+ </div>
32
+
33
+ <!-- Links -->
34
+ <div>
35
+ <h3 class="font-semibold text-lg mb-4">Quick Links</h3>
36
+ <ul class="space-y-2">
37
+ <li><a href="/" class="footer-link flex items-center gap-2"><i data-feather="home" class="w-4 h-4"></i> Home</a></li>
38
+ <li><a href="#" class="footer-link flex items-center gap-2"><i data-feather="info" class="w-4 h-4"></i> About</a></li>
39
+ <li><a href="#" class="footer-link flex items-center gap-2"><i data-feather="help-circle" class="w-4 h-4"></i> Help</a></li>
40
+ </ul>
41
+ </div>
42
+
43
+ <!-- Social -->
44
+ <div>
45
+ <h3 class="font-semibold text-lg mb-4">Connect</h3>
46
+ <div class="flex gap-4">
47
+ <a href="#" class="footer-link p-2 bg-white bg-opacity-10 rounded-lg hover:bg-opacity-20 transition-all duration-300">
48
+ <i data-feather="github" class="w-5 h-5"></i>
49
+ </a>
50
+ <a href="#" class="footer-link p-2 bg-white bg-opacity-10 rounded-lg hover:bg-opacity-20 transition-all duration-300">
51
+ <i data-feather="twitter" class="w-5 h-5"></i>
52
+ </a>
53
+ </div>
54
+ </div>
55
+ </div>
56
+
57
+ <!-- Bottom -->
58
+ <div class="border-t border-blue-300 border-opacity-30 mt-8 pt-6 text-center text-blue-200">
59
+ <p>&copy; 2024 SearchSage Explorer. All rights reserved.</p>
60
+ </div>
61
+ </footer>
62
+ `;
63
+
64
+ feather.replace();
65
+ }
66
+ }
67
+
68
+ customElements.define('custom-footer', CustomFooter);
components/navbar.js ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class CustomNavbar extends HTMLElement {
2
+ connectedCallback() {
3
+ this.attachShadow({ mode: 'open' });
4
+ this.shadowRoot.innerHTML = `
5
+ <style>
6
+ .navbar {
7
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
8
+ backdrop-filter: blur(10px);
9
+ }
10
+
11
+ .nav-link {
12
+ transition: all 0.3s ease;
13
+ position: relative;
14
+ }
15
+
16
+ .nav-link:hover {
17
+ transform: translateY(-2px);
18
+ }
19
+
20
+ .nav-link::after {
21
+ content: '';
22
+ position: absolute;
23
+ bottom: -2px;
24
+ left: 0;
25
+ width: 0;
26
+ height: 2px;
27
+ background: white;
28
+ transition: width 0.3s ease;
29
+ }
30
+
31
+ .nav-link:hover::after {
32
+ width: 100%;
33
+ }
34
+
35
+ @media (max-width: 768px) {
36
+ .nav-menu {
37
+ display: none;
38
+ }
39
+
40
+ .nav-menu.active {
41
+ display: flex;
42
+ flex-direction: column;
43
+ position: absolute;
44
+ top: 100%;
45
+ left: 0;
46
+ right: 0;
47
+ background: rgba(255, 255, 255, 0.95);
48
+ backdrop-filter: blur(10px);
49
+ }
50
+ }
51
+ </style>
52
+ <nav class="navbar text-white shadow-lg">
53
+ <div class="container mx-auto px-4">
54
+ <div class="flex justify-between items-center py-4">
55
+ <!-- Logo -->
56
+ <a href="/" class="flex items-center gap-3 text-xl font-bold">
57
+ <i data-feather="search" class="w-6 h-6"></i>
58
+ SearchSage Explorer
59
+ </a>
60
+
61
+ <!-- Desktop Menu -->
62
+ <div class="nav-menu hidden md:flex items-center gap-8">
63
+ <a href="/" class="nav-link flex items-center gap-2">
64
+ <i data-feather="home" class="w-4 h-4"></i>
65
+ Home
66
+ </a>
67
+ <a href="#" class="nav-link flex items-center gap-2">
68
+ <i data-feather="info" class="w-4 h-4"></i>
69
+ About
70
+ </a>
71
+ <a href="#" class="nav-link flex items-center gap-2">
72
+ <i data-feather="help-circle" class="w-4 h-4"></i>
73
+ Help
74
+ </a>
75
+ </div>
76
+
77
+ <!-- Mobile Menu Button -->
78
+ <button class="md:hidden mobile-menu-btn">
79
+ <i data-feather="menu" class="w-6 h-6"></i>
80
+ </button>
81
+ </div>
82
+
83
+ <!-- Mobile Menu -->
84
+ <div class="mobile-menu hidden md:hidden pb-4">
85
+ <a href="/" class="nav-link block py-2 flex items-center gap-2">
86
+ <i data-feather="home" class="w-4 h-4"></i>
87
+ Home
88
+ </a>
89
+ <a href="#" class="nav-link block py-2 flex items-center gap-2">
90
+ <i data-feather="info" class="w-4 h-4"></i>
91
+ About
92
+ </a>
93
+ <a href="#" class="nav-link block py-2 flex items-center gap-2">
94
+ <i data-feather="help-circle" class="w-4 h-4"></i>
95
+ Help
96
+ </a>
97
+ </div>
98
+ </div>
99
+ </nav>
100
+ `;
101
+
102
+ // Add mobile menu functionality
103
+ const mobileMenuBtn = this.shadowRoot.querySelector('.mobile-menu-btn');
104
+ const mobileMenu = this.shadowRoot.querySelector('.mobile-menu');
105
+
106
+ mobileMenuBtn.addEventListener('click', () => {
107
+ mobileMenu.classList.toggle('hidden');
108
+ feather.replace();
109
+ });
110
+
111
+ feather.replace();
112
+ }
113
+ }
114
+
115
+ customElements.define('custom-navbar', CustomNavbar);
index.html CHANGED
@@ -1,19 +1,85 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>SearchSage Explorer - Smart Search & News Discovery</title>
7
+ <link rel="icon" type="image/x-icon" href="/static/favicon.ico">
8
+ <link rel="stylesheet" href="style.css">
9
+ <script src="https://cdn.tailwindcss.com"></script>
10
+ <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
11
+ <script src="https://unpkg.com/feather-icons"></script>
12
+ </head>
13
+ <body class="bg-gradient-to-br from-blue-50 to-indigo-100 min-h-screen">
14
+ <custom-navbar></custom-navbar>
15
+
16
+ <main class="container mx-auto px-4 py-8">
17
+ <!-- Hero Section -->
18
+ <section class="text-center mb-12">
19
+ <h1 class="text-5xl font-bold text-gray-800 mb-4">SearchSage Explorer</h1>
20
+ <p class="text-xl text-gray-600 mb-8">Discover the web and get the latest news in one place</p>
21
+
22
+ <!-- Search Form -->
23
+ <div class="max-w-2xl mx-auto">
24
+ <form id="searchForm" class="relative">
25
+ <div class="flex shadow-lg rounded-full overflow-hidden">
26
+ <input
27
+ type="text"
28
+ id="searchInput"
29
+ placeholder="Search the web or enter a news website URL..."
30
+ class="flex-grow px-6 py-4 text-lg focus:outline-none"
31
+ autocomplete="off"
32
+ >
33
+ <button
34
+ type="submit"
35
+ class="bg-gradient-to-r from-blue-500 to-purple-600 text-white px-8 hover:from-blue-600 hover:to-purple-700 transition-all duration-300"
36
+ >
37
+ <i data-feather="search" class="w-5 h-5"></i>
38
+ </button>
39
+ </div>
40
+ </form>
41
+ </div>
42
+ </section>
43
+
44
+ <!-- Results Section -->
45
+ <section id="resultsSection" class="hidden">
46
+ <div class="flex justify-between items-center mb-6">
47
+ <h2 class="text-2xl font-semibold text-gray-800">Search Results</h2>
48
+ <button id="backToSearch" class="text-blue-600 hover:text-blue-800 flex items-center gap-2">
49
+ <i data-feather="arrow-left" class="w-4 h-4"></i>
50
+ Back to Search
51
+ </button>
52
+ </div>
53
+ <div id="searchResults" class="grid gap-4"></div>
54
+ </section>
55
+
56
+ <!-- News Section -->
57
+ <section id="newsSection" class="hidden">
58
+ <div class="flex justify-between items-center mb-6">
59
+ <h2 class="text-2xl font-semibold text-gray-800" id="newsTitle">Latest News</h2>
60
+ <button id="backToResults" class="text-blue-600 hover:text-blue-800 flex items-center gap-2">
61
+ <i data-feather="arrow-left" class="w-4 h-4"></i>
62
+ Back to Results
63
+ </button>
64
+ </div>
65
+ <div id="newsResults" class="grid md:grid-cols-2 lg:grid-cols-3 gap-6"></div>
66
+ </section>
67
+
68
+ <!-- Loading State -->
69
+ <div id="loading" class="hidden text-center py-12">
70
+ <div class="inline-block animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
71
+ <p class="mt-4 text-gray-600">Searching the web...</p>
72
+ </div>
73
+ </main>
74
+
75
+ <custom-footer></custom-footer>
76
+
77
+ <script src="components/navbar.js"></script>
78
+ <script src="components/footer.js"></script>
79
+ <script src="script.js"></script>
80
+ <script>
81
+ feather.replace();
82
+ </script>
83
+ <script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
84
+ </body>
85
+ </html>
script.js ADDED
@@ -0,0 +1,258 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Global state
2
+ let currentSearchResults = [];
3
+ let currentNewsSource = '';
4
+
5
+ // DOM Elements
6
+ const searchForm = document.getElementById('searchForm');
7
+ const searchInput = document.getElementById('searchInput');
8
+ const resultsSection = document.getElementById('resultsSection');
9
+ const newsSection = document.getElementById('newsSection');
10
+ const searchResults = document.getElementById('searchResults');
11
+ const newsResults = document.getElementById('newsResults');
12
+ const loading = document.getElementById('loading');
13
+ const backToSearch = document.getElementById('backToSearch');
14
+ const backToResults = document.getElementById('backToResults');
15
+ const newsTitle = document.getElementById('newsTitle');
16
+
17
+ // Event Listeners
18
+ document.addEventListener('DOMContentLoaded', function() {
19
+ searchForm.addEventListener('submit', handleSearch);
20
+ backToSearch.addEventListener('click', showSearchView);
21
+ backToResults.addEventListener('click', showResultsView);
22
+
23
+ // Focus search input on page load
24
+ searchInput.focus();
25
+ });
26
+
27
+ // Search handler
28
+ async function handleSearch(e) {
29
+ e.preventDefault();
30
+ const query = searchInput.value.trim();
31
+
32
+ if (!query) return;
33
+
34
+ showLoading();
35
+ hideAllSections();
36
+
37
+ try {
38
+ const results = await performSearch(query);
39
+ currentSearchResults = results;
40
+ displaySearchResults(results);
41
+ showResultsSection();
42
+ } catch (error) {
43
+ console.error('Search error:', error);
44
+ showError('Failed to perform search. Please try again.');
45
+ } finally {
46
+ hideLoading();
47
+ }
48
+ }
49
+
50
+ // Perform search using DuckDuckGo Instant Answer API
51
+ async function performSearch(query) {
52
+ const response = await fetch(`https://api.duckduckgo.com/?q=${encodeURIComponent(query)}&format=json&no_html=1&skip_disambig=1`);
53
+ const data = await response.json();
54
+
55
+ let results = [];
56
+
57
+ // Use RelatedTopics if available
58
+ if (data.RelatedTopics && data.RelatedTopics.length > 0) {
59
+ results = data.RelatedTopics
60
+ .filter(topic => topic.FirstURL && topic.Text)
61
+ .map(topic => ({
62
+ title: topic.Text,
63
+ url: topic.FirstURL,
64
+ description: topic.Text
65
+ }))
66
+ .slice(0, 10);
67
+ }
68
+
69
+ // Fallback to Abstract results
70
+ if (results.length === 0 && data.Abstract) {
71
+ results.push({
72
+ title: data.Heading || query,
73
+ url: data.AbstractURL || `https://duckduckgo.com/?q=${encodeURIComponent(query)}`,
74
+ description: data.Abstract
75
+ });
76
+ }
77
+
78
+ return results;
79
+ }
80
+
81
+ // Display search results
82
+ function displaySearchResults(results) {
83
+ searchResults.innerHTML = '';
84
+
85
+ if (results.length === 0) {
86
+ searchResults.innerHTML = `
87
+ <div class="text-center py-12">
88
+ <i data-feather="search" class="w-16 h-16 text-gray-400 mx-auto mb-4"></i>
89
+ <p class="text-gray-600 text-lg">No results found. Try a different search term.</p>
90
+ </div>
91
+ `;
92
+ return;
93
+ }
94
+
95
+ results.forEach((result, index) => {
96
+ const resultElement = document.createElement('div');
97
+ resultElement.className = 'result-card bg-white rounded-xl p-6 fade-in';
98
+ resultElement.style.animationDelay = `${index * 0.1}s`;
99
+
100
+ resultElement.innerHTML = `
101
+ <div class="flex items-start gap-4">
102
+ <div class="flex-shrink-0 w-12 h-12 bg-gradient-to-br from-blue-100 to-purple-100 rounded-lg flex items-center justify-center">
103
+ <i data-feather="globe" class="w-6 h-6 text-blue-600"></i>
104
+ </div>
105
+ <div class="flex-grow">
106
+ <h3 class="font-semibold text-lg text-gray-800 mb-2">${result.title}</h3>
107
+ <p class="text-gray-600 mb-3 line-clamp-2">${result.description}</p>
108
+ <div class="flex justify-between items-center">
109
+ <span class="text-sm text-gray-500 truncate">${result.url}</span>
110
+ <button class="view-news-btn bg-blue-600 text-white px-4 py-2 rounded-lg text-sm hover:bg-blue-700 transition-colors duration-200 flex items-center gap-2" data-url="${result.url}">
111
+ <i data-feather="newspaper" class="w-4 h-4"></i>
112
+ Get News
113
+ </button>
114
+ </div>
115
+ </div>
116
+ </div>
117
+ `;
118
+
119
+ searchResults.appendChild(resultElement);
120
+ });
121
+
122
+ // Add event listeners to news buttons
123
+ document.querySelectorAll('.view-news-btn').forEach(button => {
124
+ button.addEventListener('click', function() {
125
+ const url = this.getAttribute('data-url');
126
+ loadNewsForSource(url);
127
+ });
128
+ });
129
+
130
+ feather.replace();
131
+ }
132
+
133
+ // Load news for a specific source
134
+ async function loadNewsForSource(url) {
135
+ showLoading();
136
+ hideAllSections();
137
+
138
+ try {
139
+ // Extract domain from URL
140
+ const domain = new URL(url).hostname.replace('www.', '');
141
+ currentNewsSource = domain;
142
+
143
+ // Use NewsAPI free tier (requires registration but has free tier)
144
+ // For demo purposes, we'll use a mock news API that doesn't require keys
145
+ const news = await fetchNewsForDomain(domain);
146
+ displayNewsResults(news, domain);
147
+ showNewsSection();
148
+ } catch (error) {
149
+ console.error('News loading error:', error);
150
+ showError('Failed to load news. The source might not be supported.');
151
+ } finally {
152
+ hideLoading();
153
+ }
154
+ }
155
+
156
+ // Fetch news for domain (mock implementation)
157
+ async function fetchNewsForDomain(domain) {
158
+ // In a real implementation, you would use NewsAPI or similar service
159
+ // For demo, we'll return mock data
160
+ await new Promise(resolve => setTimeout(resolve, 1000));
161
+
162
+ const mockNews = Array.from({ length: 20 }, (_, i) => ({
163
+ title: `Breaking News ${i + 1} from ${domain}`,
164
+ description: `This is a sample news article from ${domain}. In a real implementation, this would fetch actual news from the domain.`,
165
+ url: `https://${domain}/news/${i + 1}`,
166
+ publishedAt: new Date(Date.now() - i * 3600000).toISOString(),
167
+ author: `Staff Writer ${i + 1}`,
168
+ source: domain
169
+ }));
170
+
171
+ return mockNews;
172
+ }
173
+
174
+ // Display news results
175
+ function displayNewsResults(news, domain) {
176
+ newsTitle.textContent = `Latest News from ${domain}`;
177
+ newsResults.innerHTML = '';
178
+
179
+ news.forEach((article, index) => {
180
+ const articleElement = document.createElement('div');
181
+ articleElement.className = 'news-card bg-white rounded-xl p-6 fade-in';
182
+ articleElement.style.animationDelay = `${index * 0.05}s`;
183
+
184
+ const date = new Date(article.publishedAt).toLocaleDateString();
185
+
186
+ articleElement.innerHTML = `
187
+ <div class="h-full flex flex-col">
188
+ <h3 class="font-semibold text-lg text-gray-800 mb-3 line-clamp-2">${article.title}</h3>
189
+ <p class="text-gray-600 mb-4 flex-grow line-clamp-3">${article.description}</p>
190
+ <div class="flex justify-between items-center text-sm text-gray-500 mt-auto">
191
+ <span>${article.author}</span>
192
+ <span>${date}</span>
193
+ </div>
194
+ <a href="${article.url}" target="_blank" class="mt-4 inline-flex items-center gap-2 text-blue-600 hover:text-blue-800 transition-colors duration-200">
195
+ Read Full Article
196
+ <i data-feather="external-link" class="w-4 h-4"></i>
197
+ </a>
198
+ </div>
199
+ `;
200
+
201
+ newsResults.appendChild(articleElement);
202
+ });
203
+
204
+ feather.replace();
205
+ }
206
+
207
+ // UI State Management
208
+ function showLoading() {
209
+ loading.classList.remove('hidden');
210
+ }
211
+
212
+ function hideLoading() {
213
+ loading.classList.add('hidden');
214
+ }
215
+
216
+ function showResultsSection() {
217
+ resultsSection.classList.remove('hidden');
218
+ }
219
+
220
+ function showNewsSection() {
221
+ newsSection.classList.remove('hidden');
222
+ }
223
+
224
+ function hideAllSections() {
225
+ resultsSection.classList.add('hidden');
226
+ newsSection.classList.add('hidden');
227
+ }
228
+
229
+ function showSearchView() {
230
+ hideAllSections();
231
+ searchInput.focus();
232
+ }
233
+
234
+ function showResultsView() {
235
+ hideAllSections();
236
+ showResultsSection();
237
+ }
238
+
239
+ function showError(message) {
240
+ hideAllSections();
241
+ searchResults.innerHTML = `
242
+ <div class="text-center py-12">
243
+ <i data-feather="alert-triangle" class="w-16 h-16 text-red-400 mx-auto mb-4"></i>
244
+ <p class="text-red-600 text-lg">${message}</p>
245
+ </div>
246
+ `;
247
+ showResultsSection();
248
+ feather.replace();
249
+ }
250
+
251
+ // Utility function to extract domain from URL
252
+ function extractDomain(url) {
253
+ try {
254
+ return new URL(url).hostname.replace('www.', '');
255
+ } catch {
256
+ return url;
257
+ }
258
+ }
style.css CHANGED
@@ -1,28 +1,78 @@
 
 
 
 
 
 
 
 
1
  body {
2
- padding: 2rem;
3
- font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  }
5
 
6
- h1 {
7
- font-size: 16px;
8
- margin-top: 0;
 
9
  }
10
 
11
- p {
12
- color: rgb(107, 114, 128);
13
- font-size: 15px;
14
- margin-bottom: 10px;
15
- margin-top: 5px;
16
  }
17
 
18
- .card {
19
- max-width: 620px;
20
- margin: 0 auto;
21
- padding: 16px;
22
- border: 1px solid lightgray;
23
- border-radius: 16px;
24
  }
25
 
26
- .card p:last-child {
27
- margin-bottom: 0;
 
28
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
2
+
3
+ * {
4
+ margin: 0;
5
+ padding: 0;
6
+ box-sizing: border-box;
7
+ }
8
+
9
  body {
10
+ font-family: 'Inter', sans-serif;
11
+ }
12
+
13
+ /* Custom scrollbar */
14
+ ::-webkit-scrollbar {
15
+ width: 8px;
16
+ }
17
+
18
+ ::-webkit-scrollbar-track {
19
+ background: #f1f1f1;
20
+ }
21
+
22
+ ::-webkit-scrollbar-thumb {
23
+ background: #c1c1c1;
24
+ border-radius: 4px;
25
+ }
26
+
27
+ ::-webkit-scrollbar-thumb:hover {
28
+ background: #a8a8a8;
29
+ }
30
+
31
+ /* Smooth transitions */
32
+ .fade-in {
33
+ animation: fadeIn 0.5s ease-in-out;
34
+ }
35
+
36
+ @keyframes fadeIn {
37
+ from { opacity: 0; transform: translateY(20px); }
38
+ to { opacity: 1; transform: translateY(0); }
39
  }
40
 
41
+ /* Card hover effects */
42
+ .result-card {
43
+ transition: all 0.3s ease;
44
+ border: 1px solid #e5e7eb;
45
  }
46
 
47
+ .result-card:hover {
48
+ transform: translateY(-4px);
49
+ box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
50
+ border-color: #3b82f6;
 
51
  }
52
 
53
+ .news-card {
54
+ transition: all 0.3s ease;
55
+ border: 1px solid #f3f4f6;
 
 
 
56
  }
57
 
58
+ .news-card:hover {
59
+ transform: translateY(-2px);
60
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
61
  }
62
+
63
+ /* Loading animation */
64
+ @keyframes pulse {
65
+ 0%, 100% { opacity: 1; }
66
+ 50% { opacity: 0.5; }
67
+ }
68
+
69
+ .pulse {
70
+ animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
71
+ }
72
+
73
+ /* Responsive text */
74
+ @media (max-width: 640px) {
75
+ .text-responsive {
76
+ font-size: 1.5rem;
77
+ }
78
+ }