doculift-cli 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- doculift/__init__.py +0 -0
- doculift/__main__.py +4 -0
- doculift/app.py +97 -0
- doculift/cli.py +177 -0
- doculift/scraper.py +442 -0
- doculift/static/css/style.css +481 -0
- doculift/static/doculift_logo.png +0 -0
- doculift/static/js/main.js +82 -0
- doculift/templates/index.html +91 -0
- doculift_cli-0.1.0.dist-info/METADATA +240 -0
- doculift_cli-0.1.0.dist-info/RECORD +14 -0
- doculift_cli-0.1.0.dist-info/WHEEL +5 -0
- doculift_cli-0.1.0.dist-info/entry_points.txt +2 -0
- doculift_cli-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--primary: #4f46e5;
|
|
3
|
+
--primary-hover: #4338ca;
|
|
4
|
+
--primary-glow: rgba(79, 70, 229, 0.25);
|
|
5
|
+
--accent: #9333ea;
|
|
6
|
+
--bg-start: #f8fafc;
|
|
7
|
+
--bg-end: #e2e8f0;
|
|
8
|
+
--card-bg: rgba(255, 255, 255, 0.85);
|
|
9
|
+
--card-border: rgba(255, 255, 255, 1);
|
|
10
|
+
--input-bg: rgba(255, 255, 255, 0.9);
|
|
11
|
+
--input-border: rgba(203, 213, 225, 0.8);
|
|
12
|
+
--text-main: #0f172a;
|
|
13
|
+
--text-muted: #475569;
|
|
14
|
+
--text-label: #64748b;
|
|
15
|
+
--glass-blur: blur(24px);
|
|
16
|
+
--shadow-card: 0 20px 40px -10px rgba(0, 0, 0, 0.08);
|
|
17
|
+
--shadow-glow: 0 0 40px -10px rgba(79, 70, 229, 0.15);
|
|
18
|
+
--radius-card: 28px;
|
|
19
|
+
--radius-input: 12px;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
* {
|
|
23
|
+
margin: 0;
|
|
24
|
+
padding: 0;
|
|
25
|
+
box-sizing: border-box;
|
|
26
|
+
font-family: 'Inter', system-ui, -apple-system, sans-serif;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
body {
|
|
30
|
+
background: radial-gradient(ellipse at 20% 20%, #e0e7ff 0%, transparent 50%),
|
|
31
|
+
radial-gradient(ellipse at 80% 80%, #f3e8ff 0%, transparent 50%),
|
|
32
|
+
linear-gradient(135deg, var(--bg-start) 0%, var(--bg-end) 100%);
|
|
33
|
+
background-attachment: fixed;
|
|
34
|
+
color: var(--text-main);
|
|
35
|
+
min-height: 100vh;
|
|
36
|
+
display: flex;
|
|
37
|
+
justify-content: center;
|
|
38
|
+
align-items: center;
|
|
39
|
+
padding: 2rem;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Ambient orbs */
|
|
43
|
+
body::before,
|
|
44
|
+
body::after {
|
|
45
|
+
content: '';
|
|
46
|
+
position: fixed;
|
|
47
|
+
border-radius: 50%;
|
|
48
|
+
filter: blur(80px);
|
|
49
|
+
pointer-events: none;
|
|
50
|
+
z-index: 0;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
body::before {
|
|
54
|
+
width: 500px;
|
|
55
|
+
height: 500px;
|
|
56
|
+
background: radial-gradient(circle, rgba(99, 102, 241, 0.15) 0%, transparent 70%);
|
|
57
|
+
top: -100px;
|
|
58
|
+
left: -100px;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
body::after {
|
|
62
|
+
width: 400px;
|
|
63
|
+
height: 400px;
|
|
64
|
+
background: radial-gradient(circle, rgba(168, 85, 247, 0.12) 0%, transparent 70%);
|
|
65
|
+
bottom: -80px;
|
|
66
|
+
right: -80px;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.container {
|
|
70
|
+
width: 100%;
|
|
71
|
+
max-width: 820px;
|
|
72
|
+
z-index: 1;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.glass-card {
|
|
76
|
+
background: var(--card-bg);
|
|
77
|
+
backdrop-filter: var(--glass-blur);
|
|
78
|
+
-webkit-backdrop-filter: var(--glass-blur);
|
|
79
|
+
border: 1px solid var(--card-border);
|
|
80
|
+
border-radius: var(--radius-card);
|
|
81
|
+
box-shadow: var(--shadow-card), var(--shadow-glow);
|
|
82
|
+
padding: 3rem;
|
|
83
|
+
animation: fadeIn 0.7s cubic-bezier(0.16, 1, 0.3, 1);
|
|
84
|
+
position: relative;
|
|
85
|
+
overflow: hidden;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* Subtle top-edge highlight */
|
|
89
|
+
.glass-card::before {
|
|
90
|
+
content: '';
|
|
91
|
+
position: absolute;
|
|
92
|
+
top: 0;
|
|
93
|
+
left: 10%;
|
|
94
|
+
right: 10%;
|
|
95
|
+
height: 1px;
|
|
96
|
+
background: linear-gradient(to right, transparent, rgba(255,255,255,0.8), transparent);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
@keyframes fadeIn {
|
|
100
|
+
from { opacity: 0; transform: translateY(24px) scale(0.98); }
|
|
101
|
+
to { opacity: 1; transform: translateY(0) scale(1); }
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* ── Header & Logo ── */
|
|
105
|
+
.app-header {
|
|
106
|
+
display: flex;
|
|
107
|
+
align-items: center;
|
|
108
|
+
justify-content: center;
|
|
109
|
+
gap: 1.5rem;
|
|
110
|
+
margin-bottom: 2.75rem;
|
|
111
|
+
text-align: left;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.app-logo {
|
|
115
|
+
width: 90px;
|
|
116
|
+
height: 90px;
|
|
117
|
+
object-fit: contain;
|
|
118
|
+
filter: drop-shadow(0 8px 16px rgba(0,0,0,0.08));
|
|
119
|
+
animation: float 6s ease-in-out infinite;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@keyframes float {
|
|
123
|
+
0% { transform: translateY(0px); }
|
|
124
|
+
50% { transform: translateY(-8px); }
|
|
125
|
+
100% { transform: translateY(0px); }
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
h1 {
|
|
129
|
+
font-size: 3.25rem;
|
|
130
|
+
font-weight: 800;
|
|
131
|
+
letter-spacing: -0.03em;
|
|
132
|
+
line-height: 1;
|
|
133
|
+
margin-bottom: 0.4rem;
|
|
134
|
+
background: linear-gradient(135deg, #1e1b4b 0%, #4f46e5 40%, #9333ea 100%);
|
|
135
|
+
-webkit-background-clip: text;
|
|
136
|
+
-webkit-text-fill-color: transparent;
|
|
137
|
+
background-clip: text;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.subtitle {
|
|
141
|
+
color: var(--text-muted);
|
|
142
|
+
font-size: 1.05rem;
|
|
143
|
+
letter-spacing: 0.01em;
|
|
144
|
+
font-weight: 500;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* ── Divider ── */
|
|
148
|
+
.section-divider {
|
|
149
|
+
height: 1px;
|
|
150
|
+
background: linear-gradient(to right, transparent, rgba(0,0,0,0.05), transparent);
|
|
151
|
+
margin: 2rem 0;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/* ── Form ── */
|
|
155
|
+
.form-group {
|
|
156
|
+
margin-bottom: 0;
|
|
157
|
+
display: flex;
|
|
158
|
+
flex-direction: column;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
label {
|
|
162
|
+
display: block;
|
|
163
|
+
margin-bottom: 0.6rem;
|
|
164
|
+
font-weight: 700;
|
|
165
|
+
color: var(--text-label);
|
|
166
|
+
font-size: 0.75rem;
|
|
167
|
+
text-transform: uppercase;
|
|
168
|
+
letter-spacing: 0.08em;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
textarea, input, select {
|
|
172
|
+
width: 100%;
|
|
173
|
+
background: var(--input-bg);
|
|
174
|
+
border: 1px solid var(--input-border);
|
|
175
|
+
border-radius: var(--radius-input);
|
|
176
|
+
padding: 0.85rem 1rem;
|
|
177
|
+
color: var(--text-main);
|
|
178
|
+
font-size: 0.95rem;
|
|
179
|
+
transition: border-color 0.2s ease, background 0.2s ease, box-shadow 0.2s ease;
|
|
180
|
+
appearance: none;
|
|
181
|
+
-webkit-appearance: none;
|
|
182
|
+
box-shadow: inset 0 2px 4px rgba(0,0,0,0.02);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
select {
|
|
186
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%2364748b' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
|
|
187
|
+
background-repeat: no-repeat;
|
|
188
|
+
background-position: right 1rem center;
|
|
189
|
+
padding-right: 2.5rem;
|
|
190
|
+
cursor: pointer;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
textarea::placeholder, input::placeholder {
|
|
194
|
+
color: #94a3b8;
|
|
195
|
+
font-size: 0.9rem;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
textarea:focus, input:focus, select:focus {
|
|
199
|
+
outline: none;
|
|
200
|
+
border-color: var(--primary);
|
|
201
|
+
background: #ffffff;
|
|
202
|
+
box-shadow: 0 0 0 4px var(--primary-glow);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/* URL textarea specifics */
|
|
206
|
+
#urls {
|
|
207
|
+
resize: vertical;
|
|
208
|
+
min-height: 110px;
|
|
209
|
+
font-family: 'SF Mono', 'Fira Code', monospace;
|
|
210
|
+
font-size: 0.875rem;
|
|
211
|
+
line-height: 1.6;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/* ── Control row ── */
|
|
215
|
+
.controls-section {
|
|
216
|
+
margin-top: 1.5rem;
|
|
217
|
+
display: grid;
|
|
218
|
+
grid-template-columns: repeat(4, 1fr);
|
|
219
|
+
gap: 1rem;
|
|
220
|
+
align-items: start;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/* ── Number input ── */
|
|
224
|
+
input[type="number"] {
|
|
225
|
+
-moz-appearance: textfield;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
input[type="number"]::-webkit-inner-spin-button,
|
|
229
|
+
input[type="number"]::-webkit-outer-spin-button {
|
|
230
|
+
-webkit-appearance: none;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/* ── Button ── */
|
|
234
|
+
.btn-primary {
|
|
235
|
+
width: 100%;
|
|
236
|
+
background: linear-gradient(135deg, var(--primary) 0%, var(--accent) 100%);
|
|
237
|
+
color: white;
|
|
238
|
+
border: none;
|
|
239
|
+
border-radius: var(--radius-input);
|
|
240
|
+
padding: 0.95rem 1rem;
|
|
241
|
+
font-size: 1.05rem;
|
|
242
|
+
font-weight: 700;
|
|
243
|
+
cursor: pointer;
|
|
244
|
+
transition: opacity 0.2s ease, transform 0.2s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.2s ease;
|
|
245
|
+
margin-top: 1.75rem;
|
|
246
|
+
letter-spacing: 0.02em;
|
|
247
|
+
box-shadow: 0 4px 15px rgba(79, 70, 229, 0.4);
|
|
248
|
+
position: relative;
|
|
249
|
+
overflow: hidden;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.btn-primary::after {
|
|
253
|
+
content: '';
|
|
254
|
+
position: absolute;
|
|
255
|
+
inset: 0;
|
|
256
|
+
background: linear-gradient(135deg, rgba(255,255,255,0.2) 0%, transparent 60%);
|
|
257
|
+
pointer-events: none;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.btn-primary:hover {
|
|
261
|
+
opacity: 0.95;
|
|
262
|
+
transform: translateY(-2px);
|
|
263
|
+
box-shadow: 0 8px 25px rgba(79, 70, 229, 0.5);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.btn-primary:active {
|
|
267
|
+
transform: translateY(0);
|
|
268
|
+
opacity: 1;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.btn-primary:disabled {
|
|
272
|
+
opacity: 0.6;
|
|
273
|
+
cursor: not-allowed;
|
|
274
|
+
transform: none;
|
|
275
|
+
background: #cbd5e1;
|
|
276
|
+
box-shadow: none;
|
|
277
|
+
color: #64748b;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/* Secondary / ghost button */
|
|
281
|
+
button:not(.btn-primary) {
|
|
282
|
+
width: 100%;
|
|
283
|
+
background: #f8fafc;
|
|
284
|
+
color: var(--text-main);
|
|
285
|
+
border: 1px solid var(--input-border);
|
|
286
|
+
border-radius: var(--radius-input);
|
|
287
|
+
padding: 0.85rem 1rem;
|
|
288
|
+
font-size: 0.95rem;
|
|
289
|
+
font-weight: 600;
|
|
290
|
+
cursor: pointer;
|
|
291
|
+
transition: all 0.2s ease;
|
|
292
|
+
margin-top: 1.2rem;
|
|
293
|
+
letter-spacing: 0.01em;
|
|
294
|
+
box-shadow: 0 2px 5px rgba(0,0,0,0.02);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
button:not(.btn-primary):hover {
|
|
298
|
+
background: #ffffff;
|
|
299
|
+
border-color: #cbd5e1;
|
|
300
|
+
box-shadow: 0 4px 10px rgba(0,0,0,0.04);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/* ── Progress / Status ── */
|
|
304
|
+
#status-container {
|
|
305
|
+
margin-top: 2rem;
|
|
306
|
+
padding-top: 2rem;
|
|
307
|
+
border-top: 1px solid rgba(0,0,0,0.05);
|
|
308
|
+
display: none;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
.progress-info label {
|
|
312
|
+
margin-bottom: 0.4rem;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
.progress-bar-bg {
|
|
316
|
+
width: 100%;
|
|
317
|
+
height: 8px;
|
|
318
|
+
background: #e2e8f0;
|
|
319
|
+
border-radius: 999px;
|
|
320
|
+
overflow: hidden;
|
|
321
|
+
margin: 0.85rem 0 0.8rem;
|
|
322
|
+
box-shadow: inset 0 1px 3px rgba(0,0,0,0.1);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
#progress-bar {
|
|
326
|
+
width: 0%;
|
|
327
|
+
height: 100%;
|
|
328
|
+
background: linear-gradient(to right, var(--primary), var(--accent));
|
|
329
|
+
border-radius: 999px;
|
|
330
|
+
transition: width 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
|
331
|
+
box-shadow: 0 0 8px var(--primary-glow);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
#current-status {
|
|
335
|
+
font-size: 0.85rem;
|
|
336
|
+
color: var(--text-muted);
|
|
337
|
+
font-family: 'SF Mono', 'Fira Code', monospace;
|
|
338
|
+
white-space: nowrap;
|
|
339
|
+
overflow: hidden;
|
|
340
|
+
text-overflow: ellipsis;
|
|
341
|
+
font-weight: 500;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/* ── Per-URL stats ── */
|
|
345
|
+
#per-url-stats {
|
|
346
|
+
margin-top: 1.5rem;
|
|
347
|
+
display: flex;
|
|
348
|
+
flex-direction: column;
|
|
349
|
+
gap: 0.5rem;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
.stat-row {
|
|
353
|
+
display: flex;
|
|
354
|
+
justify-content: space-between;
|
|
355
|
+
align-items: center;
|
|
356
|
+
padding: 0.6rem 1rem;
|
|
357
|
+
background: #f1f5f9;
|
|
358
|
+
border: 1px solid #e2e8f0;
|
|
359
|
+
border-radius: 10px;
|
|
360
|
+
gap: 1rem;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
.stat-url {
|
|
364
|
+
font-size: 0.8rem;
|
|
365
|
+
color: var(--text-muted);
|
|
366
|
+
font-family: 'SF Mono', 'Fira Code', monospace;
|
|
367
|
+
overflow: hidden;
|
|
368
|
+
text-overflow: ellipsis;
|
|
369
|
+
white-space: nowrap;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.stat-count {
|
|
373
|
+
font-size: 0.85rem;
|
|
374
|
+
font-weight: 700;
|
|
375
|
+
color: var(--primary);
|
|
376
|
+
white-space: nowrap;
|
|
377
|
+
flex-shrink: 0;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/* ── File list ── */
|
|
381
|
+
#results label {
|
|
382
|
+
margin-top: 2rem;
|
|
383
|
+
display: block;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.file-list {
|
|
387
|
+
margin-top: 0.75rem;
|
|
388
|
+
list-style: none;
|
|
389
|
+
display: flex;
|
|
390
|
+
flex-direction: column;
|
|
391
|
+
gap: 0.5rem;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.file-item {
|
|
395
|
+
display: flex;
|
|
396
|
+
justify-content: space-between;
|
|
397
|
+
align-items: center;
|
|
398
|
+
background: #ffffff;
|
|
399
|
+
padding: 0.85rem 1.1rem;
|
|
400
|
+
border-radius: 12px;
|
|
401
|
+
border: 1px solid var(--input-border);
|
|
402
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.02);
|
|
403
|
+
transition: all 0.2s ease;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
.file-item:hover {
|
|
407
|
+
border-color: #cbd5e1;
|
|
408
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
.file-item span {
|
|
412
|
+
font-size: 0.85rem;
|
|
413
|
+
color: var(--text-main);
|
|
414
|
+
font-family: 'SF Mono', 'Fira Code', monospace;
|
|
415
|
+
font-weight: 500;
|
|
416
|
+
overflow: hidden;
|
|
417
|
+
text-overflow: ellipsis;
|
|
418
|
+
white-space: nowrap;
|
|
419
|
+
margin-right: 1rem;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
.file-link {
|
|
423
|
+
color: var(--primary);
|
|
424
|
+
text-decoration: none;
|
|
425
|
+
font-weight: 600;
|
|
426
|
+
font-size: 0.82rem;
|
|
427
|
+
white-space: nowrap;
|
|
428
|
+
padding: 0.4rem 0.85rem;
|
|
429
|
+
border: 1px solid rgba(79, 70, 229, 0.2);
|
|
430
|
+
background: rgba(79, 70, 229, 0.05);
|
|
431
|
+
border-radius: 8px;
|
|
432
|
+
transition: all 0.2s ease;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
.file-link:hover {
|
|
436
|
+
background: rgba(79, 70, 229, 0.1);
|
|
437
|
+
border-color: rgba(79, 70, 229, 0.3);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/* ── Footer ── */
|
|
441
|
+
footer.app-footer {
|
|
442
|
+
margin-top: 2rem;
|
|
443
|
+
text-align: center;
|
|
444
|
+
font-size: 0.8rem;
|
|
445
|
+
color: var(--text-muted);
|
|
446
|
+
font-weight: 500;
|
|
447
|
+
letter-spacing: 0.02em;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/* ── Responsive ── */
|
|
451
|
+
@media (max-width: 640px) {
|
|
452
|
+
.glass-card {
|
|
453
|
+
padding: 2rem 1.5rem;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
.app-header {
|
|
457
|
+
flex-direction: column;
|
|
458
|
+
text-align: center;
|
|
459
|
+
gap: 0.75rem;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
.app-logo {
|
|
463
|
+
width: 70px;
|
|
464
|
+
height: 70px;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
h1 {
|
|
468
|
+
font-size: 2.25rem;
|
|
469
|
+
margin-bottom: 0.25rem;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
.controls-section {
|
|
473
|
+
grid-template-columns: repeat(2, 1fr);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
@media (max-width: 400px) {
|
|
478
|
+
.controls-section {
|
|
479
|
+
grid-template-columns: 1fr;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
document.getElementById('start-btn').addEventListener('click', async () => {
|
|
2
|
+
const urlsText = document.getElementById('urls').value;
|
|
3
|
+
const urls = urlsText.split('\n').filter(u => u.trim() !== '');
|
|
4
|
+
const format = document.getElementById('format').value;
|
|
5
|
+
const maxPages = document.getElementById('max-pages').value;
|
|
6
|
+
const scopeType = document.getElementById('scope-type').value;
|
|
7
|
+
const extractMode = document.getElementById('extract-mode').value;
|
|
8
|
+
|
|
9
|
+
if (urls.length === 0) {
|
|
10
|
+
alert('Please enter at least one URL');
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Hide form, show status
|
|
15
|
+
document.getElementById('scrape-form').style.opacity = '0.5';
|
|
16
|
+
document.getElementById('scrape-form').style.pointerEvents = 'none';
|
|
17
|
+
document.getElementById('status-container').style.display = 'block';
|
|
18
|
+
document.getElementById('start-btn').disabled = true;
|
|
19
|
+
document.getElementById('start-btn').innerText = 'Lifting...';
|
|
20
|
+
|
|
21
|
+
const response = await fetch('/scrape', {
|
|
22
|
+
method: 'POST',
|
|
23
|
+
headers: { 'Content-Type': 'application/json' },
|
|
24
|
+
body: JSON.stringify({ urls, format, max_pages: maxPages, scope_type: scopeType, extract_mode: extractMode })
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const data = await response.json();
|
|
28
|
+
if (data.job_id) {
|
|
29
|
+
pollStatus(data.job_id);
|
|
30
|
+
} else {
|
|
31
|
+
alert('Error: ' + data.error);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
async function pollStatus(jobId) {
|
|
36
|
+
const progressBar = document.getElementById('progress-bar');
|
|
37
|
+
const statusText = document.getElementById('current-status');
|
|
38
|
+
const resultsArea = document.getElementById('results');
|
|
39
|
+
const fileList = document.getElementById('file-list');
|
|
40
|
+
|
|
41
|
+
const interval = setInterval(async () => {
|
|
42
|
+
const response = await fetch(`/status/${jobId}`);
|
|
43
|
+
const data = await response.json();
|
|
44
|
+
|
|
45
|
+
progressBar.style.width = data.progress + '%';
|
|
46
|
+
statusText.innerText = data.status;
|
|
47
|
+
|
|
48
|
+
if (data.is_finished) {
|
|
49
|
+
clearInterval(interval);
|
|
50
|
+
progressBar.style.width = '100%';
|
|
51
|
+
statusText.innerText = 'Mission Accomplished';
|
|
52
|
+
|
|
53
|
+
// Show per-URL stats
|
|
54
|
+
if (data.per_url_stats && Object.keys(data.per_url_stats).length > 0) {
|
|
55
|
+
const statsEl = document.getElementById('per-url-stats');
|
|
56
|
+
statsEl.innerHTML = '';
|
|
57
|
+
const label = data.urls_extracted > 0 ? 'URLs extracted' : 'pages scraped';
|
|
58
|
+
for (const [url, count] of Object.entries(data.per_url_stats)) {
|
|
59
|
+
const row = document.createElement('div');
|
|
60
|
+
row.className = 'stat-row';
|
|
61
|
+
row.innerHTML = `<span class="stat-url">${url}</span><span class="stat-count">${count} ${label}</span>`;
|
|
62
|
+
statsEl.appendChild(row);
|
|
63
|
+
}
|
|
64
|
+
statsEl.style.display = 'block';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (data.files && data.files.length > 0) {
|
|
68
|
+
fileList.innerHTML = '';
|
|
69
|
+
data.files.forEach(file => {
|
|
70
|
+
const li = document.createElement('li');
|
|
71
|
+
li.className = 'file-item';
|
|
72
|
+
li.innerHTML = `
|
|
73
|
+
<span>${file}</span>
|
|
74
|
+
<a href="/download/${jobId}/${file}" class="file-link" download>Download</a>
|
|
75
|
+
`;
|
|
76
|
+
fileList.appendChild(li);
|
|
77
|
+
});
|
|
78
|
+
resultsArea.style.display = 'block';
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}, 1500);
|
|
82
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<title>DocuLift | Anti-Gravity Data Extraction</title>
|
|
8
|
+
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
|
9
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
10
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
11
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&display=swap" rel="stylesheet">
|
|
12
|
+
</head>
|
|
13
|
+
|
|
14
|
+
<body>
|
|
15
|
+
<div class="container">
|
|
16
|
+
<main class="glass-card">
|
|
17
|
+
<header class="app-header">
|
|
18
|
+
<img src="{{ url_for('static', filename='doculift_logo.png') }}" alt="DocuLift Logo" class="app-logo">
|
|
19
|
+
<div>
|
|
20
|
+
<h1>DocuLift</h1>
|
|
21
|
+
<p class="subtitle">Effortlessly lift documentation for AI</p>
|
|
22
|
+
</div>
|
|
23
|
+
</header>
|
|
24
|
+
|
|
25
|
+
<div id="scrape-form">
|
|
26
|
+
<div class="form-group">
|
|
27
|
+
<label for="urls">Target URLs (one per line)</label>
|
|
28
|
+
<textarea id="urls" rows="5"
|
|
29
|
+
placeholder="https://docs.docker.com/reference/ https://modelcontextprotocol.io/docs/"></textarea>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<div class="controls-section">
|
|
33
|
+
<div class="form-group">
|
|
34
|
+
<label for="extract-mode">Extract Mode</label>
|
|
35
|
+
<select id="extract-mode">
|
|
36
|
+
<option value="content">Extract Content</option>
|
|
37
|
+
<option value="urls">Extract URLs Only</option>
|
|
38
|
+
</select>
|
|
39
|
+
</div>
|
|
40
|
+
<div class="form-group">
|
|
41
|
+
<label for="scope-type">Scoping Strategy</label>
|
|
42
|
+
<select id="scope-type">
|
|
43
|
+
<option value="section">Section Only</option>
|
|
44
|
+
<option value="domain">Entire Domain</option>
|
|
45
|
+
</select>
|
|
46
|
+
</div>
|
|
47
|
+
<div class="form-group">
|
|
48
|
+
<label for="format">Output Format</label>
|
|
49
|
+
<select id="format">
|
|
50
|
+
<option value="md">Markdown (.md)</option>
|
|
51
|
+
<option value="txt">Plain Text (.txt)</option>
|
|
52
|
+
<option value="csv">Structured (.csv)</option>
|
|
53
|
+
</select>
|
|
54
|
+
</div>
|
|
55
|
+
<div class="form-group">
|
|
56
|
+
<label for="max-pages">Max Pages / URL</label>
|
|
57
|
+
<input type="number" id="max-pages" value="500" min="1" max="1000">
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<button class="btn-primary" id="start-btn">Siphon Content</button>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<section id="status-container">
|
|
65
|
+
<div class="progress-info">
|
|
66
|
+
<label>Extraction Progress</label>
|
|
67
|
+
<div class="progress-bar-bg">
|
|
68
|
+
<div id="progress-bar"></div>
|
|
69
|
+
</div>
|
|
70
|
+
<p id="current-status">Initializing...</p>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<div id="per-url-stats" style="display: none;"></div>
|
|
74
|
+
|
|
75
|
+
<div id="results" style="display: none;">
|
|
76
|
+
<label>Lifted Files</label>
|
|
77
|
+
<ul class="file-list" id="file-list"></ul>
|
|
78
|
+
<button onclick="window.location.reload()">Start New Mission</button>
|
|
79
|
+
</div>
|
|
80
|
+
</section>
|
|
81
|
+
</main>
|
|
82
|
+
|
|
83
|
+
<footer class="app-footer">
|
|
84
|
+
© 2026 DocuLift
|
|
85
|
+
</footer>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
|
|
89
|
+
</body>
|
|
90
|
+
|
|
91
|
+
</html>
|