Jimmyzheng-10 commited on
Commit
410e3f4
·
1 Parent(s): 7aaa630
Files changed (1) hide show
  1. app.py +372 -5
app.py CHANGED
@@ -349,13 +349,380 @@ with gr.Blocks(css="""
349
  font-weight: 600 !important;
350
  color: #374151 !important;
351
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
352
  """) as demo:
353
  gr.Markdown("# ScreenCoder: Screenshot to Code")
354
  with gr.Row():
355
  with gr.Column(scale=1):
356
  gr.Markdown("## Step 1: Provide an Image")
357
- active_image = gr.Image(type="filepath", height=400)
358
- upload_button = gr.UploadButton("Click to Upload", file_types=["image"], variant="primary")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
359
 
360
  gr.Markdown("## Step 2: Write Prompts (Optional)")
361
  with gr.Accordion("Component-specific Prompts", open=False):
@@ -379,9 +746,9 @@ with gr.Blocks(css="""
379
  html_preview = gr.HTML(label="Rendered HTML", show_label=False)
380
  with gr.TabItem("Preview"):
381
  with gr.Row():
382
- scale_slider_with_placeholder = gr.Slider(0.2, 1.5, value=0.55, step=0.05, label="🔍 Zoom")
383
- width_slider_with_placeholder = gr.Slider(400, 2000, value=1920, step=100, label="📏 Canvas Width (px)")
384
- height_slider_with_placeholder = gr.Slider(300, 1200, value=1080, step=50, label="📐 Canvas Height (px)")
385
 
386
  html_preview_with_placeholder = gr.HTML(label="Rendered HTML", show_label=False)
387
  with gr.TabItem("Code"):
 
349
  font-weight: 600 !important;
350
  color: #374151 !important;
351
  }
352
+
353
+ /* 截图工具区域样式 */
354
+ .screenshot-tip {
355
+ background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%) !important;
356
+ border: 1px solid #bae6fd !important;
357
+ border-radius: 8px !important;
358
+ padding: 12px !important;
359
+ margin-top: 8px !important;
360
+ }
361
+
362
+ .screenshot-tip div {
363
+ line-height: 1.5 !important;
364
+ }
365
+
366
+ /* 截图按钮特殊样式 */
367
+ .gr-button[data-testid="Take Screenshot"] {
368
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
369
+ color: white !important;
370
+ border: none !important;
371
+ font-weight: 600 !important;
372
+ transition: all 0.3s ease !important;
373
+ }
374
+
375
+ .gr-button[data-testid="Take Screenshot"]:hover {
376
+ transform: translateY(-2px) !important;
377
+ box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4) !important;
378
+ }
379
+
380
+ /* 全屏截图按钮样式 */
381
+ .gr-button:has-text("Full Screen") {
382
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
383
+ color: white !important;
384
+ border: none !important;
385
+ font-weight: 600 !important;
386
+ transition: all 0.3s ease !important;
387
+ }
388
+
389
+ .gr-button:has-text("Full Screen"):hover {
390
+ transform: translateY(-2px) !important;
391
+ box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4) !important;
392
+ }
393
+
394
+ /* 区域截图按钮样式 */
395
+ .gr-button:has-text("Select Area") {
396
+ background: linear-gradient(135deg, #10b981 0%, #059669 100%) !important;
397
+ color: white !important;
398
+ border: none !important;
399
+ font-weight: 600 !important;
400
+ transition: all 0.3s ease !important;
401
+ }
402
+
403
+ .gr-button:has-text("Select Area"):hover {
404
+ transform: translateY(-2px) !important;
405
+ box-shadow: 0 8px 25px rgba(16, 185, 129, 0.4) !important;
406
+ }
407
+
408
+ /* 截图工具区域容器 */
409
+ .screenshot-tools-container {
410
+ background: #f8fafc !important;
411
+ border: 1px solid #e2e8f0 !important;
412
+ border-radius: 12px !important;
413
+ padding: 16px !important;
414
+ margin-bottom: 16px !important;
415
+ }
416
  """) as demo:
417
  gr.Markdown("# ScreenCoder: Screenshot to Code")
418
  with gr.Row():
419
  with gr.Column(scale=1):
420
  gr.Markdown("## Step 1: Provide an Image")
421
+
422
+ # 添加截图功能区域
423
+ with gr.Row(elem_classes=["screenshot-tools-container"]):
424
+ with gr.Column(scale=1):
425
+ active_image = gr.Image(type="filepath", height=400)
426
+ upload_button = gr.UploadButton("Click to Upload", file_types=["image"], variant="primary")
427
+
428
+ with gr.Column(scale=1):
429
+ gr.Markdown("**📸 Screenshot Tools**")
430
+ with gr.Row():
431
+ screenshot_btn = gr.Button("🎯 Full Screen", variant="secondary", size="sm")
432
+ area_screenshot_btn = gr.Button("✂️ Select Area", variant="secondary", size="sm")
433
+ gr.Markdown("""
434
+ <div style="font-size: 12px; color: #666; margin-top: 8px;">
435
+ 💡 Click "Full Screen" to capture entire screen.
436
+ <br>Click "Select Area" to choose specific region.
437
+ <br>Press ESC to cancel area selection.
438
+ </div>
439
+ """, elem_classes=["screenshot-tip"])
440
+
441
+ # 添加截图功能的JavaScript
442
+ gr.HTML("""
443
+ <script>
444
+ // 截图功能
445
+ async function takeScreenshot() {
446
+ try {
447
+ // 检查浏览器是否支持截图API
448
+ if (!navigator.mediaDevices || !navigator.mediaDevices.getDisplayMedia) {
449
+ alert('Screenshot feature is not supported in this browser. Please use Chrome, Firefox, or Edge.');
450
+ return;
451
+ }
452
+
453
+ // 请求屏幕共享权限
454
+ const stream = await navigator.mediaDevices.getDisplayMedia({
455
+ video: {
456
+ mediaSource: 'screen',
457
+ width: { ideal: 1920 },
458
+ height: { ideal: 1080 }
459
+ }
460
+ });
461
+
462
+ // 创建视频元素来捕获屏幕
463
+ const video = document.createElement('video');
464
+ video.srcObject = stream;
465
+ video.onloadedmetadata = () => {
466
+ video.play();
467
+
468
+ // 创建canvas来绘制截图
469
+ const canvas = document.createElement('canvas');
470
+ const ctx = canvas.getContext('2d');
471
+
472
+ // 设置canvas尺寸
473
+ canvas.width = video.videoWidth;
474
+ canvas.height = video.videoHeight;
475
+
476
+ // 绘制视频帧到canvas
477
+ ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
478
+
479
+ // 停止视频流
480
+ stream.getTracks().forEach(track => track.stop());
481
+
482
+ // 将canvas转换为blob
483
+ canvas.toBlob((blob) => {
484
+ // 创建文件对象
485
+ const file = new File([blob], 'screenshot.png', { type: 'image/png' });
486
+
487
+ // 创建文件列表
488
+ const dataTransfer = new DataTransfer();
489
+ dataTransfer.items.add(file);
490
+
491
+ // 找到图片上传组件并设置文件
492
+ const imageInput = document.querySelector('input[type="file"]');
493
+ if (imageInput) {
494
+ imageInput.files = dataTransfer.files;
495
+ // 触发change事件
496
+ const event = new Event('change', { bubbles: true });
497
+ imageInput.dispatchEvent(event);
498
+ }
499
+
500
+ // 显示成功消息
501
+ showNotification('Screenshot captured successfully!', 'success');
502
+ }, 'image/png');
503
+ };
504
+
505
+ } catch (error) {
506
+ console.error('Screenshot error:', error);
507
+ if (error.name === 'NotAllowedError') {
508
+ alert('Screenshot permission denied. Please allow screen sharing when prompted.');
509
+ } else {
510
+ alert('Failed to take screenshot: ' + error.message);
511
+ }
512
+ }
513
+ }
514
+
515
+ // 区域截图功能
516
+ async function takeAreaScreenshot() {
517
+ try {
518
+ // 检查浏览器是否支持截图API
519
+ if (!navigator.mediaDevices || !navigator.mediaDevices.getDisplayMedia) {
520
+ alert('Area screenshot feature is not supported in this browser.');
521
+ return;
522
+ }
523
+
524
+ // 请求屏幕共享权限
525
+ const stream = await navigator.mediaDevices.getDisplayMedia({
526
+ video: {
527
+ mediaSource: 'screen',
528
+ width: { ideal: 1920 },
529
+ height: { ideal: 1080 }
530
+ }
531
+ });
532
+
533
+ // 创建视频元素
534
+ const video = document.createElement('video');
535
+ video.srcObject = stream;
536
+ video.onloadedmetadata = () => {
537
+ video.play();
538
+
539
+ // 创建选择区域界面
540
+ createSelectionOverlay(video, stream);
541
+ };
542
+
543
+ } catch (error) {
544
+ console.error('Area screenshot error:', error);
545
+ if (error.name === 'NotAllowedError') {
546
+ alert('Screenshot permission denied. Please allow screen sharing when prompted.');
547
+ } else {
548
+ alert('Failed to take area screenshot: ' + error.message);
549
+ }
550
+ }
551
+ }
552
+
553
+ // 创建选择区域覆盖层
554
+ function createSelectionOverlay(video, stream) {
555
+ // 创建覆盖层
556
+ const overlay = document.createElement('div');
557
+ overlay.style.cssText = `
558
+ position: fixed;
559
+ top: 0;
560
+ left: 0;
561
+ width: 100vw;
562
+ height: 100vh;
563
+ background: rgba(0, 0, 0, 0.3);
564
+ z-index: 9999;
565
+ cursor: crosshair;
566
+ `;
567
+
568
+ // 创建选择框
569
+ const selectionBox = document.createElement('div');
570
+ selectionBox.style.cssText = `
571
+ position: absolute;
572
+ border: 2px dashed #3b82f6;
573
+ background: rgba(59, 130, 246, 0.1);
574
+ display: none;
575
+ `;
576
+
577
+ overlay.appendChild(selectionBox);
578
+ document.body.appendChild(overlay);
579
+
580
+ let isSelecting = false;
581
+ let startX, startY;
582
+
583
+ // 鼠标事件处理
584
+ overlay.addEventListener('mousedown', (e) => {
585
+ isSelecting = true;
586
+ startX = e.clientX;
587
+ startY = e.clientY;
588
+ selectionBox.style.display = 'block';
589
+ selectionBox.style.left = startX + 'px';
590
+ selectionBox.style.top = startY + 'px';
591
+ selectionBox.style.width = '0px';
592
+ selectionBox.style.height = '0px';
593
+ });
594
+
595
+ overlay.addEventListener('mousemove', (e) => {
596
+ if (!isSelecting) return;
597
+
598
+ const currentX = e.clientX;
599
+ const currentY = e.clientY;
600
+
601
+ const left = Math.min(startX, currentX);
602
+ const top = Math.min(startY, currentY);
603
+ const width = Math.abs(currentX - startX);
604
+ const height = Math.abs(currentY - startY);
605
+
606
+ selectionBox.style.left = left + 'px';
607
+ selectionBox.style.top = top + 'px';
608
+ selectionBox.style.width = width + 'px';
609
+ selectionBox.style.height = height + 'px';
610
+ });
611
+
612
+ overlay.addEventListener('mouseup', (e) => {
613
+ if (!isSelecting) return;
614
+ isSelecting = false;
615
+
616
+ const rect = selectionBox.getBoundingClientRect();
617
+ captureSelectedArea(video, rect, stream);
618
+
619
+ // 清理
620
+ document.body.removeChild(overlay);
621
+ });
622
+
623
+ // ESC键取消
624
+ const handleKeyDown = (e) => {
625
+ if (e.key === 'Escape') {
626
+ document.body.removeChild(overlay);
627
+ stream.getTracks().forEach(track => track.stop());
628
+ document.removeEventListener('keydown', handleKeyDown);
629
+ }
630
+ };
631
+ document.addEventListener('keydown', handleKeyDown);
632
+ }
633
+
634
+ // 捕获选定区域
635
+ function captureSelectedArea(video, rect, stream) {
636
+ const canvas = document.createElement('canvas');
637
+ const ctx = canvas.getContext('2d');
638
+
639
+ // 设置canvas尺寸为选择区域大小
640
+ canvas.width = rect.width;
641
+ canvas.height = rect.height;
642
+
643
+ // 绘制选定区域
644
+ ctx.drawImage(video, rect.left, rect.top, rect.width, rect.height, 0, 0, rect.width, rect.height);
645
+
646
+ // 停止视频流
647
+ stream.getTracks().forEach(track => track.stop());
648
+
649
+ // 转换为blob并上传
650
+ canvas.toBlob((blob) => {
651
+ const file = new File([blob], 'area_screenshot.png', { type: 'image/png' });
652
+ const dataTransfer = new DataTransfer();
653
+ dataTransfer.items.add(file);
654
+
655
+ const imageInput = document.querySelector('input[type="file"]');
656
+ if (imageInput) {
657
+ imageInput.files = dataTransfer.files;
658
+ const event = new Event('change', { bubbles: true });
659
+ imageInput.dispatchEvent(event);
660
+ }
661
+
662
+ showNotification('Area screenshot captured successfully!', 'success');
663
+ }, 'image/png');
664
+ }
665
+
666
+ // 显示通知的函数
667
+ function showNotification(message, type = 'info') {
668
+ const notification = document.createElement('div');
669
+ notification.style.cssText = `
670
+ position: fixed;
671
+ top: 20px;
672
+ right: 20px;
673
+ padding: 12px 20px;
674
+ border-radius: 8px;
675
+ color: white;
676
+ font-weight: 500;
677
+ z-index: 10000;
678
+ animation: slideIn 0.3s ease-out;
679
+ background: ${type === 'success' ? '#10b981' : '#3b82f6'};
680
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
681
+ `;
682
+ notification.textContent = message;
683
+
684
+ // 添加动画样式
685
+ const style = document.createElement('style');
686
+ style.textContent = `
687
+ @keyframes slideIn {
688
+ from { transform: translateX(100%); opacity: 0; }
689
+ to { transform: translateX(0); opacity: 1; }
690
+ }
691
+ `;
692
+ document.head.appendChild(style);
693
+
694
+ document.body.appendChild(notification);
695
+
696
+ // 3秒后自动移除
697
+ setTimeout(() => {
698
+ notification.remove();
699
+ }, 3000);
700
+ }
701
+
702
+ // 等待页面加载完成后绑定事件
703
+ document.addEventListener('DOMContentLoaded', function() {
704
+ // 查找截图按钮并绑定点击事件
705
+ const observer = new MutationObserver(function(mutations) {
706
+ mutations.forEach(function(mutation) {
707
+ mutation.addedNodes.forEach(function(node) {
708
+ if (node.nodeType === 1) { // 元素节点
709
+ const buttons = node.querySelectorAll ? node.querySelectorAll('button') : [];
710
+ buttons.forEach(function(button) {
711
+ if (button.textContent.includes('Full Screen')) {
712
+ button.addEventListener('click', takeScreenshot);
713
+ } else if (button.textContent.includes('Select Area')) {
714
+ button.addEventListener('click', takeAreaScreenshot);
715
+ }
716
+ });
717
+ }
718
+ });
719
+ });
720
+ });
721
+
722
+ observer.observe(document.body, { childList: true, subtree: true });
723
+ });
724
+ </script>
725
+ """)
726
 
727
  gr.Markdown("## Step 2: Write Prompts (Optional)")
728
  with gr.Accordion("Component-specific Prompts", open=False):
 
746
  html_preview = gr.HTML(label="Rendered HTML", show_label=False)
747
  with gr.TabItem("Preview"):
748
  with gr.Row():
749
+ scale_slider_with_placeholder = gr.Slider(0.2, 1.5, value=0.55, step=0.05, label="Zoom")
750
+ width_slider_with_placeholder = gr.Slider(400, 2000, value=1920, step=100, label="Canvas Width (px)")
751
+ height_slider_with_placeholder = gr.Slider(300, 1200, value=1080, step=50, label="Canvas Height (px)")
752
 
753
  html_preview_with_placeholder = gr.HTML(label="Rendered HTML", show_label=False)
754
  with gr.TabItem("Code"):