MikePy commited on
Commit
db3676c
·
verified ·
1 Parent(s): cf227a0

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +470 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Web Extractor
3
- emoji: 🐠
4
- colorFrom: gray
5
- colorTo: yellow
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: web-extractor
3
+ emoji: 🐳
4
+ colorFrom: blue
5
+ colorTo: gray
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,470 @@
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>Smart Web Extractor</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ /* Custom CSS for things Tailwind can't handle */
11
+ .dark-mode {
12
+ background-color: #1a202c;
13
+ color: #f7fafc;
14
+ }
15
+ .light-mode {
16
+ background-color: #f7fafc;
17
+ color: #1a202c;
18
+ }
19
+ .popup-container {
20
+ width: 350px;
21
+ min-height: 200px;
22
+ max-height: 600px;
23
+ overflow-y: auto;
24
+ }
25
+ .tab-active {
26
+ border-bottom: 3px solid #4299e1;
27
+ }
28
+ .highlight {
29
+ background-color: rgba(255, 255, 0, 0.4);
30
+ }
31
+ .domain-badge {
32
+ font-size: 0.75rem;
33
+ border-radius: 9999px;
34
+ }
35
+ .slide-fade-enter-active {
36
+ transition: all 0.3s ease-out;
37
+ }
38
+ .slide-fade-leave-active {
39
+ transition: all 0.3s ease-in;
40
+ }
41
+ .slide-fade-enter-from,
42
+ .slide-fade-leave-to {
43
+ transform: translateY(10px);
44
+ opacity: 0;
45
+ }
46
+ </style>
47
+ </head>
48
+ <body class="font-sans text-sm">
49
+ <div id="app" class="popup-container transition-colors duration-200" :class="isDarkMode ? 'dark-mode' : 'light-mode'">
50
+ <!-- Header with logo and theme toggle -->
51
+ <div class="flex justify-between items-center p-4 border-b">
52
+ <div class="flex items-center">
53
+ <i class="fas fa-globe-americas text-blue-500 text-xl mr-2"></i>
54
+ <h1 class="font-bold text-lg">Smart Web Extractor</h1>
55
+ </div>
56
+ <button @click="toggleTheme" class="p-2 rounded-full hover:bg-gray-200 dark:hover:bg-gray-700">
57
+ <i :class="isDarkMode ? 'fas fa-sun text-yellow-300' : 'fas fa-moon text-gray-600'"></i>
58
+ </button>
59
+ </div>
60
+
61
+ <!-- Main content area with tabs -->
62
+ <div class="p-4">
63
+ <!-- Tab navigation -->
64
+ <div class="flex border-b mb-4">
65
+ <button @click="activeTab = 'extract'" class="px-4 py-2 font-medium" :class="{'tab-active text-blue-500': activeTab === 'extract'}">
66
+ <i class="fas fa-download mr-1"></i> Extract
67
+ </button>
68
+ <button @click="activeTab = 'results'" class="px-4 py-2 font-medium" :class="{'tab-active text-blue-500': activeTab === 'results'}">
69
+ <i class="fas fa-list mr-1"></i> Results
70
+ </button>
71
+ <button @click="activeTab = 'settings'" class="px-4 py-2 font-medium" :class="{'tab-active text-blue-500': activeTab === 'settings'}">
72
+ <i class="fas fa-cog mr-1"></i> Settings
73
+ </button>
74
+ </div>
75
+
76
+ <!-- Extract Tab -->
77
+ <div v-if="activeTab === 'extract'" class="transition-all duration-300">
78
+ <div class="mb-4">
79
+ <label class="block text-sm font-medium mb-1">Extraction Scope</label>
80
+ <div class="flex space-x-2">
81
+ <button @click="scope = 'visible'" class="flex-1 py-2 px-3 rounded border" :class="scope === 'visible' ? 'bg-blue-100 border-blue-300 dark:bg-blue-900 dark:border-blue-700' : 'hover:bg-gray-100 dark:hover:bg-gray-700'">
82
+ <i class="fas fa-eye mr-1"></i> Visible Content
83
+ </button>
84
+ <button @click="scope = 'full'" class="flex-1 py-2 px-3 rounded border" :class="scope === 'full' ? 'bg-blue-100 border-blue-300 dark:bg-blue-900 dark:border-blue-700' : 'hover:bg-gray-100 dark:hover:bg-gray-700'">
85
+ <i class="fas fa-file-alt mr-1"></i> Full Page
86
+ </button>
87
+ <button @click="scope = 'multi'" class="flex-1 py-2 px-3 rounded border" :class="scope === 'multi' ? 'bg-blue-100 border-blue-300 dark:bg-blue-900 dark:border-blue-700' : 'hover:bg-gray-100 dark:hover:bg-gray-700'">
88
+ <i class="fas fa-copy mr-1"></i> Multi-Page
89
+ </button>
90
+ </div>
91
+ </div>
92
+
93
+ <div class="mb-4">
94
+ <label class="block text-sm font-medium mb-1">Filter Options</label>
95
+ <input v-model="filterKeyword" type="text" placeholder="Keywords (comma separated)" class="w-full p-2 border rounded mb-2 dark:bg-gray-800 dark:border-gray-600">
96
+ <input v-model="filterRegex" type="text" placeholder="Regular expression" class="w-full p-2 border rounded dark:bg-gray-800 dark:border-gray-600">
97
+ <div class="flex items-center mt-2">
98
+ <input v-model="removeDuplicates" type="checkbox" id="removeDuplicates" class="mr-2">
99
+ <label for="removeDuplicates">Remove duplicates</label>
100
+ </div>
101
+ <div class="flex items-center mt-1">
102
+ <input v-model="groupByDomain" type="checkbox" id="groupByDomain" class="mr-2">
103
+ <label for="groupByDomain">Group by domain</label>
104
+ </div>
105
+ </div>
106
+
107
+ <div class="flex space-x-2">
108
+ <button @click="extractContent" class="flex-1 bg-blue-500 hover:bg-blue-600 text-white py-2 px-4 rounded flex items-center justify-center">
109
+ <i class="fas fa-play mr-2"></i> Extract Now
110
+ </button>
111
+ <button @click="showAdvanced = !showAdvanced" class="bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600 py-2 px-3 rounded">
112
+ <i class="fas fa-sliders-h"></i>
113
+ </button>
114
+ </div>
115
+
116
+ <div v-if="showAdvanced" class="mt-4 p-3 bg-gray-100 dark:bg-gray-800 rounded">
117
+ <h3 class="font-medium mb-2">Advanced Options</h3>
118
+ <div class="mb-2">
119
+ <label class="block text-xs mb-1">Tab delay (ms)</label>
120
+ <input v-model="tabDelay" type="number" min="0" class="w-full p-2 border rounded dark:bg-gray-700 dark:border-gray-600">
121
+ </div>
122
+ <div class="flex items-center">
123
+ <input v-model="keepHighlights" type="checkbox" id="keepHighlights" class="mr-2">
124
+ <label for="keepHighlights">Keep highlights</label>
125
+ </div>
126
+ </div>
127
+ </div>
128
+
129
+ <!-- Results Tab -->
130
+ <div v-if="activeTab === 'results'" class="transition-all duration-300">
131
+ <div class="flex justify-between items-center mb-3">
132
+ <h3 class="font-medium">Extracted Items ({{filteredResults.length}})</h3>
133
+ <div class="flex space-x-1">
134
+ <button @click="exportResults('csv')" class="p-2 hover:bg-gray-200 dark:hover:bg-gray-700 rounded" title="Export to CSV">
135
+ <i class="fas fa-file-csv text-green-500"></i>
136
+ </button>
137
+ <button @click="exportResults('excel')" class="p-2 hover:bg-gray-200 dark:hover:bg-gray-700 rounded" title="Export to Excel">
138
+ <i class="fas fa-file-excel text-green-600"></i>
139
+ </button>
140
+ <button @click="exportResults('text')" class="p-2 hover:bg-gray-200 dark:hover:bg-gray-700 rounded" title="Export to Text">
141
+ <i class="fas fa-file-alt text-blue-500"></i>
142
+ </button>
143
+ <button @click="exportResults('bookmarks')" class="p-2 hover:bg-gray-200 dark:hover:bg-gray-700 rounded" title="Save as Bookmarks">
144
+ <i class="fas fa-bookmark text-yellow-500"></i>
145
+ </button>
146
+ <button @click="copyToClipboard" class="p-2 hover:bg-gray-200 dark:hover:bg-gray-700 rounded" title="Copy to Clipboard">
147
+ <i class="fas fa-copy text-purple-500"></i>
148
+ </button>
149
+ </div>
150
+ </div>
151
+
152
+ <div v-if="filteredResults.length === 0" class="text-center py-8 text-gray-500">
153
+ <i class="fas fa-inbox text-3xl mb-2"></i>
154
+ <p>No extracted content yet</p>
155
+ </div>
156
+
157
+ <div v-else class="space-y-2 max-h-96 overflow-y-auto">
158
+ <div v-for="(result, index) in filteredResults" :key="index" class="p-3 border rounded hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors">
159
+ <div class="flex justify-between items-start">
160
+ <div class="flex-1 min-w-0">
161
+ <p class="truncate font-medium">{{ result.title || 'Untitled' }}</p>
162
+ <p class="text-xs text-gray-500 dark:text-gray-400 truncate">{{ result.url }}</p>
163
+ <div v-if="result.domain" class="mt-1">
164
+ <span class="domain-badge px-2 py-0.5 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300">{{ result.domain }}</span>
165
+ </div>
166
+ </div>
167
+ <button @click="openUrl(result.url)" class="ml-2 p-1 hover:bg-gray-200 dark:hover:bg-gray-700 rounded" title="Open in new tab">
168
+ <i class="fas fa-external-link-alt text-xs"></i>
169
+ </button>
170
+ </div>
171
+ <div v-if="result.content" class="mt-2 text-sm text-gray-700 dark:text-gray-300">
172
+ <p class="line-clamp-2">{{ result.content }}</p>
173
+ </div>
174
+ </div>
175
+ </div>
176
+ </div>
177
+
178
+ <!-- Settings Tab -->
179
+ <div v-if="activeTab === 'settings'" class="transition-all duration-300">
180
+ <div class="mb-4">
181
+ <h3 class="font-medium mb-2">Keyboard Shortcuts</h3>
182
+ <div class="space-y-2">
183
+ <div v-for="(shortcut, name) in shortcuts" :key="name" class="flex items-center">
184
+ <label class="w-32 text-sm">{{ name.replace(/_/g, ' ') }}</label>
185
+ <input v-model="shortcuts[name]" type="text" class="flex-1 p-2 border rounded dark:bg-gray-800 dark:border-gray-600">
186
+ </div>
187
+ </div>
188
+ </div>
189
+
190
+ <div class="mb-4">
191
+ <h3 class="font-medium mb-2">Blacklisted Sites</h3>
192
+ <div class="flex mb-2">
193
+ <input v-model="newBlacklistItem" type="text" placeholder="example.com" class="flex-1 p-2 border rounded-l dark:bg-gray-800 dark:border-gray-600">
194
+ <button @click="addToBlacklist" class="bg-blue-500 hover:bg-blue-600 text-white px-3 rounded-r">
195
+ <i class="fas fa-plus"></i>
196
+ </button>
197
+ </div>
198
+ <div class="max-h-32 overflow-y-auto border rounded dark:border-gray-600">
199
+ <div v-for="(site, index) in blacklist" :key="index" class="flex items-center justify-between p-2 hover:bg-gray-100 dark:hover:bg-gray-800">
200
+ <span>{{ site }}</span>
201
+ <button @click="removeFromBlacklist(index)" class="text-red-500 hover:text-red-700">
202
+ <i class="fas fa-times"></i>
203
+ </button>
204
+ </div>
205
+ </div>
206
+ </div>
207
+
208
+ <div class="mb-4">
209
+ <h3 class="font-medium mb-2">Appearance</h3>
210
+ <div class="flex items-center">
211
+ <input v-model="autoTheme" type="checkbox" id="autoTheme" class="mr-2">
212
+ <label for="autoTheme">Auto light/dark mode</label>
213
+ </div>
214
+ </div>
215
+
216
+ <div class="flex justify-end">
217
+ <button @click="saveSettings" class="bg-blue-500 hover:bg-blue-600 text-white py-2 px-4 rounded">
218
+ Save Settings
219
+ </button>
220
+ </div>
221
+ </div>
222
+ </div>
223
+
224
+ <!-- Status bar -->
225
+ <div class="p-2 border-t text-xs text-gray-500 dark:text-gray-400 flex justify-between">
226
+ <span v-if="statusMessage">{{ statusMessage }}</span>
227
+ <span v-else>Ready</span>
228
+ <span v-if="isProcessing" class="text-blue-500">
229
+ <i class="fas fa-spinner fa-spin mr-1"></i> Processing...
230
+ </span>
231
+ </div>
232
+ </div>
233
+
234
+ <script>
235
+ // Vue.js-like simple reactive framework for the extension
236
+ const app = {
237
+ data() {
238
+ return {
239
+ activeTab: 'extract',
240
+ isDarkMode: false,
241
+ autoTheme: true,
242
+ scope: 'visible',
243
+ filterKeyword: '',
244
+ filterRegex: '',
245
+ removeDuplicates: true,
246
+ groupByDomain: true,
247
+ showAdvanced: false,
248
+ tabDelay: 500,
249
+ keepHighlights: true,
250
+ isProcessing: false,
251
+ statusMessage: '',
252
+ results: [],
253
+ shortcuts: {
254
+ extract_content: 'Ctrl+Shift+E',
255
+ open_results: 'Ctrl+Shift+R',
256
+ toggle_theme: 'Ctrl+Shift+T'
257
+ },
258
+ blacklist: [],
259
+ newBlacklistItem: ''
260
+ }
261
+ },
262
+ computed: {
263
+ filteredResults() {
264
+ let results = [...this.results];
265
+
266
+ // Filter by keyword if provided
267
+ if (this.filterKeyword) {
268
+ const keywords = this.filterKeyword.split(',').map(k => k.trim().toLowerCase());
269
+ results = results.filter(item => {
270
+ const content = (item.title + ' ' + item.content + ' ' + item.url).toLowerCase();
271
+ return keywords.some(k => content.includes(k));
272
+ });
273
+ }
274
+
275
+ // Filter by regex if provided
276
+ if (this.filterRegex) {
277
+ try {
278
+ const regex = new RegExp(this.filterRegex, 'i');
279
+ results = results.filter(item => {
280
+ return regex.test(item.title) || regex.test(item.content) || regex.test(item.url);
281
+ });
282
+ } catch (e) {
283
+ this.statusMessage = 'Invalid regular expression';
284
+ setTimeout(() => this.statusMessage = '', 3000);
285
+ }
286
+ }
287
+
288
+ // Remove duplicates if enabled
289
+ if (this.removeDuplicates) {
290
+ const uniqueUrls = new Set();
291
+ results = results.filter(item => {
292
+ if (uniqueUrls.has(item.url)) {
293
+ return false;
294
+ }
295
+ uniqueUrls.add(item.url);
296
+ return true;
297
+ });
298
+ }
299
+
300
+ // Group by domain if enabled
301
+ if (this.groupByDomain) {
302
+ const domainMap = {};
303
+ results.forEach(item => {
304
+ const domain = this.extractDomain(item.url);
305
+ if (!domainMap[domain]) {
306
+ domainMap[domain] = [];
307
+ }
308
+ domainMap[domain].push(item);
309
+ });
310
+
311
+ // Flatten back to array but with domain property
312
+ results = [];
313
+ for (const domain in domainMap) {
314
+ domainMap[domain].forEach(item => {
315
+ results.push({
316
+ ...item,
317
+ domain
318
+ });
319
+ });
320
+ }
321
+ }
322
+
323
+ return results;
324
+ }
325
+ },
326
+ methods: {
327
+ toggleTheme() {
328
+ this.isDarkMode = !this.isDarkMode;
329
+ if (!this.autoTheme) {
330
+ this.saveSettings();
331
+ }
332
+ },
333
+ checkSystemTheme() {
334
+ if (this.autoTheme) {
335
+ const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
336
+ this.isDarkMode = darkModeMediaQuery.matches;
337
+ }
338
+ },
339
+ extractContent() {
340
+ this.isProcessing = true;
341
+ this.statusMessage = 'Extracting content...';
342
+
343
+ // Simulate extraction (in a real extension, this would use chrome APIs)
344
+ setTimeout(() => {
345
+ // Mock data for demonstration
346
+ const mockResults = [
347
+ {
348
+ title: 'Example Page 1',
349
+ url: 'https://example.com/page1',
350
+ content: 'This is some example content from page 1 with relevant information.'
351
+ },
352
+ {
353
+ title: 'Example Page 2',
354
+ url: 'https://example.com/page2',
355
+ content: 'More example content here, this time from page 2 with different data.'
356
+ },
357
+ {
358
+ title: 'SEO Best Practices',
359
+ url: 'https://seo-example.org/best-practices',
360
+ content: 'Learn about the latest SEO techniques and how to implement them.'
361
+ },
362
+ {
363
+ title: 'Marketing Trends 2023',
364
+ url: 'https://marketing-site.com/trends',
365
+ content: 'The top marketing trends you need to know for 2023 and beyond.'
366
+ },
367
+ {
368
+ title: 'Academic Research Paper',
369
+ url: 'https://academic.edu/research/important-study',
370
+ content: 'A groundbreaking study in the field of computer science.'
371
+ }
372
+ ];
373
+
374
+ // Add domain to each result
375
+ mockResults.forEach(item => {
376
+ item.domain = this.extractDomain(item.url);
377
+ });
378
+
379
+ this.results = [...this.results, ...mockResults];
380
+ this.isProcessing = false;
381
+ this.statusMessage = `Extracted ${mockResults.length} items`;
382
+ this.activeTab = 'results';
383
+
384
+ setTimeout(() => {
385
+ this.statusMessage = '';
386
+ }, 3000);
387
+ }, 1500);
388
+ },
389
+ extractDomain(url) {
390
+ try {
391
+ const domain = new URL(url).hostname.replace('www.', '');
392
+ return domain;
393
+ } catch {
394
+ return 'unknown';
395
+ }
396
+ },
397
+ openUrl(url) {
398
+ // In a real extension: chrome.tabs.create({ url });
399
+ this.statusMessage = `Opening: ${url}`;
400
+ setTimeout(() => {
401
+ this.statusMessage = '';
402
+ }, 2000);
403
+ },
404
+ exportResults(format) {
405
+ this.statusMessage = `Exporting to ${format}...`;
406
+ setTimeout(() => {
407
+ this.statusMessage = `Exported ${this.filteredResults.length} items to ${format}`;
408
+ setTimeout(() => {
409
+ this.statusMessage = '';
410
+ }, 3000);
411
+ }, 1000);
412
+ },
413
+ copyToClipboard() {
414
+ // In a real extension: navigator.clipboard.writeText()
415
+ this.statusMessage = 'Copied to clipboard';
416
+ setTimeout(() => {
417
+ this.statusMessage = '';
418
+ }, 2000);
419
+ },
420
+ addToBlacklist() {
421
+ if (this.newBlacklistItem.trim() && !this.blacklist.includes(this.newBlacklistItem.trim())) {
422
+ this.blacklist.push(this.newBlacklistItem.trim());
423
+ this.newBlacklistItem = '';
424
+ }
425
+ },
426
+ removeFromBlacklist(index) {
427
+ this.blacklist.splice(index, 1);
428
+ },
429
+ saveSettings() {
430
+ // In a real extension: chrome.storage.sync.set()
431
+ this.statusMessage = 'Settings saved';
432
+ setTimeout(() => {
433
+ this.statusMessage = '';
434
+ }, 2000);
435
+ },
436
+ loadSettings() {
437
+ // In a real extension: chrome.storage.sync.get()
438
+ // Mock loading settings
439
+ setTimeout(() => {
440
+ this.shortcuts = {
441
+ extract_content: 'Ctrl+Shift+E',
442
+ open_results: 'Ctrl+Shift+R',
443
+ toggle_theme: 'Ctrl+Shift+T'
444
+ };
445
+ this.blacklist = ['socialmedia.com', 'news.site'];
446
+ this.autoTheme = true;
447
+ this.checkSystemTheme();
448
+ }, 500);
449
+ }
450
+ },
451
+ mounted() {
452
+ this.loadSettings();
453
+
454
+ // Listen for system theme changes
455
+ window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
456
+ this.checkSystemTheme();
457
+ });
458
+
459
+ // Check theme on startup
460
+ this.checkSystemTheme();
461
+ }
462
+ };
463
+
464
+ // Initialize the app
465
+ const { createApp } = Vue;
466
+ createApp(app).mount('#app');
467
+ </script>
468
+ <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
469
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=MikePy/web-extractor" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
470
+ </html>