deepspider 0.3.0 → 0.3.2
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.
- package/.env.example +3 -0
- package/README.md +13 -13
- package/package.json +6 -6
- package/src/agent/core/PanelBridge.js +29 -77
- package/src/agent/core/StreamHandler.js +139 -14
- package/src/agent/index.js +51 -12
- package/src/agent/logger.js +184 -9
- package/src/agent/middleware/report.js +42 -16
- package/src/agent/middleware/subagent.js +233 -0
- package/src/agent/middleware/toolGuard.js +77 -0
- package/src/agent/middleware/validationWorkflow.js +171 -0
- package/src/agent/prompts/system.js +181 -59
- package/src/agent/run.js +41 -6
- package/src/agent/skills/crawler/SKILL.md +64 -3
- package/src/agent/skills/crawler/evolved.md +9 -1
- package/src/agent/skills/dynamic-analysis/SKILL.md +74 -7
- package/src/agent/skills/env/SKILL.md +75 -0
- package/src/agent/skills/evolve.js +0 -3
- package/src/agent/skills/sandbox/SKILL.md +35 -0
- package/src/agent/skills/static-analysis/SKILL.md +98 -2
- package/src/agent/subagents/anti-detect.js +10 -20
- package/src/agent/subagents/captcha.js +7 -19
- package/src/agent/subagents/crawler.js +25 -37
- package/src/agent/subagents/factory.js +109 -9
- package/src/agent/subagents/index.js +4 -13
- package/src/agent/subagents/js2python.js +7 -19
- package/src/agent/subagents/reverse.js +180 -0
- package/src/agent/tools/analysis.js +84 -1
- package/src/agent/tools/anti-detect.js +5 -2
- package/src/agent/tools/browser.js +160 -0
- package/src/agent/tools/captcha.js +1 -1
- package/src/agent/tools/capture.js +24 -3
- package/src/agent/tools/correlate.js +129 -15
- package/src/agent/tools/crawler.js +2 -1
- package/src/agent/tools/crawlerGenerator.js +90 -0
- package/src/agent/tools/debug.js +43 -6
- package/src/agent/tools/evolve.js +6 -3
- package/src/agent/tools/extractor.js +5 -1
- package/src/agent/tools/file.js +16 -7
- package/src/agent/tools/generateHook.js +66 -0
- package/src/agent/tools/hookManager.js +19 -9
- package/src/agent/tools/index.js +33 -20
- package/src/agent/tools/nodejs.js +41 -6
- package/src/agent/tools/python.js +4 -4
- package/src/agent/tools/report.js +2 -2
- package/src/agent/tools/runtime.js +1 -1
- package/src/agent/tools/sandbox.js +21 -1
- package/src/agent/tools/scratchpad.js +70 -0
- package/src/agent/tools/tracing.js +26 -0
- package/src/agent/tools/verifyAlgorithm.js +117 -0
- package/src/analyzer/EncryptionAnalyzer.js +2 -2
- package/src/browser/EnvBridge.js +27 -13
- package/src/browser/client.js +124 -18
- package/src/browser/collector.js +101 -22
- package/src/browser/defaultHooks.js +3 -1
- package/src/browser/hooks/index.js +5 -0
- package/src/browser/interceptors/AntiDebugInterceptor.js +132 -0
- package/src/browser/interceptors/NetworkInterceptor.js +77 -13
- package/src/browser/interceptors/ScriptInterceptor.js +34 -9
- package/src/browser/interceptors/index.js +1 -0
- package/src/browser/ui/analysisPanel.js +469 -464
- package/src/cli/commands/config.js +11 -3
- package/src/config/paths.js +9 -1
- package/src/config/settings.js +7 -1
- package/src/core/PatchGenerator.js +26 -6
- package/src/core/Sandbox.js +140 -3
- package/src/env/EnvCodeGenerator.js +60 -88
- package/src/env/modules/bom/history.js +6 -0
- package/src/env/modules/bom/location.js +6 -0
- package/src/env/modules/bom/navigator.js +13 -0
- package/src/env/modules/bom/screen.js +6 -0
- package/src/env/modules/bom/storage.js +7 -0
- package/src/env/modules/dom/document.js +14 -0
- package/src/env/modules/dom/event.js +4 -0
- package/src/env/modules/index.js +27 -10
- package/src/env/modules/webapi/fetch.js +4 -0
- package/src/env/modules/webapi/url.js +4 -0
- package/src/env/modules/webapi/xhr.js +8 -0
- package/src/store/DataStore.js +130 -47
- package/src/store/Store.js +2 -1
- package/src/agent/subagents/dynamic.js +0 -64
- package/src/agent/subagents/env-agent.js +0 -82
- package/src/agent/subagents/sandbox.js +0 -55
- package/src/agent/subagents/static.js +0 -66
|
@@ -157,8 +157,6 @@ export function getAnalysisPanelScript() {
|
|
|
157
157
|
|
|
158
158
|
// 状态 - 从 sessionStorage 恢复消息
|
|
159
159
|
const STORAGE_KEY = 'deepspider_chat_messages';
|
|
160
|
-
const STAGES_STORAGE_KEY = 'deepspider_stages';
|
|
161
|
-
const CURRENT_STAGE_KEY = 'deepspider_current_stage';
|
|
162
160
|
const SELECTED_ELEMENTS_KEY = 'deepspider_selected_elements';
|
|
163
161
|
try {
|
|
164
162
|
const saved = sessionStorage.getItem(STORAGE_KEY);
|
|
@@ -166,22 +164,6 @@ export function getAnalysisPanelScript() {
|
|
|
166
164
|
} catch (e) {
|
|
167
165
|
deepspider.chatMessages = [];
|
|
168
166
|
}
|
|
169
|
-
// 阶段配置 - 支持多阶段爬取流程
|
|
170
|
-
try {
|
|
171
|
-
const savedStages = sessionStorage.getItem(STAGES_STORAGE_KEY);
|
|
172
|
-
deepspider.stages = savedStages ? JSON.parse(savedStages) : [
|
|
173
|
-
{ name: 'list', fields: [], entry: null, pagination: null }
|
|
174
|
-
];
|
|
175
|
-
} catch (e) {
|
|
176
|
-
deepspider.stages = [{ name: 'list', fields: [], entry: null, pagination: null }];
|
|
177
|
-
}
|
|
178
|
-
// 当前选中的阶段
|
|
179
|
-
try {
|
|
180
|
-
const savedCurrentStage = sessionStorage.getItem(CURRENT_STAGE_KEY);
|
|
181
|
-
deepspider.currentStageIndex = savedCurrentStage ? parseInt(savedCurrentStage) : 0;
|
|
182
|
-
} catch (e) {
|
|
183
|
-
deepspider.currentStageIndex = 0;
|
|
184
|
-
}
|
|
185
167
|
// 已选元素列表 - 从 sessionStorage 恢复
|
|
186
168
|
try {
|
|
187
169
|
const savedElements = sessionStorage.getItem(SELECTED_ELEMENTS_KEY);
|
|
@@ -201,16 +183,6 @@ export function getAnalysisPanelScript() {
|
|
|
201
183
|
}
|
|
202
184
|
}
|
|
203
185
|
|
|
204
|
-
// 保存阶段配置到 sessionStorage
|
|
205
|
-
function saveStages() {
|
|
206
|
-
try {
|
|
207
|
-
sessionStorage.setItem(STAGES_STORAGE_KEY, JSON.stringify(deepspider.stages));
|
|
208
|
-
sessionStorage.setItem(CURRENT_STAGE_KEY, String(deepspider.currentStageIndex));
|
|
209
|
-
} catch (e) {
|
|
210
|
-
console.warn('[DeepSpider] 保存阶段配置失败:', e);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
186
|
// 保存已选元素到 sessionStorage
|
|
215
187
|
function saveSelectedElements() {
|
|
216
188
|
try {
|
|
@@ -251,7 +223,12 @@ export function getAnalysisPanelScript() {
|
|
|
251
223
|
position: fixed;
|
|
252
224
|
top: 20px; right: 20px;
|
|
253
225
|
width: 400px;
|
|
254
|
-
|
|
226
|
+
height: 70vh;
|
|
227
|
+
min-width: 320px;
|
|
228
|
+
min-height: 300px;
|
|
229
|
+
max-width: 90vw;
|
|
230
|
+
max-height: 95vh;
|
|
231
|
+
overflow: hidden;
|
|
255
232
|
background: linear-gradient(180deg, #1e2530 0%, #161b22 100%);
|
|
256
233
|
border: 1px solid rgba(99, 179, 237, 0.2);
|
|
257
234
|
border-radius: 16px;
|
|
@@ -266,10 +243,25 @@ export function getAnalysisPanelScript() {
|
|
|
266
243
|
backdrop-filter: blur(10px);
|
|
267
244
|
}
|
|
268
245
|
#deepspider-panel.visible { display: flex; animation: deepspider-fadein 0.25s ease-out; }
|
|
246
|
+
#deepspider-panel.resizing { transition: none; }
|
|
269
247
|
#deepspider-panel.minimized { max-height: 48px; overflow: hidden; }
|
|
270
248
|
#deepspider-panel.minimized .deepspider-messages,
|
|
271
249
|
#deepspider-panel.minimized .deepspider-input,
|
|
272
|
-
#deepspider-panel.minimized .deepspider-report-btn
|
|
250
|
+
#deepspider-panel.minimized .deepspider-report-btn,
|
|
251
|
+
#deepspider-panel.minimized .deepspider-resize-handle { display: none !important; }
|
|
252
|
+
/* 边缘拖拽缩放手柄 */
|
|
253
|
+
.deepspider-resize-handle {
|
|
254
|
+
position: absolute;
|
|
255
|
+
z-index: 1;
|
|
256
|
+
}
|
|
257
|
+
.deepspider-resize-handle.top { top: -4px; left: 8px; right: 8px; height: 8px; cursor: n-resize; }
|
|
258
|
+
.deepspider-resize-handle.bottom { bottom: -4px; left: 8px; right: 8px; height: 8px; cursor: s-resize; }
|
|
259
|
+
.deepspider-resize-handle.left { left: -4px; top: 8px; bottom: 8px; width: 8px; cursor: w-resize; }
|
|
260
|
+
.deepspider-resize-handle.right { right: -4px; top: 8px; bottom: 8px; width: 8px; cursor: e-resize; }
|
|
261
|
+
.deepspider-resize-handle.top-left { top: -4px; left: -4px; width: 14px; height: 14px; cursor: nw-resize; }
|
|
262
|
+
.deepspider-resize-handle.top-right { top: -4px; right: -4px; width: 14px; height: 14px; cursor: ne-resize; }
|
|
263
|
+
.deepspider-resize-handle.bottom-left { bottom: -4px; left: -4px; width: 14px; height: 14px; cursor: sw-resize; }
|
|
264
|
+
.deepspider-resize-handle.bottom-right { bottom: -4px; right: -4px; width: 14px; height: 14px; cursor: se-resize; }
|
|
273
265
|
@keyframes deepspider-fadein {
|
|
274
266
|
from { opacity: 0; transform: translateY(-12px) scale(0.98); }
|
|
275
267
|
to { opacity: 1; transform: translateY(0) scale(1); }
|
|
@@ -337,12 +329,29 @@ export function getAnalysisPanelScript() {
|
|
|
337
329
|
font-size: 13px;
|
|
338
330
|
cursor: pointer;
|
|
339
331
|
text-align: center;
|
|
340
|
-
transition: all 0.
|
|
332
|
+
transition: all 0.25s;
|
|
341
333
|
box-shadow: 0 2px 8px rgba(72, 187, 120, 0.3);
|
|
342
334
|
}
|
|
343
335
|
.deepspider-report-btn:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(72, 187, 120, 0.4); }
|
|
344
336
|
.deepspider-report-btn:active { transform: translateY(0); }
|
|
345
337
|
.deepspider-report-btn.visible { display: block; }
|
|
338
|
+
.deepspider-report-btn.viewed {
|
|
339
|
+
margin: 6px 14px;
|
|
340
|
+
padding: 6px 12px;
|
|
341
|
+
background: rgba(72, 187, 120, 0.1);
|
|
342
|
+
border: 1px solid rgba(72, 187, 120, 0.25);
|
|
343
|
+
color: #48bb78;
|
|
344
|
+
font-size: 12px;
|
|
345
|
+
font-weight: 500;
|
|
346
|
+
box-shadow: none;
|
|
347
|
+
border-radius: 8px;
|
|
348
|
+
}
|
|
349
|
+
.deepspider-report-btn.viewed:hover {
|
|
350
|
+
background: rgba(72, 187, 120, 0.18);
|
|
351
|
+
border-color: rgba(72, 187, 120, 0.4);
|
|
352
|
+
transform: none;
|
|
353
|
+
box-shadow: none;
|
|
354
|
+
}
|
|
346
355
|
/* 报告模态框 */
|
|
347
356
|
#deepspider-report-modal {
|
|
348
357
|
display: none;
|
|
@@ -414,28 +423,32 @@ export function getAnalysisPanelScript() {
|
|
|
414
423
|
flex: 1;
|
|
415
424
|
overflow-y: auto;
|
|
416
425
|
padding: 28px 32px;
|
|
417
|
-
color: #c9d1d9;
|
|
426
|
+
color: #c9d1d9 !important;
|
|
418
427
|
font-size: 14px;
|
|
419
428
|
line-height: 1.7;
|
|
420
429
|
}
|
|
430
|
+
.deepspider-report-content * {
|
|
431
|
+
color: inherit !important;
|
|
432
|
+
font-family: inherit;
|
|
433
|
+
}
|
|
421
434
|
.deepspider-report-content::-webkit-scrollbar { width: 10px; }
|
|
422
435
|
.deepspider-report-content::-webkit-scrollbar-track { background: rgba(0,0,0,0.2); border-radius: 5px; }
|
|
423
436
|
.deepspider-report-content::-webkit-scrollbar-thumb { background: rgba(99, 179, 237, 0.3); border-radius: 5px; }
|
|
424
437
|
.deepspider-report-content::-webkit-scrollbar-thumb:hover { background: rgba(99, 179, 237, 0.5); }
|
|
425
438
|
.deepspider-report-content h1, .deepspider-report-content h2, .deepspider-report-content h3 {
|
|
426
|
-
color: #63b3ed;
|
|
439
|
+
color: #63b3ed !important;
|
|
427
440
|
margin-top: 1.8em;
|
|
428
441
|
margin-bottom: 0.6em;
|
|
429
442
|
font-weight: 600;
|
|
430
443
|
}
|
|
431
444
|
.deepspider-report-content h1 { font-size: 24px; border-bottom: 1px solid rgba(99, 179, 237, 0.2); padding-bottom: 12px; }
|
|
432
445
|
.deepspider-report-content h2 { font-size: 20px; }
|
|
433
|
-
.deepspider-report-content h3 { font-size: 16px; color: #8b949e; }
|
|
446
|
+
.deepspider-report-content h3 { font-size: 16px; color: #8b949e !important; }
|
|
434
447
|
.deepspider-report-content h1:first-child { margin-top: 0; }
|
|
435
448
|
.deepspider-report-content p { margin: 12px 0; }
|
|
436
449
|
.deepspider-report-content ul, .deepspider-report-content ol { margin: 12px 0; padding-left: 24px; }
|
|
437
450
|
.deepspider-report-content li { margin: 6px 0; }
|
|
438
|
-
.deepspider-report-content strong { color: #e6edf3; font-weight: 600; }
|
|
451
|
+
.deepspider-report-content strong { color: #e6edf3 !important; font-weight: 600; }
|
|
439
452
|
/* 代码块容器 - 支持复制 */
|
|
440
453
|
.deepspider-code-block {
|
|
441
454
|
position: relative;
|
|
@@ -487,11 +500,11 @@ export function getAnalysisPanelScript() {
|
|
|
487
500
|
background: rgba(99, 179, 237, 0.1);
|
|
488
501
|
padding: 3px 8px;
|
|
489
502
|
border-radius: 6px;
|
|
490
|
-
font-family: 'SF Mono', 'Monaco', 'Menlo', 'Consolas', monospace;
|
|
503
|
+
font-family: 'SF Mono', 'Monaco', 'Menlo', 'Consolas', monospace !important;
|
|
491
504
|
font-size: 13px;
|
|
492
|
-
color: #79c0ff;
|
|
505
|
+
color: #79c0ff !important;
|
|
493
506
|
}
|
|
494
|
-
.deepspider-report-content pre code { background: transparent; padding: 0; color: #c9d1d9; }
|
|
507
|
+
.deepspider-report-content pre code { background: transparent; padding: 0; color: #c9d1d9 !important; }
|
|
495
508
|
.deepspider-report-content table {
|
|
496
509
|
width: 100%;
|
|
497
510
|
border-collapse: collapse;
|
|
@@ -505,14 +518,16 @@ export function getAnalysisPanelScript() {
|
|
|
505
518
|
padding: 12px 16px;
|
|
506
519
|
text-align: left;
|
|
507
520
|
}
|
|
508
|
-
.deepspider-report-content th { background: rgba(99, 179, 237, 0.08); color: #63b3ed; font-weight: 600; }
|
|
521
|
+
.deepspider-report-content th { background: rgba(99, 179, 237, 0.08); color: #63b3ed !important; font-weight: 600; }
|
|
509
522
|
.deepspider-report-content tr:hover td { background: rgba(99, 179, 237, 0.03); }
|
|
523
|
+
.deepspider-report-content a { color: #79c0ff !important; text-decoration: underline; }
|
|
524
|
+
.deepspider-report-content blockquote { border-left: 3px solid rgba(99, 179, 237, 0.3); padding-left: 14px; color: #8b949e !important; margin: 12px 0; }
|
|
525
|
+
.deepspider-report-content hr { border: none; border-top: 1px solid rgba(99, 179, 237, 0.15); margin: 20px 0; }
|
|
510
526
|
.deepspider-messages {
|
|
511
527
|
flex: 1;
|
|
512
528
|
overflow-y: auto;
|
|
513
529
|
padding: 14px;
|
|
514
|
-
|
|
515
|
-
min-height: 120px;
|
|
530
|
+
min-height: 0;
|
|
516
531
|
background: rgba(0,0,0,0.15);
|
|
517
532
|
}
|
|
518
533
|
.deepspider-messages::-webkit-scrollbar { width: 6px; }
|
|
@@ -534,6 +549,7 @@ export function getAnalysisPanelScript() {
|
|
|
534
549
|
line-height: 1.6;
|
|
535
550
|
word-break: break-word;
|
|
536
551
|
animation: deepspider-msg-in 0.25s ease-out;
|
|
552
|
+
color: #c9d1d9;
|
|
537
553
|
}
|
|
538
554
|
.deepspider-msg pre {
|
|
539
555
|
background: #0d1117;
|
|
@@ -589,19 +605,57 @@ export function getAnalysisPanelScript() {
|
|
|
589
605
|
.deepspider-msg-user {
|
|
590
606
|
background: linear-gradient(135deg, #1d4ed8 0%, #2563eb 100%);
|
|
591
607
|
margin-left: 40px;
|
|
592
|
-
color: #fff;
|
|
608
|
+
color: #fff !important;
|
|
593
609
|
box-shadow: 0 2px 8px rgba(37, 99, 235, 0.3);
|
|
594
610
|
}
|
|
611
|
+
.deepspider-msg-user * { color: inherit !important; }
|
|
595
612
|
.deepspider-msg-assistant {
|
|
596
613
|
background: rgba(99, 179, 237, 0.08);
|
|
597
614
|
margin-right: 40px;
|
|
598
615
|
border: 1px solid rgba(99, 179, 237, 0.15);
|
|
616
|
+
color: #c9d1d9 !important;
|
|
617
|
+
}
|
|
618
|
+
.deepspider-msg-assistant * { color: inherit !important; }
|
|
619
|
+
.deepspider-msg-assistant code { color: #79c0ff !important; }
|
|
620
|
+
.deepspider-msg-assistant pre code { color: #c9d1d9 !important; }
|
|
621
|
+
.deepspider-msg-assistant a { color: #79c0ff !important; }
|
|
622
|
+
/* 选项卡片 */
|
|
623
|
+
.deepspider-choices { margin-top: 10px; }
|
|
624
|
+
.deepspider-choices-question { margin-bottom: 10px; font-size: 13px; color: #c9d1d9; }
|
|
625
|
+
.deepspider-choices-grid { display: flex; flex-direction: column; gap: 8px; }
|
|
626
|
+
.deepspider-choice-btn {
|
|
627
|
+
padding: 10px 14px;
|
|
628
|
+
background: rgba(99, 179, 237, 0.08);
|
|
629
|
+
border: 1px solid rgba(99, 179, 237, 0.2);
|
|
630
|
+
border-radius: 10px;
|
|
631
|
+
color: #c9d1d9;
|
|
632
|
+
cursor: pointer;
|
|
633
|
+
text-align: left;
|
|
634
|
+
transition: all 0.2s;
|
|
635
|
+
font-size: 13px;
|
|
599
636
|
}
|
|
600
|
-
.deepspider-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
637
|
+
.deepspider-choice-btn:hover {
|
|
638
|
+
background: rgba(99, 179, 237, 0.15);
|
|
639
|
+
border-color: rgba(99, 179, 237, 0.4);
|
|
640
|
+
}
|
|
641
|
+
.deepspider-choice-btn.selected {
|
|
642
|
+
background: rgba(99, 179, 237, 0.2);
|
|
643
|
+
border-color: #63b3ed;
|
|
644
|
+
color: #63b3ed;
|
|
645
|
+
}
|
|
646
|
+
.deepspider-choice-label { font-weight: 500; }
|
|
647
|
+
.deepspider-choice-desc { font-size: 11px; color: #8b949e; margin-top: 4px; }
|
|
648
|
+
/* 确认按钮组 */
|
|
649
|
+
.deepspider-confirm-btns { display: flex; gap: 8px; margin-top: 10px; }
|
|
650
|
+
.deepspider-confirm-btn {
|
|
651
|
+
flex: 1; padding: 10px; border-radius: 8px; font-size: 13px;
|
|
652
|
+
font-weight: 500; cursor: pointer; transition: all 0.2s; border: none;
|
|
653
|
+
}
|
|
654
|
+
.deepspider-confirm-yes {
|
|
655
|
+
background: linear-gradient(135deg, #48bb78, #38a169); color: #fff;
|
|
656
|
+
}
|
|
657
|
+
.deepspider-confirm-no {
|
|
658
|
+
background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.15); color: #8b949e;
|
|
605
659
|
}
|
|
606
660
|
.deepspider-msg-system {
|
|
607
661
|
background: transparent;
|
|
@@ -616,7 +670,6 @@ export function getAnalysisPanelScript() {
|
|
|
616
670
|
display: flex;
|
|
617
671
|
gap: 10px;
|
|
618
672
|
background: rgba(0,0,0,0.2);
|
|
619
|
-
border-radius: 0 0 16px 16px;
|
|
620
673
|
}
|
|
621
674
|
.deepspider-input textarea {
|
|
622
675
|
flex: 1;
|
|
@@ -630,6 +683,9 @@ export function getAnalysisPanelScript() {
|
|
|
630
683
|
font-family: inherit;
|
|
631
684
|
transition: all 0.2s;
|
|
632
685
|
outline: none;
|
|
686
|
+
min-height: 40px;
|
|
687
|
+
max-height: 110px;
|
|
688
|
+
overflow-y: auto;
|
|
633
689
|
}
|
|
634
690
|
.deepspider-input textarea:focus {
|
|
635
691
|
border-color: rgba(99, 179, 237, 0.5);
|
|
@@ -653,6 +709,16 @@ export function getAnalysisPanelScript() {
|
|
|
653
709
|
.deepspider-input button:active:not(:disabled) { transform: translateY(0); }
|
|
654
710
|
.deepspider-input button:disabled { background: rgba(255,255,255,0.1); color: #6e7681; cursor: not-allowed; box-shadow: none; }
|
|
655
711
|
/* 已选元素标签区域 */
|
|
712
|
+
.deepspider-bottom-section {
|
|
713
|
+
flex-shrink: 0;
|
|
714
|
+
max-height: 50%;
|
|
715
|
+
overflow-y: auto;
|
|
716
|
+
display: flex;
|
|
717
|
+
flex-direction: column;
|
|
718
|
+
}
|
|
719
|
+
.deepspider-bottom-section::-webkit-scrollbar { width: 4px; }
|
|
720
|
+
.deepspider-bottom-section::-webkit-scrollbar-track { background: transparent; }
|
|
721
|
+
.deepspider-bottom-section::-webkit-scrollbar-thumb { background: rgba(99, 179, 237, 0.2); border-radius: 2px; }
|
|
656
722
|
.deepspider-selected-tags {
|
|
657
723
|
padding: 10px 14px;
|
|
658
724
|
border-bottom: 1px solid rgba(99, 179, 237, 0.15);
|
|
@@ -718,44 +784,49 @@ export function getAnalysisPanelScript() {
|
|
|
718
784
|
/* 功能按钮行 */
|
|
719
785
|
.deepspider-action-buttons {
|
|
720
786
|
display: flex;
|
|
787
|
+
flex-direction: column;
|
|
721
788
|
gap: 8px;
|
|
722
789
|
padding: 10px 14px;
|
|
723
790
|
border-top: 1px solid rgba(99, 179, 237, 0.1);
|
|
724
791
|
background: rgba(0,0,0,0.1);
|
|
792
|
+
flex-shrink: 0;
|
|
725
793
|
}
|
|
726
|
-
.deepspider-
|
|
727
|
-
|
|
728
|
-
|
|
794
|
+
.deepspider-quick-actions {
|
|
795
|
+
display: none;
|
|
796
|
+
flex-direction: column;
|
|
797
|
+
gap: 6px;
|
|
798
|
+
width: 100%;
|
|
799
|
+
}
|
|
800
|
+
.deepspider-quick-actions.visible { display: flex; }
|
|
801
|
+
.deepspider-quick-btn {
|
|
802
|
+
width: 100%;
|
|
803
|
+
padding: 9px 14px;
|
|
804
|
+
background: rgba(255,255,255,0.03);
|
|
805
|
+
border: 1px solid rgba(255,255,255,0.1);
|
|
729
806
|
border-radius: 8px;
|
|
807
|
+
color: #c9d1d9;
|
|
730
808
|
font-size: 12px;
|
|
731
809
|
font-weight: 500;
|
|
732
810
|
cursor: pointer;
|
|
811
|
+
text-align: left;
|
|
733
812
|
transition: all 0.2s;
|
|
734
|
-
display: flex;
|
|
735
|
-
align-items: center;
|
|
736
|
-
justify-content: center;
|
|
737
|
-
gap: 6px;
|
|
738
813
|
}
|
|
739
|
-
.deepspider-btn
|
|
740
|
-
background:
|
|
741
|
-
border:
|
|
742
|
-
color: #
|
|
743
|
-
box-shadow: 0 2px 8px rgba(128, 90, 213, 0.3);
|
|
744
|
-
}
|
|
745
|
-
.deepspider-btn-analyze:hover:not(:disabled) {
|
|
746
|
-
transform: translateY(-1px);
|
|
747
|
-
box-shadow: 0 4px 12px rgba(128, 90, 213, 0.4);
|
|
748
|
-
}
|
|
749
|
-
.deepspider-btn-analyze:disabled {
|
|
750
|
-
background: rgba(128, 90, 213, 0.3);
|
|
751
|
-
color: rgba(255,255,255,0.5);
|
|
752
|
-
cursor: not-allowed;
|
|
753
|
-
box-shadow: none;
|
|
814
|
+
.deepspider-quick-btn:hover {
|
|
815
|
+
background: rgba(99, 179, 237, 0.1);
|
|
816
|
+
border-color: rgba(99, 179, 237, 0.3);
|
|
817
|
+
color: #63b3ed;
|
|
754
818
|
}
|
|
755
819
|
.deepspider-btn-send-msg {
|
|
820
|
+
width: 100%;
|
|
821
|
+
padding: 10px 14px;
|
|
822
|
+
border-radius: 8px;
|
|
823
|
+
font-size: 12px;
|
|
824
|
+
font-weight: 500;
|
|
756
825
|
background: linear-gradient(135deg, #63b3ed 0%, #4299e1 100%);
|
|
757
826
|
border: none;
|
|
758
827
|
color: #fff;
|
|
828
|
+
cursor: pointer;
|
|
829
|
+
transition: all 0.2s;
|
|
759
830
|
box-shadow: 0 2px 8px rgba(99, 179, 237, 0.3);
|
|
760
831
|
}
|
|
761
832
|
.deepspider-btn-send-msg:hover:not(:disabled) {
|
|
@@ -768,6 +839,52 @@ export function getAnalysisPanelScript() {
|
|
|
768
839
|
cursor: not-allowed;
|
|
769
840
|
box-shadow: none;
|
|
770
841
|
}
|
|
842
|
+
.deepspider-select-banner {
|
|
843
|
+
display: none;
|
|
844
|
+
padding: 8px 14px;
|
|
845
|
+
background: linear-gradient(135deg, rgba(99,179,237,0.15) 0%, rgba(99,179,237,0.08) 100%);
|
|
846
|
+
border-bottom: 1px solid rgba(99,179,237,0.2);
|
|
847
|
+
font-size: 12px;
|
|
848
|
+
color: #63b3ed;
|
|
849
|
+
align-items: center;
|
|
850
|
+
justify-content: space-between;
|
|
851
|
+
animation: deepspider-fadein 0.2s ease-out;
|
|
852
|
+
}
|
|
853
|
+
.deepspider-select-banner.visible { display: flex; }
|
|
854
|
+
.deepspider-select-banner button {
|
|
855
|
+
background: rgba(255,255,255,0.1);
|
|
856
|
+
border: 1px solid rgba(99,179,237,0.3);
|
|
857
|
+
color: #63b3ed;
|
|
858
|
+
padding: 4px 10px;
|
|
859
|
+
border-radius: 6px;
|
|
860
|
+
font-size: 11px;
|
|
861
|
+
cursor: pointer;
|
|
862
|
+
}
|
|
863
|
+
#deepspider-reopen-btn {
|
|
864
|
+
display: none;
|
|
865
|
+
position: fixed;
|
|
866
|
+
bottom: 24px;
|
|
867
|
+
right: 24px;
|
|
868
|
+
width: 44px;
|
|
869
|
+
height: 44px;
|
|
870
|
+
background: linear-gradient(135deg, #1e2530 0%, #161b22 100%);
|
|
871
|
+
border: 1px solid rgba(99, 179, 237, 0.3);
|
|
872
|
+
border-radius: 50%;
|
|
873
|
+
color: #63b3ed;
|
|
874
|
+
font-size: 20px;
|
|
875
|
+
cursor: pointer;
|
|
876
|
+
z-index: 2147483640;
|
|
877
|
+
align-items: center;
|
|
878
|
+
justify-content: center;
|
|
879
|
+
box-shadow: 0 4px 16px rgba(0,0,0,0.4);
|
|
880
|
+
transition: all 0.2s;
|
|
881
|
+
}
|
|
882
|
+
#deepspider-reopen-btn:hover {
|
|
883
|
+
transform: scale(1.1);
|
|
884
|
+
box-shadow: 0 6px 24px rgba(99,179,237,0.3);
|
|
885
|
+
border-color: rgba(99,179,237,0.5);
|
|
886
|
+
}
|
|
887
|
+
#deepspider-reopen-btn.visible { display: flex; }
|
|
771
888
|
#deepspider-overlay {
|
|
772
889
|
position: fixed;
|
|
773
890
|
pointer-events: none;
|
|
@@ -793,7 +910,6 @@ export function getAnalysisPanelScript() {
|
|
|
793
910
|
box-shadow: 0 4px 12px rgba(0,0,0,0.4);
|
|
794
911
|
border: 1px solid rgba(99, 179, 237, 0.2);
|
|
795
912
|
}
|
|
796
|
-
#deepspider-config-modal.visible { display: flex !important; }
|
|
797
913
|
\`;
|
|
798
914
|
document.head.appendChild(style);
|
|
799
915
|
|
|
@@ -812,23 +928,37 @@ export function getAnalysisPanelScript() {
|
|
|
812
928
|
<button id="deepspider-btn-close" title="关闭">×</button>
|
|
813
929
|
</div>
|
|
814
930
|
</div>
|
|
815
|
-
<
|
|
816
|
-
|
|
817
|
-
<
|
|
818
|
-
<div class="deepspider-empty-icon">🔍</div>
|
|
819
|
-
点击上方 ⦿ 按钮选择页面元素<br>或在下方输入问题开始分析
|
|
820
|
-
</div>
|
|
931
|
+
<div class="deepspider-select-banner" id="deepspider-select-banner">
|
|
932
|
+
<span>选择模式 · 点击选择元素 · ESC 退出</span>
|
|
933
|
+
<button id="deepspider-exit-select">退出选择</button>
|
|
821
934
|
</div>
|
|
935
|
+
<button id="deepspider-report-btn" class="deepspider-report-btn">📊 查看分析报告</button>
|
|
936
|
+
<div class="deepspider-messages" id="deepspider-messages"></div>
|
|
937
|
+
<div class="deepspider-bottom-section">
|
|
822
938
|
<div class="deepspider-selected-tags" id="deepspider-selected-tags">
|
|
823
939
|
<div class="deepspider-selected-tags-list" id="deepspider-selected-tags-list"></div>
|
|
824
940
|
</div>
|
|
825
941
|
<div class="deepspider-input">
|
|
826
|
-
<textarea id="deepspider-chat-input" placeholder="输入问题,按 Enter 发送..." rows="
|
|
942
|
+
<textarea id="deepspider-chat-input" placeholder="输入问题,按 Enter 发送..." rows="1"></textarea>
|
|
827
943
|
</div>
|
|
828
944
|
<div class="deepspider-action-buttons" id="deepspider-action-buttons">
|
|
829
|
-
<
|
|
945
|
+
<div class="deepspider-quick-actions" id="deepspider-quick-actions">
|
|
946
|
+
<button class="deepspider-quick-btn" data-action="trace">🔍 追踪数据来源</button>
|
|
947
|
+
<button class="deepspider-quick-btn" data-action="decrypt">🔓 分析加密参数</button>
|
|
948
|
+
<button class="deepspider-quick-btn" data-action="full">🚀 完整分析并生成爬虫</button>
|
|
949
|
+
<button class="deepspider-quick-btn" data-action="extract">📋 提取页面结构</button>
|
|
950
|
+
</div>
|
|
830
951
|
<button class="deepspider-btn-send-msg" id="deepspider-btn-send-msg" disabled>发送</button>
|
|
831
952
|
</div>
|
|
953
|
+
</div>
|
|
954
|
+
<div class="deepspider-resize-handle top"></div>
|
|
955
|
+
<div class="deepspider-resize-handle bottom"></div>
|
|
956
|
+
<div class="deepspider-resize-handle left"></div>
|
|
957
|
+
<div class="deepspider-resize-handle right"></div>
|
|
958
|
+
<div class="deepspider-resize-handle top-left"></div>
|
|
959
|
+
<div class="deepspider-resize-handle top-right"></div>
|
|
960
|
+
<div class="deepspider-resize-handle bottom-left"></div>
|
|
961
|
+
<div class="deepspider-resize-handle bottom-right"></div>
|
|
832
962
|
\`;
|
|
833
963
|
document.body.appendChild(panel);
|
|
834
964
|
|
|
@@ -846,306 +976,26 @@ export function getAnalysisPanelScript() {
|
|
|
846
976
|
\`;
|
|
847
977
|
document.body.appendChild(reportModal);
|
|
848
978
|
|
|
849
|
-
// ==========
|
|
850
|
-
const
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
<div style="width:400px;background:linear-gradient(180deg,#1e2530,#161b22);border:1px solid rgba(99,179,237,0.2);border-radius:16px;overflow:hidden;">
|
|
855
|
-
<div style="padding:16px 20px;background:linear-gradient(180deg,rgba(99,179,237,0.08),transparent);border-bottom:1px solid rgba(99,179,237,0.15);display:flex;justify-content:space-between;align-items:center;">
|
|
856
|
-
<h4 style="margin:0;color:#63b3ed;font-size:15px;">⚙️ 配置爬虫</h4>
|
|
857
|
-
<button id="deepspider-config-close" style="background:none;border:none;color:#8b949e;font-size:20px;cursor:pointer;">×</button>
|
|
858
|
-
</div>
|
|
859
|
-
<div style="padding:16px 20px;">
|
|
860
|
-
<div style="margin-bottom:16px;">
|
|
861
|
-
<label style="display:block;color:#8b949e;font-size:12px;margin-bottom:6px;">抓取方式</label>
|
|
862
|
-
<select id="deepspider-grab-method" style="width:100%;padding:8px 12px;background:rgba(255,255,255,0.05);border:1px solid rgba(255,255,255,0.1);border-radius:6px;color:#c9d1d9;font-size:13px;">
|
|
863
|
-
<option value="browser">浏览器渲染 (browser)</option>
|
|
864
|
-
<option value="html">静态HTML (html)</option>
|
|
865
|
-
<option value="api">API请求 (api)</option>
|
|
866
|
-
</select>
|
|
867
|
-
</div>
|
|
868
|
-
<div style="margin-bottom:16px;">
|
|
869
|
-
<label style="display:block;color:#8b949e;font-size:12px;margin-bottom:6px;">最大页数</label>
|
|
870
|
-
<input type="number" id="deepspider-max-page" value="10" style="width:100%;padding:8px 12px;background:rgba(255,255,255,0.05);border:1px solid rgba(255,255,255,0.1);border-radius:6px;color:#c9d1d9;font-size:13px;box-sizing:border-box;">
|
|
871
|
-
</div>
|
|
872
|
-
<div style="margin-bottom:16px;">
|
|
873
|
-
<label style="display:block;color:#8b949e;font-size:12px;margin-bottom:6px;">下一页按钮 XPath(可选)</label>
|
|
874
|
-
<input type="text" id="deepspider-next-xpath" placeholder="例如: //a[contains(text(),'下一页')]" style="width:100%;padding:8px 12px;background:rgba(255,255,255,0.05);border:1px solid rgba(255,255,255,0.1);border-radius:6px;color:#c9d1d9;font-size:13px;box-sizing:border-box;">
|
|
875
|
-
</div>
|
|
876
|
-
<button id="deepspider-config-submit" style="width:100%;padding:12px;background:linear-gradient(135deg,#48bb78,#38a169);border:none;border-radius:8px;color:#fff;font-size:13px;font-weight:600;cursor:pointer;">生成爬虫</button>
|
|
877
|
-
</div>
|
|
878
|
-
</div>
|
|
879
|
-
\`;
|
|
880
|
-
document.body.appendChild(configModal);
|
|
979
|
+
// ========== 创建重新打开按钮 ==========
|
|
980
|
+
const reopenBtn = document.createElement('div');
|
|
981
|
+
reopenBtn.id = 'deepspider-reopen-btn';
|
|
982
|
+
reopenBtn.textContent = '🔍';
|
|
983
|
+
document.body.appendChild(reopenBtn);
|
|
881
984
|
|
|
882
|
-
|
|
883
|
-
document.getElementById('deepspider-config-close').onclick = () => {
|
|
884
|
-
configModal.classList.remove('visible');
|
|
885
|
-
};
|
|
886
|
-
configModal.addEventListener('click', (e) => {
|
|
887
|
-
if (e.target === configModal) configModal.classList.remove('visible');
|
|
888
|
-
});
|
|
889
|
-
document.getElementById('deepspider-config-submit').onclick = submitConfig;
|
|
890
|
-
|
|
891
|
-
// 创建空阶段对象
|
|
892
|
-
function createStage(name) {
|
|
893
|
-
return { name: name, fields: [], entry: null, pagination: null };
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
// 更新阶段面板显示
|
|
897
|
-
function updateStagesPanel() {
|
|
898
|
-
let stagesPanel = document.getElementById('deepspider-stages-panel');
|
|
899
|
-
if (!stagesPanel) {
|
|
900
|
-
stagesPanel = document.createElement('div');
|
|
901
|
-
stagesPanel.id = 'deepspider-stages-panel';
|
|
902
|
-
stagesPanel.style.cssText = 'padding:10px 14px;border-top:1px solid rgba(99,179,237,0.15);background:rgba(0,0,0,0.1);';
|
|
903
|
-
const inputArea = panel.querySelector('.deepspider-input');
|
|
904
|
-
panel.insertBefore(stagesPanel, inputArea);
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
const totalFields = deepspider.stages.reduce((sum, s) => sum + s.fields.length, 0);
|
|
908
|
-
if (totalFields === 0 && !deepspider.stages.some(s => s.entry)) {
|
|
909
|
-
stagesPanel.style.display = 'none';
|
|
910
|
-
return;
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
stagesPanel.style.display = 'block';
|
|
914
|
-
const currentStage = deepspider.stages[deepspider.currentStageIndex];
|
|
915
|
-
|
|
916
|
-
// 阶段标签
|
|
917
|
-
const stageTabs = deepspider.stages.map((s, i) => {
|
|
918
|
-
const isActive = i === deepspider.currentStageIndex;
|
|
919
|
-
const fieldCount = s.fields.length;
|
|
920
|
-
const hasEntry = s.entry ? ' →' : '';
|
|
921
|
-
return '<span data-stage="' + i + '" style="' +
|
|
922
|
-
'background:' + (isActive ? 'rgba(99,179,237,0.3)' : 'rgba(99,179,237,0.1)') + ';' +
|
|
923
|
-
'border:1px solid ' + (isActive ? 'rgba(99,179,237,0.5)' : 'rgba(99,179,237,0.2)') + ';' +
|
|
924
|
-
'padding:4px 10px;border-radius:6px;font-size:11px;color:#63b3ed;cursor:pointer;' +
|
|
925
|
-
'display:inline-flex;align-items:center;gap:4px;">' +
|
|
926
|
-
s.name + ' (' + fieldCount + ')' + hasEntry + '</span>';
|
|
927
|
-
}).join('');
|
|
928
|
-
|
|
929
|
-
// 当前阶段的字段
|
|
930
|
-
const fieldTags = currentStage.fields.map((f, i) =>
|
|
931
|
-
'<span style="background:rgba(72,187,120,0.15);border:1px solid rgba(72,187,120,0.2);' +
|
|
932
|
-
'padding:4px 8px;border-radius:6px;font-size:11px;color:#48bb78;' +
|
|
933
|
-
'display:inline-flex;align-items:center;gap:4px;">' +
|
|
934
|
-
f.name + '<span style="cursor:pointer;color:#8b949e;" data-remove="' + i + '">×</span></span>'
|
|
935
|
-
).join('');
|
|
936
|
-
|
|
937
|
-
// 入口显示
|
|
938
|
-
const entryTag = currentStage.entry ?
|
|
939
|
-
'<span style="background:rgba(237,137,54,0.15);border:1px solid rgba(237,137,54,0.2);' +
|
|
940
|
-
'padding:4px 8px;border-radius:6px;font-size:11px;color:#ed8936;' +
|
|
941
|
-
'display:inline-flex;align-items:center;gap:4px;">' +
|
|
942
|
-
currentStage.entry.field + ' → ' + currentStage.entry.to_stage +
|
|
943
|
-
'<span style="cursor:pointer;color:#8b949e;" data-remove-entry="1">×</span></span>' : '';
|
|
944
|
-
|
|
945
|
-
stagesPanel.innerHTML =
|
|
946
|
-
'<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;">' +
|
|
947
|
-
'<div style="display:flex;gap:6px;flex-wrap:wrap;">' + stageTabs +
|
|
948
|
-
'<span id="deepspider-add-stage" style="background:rgba(255,255,255,0.05);border:1px dashed rgba(255,255,255,0.2);' +
|
|
949
|
-
'padding:4px 10px;border-radius:6px;font-size:11px;color:#8b949e;cursor:pointer;">+ 阶段</span></div>' +
|
|
950
|
-
'<div style="display:flex;gap:6px;">' +
|
|
951
|
-
'<button id="deepspider-gen-config" style="background:linear-gradient(135deg,#48bb78,#38a169);' +
|
|
952
|
-
'border:none;color:#fff;padding:4px 10px;border-radius:6px;font-size:11px;cursor:pointer;">生成配置</button>' +
|
|
953
|
-
'<button id="deepspider-clear-all" style="background:rgba(248,81,73,0.2);border:1px solid rgba(248,81,73,0.3);' +
|
|
954
|
-
'color:#f85149;padding:4px 10px;border-radius:6px;font-size:11px;cursor:pointer;">清空</button>' +
|
|
955
|
-
'</div></div>' +
|
|
956
|
-
'<div style="margin-bottom:6px;font-size:11px;color:#8b949e;">阶段: ' + currentStage.name + '</div>' +
|
|
957
|
-
'<div style="display:flex;flex-wrap:wrap;gap:6px;">' + fieldTags + entryTag + '</div>';
|
|
958
|
-
|
|
959
|
-
bindStagesPanelEvents(stagesPanel);
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
// 绑定阶段面板事件
|
|
963
|
-
function bindStagesPanelEvents(stagesPanel) {
|
|
964
|
-
// 阶段切换
|
|
965
|
-
stagesPanel.querySelectorAll('[data-stage]').forEach(tab => {
|
|
966
|
-
tab.onclick = () => {
|
|
967
|
-
const idx = parseInt(tab.dataset.stage);
|
|
968
|
-
if (idx >= 0 && idx < deepspider.stages.length) {
|
|
969
|
-
deepspider.currentStageIndex = idx;
|
|
970
|
-
saveStages();
|
|
971
|
-
updateStagesPanel();
|
|
972
|
-
}
|
|
973
|
-
};
|
|
974
|
-
});
|
|
975
|
-
// 添加阶段
|
|
976
|
-
document.getElementById('deepspider-add-stage').onclick = addStage;
|
|
977
|
-
// 生成配置
|
|
978
|
-
document.getElementById('deepspider-gen-config').onclick = generateConfig;
|
|
979
|
-
// 清空
|
|
980
|
-
document.getElementById('deepspider-clear-all').onclick = clearAll;
|
|
981
|
-
// 移除字段
|
|
982
|
-
stagesPanel.querySelectorAll('[data-remove]').forEach(btn => {
|
|
983
|
-
btn.onclick = () => removeField(parseInt(btn.dataset.remove));
|
|
984
|
-
});
|
|
985
|
-
// 移除入口
|
|
986
|
-
stagesPanel.querySelectorAll('[data-remove-entry]').forEach(btn => {
|
|
987
|
-
btn.onclick = removeEntry;
|
|
988
|
-
});
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
// 移除当前阶段的字段
|
|
992
|
-
function removeField(index) {
|
|
993
|
-
const currentStage = deepspider.stages[deepspider.currentStageIndex];
|
|
994
|
-
if (!currentStage || index < 0 || index >= currentStage.fields.length) return;
|
|
995
|
-
currentStage.fields.splice(index, 1);
|
|
996
|
-
saveStages();
|
|
997
|
-
updateStagesPanel();
|
|
998
|
-
}
|
|
999
|
-
|
|
1000
|
-
// 移除当前阶段的入口
|
|
1001
|
-
function removeEntry() {
|
|
1002
|
-
const currentStage = deepspider.stages[deepspider.currentStageIndex];
|
|
1003
|
-
if (!currentStage) return;
|
|
1004
|
-
currentStage.entry = null;
|
|
1005
|
-
saveStages();
|
|
1006
|
-
updateStagesPanel();
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
// 添加新阶段
|
|
1010
|
-
function addStage() {
|
|
1011
|
-
const name = 'stage_' + (deepspider.stages.length + 1);
|
|
1012
|
-
deepspider.stages.push(createStage(name));
|
|
1013
|
-
deepspider.currentStageIndex = deepspider.stages.length - 1;
|
|
1014
|
-
saveStages();
|
|
1015
|
-
updateStagesPanel();
|
|
1016
|
-
addMessage('system', '✅ 已添加阶段: ' + name);
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
|
-
// 清空所有阶段
|
|
1020
|
-
function clearAll() {
|
|
1021
|
-
deepspider.stages = [createStage('list')];
|
|
1022
|
-
deepspider.currentStageIndex = 0;
|
|
1023
|
-
saveStages();
|
|
1024
|
-
updateStagesPanel();
|
|
1025
|
-
}
|
|
1026
|
-
|
|
1027
|
-
// 生成爬虫配置 - 显示配置弹窗
|
|
1028
|
-
function generateConfig() {
|
|
1029
|
-
showConfigModal();
|
|
1030
|
-
}
|
|
1031
|
-
|
|
1032
|
-
// 显示配置弹窗
|
|
1033
|
-
function showConfigModal() {
|
|
1034
|
-
const modal = document.getElementById('deepspider-config-modal');
|
|
1035
|
-
if (!modal) return;
|
|
1036
|
-
|
|
1037
|
-
// 更新阶段分页配置区域
|
|
1038
|
-
const stagesConfigHtml = deepspider.stages.map((stage, i) => {
|
|
1039
|
-
const hasPagination = stage.pagination !== null;
|
|
1040
|
-
return '<div style="margin-bottom:12px;padding:10px;background:rgba(0,0,0,0.2);border-radius:8px;">' +
|
|
1041
|
-
'<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;">' +
|
|
1042
|
-
'<span style="color:#63b3ed;font-size:12px;font-weight:600;">' + stage.name + '</span>' +
|
|
1043
|
-
'<span style="color:#8b949e;font-size:11px;">' + stage.fields.length + ' 字段' +
|
|
1044
|
-
(stage.entry ? ' → ' + stage.entry.to_stage : '') + '</span></div>' +
|
|
1045
|
-
'<div style="display:flex;gap:8px;align-items:center;">' +
|
|
1046
|
-
'<label style="color:#8b949e;font-size:11px;white-space:nowrap;">分页:</label>' +
|
|
1047
|
-
'<input type="checkbox" data-stage-pagination="' + i + '"' + (hasPagination ? ' checked' : '') + '>' +
|
|
1048
|
-
'<input type="text" data-stage-xpath="' + i + '" placeholder="下一页XPath" ' +
|
|
1049
|
-
'value="' + (stage.pagination?.next_page_xpath || '') + '" ' +
|
|
1050
|
-
'style="flex:1;padding:4px 8px;background:rgba(255,255,255,0.05);border:1px solid rgba(255,255,255,0.1);' +
|
|
1051
|
-
'border-radius:4px;color:#c9d1d9;font-size:11px;' + (hasPagination ? '' : 'opacity:0.5;') + '">' +
|
|
1052
|
-
'<input type="number" data-stage-max="' + i + '" placeholder="页数" ' +
|
|
1053
|
-
'value="' + (stage.pagination?.max_page || 10) + '" ' +
|
|
1054
|
-
'style="width:50px;padding:4px 8px;background:rgba(255,255,255,0.05);border:1px solid rgba(255,255,255,0.1);' +
|
|
1055
|
-
'border-radius:4px;color:#c9d1d9;font-size:11px;' + (hasPagination ? '' : 'opacity:0.5;') + '">' +
|
|
1056
|
-
'</div></div>';
|
|
1057
|
-
}).join('');
|
|
1058
|
-
|
|
1059
|
-
const configContent = modal.querySelector('div > div:last-child');
|
|
1060
|
-
configContent.innerHTML =
|
|
1061
|
-
'<div style="margin-bottom:16px;">' +
|
|
1062
|
-
'<label style="display:block;color:#8b949e;font-size:12px;margin-bottom:6px;">抓取方式</label>' +
|
|
1063
|
-
'<select id="deepspider-grab-method" style="width:100%;padding:8px 12px;background:rgba(255,255,255,0.05);' +
|
|
1064
|
-
'border:1px solid rgba(255,255,255,0.1);border-radius:6px;color:#c9d1d9;font-size:13px;">' +
|
|
1065
|
-
'<option value="browser">浏览器渲染 (browser)</option>' +
|
|
1066
|
-
'<option value="html">静态HTML (html)</option>' +
|
|
1067
|
-
'<option value="api">API请求 (api)</option></select></div>' +
|
|
1068
|
-
'<div style="margin-bottom:16px;">' +
|
|
1069
|
-
'<label style="display:block;color:#8b949e;font-size:12px;margin-bottom:6px;">阶段配置</label>' +
|
|
1070
|
-
stagesConfigHtml + '</div>' +
|
|
1071
|
-
'<button id="deepspider-config-submit" style="width:100%;padding:12px;' +
|
|
1072
|
-
'background:linear-gradient(135deg,#48bb78,#38a169);border:none;border-radius:8px;' +
|
|
1073
|
-
'color:#fff;font-size:13px;font-weight:600;cursor:pointer;">生成爬虫</button>';
|
|
1074
|
-
|
|
1075
|
-
// 绑定分页复选框事件
|
|
1076
|
-
configContent.querySelectorAll('[data-stage-pagination]').forEach(checkbox => {
|
|
1077
|
-
checkbox.onchange = () => {
|
|
1078
|
-
const idx = checkbox.dataset.stagePagination;
|
|
1079
|
-
const xpathInput = configContent.querySelector('[data-stage-xpath="' + idx + '"]');
|
|
1080
|
-
const maxInput = configContent.querySelector('[data-stage-max="' + idx + '"]');
|
|
1081
|
-
xpathInput.style.opacity = checkbox.checked ? '1' : '0.5';
|
|
1082
|
-
maxInput.style.opacity = checkbox.checked ? '1' : '0.5';
|
|
1083
|
-
};
|
|
1084
|
-
});
|
|
1085
|
-
|
|
1086
|
-
document.getElementById('deepspider-config-submit').onclick = submitConfig;
|
|
1087
|
-
modal.classList.add('visible');
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1090
|
-
// 提交配置
|
|
1091
|
-
function submitConfig() {
|
|
1092
|
-
const modal = document.getElementById('deepspider-config-modal');
|
|
1093
|
-
const grabMethod = document.getElementById('deepspider-grab-method')?.value || 'browser';
|
|
1094
|
-
|
|
1095
|
-
// 收集各阶段的分页配置
|
|
1096
|
-
deepspider.stages.forEach((stage, i) => {
|
|
1097
|
-
const checkbox = modal.querySelector('[data-stage-pagination="' + i + '"]');
|
|
1098
|
-
const xpathInput = modal.querySelector('[data-stage-xpath="' + i + '"]');
|
|
1099
|
-
const maxInput = modal.querySelector('[data-stage-max="' + i + '"]');
|
|
1100
|
-
|
|
1101
|
-
if (checkbox?.checked && xpathInput?.value) {
|
|
1102
|
-
stage.pagination = {
|
|
1103
|
-
next_page_xpath: xpathInput.value,
|
|
1104
|
-
max_page: parseInt(maxInput?.value) || 10
|
|
1105
|
-
};
|
|
1106
|
-
} else {
|
|
1107
|
-
stage.pagination = null;
|
|
1108
|
-
}
|
|
1109
|
-
});
|
|
1110
|
-
saveStages();
|
|
1111
|
-
|
|
1112
|
-
// 构建阶段化配置
|
|
1113
|
-
const config = {
|
|
1114
|
-
url: location.href,
|
|
1115
|
-
grab_method: grabMethod,
|
|
1116
|
-
stages: deepspider.stages.map(s => ({
|
|
1117
|
-
name: s.name,
|
|
1118
|
-
fields: s.fields.map(f => ({
|
|
1119
|
-
name: f.name,
|
|
1120
|
-
xpath: f.xpath,
|
|
1121
|
-
type: f.type
|
|
1122
|
-
})),
|
|
1123
|
-
entry: s.entry,
|
|
1124
|
-
pagination: s.pagination
|
|
1125
|
-
}))
|
|
1126
|
-
};
|
|
1127
|
-
|
|
1128
|
-
modal?.classList.remove('visible');
|
|
985
|
+
reopenBtn.onclick = () => {
|
|
1129
986
|
panel.classList.add('visible');
|
|
987
|
+
reopenBtn.classList.remove('visible');
|
|
988
|
+
};
|
|
1130
989
|
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
if (typeof __deepspider_send__ === 'function') {
|
|
1136
|
-
__deepspider_send__(JSON.stringify({
|
|
1137
|
-
__ds__: true,
|
|
1138
|
-
type: 'generate-config',
|
|
1139
|
-
config: config,
|
|
1140
|
-
url: location.href
|
|
1141
|
-
}));
|
|
1142
|
-
}
|
|
1143
|
-
}
|
|
990
|
+
// 退出选择按钮
|
|
991
|
+
document.getElementById('deepspider-exit-select').onclick = () => {
|
|
992
|
+
stopSelectMode();
|
|
993
|
+
};
|
|
1144
994
|
|
|
1145
995
|
// 点击背景关闭模态框
|
|
1146
996
|
reportModal.addEventListener('click', (e) => {
|
|
1147
997
|
if (e.target === reportModal) {
|
|
1148
|
-
|
|
998
|
+
closeReportModal();
|
|
1149
999
|
}
|
|
1150
1000
|
});
|
|
1151
1001
|
|
|
@@ -1158,30 +1008,105 @@ export function getAnalysisPanelScript() {
|
|
|
1158
1008
|
infoBox.id = 'deepspider-info';
|
|
1159
1009
|
document.body.appendChild(infoBox);
|
|
1160
1010
|
|
|
1161
|
-
// ========== 面板拖动 ==========
|
|
1011
|
+
// ========== 面板拖动 + 边缘缩放 ==========
|
|
1162
1012
|
let isDragging = false;
|
|
1013
|
+
let isResizing = false;
|
|
1014
|
+
let resizeDir = '';
|
|
1163
1015
|
let dragOffset = { x: 0, y: 0 };
|
|
1016
|
+
let startRect = null;
|
|
1017
|
+
let startMouse = { x: 0, y: 0 };
|
|
1164
1018
|
const header = panel.querySelector('.deepspider-header');
|
|
1019
|
+
const MIN_W = 320, MIN_H = 300;
|
|
1165
1020
|
|
|
1021
|
+
// 拖动 header 移动面板
|
|
1166
1022
|
header.addEventListener('mousedown', (e) => {
|
|
1167
1023
|
if (e.target.tagName === 'BUTTON') return;
|
|
1168
1024
|
isDragging = true;
|
|
1169
|
-
|
|
1170
|
-
|
|
1025
|
+
// 确保 panel 用 left/top 定位
|
|
1026
|
+
const rect = panel.getBoundingClientRect();
|
|
1027
|
+
panel.style.left = rect.left + 'px';
|
|
1028
|
+
panel.style.top = rect.top + 'px';
|
|
1029
|
+
panel.style.right = 'auto';
|
|
1030
|
+
dragOffset.x = e.clientX - rect.left;
|
|
1031
|
+
dragOffset.y = e.clientY - rect.top;
|
|
1032
|
+
panel.classList.add('resizing');
|
|
1033
|
+
e.preventDefault();
|
|
1034
|
+
});
|
|
1035
|
+
|
|
1036
|
+
// 边缘手柄 mousedown → 开始缩放
|
|
1037
|
+
panel.querySelectorAll('.deepspider-resize-handle').forEach(handle => {
|
|
1038
|
+
handle.addEventListener('mousedown', (e) => {
|
|
1039
|
+
isResizing = true;
|
|
1040
|
+
resizeDir = handle.className.replace('deepspider-resize-handle ', '');
|
|
1041
|
+
const rect = panel.getBoundingClientRect();
|
|
1042
|
+
startRect = { left: rect.left, top: rect.top, width: rect.width, height: rect.height };
|
|
1043
|
+
startMouse = { x: e.clientX, y: e.clientY };
|
|
1044
|
+
// 切换到 left/top 定位
|
|
1045
|
+
panel.style.left = rect.left + 'px';
|
|
1046
|
+
panel.style.top = rect.top + 'px';
|
|
1047
|
+
panel.style.right = 'auto';
|
|
1048
|
+
panel.classList.add('resizing');
|
|
1049
|
+
e.preventDefault();
|
|
1050
|
+
e.stopPropagation();
|
|
1051
|
+
});
|
|
1171
1052
|
});
|
|
1172
1053
|
|
|
1173
1054
|
document.addEventListener('mousemove', (e) => {
|
|
1174
|
-
if (
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1055
|
+
if (isDragging) {
|
|
1056
|
+
panel.style.left = (e.clientX - dragOffset.x) + 'px';
|
|
1057
|
+
panel.style.top = (e.clientY - dragOffset.y) + 'px';
|
|
1058
|
+
return;
|
|
1059
|
+
}
|
|
1060
|
+
if (!isResizing) return;
|
|
1061
|
+
|
|
1062
|
+
const dx = e.clientX - startMouse.x;
|
|
1063
|
+
const dy = e.clientY - startMouse.y;
|
|
1064
|
+
const dir = resizeDir;
|
|
1065
|
+
let { left, top, width, height } = startRect;
|
|
1066
|
+
|
|
1067
|
+
// 水平
|
|
1068
|
+
if (dir.includes('right')) {
|
|
1069
|
+
width = Math.max(MIN_W, startRect.width + dx);
|
|
1070
|
+
} else if (dir.includes('left')) {
|
|
1071
|
+
const newW = Math.max(MIN_W, startRect.width - dx);
|
|
1072
|
+
left = startRect.left + (startRect.width - newW);
|
|
1073
|
+
width = newW;
|
|
1074
|
+
}
|
|
1075
|
+
// 垂直
|
|
1076
|
+
if (dir.includes('bottom')) {
|
|
1077
|
+
height = Math.max(MIN_H, startRect.height + dy);
|
|
1078
|
+
} else if (dir.includes('top') && dir !== 'top-left' && dir !== 'top-right') {
|
|
1079
|
+
// 纯 top
|
|
1080
|
+
const newH = Math.max(MIN_H, startRect.height - dy);
|
|
1081
|
+
top = startRect.top + (startRect.height - newH);
|
|
1082
|
+
height = newH;
|
|
1083
|
+
}
|
|
1084
|
+
if (dir === 'top-left' || dir === 'top-right') {
|
|
1085
|
+
const newH = Math.max(MIN_H, startRect.height - dy);
|
|
1086
|
+
top = startRect.top + (startRect.height - newH);
|
|
1087
|
+
height = newH;
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
panel.style.left = left + 'px';
|
|
1091
|
+
panel.style.top = top + 'px';
|
|
1092
|
+
panel.style.width = width + 'px';
|
|
1093
|
+
panel.style.height = height + 'px';
|
|
1178
1094
|
});
|
|
1179
1095
|
|
|
1180
|
-
document.addEventListener('mouseup', () => {
|
|
1096
|
+
document.addEventListener('mouseup', () => {
|
|
1097
|
+
if (isDragging || isResizing) {
|
|
1098
|
+
panel.classList.remove('resizing');
|
|
1099
|
+
}
|
|
1100
|
+
isDragging = false;
|
|
1101
|
+
isResizing = false;
|
|
1102
|
+
resizeDir = '';
|
|
1103
|
+
startRect = null;
|
|
1104
|
+
});
|
|
1181
1105
|
|
|
1182
1106
|
// ========== 关闭按钮 ==========
|
|
1183
1107
|
document.getElementById('deepspider-btn-close').onclick = () => {
|
|
1184
1108
|
panel.classList.remove('visible');
|
|
1109
|
+
reopenBtn.classList.add('visible');
|
|
1185
1110
|
};
|
|
1186
1111
|
|
|
1187
1112
|
// ========== 最小化按钮 ==========
|
|
@@ -1231,6 +1156,7 @@ export function getAnalysisPanelScript() {
|
|
|
1231
1156
|
document.addEventListener('keydown', onSelectKey, true);
|
|
1232
1157
|
// 通知所有 iframe 进入选择模式
|
|
1233
1158
|
broadcastToIframes({ type: 'deepspider-start-select' });
|
|
1159
|
+
document.getElementById('deepspider-select-banner').classList.add('visible');
|
|
1234
1160
|
}
|
|
1235
1161
|
|
|
1236
1162
|
function stopSelectMode() {
|
|
@@ -1245,6 +1171,7 @@ export function getAnalysisPanelScript() {
|
|
|
1245
1171
|
document.removeEventListener('keydown', onSelectKey, true);
|
|
1246
1172
|
// 通知所有 iframe 退出选择模式
|
|
1247
1173
|
broadcastToIframes({ type: 'deepspider-stop-select' });
|
|
1174
|
+
document.getElementById('deepspider-select-banner').classList.remove('visible');
|
|
1248
1175
|
}
|
|
1249
1176
|
|
|
1250
1177
|
function onSelectMove(e) {
|
|
@@ -1304,43 +1231,142 @@ export function getAnalysisPanelScript() {
|
|
|
1304
1231
|
// ========== 消息渲染 ==========
|
|
1305
1232
|
const messagesEl = document.getElementById('deepspider-messages');
|
|
1306
1233
|
|
|
1307
|
-
function
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
deepspider.chatMessages.push({ __ds__: true, role, content, time: Date.now(), complete: isComplete });
|
|
1234
|
+
function addStructuredMessage(type, data) {
|
|
1235
|
+
const role = type === 'user' ? 'user' : type === 'system' ? 'system' : 'assistant';
|
|
1236
|
+
deepspider.chatMessages.push({ __ds__: true, role, type, data, time: Date.now() });
|
|
1311
1237
|
saveMessages();
|
|
1312
1238
|
renderMessages();
|
|
1313
1239
|
}
|
|
1314
1240
|
|
|
1241
|
+
function addMessage(role, content) {
|
|
1242
|
+
const type = role === 'system' ? 'system' : role === 'user' ? 'user' : 'text';
|
|
1243
|
+
addStructuredMessage(type, { content });
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
function getEmptyStateHtml() {
|
|
1247
|
+
return '<div class="deepspider-empty">' +
|
|
1248
|
+
'<div class="deepspider-empty-icon">🔍</div>' +
|
|
1249
|
+
'<div style="font-size:14px;color:#c9d1d9;margin-bottom:16px;">开始分析</div>' +
|
|
1250
|
+
'<div style="text-align:left;display:inline-block;">' +
|
|
1251
|
+
'<div style="margin-bottom:10px;display:flex;gap:8px;align-items:flex-start;">' +
|
|
1252
|
+
'<span style="color:#63b3ed;font-weight:600;">1.</span>' +
|
|
1253
|
+
'<span>在网站上操作(登录、翻页等),系统自动记录数据</span></div>' +
|
|
1254
|
+
'<div style="margin-bottom:10px;display:flex;gap:8px;align-items:flex-start;">' +
|
|
1255
|
+
'<span style="color:#63b3ed;font-weight:600;">2.</span>' +
|
|
1256
|
+
'<span>点击 <b style="color:#63b3ed;">⦿</b> 选择目标数据元素</span></div>' +
|
|
1257
|
+
'<div style="display:flex;gap:8px;align-items:flex-start;">' +
|
|
1258
|
+
'<span style="color:#63b3ed;font-weight:600;">3.</span>' +
|
|
1259
|
+
'<span>选择操作或在下方提问</span></div>' +
|
|
1260
|
+
'</div></div>';
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1315
1263
|
function renderMessages() {
|
|
1316
1264
|
const msgs = deepspider.chatMessages;
|
|
1317
1265
|
if (msgs.length === 0) {
|
|
1318
|
-
messagesEl.innerHTML =
|
|
1319
|
-
<div class="deepspider-empty">
|
|
1320
|
-
<div class="deepspider-empty-icon">🔍</div>
|
|
1321
|
-
点击上方 ⦿ 按钮选择页面元素<br>或在下方输入问题开始分析
|
|
1322
|
-
</div>
|
|
1323
|
-
\`;
|
|
1266
|
+
messagesEl.innerHTML = getEmptyStateHtml();
|
|
1324
1267
|
} else {
|
|
1325
|
-
messagesEl.innerHTML = msgs.map(m =>
|
|
1326
|
-
let content;
|
|
1327
|
-
// 兼容旧消息:没有 complete 字段视为已完成
|
|
1328
|
-
const isComplete = m.complete !== false;
|
|
1329
|
-
if (m.role === 'assistant') {
|
|
1330
|
-
content = isComplete ? parseMarkdown(m.content) : escapeHtml(m.content);
|
|
1331
|
-
} else {
|
|
1332
|
-
content = escapeHtml(m.content);
|
|
1333
|
-
}
|
|
1334
|
-
const streamingClass = (m.role === 'assistant' && !isComplete) ? ' streaming' : '';
|
|
1335
|
-
return '<div class="deepspider-msg deepspider-msg-' + m.role + streamingClass + '">' + content + '</div>';
|
|
1336
|
-
}).join('');
|
|
1337
|
-
// 在 DOM 上处理文件路径链接化
|
|
1268
|
+
messagesEl.innerHTML = msgs.map(m => renderSingleMessage(m)).join('');
|
|
1338
1269
|
linkifyFilePaths(messagesEl);
|
|
1339
1270
|
bindFilePathClicks(messagesEl);
|
|
1271
|
+
bindChoiceClicks(messagesEl);
|
|
1272
|
+
bindConfirmClicks(messagesEl);
|
|
1340
1273
|
}
|
|
1341
1274
|
messagesEl.scrollTop = messagesEl.scrollHeight;
|
|
1342
1275
|
}
|
|
1343
1276
|
|
|
1277
|
+
function renderSingleMessage(m) {
|
|
1278
|
+
switch (m.type) {
|
|
1279
|
+
case 'text':
|
|
1280
|
+
return '<div class="deepspider-msg deepspider-msg-assistant">' + parseMarkdown(m.data.content) + '</div>';
|
|
1281
|
+
case 'system':
|
|
1282
|
+
return '<div class="deepspider-msg deepspider-msg-system">' + escapeHtml(m.data.content) + '</div>';
|
|
1283
|
+
case 'user':
|
|
1284
|
+
return '<div class="deepspider-msg deepspider-msg-user">' + escapeHtml(m.data.content) + '</div>';
|
|
1285
|
+
case 'choices':
|
|
1286
|
+
return renderChoicesMessage(m);
|
|
1287
|
+
case 'confirm':
|
|
1288
|
+
return renderConfirmMessage(m);
|
|
1289
|
+
default:
|
|
1290
|
+
return '<div class="deepspider-msg deepspider-msg-system">' + escapeHtml(JSON.stringify(m.data)) + '</div>';
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
function renderChoicesMessage(m) {
|
|
1295
|
+
const d = m.data;
|
|
1296
|
+
const answered = m.answered;
|
|
1297
|
+
let html = '<div class="deepspider-msg deepspider-msg-assistant">';
|
|
1298
|
+
html += '<div class="deepspider-choices-question">' + escapeHtml(d.question) + '</div>';
|
|
1299
|
+
html += '<div class="deepspider-choices-grid">';
|
|
1300
|
+
d.options.forEach(opt => {
|
|
1301
|
+
const selected = answered === opt.id ? ' selected' : '';
|
|
1302
|
+
const disabled = answered ? ' style="pointer-events:none;opacity:0.6;"' : '';
|
|
1303
|
+
html += '<div class="deepspider-choice-btn' + selected + '" data-choice-id="' + escapeHtml(opt.id) + '"' + disabled + '>';
|
|
1304
|
+
html += '<div class="deepspider-choice-label">' + escapeHtml(opt.label) + '</div>';
|
|
1305
|
+
if (opt.description) html += '<div class="deepspider-choice-desc">' + escapeHtml(opt.description) + '</div>';
|
|
1306
|
+
html += '</div>';
|
|
1307
|
+
});
|
|
1308
|
+
html += '</div></div>';
|
|
1309
|
+
return html;
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
function renderConfirmMessage(m) {
|
|
1313
|
+
const d = m.data;
|
|
1314
|
+
const answered = m.answered !== undefined;
|
|
1315
|
+
let html = '<div class="deepspider-msg deepspider-msg-assistant">';
|
|
1316
|
+
html += '<div class="deepspider-choices-question">' + escapeHtml(d.question) + '</div>';
|
|
1317
|
+
if (!answered) {
|
|
1318
|
+
html += '<div class="deepspider-confirm-btns">';
|
|
1319
|
+
html += '<button class="deepspider-confirm-btn deepspider-confirm-yes" data-confirm="true">' + escapeHtml(d.confirmText || '确认') + '</button>';
|
|
1320
|
+
html += '<button class="deepspider-confirm-btn deepspider-confirm-no" data-confirm="false">' + escapeHtml(d.cancelText || '取消') + '</button>';
|
|
1321
|
+
html += '</div>';
|
|
1322
|
+
} else {
|
|
1323
|
+
html += '<div style="color:#8b949e;font-size:12px;margin-top:6px;">' + (m.answered ? '✅ 已确认' : '❌ 已取消') + '</div>';
|
|
1324
|
+
}
|
|
1325
|
+
html += '</div>';
|
|
1326
|
+
return html;
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
function bindChoiceClicks(container) {
|
|
1330
|
+
container.querySelectorAll('.deepspider-choice-btn:not([style*="pointer-events"])').forEach(btn => {
|
|
1331
|
+
btn.onclick = () => {
|
|
1332
|
+
const choiceId = btn.dataset.choiceId;
|
|
1333
|
+
const msgs = deepspider.chatMessages;
|
|
1334
|
+
let chosenLabel = choiceId;
|
|
1335
|
+
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
1336
|
+
if (msgs[i].type === 'choices' && !msgs[i].answered) {
|
|
1337
|
+
msgs[i].answered = choiceId;
|
|
1338
|
+
const opt = msgs[i].data.options.find(o => o.id === choiceId);
|
|
1339
|
+
if (opt) chosenLabel = opt.label;
|
|
1340
|
+
break;
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
addStructuredMessage('user', { content: chosenLabel });
|
|
1344
|
+
if (typeof __deepspider_send__ === 'function') {
|
|
1345
|
+
__deepspider_send__(JSON.stringify({ __ds__: true, type: 'choice', value: chosenLabel }));
|
|
1346
|
+
}
|
|
1347
|
+
};
|
|
1348
|
+
});
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
function bindConfirmClicks(container) {
|
|
1352
|
+
container.querySelectorAll('[data-confirm]').forEach(btn => {
|
|
1353
|
+
btn.onclick = () => {
|
|
1354
|
+
const confirmed = btn.dataset.confirm === 'true';
|
|
1355
|
+
const msgs = deepspider.chatMessages;
|
|
1356
|
+
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
1357
|
+
if (msgs[i].type === 'confirm' && msgs[i].answered === undefined) {
|
|
1358
|
+
msgs[i].answered = confirmed;
|
|
1359
|
+
break;
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
addStructuredMessage('user', { content: confirmed ? '确认' : '取消' });
|
|
1363
|
+
if (typeof __deepspider_send__ === 'function') {
|
|
1364
|
+
__deepspider_send__(JSON.stringify({ __ds__: true, type: 'confirm-result', confirmed }));
|
|
1365
|
+
}
|
|
1366
|
+
};
|
|
1367
|
+
});
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1344
1370
|
function escapeHtml(str) {
|
|
1345
1371
|
return String(str).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
|
1346
1372
|
}
|
|
@@ -1443,7 +1469,6 @@ export function getAnalysisPanelScript() {
|
|
|
1443
1469
|
|
|
1444
1470
|
// ========== 对话输入 ==========
|
|
1445
1471
|
const chatInput = document.getElementById('deepspider-chat-input');
|
|
1446
|
-
const analyzeBtn = document.getElementById('deepspider-btn-analyze');
|
|
1447
1472
|
const sendMsgBtn = document.getElementById('deepspider-btn-send-msg');
|
|
1448
1473
|
|
|
1449
1474
|
// ========== 已选元素管理 ==========
|
|
@@ -1514,16 +1539,27 @@ export function getAnalysisPanelScript() {
|
|
|
1514
1539
|
function updateActionButtons() {
|
|
1515
1540
|
const hasElements = deepspider.selectedElements.length > 0;
|
|
1516
1541
|
const hasText = chatInput.value.trim().length > 0;
|
|
1517
|
-
|
|
1518
|
-
|
|
1542
|
+
// 快捷操作:有选中元素时显示
|
|
1543
|
+
const quickActions = document.getElementById('deepspider-quick-actions');
|
|
1544
|
+
if (hasElements) quickActions.classList.add('visible');
|
|
1545
|
+
else quickActions.classList.remove('visible');
|
|
1519
1546
|
sendMsgBtn.disabled = !hasText && !hasElements;
|
|
1520
1547
|
}
|
|
1521
1548
|
|
|
1522
1549
|
// 监听输入框变化
|
|
1523
|
-
chatInput.oninput =
|
|
1550
|
+
chatInput.oninput = () => {
|
|
1551
|
+
updateActionButtons();
|
|
1552
|
+
chatInput.style.height = 'auto';
|
|
1553
|
+
chatInput.style.height = Math.min(chatInput.scrollHeight, 110) + 'px';
|
|
1554
|
+
};
|
|
1524
1555
|
|
|
1525
1556
|
// 绑定按钮事件
|
|
1526
|
-
|
|
1557
|
+
document.querySelectorAll('.deepspider-quick-btn').forEach(btn => {
|
|
1558
|
+
btn.onclick = () => {
|
|
1559
|
+
const action = btn.dataset.action;
|
|
1560
|
+
sendQuickAction(action);
|
|
1561
|
+
};
|
|
1562
|
+
});
|
|
1527
1563
|
sendMsgBtn.onclick = sendChat;
|
|
1528
1564
|
chatInput.onkeydown = (e) => {
|
|
1529
1565
|
if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendChat(); }
|
|
@@ -1542,6 +1578,7 @@ export function getAnalysisPanelScript() {
|
|
|
1542
1578
|
// 纯文字对话
|
|
1543
1579
|
if (!text) return;
|
|
1544
1580
|
chatInput.value = '';
|
|
1581
|
+
chatInput.style.height = 'auto';
|
|
1545
1582
|
addMessage('user', text);
|
|
1546
1583
|
if (typeof __deepspider_send__ === 'function') {
|
|
1547
1584
|
__deepspider_send__(JSON.stringify({ __ds__: true, type: 'chat', text }));
|
|
@@ -1572,35 +1609,41 @@ export function getAnalysisPanelScript() {
|
|
|
1572
1609
|
}
|
|
1573
1610
|
|
|
1574
1611
|
chatInput.value = '';
|
|
1612
|
+
chatInput.style.height = 'auto';
|
|
1575
1613
|
clearSelectedElements();
|
|
1576
1614
|
}
|
|
1577
1615
|
|
|
1578
|
-
//
|
|
1579
|
-
function
|
|
1616
|
+
// 发送快捷操作(带已选元素)
|
|
1617
|
+
function sendQuickAction(action) {
|
|
1580
1618
|
const elements = deepspider.selectedElements;
|
|
1581
1619
|
if (elements.length === 0) return;
|
|
1582
1620
|
|
|
1583
1621
|
const text = chatInput.value.trim();
|
|
1584
|
-
const
|
|
1585
|
-
|
|
1622
|
+
const labels = {
|
|
1623
|
+
trace: '🔍 追踪数据来源',
|
|
1624
|
+
decrypt: '🔓 分析加密参数',
|
|
1625
|
+
full: '🚀 完整分析并生成爬虫',
|
|
1626
|
+
extract: '📋 提取页面结构',
|
|
1627
|
+
};
|
|
1628
|
+
const elementsText = elements.map(el => el.text.slice(0, 30)).join(', ');
|
|
1629
|
+
const displayMsg = labels[action] + ': ' + elementsText.slice(0, 60);
|
|
1586
1630
|
|
|
1587
1631
|
panel.classList.add('visible');
|
|
1588
1632
|
addMessage('user', displayMsg);
|
|
1589
|
-
addMessage('system', '分析中...');
|
|
1590
1633
|
|
|
1591
1634
|
if (typeof __deepspider_send__ === 'function') {
|
|
1592
1635
|
__deepspider_send__(JSON.stringify({
|
|
1593
1636
|
__ds__: true,
|
|
1594
1637
|
type: 'analysis',
|
|
1595
|
-
|
|
1638
|
+
action: action,
|
|
1596
1639
|
elements: elements,
|
|
1597
1640
|
text: text,
|
|
1598
1641
|
url: location.href
|
|
1599
1642
|
}));
|
|
1600
1643
|
}
|
|
1601
1644
|
|
|
1602
|
-
// 清空已选元素和输入框
|
|
1603
1645
|
chatInput.value = '';
|
|
1646
|
+
chatInput.style.height = 'auto';
|
|
1604
1647
|
clearSelectedElements();
|
|
1605
1648
|
}
|
|
1606
1649
|
|
|
@@ -1616,48 +1659,12 @@ export function getAnalysisPanelScript() {
|
|
|
1616
1659
|
}
|
|
1617
1660
|
});
|
|
1618
1661
|
|
|
1619
|
-
// ========== 追加到最后一条消息(流式输出优化) ==========
|
|
1620
|
-
function appendToLastMessage(role, text) {
|
|
1621
|
-
const msgs = deepspider.chatMessages;
|
|
1622
|
-
// 查找最后一条同角色的未完成消息
|
|
1623
|
-
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
1624
|
-
if (msgs[i].role === role && !msgs[i].complete) {
|
|
1625
|
-
msgs[i].content += text;
|
|
1626
|
-
// 流式输出时直接操作 DOM,不重新渲染
|
|
1627
|
-
const msgElements = messagesEl.querySelectorAll('.deepspider-msg-' + role);
|
|
1628
|
-
const lastMsgEl = msgElements[msgElements.length - 1];
|
|
1629
|
-
if (lastMsgEl) {
|
|
1630
|
-
lastMsgEl.textContent = msgs[i].content;
|
|
1631
|
-
messagesEl.scrollTop = messagesEl.scrollHeight;
|
|
1632
|
-
}
|
|
1633
|
-
return true;
|
|
1634
|
-
}
|
|
1635
|
-
}
|
|
1636
|
-
// 没找到则创建新的未完成消息
|
|
1637
|
-
addMessage(role, text, false);
|
|
1638
|
-
return true;
|
|
1639
|
-
}
|
|
1640
|
-
|
|
1641
|
-
// 完成流式输出后调用,标记完成并渲染 Markdown
|
|
1642
|
-
function finalizeMessage(role) {
|
|
1643
|
-
const msgs = deepspider.chatMessages;
|
|
1644
|
-
// 找到最后一条未完成的消息,标记为完成
|
|
1645
|
-
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
1646
|
-
if (msgs[i].role === role && !msgs[i].complete) {
|
|
1647
|
-
msgs[i].complete = true;
|
|
1648
|
-
break;
|
|
1649
|
-
}
|
|
1650
|
-
}
|
|
1651
|
-
saveMessages();
|
|
1652
|
-
renderMessages();
|
|
1653
|
-
}
|
|
1654
|
-
|
|
1655
1662
|
// ========== 更新最后一条消息(替换内容) ==========
|
|
1656
1663
|
function updateLastMessage(role, content) {
|
|
1657
1664
|
const msgs = deepspider.chatMessages;
|
|
1658
1665
|
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
1659
1666
|
if (msgs[i].role === role) {
|
|
1660
|
-
msgs[i].
|
|
1667
|
+
msgs[i].data = { content };
|
|
1661
1668
|
saveMessages();
|
|
1662
1669
|
renderMessages();
|
|
1663
1670
|
return true;
|
|
@@ -1675,15 +1682,18 @@ export function getAnalysisPanelScript() {
|
|
|
1675
1682
|
const reportContentEl = document.getElementById('deepspider-report-content');
|
|
1676
1683
|
const reportCloseBtn = document.getElementById('deepspider-report-close');
|
|
1677
1684
|
|
|
1678
|
-
//
|
|
1679
|
-
|
|
1685
|
+
// 关闭报告模态框(统一入口)
|
|
1686
|
+
function closeReportModal() {
|
|
1680
1687
|
reportModal.classList.remove('visible');
|
|
1681
|
-
|
|
1688
|
+
reportBtn.classList.add('viewed');
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
reportCloseBtn.onclick = closeReportModal;
|
|
1682
1692
|
|
|
1683
1693
|
// ESC 键关闭模态框
|
|
1684
1694
|
document.addEventListener('keydown', (e) => {
|
|
1685
1695
|
if (e.key === 'Escape' && reportModal.classList.contains('visible')) {
|
|
1686
|
-
|
|
1696
|
+
closeReportModal();
|
|
1687
1697
|
}
|
|
1688
1698
|
});
|
|
1689
1699
|
|
|
@@ -1804,11 +1814,10 @@ export function getAnalysisPanelScript() {
|
|
|
1804
1814
|
minimizeBtn.title = '最小化';
|
|
1805
1815
|
}
|
|
1806
1816
|
|
|
1807
|
-
deepspider.showPanel = () => panel.classList.add('visible');
|
|
1808
|
-
deepspider.hidePanel = () => panel.classList.remove('visible');
|
|
1817
|
+
deepspider.showPanel = () => { panel.classList.add('visible'); reopenBtn.classList.remove('visible'); };
|
|
1818
|
+
deepspider.hidePanel = () => { panel.classList.remove('visible'); reopenBtn.classList.add('visible'); };
|
|
1809
1819
|
deepspider.addMessage = addMessage;
|
|
1810
|
-
deepspider.
|
|
1811
|
-
deepspider.finalizeMessage = finalizeMessage;
|
|
1820
|
+
deepspider.addStructuredMessage = addStructuredMessage;
|
|
1812
1821
|
deepspider.updateLastMessage = updateLastMessage;
|
|
1813
1822
|
deepspider.renderMessages = renderMessages;
|
|
1814
1823
|
deepspider.clearMessages = () => { deepspider.chatMessages = []; saveMessages(); renderMessages(); };
|
|
@@ -1818,8 +1827,6 @@ export function getAnalysisPanelScript() {
|
|
|
1818
1827
|
deepspider.setBusy = setBusy;
|
|
1819
1828
|
deepspider.minimize = minimize;
|
|
1820
1829
|
deepspider.maximize = maximize;
|
|
1821
|
-
deepspider.getStages = () => deepspider.stages;
|
|
1822
|
-
deepspider.clearStages = clearAll;
|
|
1823
1830
|
deepspider.getSelectedElements = () => deepspider.selectedElements;
|
|
1824
1831
|
deepspider.clearSelectedElements = clearSelectedElements;
|
|
1825
1832
|
|
|
@@ -1827,8 +1834,6 @@ export function getAnalysisPanelScript() {
|
|
|
1827
1834
|
panel.classList.add('visible');
|
|
1828
1835
|
// 渲染恢复的消息
|
|
1829
1836
|
renderMessages();
|
|
1830
|
-
// 恢复阶段面板
|
|
1831
|
-
updateStagesPanel();
|
|
1832
1837
|
// 恢复已选元素标签
|
|
1833
1838
|
renderSelectedTags();
|
|
1834
1839
|
updateActionButtons();
|