mock-service-cli 3.5.0 → 3.6.0

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.
@@ -434,16 +434,55 @@
434
434
  }
435
435
 
436
436
  .text-preview {
437
- background: #f8f9fa;
438
- padding: 16px;
437
+ background: #1e1e1e;
438
+ color: #d4d4d4;
439
+ padding: 20px;
439
440
  border-radius: 8px;
440
- font-family: 'Courier New', monospace;
441
+ max-height: 70vh;
442
+ overflow: auto;
443
+ font-family: 'Fira Code', 'Monaco', 'Consolas', monospace;
441
444
  font-size: 13px;
442
445
  line-height: 1.6;
443
446
  white-space: pre-wrap;
444
- word-break: break-all;
445
- max-height: 70vh;
446
- overflow: auto;
447
+ word-wrap: break-word;
448
+ margin: 0;
449
+ }
450
+
451
+ .preview-container {
452
+ display: flex;
453
+ flex-direction: column;
454
+ height: 100%;
455
+ }
456
+
457
+ .preview-header {
458
+ display: flex;
459
+ justify-content: flex-end;
460
+ margin-bottom: 12px;
461
+ }
462
+
463
+ .preview-header .copy-btn {
464
+ padding: 8px 16px;
465
+ background: linear-gradient(135deg, #8b5cf6, #6366f1);
466
+ color: white;
467
+ border: none;
468
+ border-radius: 6px;
469
+ font-size: 14px;
470
+ cursor: pointer;
471
+ transition: all 0.2s;
472
+ display: flex;
473
+ align-items: center;
474
+ gap: 6px;
475
+ box-shadow: 0 2px 8px rgba(139, 92, 246, 0.3);
476
+ }
477
+
478
+ .preview-header .copy-btn:hover {
479
+ transform: translateY(-1px);
480
+ box-shadow: 0 4px 12px rgba(139, 92, 246, 0.4);
481
+ background: linear-gradient(135deg, #7c3aed, #4f46e5);
482
+ }
483
+
484
+ .preview-header .copy-btn:active {
485
+ transform: translateY(0);
447
486
  }
448
487
 
449
488
  .empty-state {
@@ -548,39 +587,69 @@
548
587
  let sortBy = 'name';
549
588
  let sortAscending = true;
550
589
  let showHiddenFiles = true;
590
+ let currentPreviewContent = '';
551
591
 
552
592
  document.addEventListener('DOMContentLoaded', () => {
553
593
  loadDirectory('/');
554
594
  });
555
595
 
556
596
  async function loadDirectory(path) {
597
+ // console.log('Loading directory:', path);
557
598
  const fileGrid = document.getElementById('fileGrid');
599
+
600
+ // 重置所有状态
601
+ currentPath = '/';
602
+ allFiles = [];
603
+ currentFiles = [];
604
+
605
+ // 显示加载状态
558
606
  fileGrid.innerHTML = '<div class="loading"><div class="spinner"></div><p>加载中...</p></div>';
559
607
 
560
608
  try {
561
609
  const response = await fetch(`/__api/list?path=${encodeURIComponent(path)}`);
610
+ // console.log('Response status:', response.status);
611
+
562
612
  const data = await response.json();
613
+ // console.log('API response data:', data);
563
614
 
564
615
  if (data.error) {
565
616
  throw new Error(data.error);
566
617
  }
567
618
 
568
619
  currentPath = data.currentPath;
569
- allFiles = data.files;
620
+ allFiles = data.files || [];
621
+
622
+ // console.log('allFiles set to:', allFiles);
570
623
 
571
624
  renderBreadcrumb(data.currentPath, data.parentPath);
572
- processAndRenderFiles();
625
+
626
+ // 确保在调用 processAndRenderFiles 之前 allFiles 已经设置好
627
+ setTimeout(() => {
628
+ processAndRenderFiles();
629
+ }, 10);
630
+
573
631
  updateBackButton(data.parentPath);
574
632
 
575
633
  // 清空搜索框
576
- document.getElementById('searchInput').value = '';
634
+ if (document.getElementById('searchInput')) {
635
+ document.getElementById('searchInput').value = '';
636
+ }
577
637
  searchQuery = '';
578
638
  } catch (error) {
639
+ console.error('Error loading directory:', error);
579
640
  fileGrid.innerHTML = `<div class="empty-state"><p>错误: ${error.message}</p></div>`;
580
641
  }
581
642
  }
582
643
 
583
644
  function processAndRenderFiles() {
645
+ // console.log('processAndRenderFiles called, allFiles:', allFiles);
646
+
647
+ if (!allFiles) {
648
+ console.error('allFiles is null or undefined');
649
+ renderFiles([]);
650
+ return;
651
+ }
652
+
584
653
  let files = [...allFiles];
585
654
 
586
655
  // 隐藏文件过滤
@@ -619,6 +688,7 @@
619
688
  });
620
689
 
621
690
  currentFiles = files;
691
+ // console.log('Rendering', files.length, 'files');
622
692
  renderFiles(files);
623
693
  }
624
694
 
@@ -718,7 +788,14 @@
718
788
  function renderFiles(files) {
719
789
  const fileGrid = document.getElementById('fileGrid');
720
790
 
721
- if (files.length === 0) {
791
+ // 确保视图类名正确
792
+ if (currentView === 'list') {
793
+ fileGrid.classList.add('list-view');
794
+ } else {
795
+ fileGrid.classList.remove('list-view');
796
+ }
797
+
798
+ if (!files || files.length === 0) {
722
799
  fileGrid.innerHTML = '<div class="empty-state"><p>该目录为空</p></div>';
723
800
  return;
724
801
  }
@@ -777,6 +854,7 @@
777
854
  }
778
855
  });
779
856
 
857
+ // 强制更新 DOM
780
858
  fileGrid.innerHTML = html;
781
859
  }
782
860
 
@@ -788,6 +866,74 @@
788
866
  }
789
867
  }
790
868
 
869
+ async function copyText(text) {
870
+ try {
871
+ if (navigator.clipboard && navigator.clipboard.writeText) {
872
+ await navigator.clipboard.writeText(text);
873
+ showToast('已复制到剪贴板');
874
+ } else {
875
+ const textarea = document.createElement('textarea');
876
+ textarea.value = text;
877
+ textarea.style.position = 'fixed';
878
+ textarea.style.top = '-9999px';
879
+ document.body.appendChild(textarea);
880
+ textarea.select();
881
+ document.execCommand('copy');
882
+ document.body.removeChild(textarea);
883
+ showToast('已复制到剪贴板');
884
+ }
885
+ } catch (error) {
886
+ console.error('复制失败:', error);
887
+ showToast('复制失败');
888
+ }
889
+ }
890
+
891
+ function copyPreviewContent() {
892
+ if (currentPreviewContent !== '') {
893
+ copyText(currentPreviewContent);
894
+ }
895
+ }
896
+
897
+ function showToast(message) {
898
+ const toast = document.createElement('div');
899
+ toast.className = 'toast';
900
+ toast.textContent = message;
901
+ toast.style.cssText = `
902
+ position: fixed;
903
+ top: 20px;
904
+ right: 20px;
905
+ background: rgba(139, 92, 246, 0.95);
906
+ color: white;
907
+ padding: 12px 24px;
908
+ border-radius: 8px;
909
+ font-size: 14px;
910
+ z-index: 10000;
911
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
912
+ animation: fadeInOut 2s ease-in-out;
913
+ `;
914
+ document.body.appendChild(toast);
915
+
916
+ const style = document.createElement('style');
917
+ style.textContent = `
918
+ @keyframes fadeInOut {
919
+ 0% { opacity: 0; transform: translateY(-20px); }
920
+ 15% { opacity: 1; transform: translateY(0); }
921
+ 85% { opacity: 1; transform: translateY(0); }
922
+ 100% { opacity: 0; transform: translateY(-20px); }
923
+ }
924
+ `;
925
+ document.head.appendChild(style);
926
+
927
+ setTimeout(() => {
928
+ if (toast.parentNode) {
929
+ document.body.removeChild(toast);
930
+ }
931
+ if (style.parentNode) {
932
+ document.head.removeChild(style);
933
+ }
934
+ }, 2000);
935
+ }
936
+
791
937
  async function previewFile(path) {
792
938
  const modal = document.getElementById('previewModal');
793
939
  const title = document.getElementById('previewTitle');
@@ -812,7 +958,15 @@
812
958
  if (contentType && contentType.includes('application/json')) {
813
959
  const data = await response.json();
814
960
  if (data.type === 'text') {
815
- body.innerHTML = `<pre class="text-preview">${escapeHtml(data.content)}</pre>`;
961
+ currentPreviewContent = data.content;
962
+ body.innerHTML = `
963
+ <div class="preview-container">
964
+ <div class="preview-header">
965
+ <button class="copy-btn" onclick="copyPreviewContent()">📋 复制全部</button>
966
+ </div>
967
+ <pre class="text-preview">${escapeHtml(data.content)}</pre>
968
+ </div>
969
+ `;
816
970
  } else {
817
971
  body.innerHTML = `<div class="empty-state"><p>该文件类型暂不支持预览</p><p><a href="/__api/file?path=${encodeURIComponent(
818
972
  path
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mock-service-cli",
3
- "version": "3.5.0",
3
+ "version": "3.6.0",
4
4
  "description": "🦅 Local Mock/Static/SPA server, Http?s request proxy, API overview page, File explorer",
5
5
  "main": "./lib/mockServer.js",
6
6
  "bin": {