cursor-feedback 1.0.1 → 1.0.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/dist/extension.js CHANGED
@@ -37,6 +37,8 @@ exports.activate = activate;
37
37
  exports.deactivate = deactivate;
38
38
  const vscode = __importStar(require("vscode"));
39
39
  const http = __importStar(require("http"));
40
+ const fs = __importStar(require("fs"));
41
+ const path = __importStar(require("path"));
40
42
  let feedbackViewProvider = null;
41
43
  let pollingInterval = null;
42
44
  function activate(context) {
@@ -511,809 +513,22 @@ class FeedbackViewProvider {
511
513
  });
512
514
  }
513
515
  _getHtmlForWebview(webview) {
514
- const config = vscode.workspace.getConfiguration('cursorFeedback');
515
- const language = config.get('language', 'zh-CN');
516
- const i18n = this._getI18n(language);
517
- // 获取本地 marked.js 文件的 URI
516
+ // 获取资源文件的 URI
518
517
  const markedJsUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'resources', 'vendor', 'marked.min.js'));
519
- return `<!DOCTYPE html>
520
- <html lang="${language}">
521
- <head>
522
- <meta charset="UTF-8">
523
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
524
- <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; script-src 'unsafe-inline' ${webview.cspSource}; img-src data:;">
525
- <title>Cursor Feedback</title>
526
- <!-- 使用本地 marked.js 进行 Markdown 渲染 -->
527
- <script src="${markedJsUri}"></script>
528
- <style>
529
- * {
530
- box-sizing: border-box;
531
- margin: 0;
532
- padding: 0;
533
- }
534
-
535
- body {
536
- font-family: var(--vscode-font-family);
537
- font-size: var(--vscode-font-size);
538
- color: var(--vscode-foreground);
539
- background-color: var(--vscode-sideBar-background);
540
- padding: 12px;
541
- min-height: 100vh;
542
- }
543
-
544
- .container {
545
- display: flex;
546
- flex-direction: column;
547
- gap: 12px;
548
- }
549
-
550
- .section {
551
- background: var(--vscode-input-background);
552
- border: 1px solid var(--vscode-input-border);
553
- border-radius: 6px;
554
- padding: 12px;
555
- }
556
-
557
- .section-title {
558
- font-weight: 600;
559
- margin-bottom: 8px;
560
- color: var(--vscode-foreground);
561
- display: flex;
562
- align-items: center;
563
- gap: 6px;
564
- }
565
-
566
- .summary-content {
567
- word-break: break-word;
568
- max-height: 300px;
569
- overflow-y: auto;
570
- font-size: 13px;
571
- line-height: 1.6;
572
- background: var(--vscode-textBlockQuote-background);
573
- padding: 12px;
574
- border-radius: 4px;
575
- }
576
-
577
- /* Markdown 样式 */
578
- .summary-content h1, .summary-content h2, .summary-content h3 {
579
- margin-top: 12px;
580
- margin-bottom: 8px;
581
- font-weight: 600;
582
- color: var(--vscode-foreground);
583
- }
584
- .summary-content h1 { font-size: 1.4em; border-bottom: 1px solid var(--vscode-panel-border); padding-bottom: 4px; }
585
- .summary-content h2 { font-size: 1.2em; }
586
- .summary-content h3 { font-size: 1.1em; }
587
- .summary-content h1:first-child, .summary-content h2:first-child, .summary-content h3:first-child { margin-top: 0; }
588
-
589
- .summary-content p { margin: 8px 0; }
590
- .summary-content ul, .summary-content ol { margin: 8px 0; padding-left: 20px; }
591
- .summary-content li { margin: 4px 0; }
592
-
593
- .summary-content code {
594
- background: var(--vscode-textCodeBlock-background);
595
- padding: 2px 6px;
596
- border-radius: 3px;
597
- font-family: var(--vscode-editor-font-family), monospace;
598
- font-size: 0.9em;
599
- }
600
-
601
- .summary-content pre {
602
- background: var(--vscode-textCodeBlock-background);
603
- padding: 10px;
604
- border-radius: 4px;
605
- overflow-x: auto;
606
- margin: 8px 0;
607
- }
608
- .summary-content pre code {
609
- background: none;
610
- padding: 0;
611
- }
612
-
613
- .summary-content blockquote {
614
- border-left: 3px solid var(--vscode-textLink-foreground);
615
- margin: 8px 0;
616
- padding: 4px 12px;
617
- color: var(--vscode-descriptionForeground);
618
- }
619
-
620
- .summary-content table {
621
- border-collapse: collapse;
622
- margin: 8px 0;
623
- width: 100%;
624
- }
625
- .summary-content th, .summary-content td {
626
- border: 1px solid var(--vscode-panel-border);
627
- padding: 6px 10px;
628
- text-align: left;
629
- }
630
- .summary-content th {
631
- background: var(--vscode-textCodeBlock-background);
632
- }
633
-
634
- .summary-content strong { font-weight: 600; }
635
- .summary-content em { font-style: italic; }
636
- .summary-content a { color: var(--vscode-textLink-foreground); }
637
- .summary-content hr { border: none; border-top: 1px solid var(--vscode-panel-border); margin: 12px 0; }
638
-
639
- .feedback-input {
640
- width: 100%;
641
- min-height: 120px;
642
- resize: vertical;
643
- background: var(--vscode-input-background);
644
- color: var(--vscode-input-foreground);
645
- border: 1px solid var(--vscode-input-border);
646
- border-radius: 4px;
647
- padding: 10px;
648
- font-family: inherit;
649
- font-size: 13px;
650
- line-height: 1.5;
651
- }
652
-
653
- .feedback-input:focus {
654
- outline: none;
655
- border-color: var(--vscode-focusBorder);
656
- }
657
-
658
-
659
- .submit-btn {
660
- width: 100%;
661
- padding: 12px;
662
- background: var(--vscode-button-background);
663
- color: var(--vscode-button-foreground);
664
- border: none;
665
- border-radius: 6px;
666
- cursor: pointer;
667
- font-weight: 600;
668
- font-size: 14px;
669
- transition: background 0.15s;
670
- }
671
-
672
- .submit-btn:hover {
673
- background: var(--vscode-button-hoverBackground);
674
- }
675
-
676
- .submit-btn:disabled {
677
- opacity: 0.5;
678
- cursor: not-allowed;
679
- }
680
-
681
- .status {
682
- text-align: center;
683
- padding: 30px 20px;
684
- color: var(--vscode-descriptionForeground);
685
- }
686
-
687
- .status-icon {
688
- font-size: 32px;
689
- margin-bottom: 12px;
690
- }
691
-
692
- .status.waiting .status-icon {
693
- animation: pulse 2s ease-in-out infinite;
694
- }
695
-
696
- @keyframes pulse {
697
- 0%, 100% { opacity: 1; }
698
- 50% { opacity: 0.5; }
699
- }
700
-
701
- .server-status {
702
- display: flex;
703
- align-items: center;
704
- gap: 6px;
705
- font-size: 11px;
706
- padding: 6px 10px;
707
- position: relative;
708
- background: var(--vscode-textBlockQuote-background);
709
- border-radius: 4px;
710
- margin-bottom: 12px;
711
- }
712
-
713
- .server-status .dot {
714
- width: 8px;
715
- height: 8px;
716
- border-radius: 50%;
717
- background: var(--vscode-errorForeground);
718
- }
719
-
720
- .server-status.connected .dot {
721
- background: var(--vscode-notificationsInfoIcon-foreground);
722
- }
723
-
724
- .debug-icon {
725
- margin-left: auto;
726
- cursor: pointer;
727
- opacity: 0.6;
728
- font-size: 12px;
729
- }
730
-
731
- .debug-icon:hover {
732
- opacity: 1;
733
- }
734
-
735
- .debug-tooltip {
736
- display: none;
737
- position: fixed;
738
- top: 50px;
739
- right: 10px;
740
- padding: 8px 10px;
741
- background: var(--vscode-editorWidget-background);
742
- border: 1px solid var(--vscode-editorWidget-border);
743
- border-radius: 4px;
744
- font-size: 11px;
745
- white-space: pre-wrap;
746
- z-index: 1000;
747
- min-width: 180px;
748
- max-width: 280px;
749
- box-shadow: 0 2px 8px rgba(0,0,0,0.3);
750
- }
751
-
752
- .debug-icon:hover + .debug-tooltip,
753
- .debug-tooltip:hover {
754
- display: block;
755
- }
756
-
757
- .attachments-area {
758
- margin-top: 10px;
759
- }
760
-
761
- .attachment-buttons {
762
- display: flex;
763
- justify-content: flex-end;
764
- gap: 4px;
765
- }
766
-
767
- .attachment-btn {
768
- display: inline-flex;
769
- align-items: center;
770
- justify-content: center;
771
- width: 28px;
772
- height: 28px;
773
- padding: 0;
774
- background: var(--vscode-button-secondaryBackground);
775
- color: var(--vscode-button-secondaryForeground);
776
- border: none;
777
- border-radius: 4px;
778
- cursor: pointer;
779
- font-size: 14px;
780
- }
781
-
782
- .attachment-btn:hover {
783
- background: var(--vscode-button-secondaryHoverBackground);
784
- }
785
-
786
- .attachment-btn[data-tooltip] {
787
- position: relative;
788
- }
789
-
790
- .attachment-btn[data-tooltip]:hover::after {
791
- content: attr(data-tooltip);
792
- position: absolute;
793
- bottom: 100%;
794
- right: 0;
795
- padding: 4px 8px;
796
- background: var(--vscode-editorWidget-background);
797
- color: var(--vscode-editorWidget-foreground);
798
- border: 1px solid var(--vscode-editorWidget-border);
799
- border-radius: 4px;
800
- font-size: 11px;
801
- white-space: nowrap;
802
- margin-bottom: 4px;
803
- z-index: 100;
804
- }
805
-
806
- .file-list {
807
- margin-top: 8px;
808
- }
809
-
810
- .file-item {
811
- display: flex;
812
- align-items: center;
813
- gap: 6px;
814
- padding: 4px 8px;
815
- background: var(--vscode-textBlockQuote-background);
816
- border-radius: 4px;
817
- margin-bottom: 4px;
818
- font-size: 11px;
819
- }
820
-
821
- .file-item .file-icon {
822
- flex-shrink: 0;
823
- }
824
-
825
- .file-item .file-path {
826
- flex: 1;
827
- overflow: hidden;
828
- text-overflow: ellipsis;
829
- white-space: nowrap;
830
- color: var(--vscode-descriptionForeground);
831
- }
832
-
833
- .file-item .file-remove {
834
- flex-shrink: 0;
835
- width: 16px;
836
- height: 16px;
837
- border-radius: 50%;
838
- background: var(--vscode-errorForeground);
839
- color: white;
840
- border: none;
841
- cursor: pointer;
842
- font-size: 10px;
843
- display: flex;
844
- align-items: center;
845
- justify-content: center;
846
- }
847
-
848
- .image-preview {
849
- display: flex;
850
- flex-wrap: wrap;
851
- gap: 8px;
852
- margin-top: 10px;
853
- }
854
-
855
- .image-preview-item {
856
- position: relative;
857
- }
858
-
859
- .image-preview img {
860
- max-width: 80px;
861
- max-height: 80px;
862
- border-radius: 4px;
863
- object-fit: cover;
864
- border: 1px solid var(--vscode-input-border);
865
- }
866
-
867
- .image-remove {
868
- position: absolute;
869
- top: -6px;
870
- right: -6px;
871
- width: 18px;
872
- height: 18px;
873
- border-radius: 50%;
874
- background: var(--vscode-errorForeground);
875
- color: white;
876
- border: none;
877
- cursor: pointer;
878
- font-size: 12px;
879
- display: flex;
880
- align-items: center;
881
- justify-content: center;
882
- }
883
-
884
- .hidden {
885
- display: none !important;
886
- }
887
-
888
- .project-info {
889
- font-size: 11px;
890
- color: var(--vscode-descriptionForeground);
891
- margin-top: 6px;
892
- padding: 6px 8px;
893
- background: var(--vscode-textBlockQuote-background);
894
- border-radius: 4px;
895
- word-break: break-all;
896
- }
897
-
898
- .timeout-info {
899
- font-size: 11px;
900
- color: var(--vscode-descriptionForeground);
901
- text-align: right;
902
- margin-top: 6px;
903
- }
904
- </style>
905
- </head>
906
- <body>
907
- <div class="container">
908
- <!-- 服务器状态 -->
909
- <div id="serverStatus" class="server-status">
910
- <span class="dot"></span>
911
- <span id="serverStatusText">${i18n.checking}</span>
912
- <span id="debugIcon" class="debug-icon" title="">🔍</span>
913
- <div id="debugTooltip" class="debug-tooltip"></div>
914
- </div>
915
-
916
- <!-- 等待状态 -->
917
- <div id="waitingStatus" class="status waiting">
918
- <div class="status-icon">⏳</div>
919
- <p>${i18n.waiting}</p>
920
- <p style="font-size: 11px; margin-top: 10px; opacity: 0.8;">${i18n.waitingHint}</p>
921
- </div>
922
-
923
- <!-- 反馈表单 -->
924
- <div id="feedbackForm" class="hidden">
925
- <!-- AI 摘要 -->
926
- <div class="section">
927
- <div class="section-title">📋 ${i18n.summary}</div>
928
- <div id="summaryContent" class="summary-content"></div>
929
- <div id="projectInfo" class="project-info"></div>
930
- </div>
931
-
932
- <!-- 反馈输入 -->
933
- <div class="section">
934
- <div class="section-title">💬 ${i18n.yourFeedback}</div>
935
- <textarea
936
- id="feedbackInput"
937
- class="feedback-input"
938
- placeholder="${i18n.placeholder}"
939
- ></textarea>
940
-
941
- <!-- 附件区域 -->
942
- <div class="attachments-area">
943
- <div class="attachment-buttons">
944
- <button id="uploadBtn" class="attachment-btn" data-tooltip="${i18n.uploadImage}">
945
- 🖼️
946
- </button>
947
- <button id="selectPathBtn" class="attachment-btn" data-tooltip="${i18n.selectPath}">
948
- 📁
949
- </button>
950
- </div>
951
- <input type="file" id="imageInput" accept="image/*" multiple style="display:none">
952
- <div id="imagePreview" class="image-preview"></div>
953
- <div id="fileList" class="file-list"></div>
954
- </div>
955
-
956
- <div id="timeoutInfo" class="timeout-info"></div>
957
- </div>
958
-
959
- <!-- 提交按钮 -->
960
- <button id="submitBtn" class="submit-btn">${i18n.submit} (Ctrl+Enter)</button>
961
- </div>
962
- </div>
963
-
964
- <script>
965
- const vscode = acquireVsCodeApi();
966
-
967
- // 使用 marked.js 渲染 Markdown
968
- function renderMarkdown(text) {
969
- if (!text) return '';
970
-
971
- try {
972
- // 配置 marked
973
- if (typeof marked !== 'undefined') {
974
- marked.setOptions({
975
- breaks: true, // 支持 GitHub 风格的换行
976
- gfm: true, // 启用 GitHub 风格 Markdown
977
- headerIds: false, // 禁用标题 ID(安全考虑)
978
- });
979
-
980
- // 使用 marked 解析
981
- return marked.parse(text);
982
- }
983
- } catch (e) {
984
- console.error('Markdown rendering error:', e);
985
- }
986
-
987
- // 降级:简单转义并保留换行
988
- return text
989
- .replace(/&/g, '&amp;')
990
- .replace(/</g, '&lt;')
991
- .replace(/>/g, '&gt;')
992
- .replace(/\\n/g, '<br>');
993
- }
994
-
995
- // DOM 元素
996
- const serverStatus = document.getElementById('serverStatus');
997
- const serverStatusText = document.getElementById('serverStatusText');
998
- const debugTooltip = document.getElementById('debugTooltip');
999
- const waitingStatus = document.getElementById('waitingStatus');
1000
- const feedbackForm = document.getElementById('feedbackForm');
1001
- const summaryContent = document.getElementById('summaryContent');
1002
- const projectInfo = document.getElementById('projectInfo');
1003
- const feedbackInput = document.getElementById('feedbackInput');
1004
- const submitBtn = document.getElementById('submitBtn');
1005
- const uploadBtn = document.getElementById('uploadBtn');
1006
- const selectPathBtn = document.getElementById('selectPathBtn');
1007
- const imageInput = document.getElementById('imageInput');
1008
- const imagePreview = document.getElementById('imagePreview');
1009
- const fileList = document.getElementById('fileList');
1010
- const timeoutInfo = document.getElementById('timeoutInfo');
1011
-
1012
- let uploadedImages = [];
1013
- let attachedFiles = [];
1014
- let currentRequestId = '';
1015
- let currentProjectDir = '';
1016
- let requestTimestamp = 0;
1017
- let requestTimeout = 300;
1018
- let countdownInterval = null;
1019
-
1020
- // 国际化文本
1021
- const i18n = ${JSON.stringify(i18n)};
1022
-
1023
-
1024
- // 图片上传
1025
- uploadBtn.addEventListener('click', () => imageInput.click());
1026
-
1027
- // 选择文件/文件夹
1028
- selectPathBtn.addEventListener('click', () => {
1029
- vscode.postMessage({ type: 'selectPath' });
1030
- });
1031
-
1032
- // 添加已选文件到列表
1033
- function addAttachedFile(path) {
1034
- if (attachedFiles.includes(path)) return;
1035
- attachedFiles.push(path);
1036
-
1037
- const item = document.createElement('div');
1038
- item.className = 'file-item';
1039
-
1040
- const icon = document.createElement('span');
1041
- icon.className = 'file-icon';
1042
- // 根据路径是否以 / 结尾或不包含扩展名来判断是否为文件夹
1043
- const isFolder = path.endsWith('/') || !path.split('/').pop().includes('.');
1044
- icon.textContent = isFolder ? '📁' : '📄';
1045
-
1046
- const pathSpan = document.createElement('span');
1047
- pathSpan.className = 'file-path';
1048
- pathSpan.textContent = path;
1049
- pathSpan.title = path;
1050
-
1051
- const removeBtn = document.createElement('button');
1052
- removeBtn.className = 'file-remove';
1053
- removeBtn.textContent = '×';
1054
- removeBtn.onclick = () => {
1055
- const idx = attachedFiles.indexOf(path);
1056
- if (idx > -1) attachedFiles.splice(idx, 1);
1057
- item.remove();
1058
- };
1059
-
1060
- item.appendChild(icon);
1061
- item.appendChild(pathSpan);
1062
- item.appendChild(removeBtn);
1063
- fileList.appendChild(item);
1064
- }
1065
-
1066
- imageInput.addEventListener('change', (e) => {
1067
- const files = e.target.files;
1068
- for (const file of files) {
1069
- addImageFile(file);
1070
- }
1071
- });
1072
-
1073
- // 添加图片文件到预览和上传列表
1074
- function addImageFile(file) {
1075
- const reader = new FileReader();
1076
- reader.onload = (e) => {
1077
- const base64 = e.target.result;
1078
- const imgData = {
1079
- name: file.name || ('pasted-image-' + Date.now() + '.png'),
1080
- data: base64.split(',')[1],
1081
- size: file.size
1082
- };
1083
- uploadedImages.push(imgData);
1084
-
1085
- // 显示预览
1086
- const container = document.createElement('div');
1087
- container.className = 'image-preview-item';
1088
-
1089
- const img = document.createElement('img');
1090
- img.src = base64;
1091
-
1092
- const removeBtn = document.createElement('button');
1093
- removeBtn.className = 'image-remove';
1094
- removeBtn.textContent = '×';
1095
- removeBtn.onclick = () => {
1096
- const index = uploadedImages.indexOf(imgData);
1097
- if (index > -1) {
1098
- uploadedImages.splice(index, 1);
1099
- }
1100
- container.remove();
1101
- };
1102
-
1103
- container.appendChild(img);
1104
- container.appendChild(removeBtn);
1105
- imagePreview.appendChild(container);
1106
- };
1107
- reader.readAsDataURL(file);
1108
- }
1109
-
1110
- // 粘贴图片支持 (Ctrl+V / Cmd+V)
1111
- document.addEventListener('paste', (e) => {
1112
- const items = e.clipboardData?.items;
1113
- if (!items) return;
1114
-
1115
- for (const item of items) {
1116
- if (item.type.startsWith('image/')) {
1117
- e.preventDefault();
1118
- const file = item.getAsFile();
1119
- if (file) {
1120
- addImageFile(file);
1121
- }
1122
- }
1123
- }
1124
- });
1125
-
1126
- // 更新倒计时
1127
- function updateCountdown() {
1128
- if (!requestTimestamp || !requestTimeout) return;
1129
-
1130
- const elapsed = Math.floor((Date.now() - requestTimestamp) / 1000);
1131
- const remaining = Math.max(0, requestTimeout - elapsed);
1132
- const minutes = Math.floor(remaining / 60);
1133
- const seconds = remaining % 60;
1134
-
1135
- timeoutInfo.textContent = i18n.timeout + ': ' + minutes + ':' + seconds.toString().padStart(2, '0');
1136
-
1137
- if (remaining <= 0) {
1138
- clearInterval(countdownInterval);
1139
- timeoutInfo.textContent = i18n.expired;
1140
- }
1141
- }
1142
-
1143
- // 提交反馈
1144
- function submitFeedback() {
1145
- const feedback = feedbackInput.value.trim();
1146
-
1147
- if (!currentRequestId) {
1148
- return;
1149
- }
1150
-
1151
- vscode.postMessage({
1152
- type: 'submitFeedback',
1153
- payload: {
1154
- requestId: currentRequestId,
1155
- interactive_feedback: feedback,
1156
- images: uploadedImages,
1157
- attachedFiles: attachedFiles,
1158
- project_directory: currentProjectDir
1159
- }
1160
- });
1161
-
1162
- // 重置表单
1163
- feedbackInput.value = '';
1164
- uploadedImages = [];
1165
- attachedFiles = [];
1166
- imagePreview.innerHTML = '';
1167
- fileList.innerHTML = '';
1168
- currentRequestId = '';
1169
-
1170
- if (countdownInterval) {
1171
- clearInterval(countdownInterval);
1172
- countdownInterval = null;
1173
- }
1174
- }
1175
-
1176
- submitBtn.addEventListener('click', submitFeedback);
1177
-
1178
- // 快捷键 Ctrl+Enter 提交
1179
- feedbackInput.addEventListener('keydown', (e) => {
1180
- if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
1181
- submitFeedback();
1182
- }
1183
- });
1184
-
1185
- // 接收来自插件的消息
1186
- window.addEventListener('message', event => {
1187
- const message = event.data;
1188
-
1189
- switch (message.type) {
1190
- case 'showFeedbackRequest':
1191
- waitingStatus.classList.add('hidden');
1192
- feedbackForm.classList.remove('hidden');
1193
-
1194
- currentRequestId = message.payload.requestId;
1195
- currentProjectDir = message.payload.projectDir;
1196
- requestTimestamp = message.payload.timestamp;
1197
- requestTimeout = message.payload.timeout;
1198
-
1199
- summaryContent.innerHTML = renderMarkdown(message.payload.summary);
1200
- projectInfo.textContent = '📁 ' + message.payload.projectDir;
1201
- feedbackInput.focus();
1202
-
1203
- // 启动倒计时
1204
- if (countdownInterval) {
1205
- clearInterval(countdownInterval);
1206
- }
1207
- updateCountdown();
1208
- countdownInterval = setInterval(updateCountdown, 1000);
1209
- break;
1210
-
1211
- case 'showWaiting':
1212
- feedbackForm.classList.add('hidden');
1213
- waitingStatus.classList.remove('hidden');
1214
-
1215
- if (countdownInterval) {
1216
- clearInterval(countdownInterval);
1217
- countdownInterval = null;
1218
- }
1219
- break;
1220
-
1221
- case 'serverStatus':
1222
- if (message.payload.connected) {
1223
- serverStatus.classList.add('connected');
1224
- serverStatusText.textContent = i18n.connected;
1225
- } else {
1226
- serverStatus.classList.remove('connected');
1227
- serverStatusText.textContent = i18n.disconnected;
1228
- }
1229
- break;
1230
-
1231
- case 'updateDebugInfo':
1232
- const debug = message.payload;
1233
- let debugText = '🔍 调试信息\\n';
1234
- debugText += '━━━━━━━━━━━━\\n';
1235
- debugText += '扫描端口: ' + debug.portRange + '\\n';
1236
- debugText += '工作区: ' + debug.workspacePath + '\\n';
1237
- debugText += '当前端口: ' + (debug.activePort || '-') + '\\n';
1238
- debugText += '已连接: ' + (debug.connectedPorts.length > 0 ? debug.connectedPorts.join(', ') : '无') + '\\n';
1239
- debugText += '状态: ' + debug.lastStatus;
1240
- debugTooltip.textContent = debugText;
1241
- break;
1242
-
1243
- case 'filesSelected':
1244
- if (message.payload.paths) {
1245
- for (const path of message.payload.paths) {
1246
- addAttachedFile(path);
1247
- }
1248
- }
1249
- break;
1250
- }
1251
- });
1252
-
1253
- // 定期检查服务器状态
1254
- setInterval(() => {
1255
- vscode.postMessage({ type: 'checkServer' });
1256
- }, 5000);
1257
-
1258
- // 通知插件 WebView 已准备就绪
1259
- vscode.postMessage({ type: 'ready' });
1260
- vscode.postMessage({ type: 'checkServer' });
1261
- </script>
1262
- </body>
1263
- </html>`;
1264
- }
1265
- /**
1266
- * 获取国际化文本
1267
- */
1268
- _getI18n(lang) {
1269
- const translations = {
1270
- 'zh-CN': {
1271
- waiting: '等待 AI 请求反馈...',
1272
- waitingHint: '当 AI 需要您的反馈时,这里会显示输入界面',
1273
- summary: 'AI 工作摘要',
1274
- yourFeedback: '您的反馈',
1275
- placeholder: '请输入您的反馈...',
1276
- uploadImage: '上传图片',
1277
- selectPath: '选择文件/文件夹',
1278
- submit: '提交反馈',
1279
- timeout: '剩余时间',
1280
- expired: '已超时',
1281
- checking: '检查连接...',
1282
- connected: 'MCP Server 已连接',
1283
- disconnected: 'MCP Server 未连接',
1284
- },
1285
- 'zh-TW': {
1286
- waiting: '等待 AI 請求回饋...',
1287
- waitingHint: '當 AI 需要您的回饋時,這裡會顯示輸入介面',
1288
- summary: 'AI 工作摘要',
1289
- yourFeedback: '您的回饋',
1290
- placeholder: '請輸入您的回饋...',
1291
- uploadImage: '上傳圖片',
1292
- selectPath: '選擇檔案/資料夾',
1293
- submit: '提交回饋',
1294
- timeout: '剩餘時間',
1295
- expired: '已超時',
1296
- checking: '檢查連接...',
1297
- connected: 'MCP Server 已連接',
1298
- disconnected: 'MCP Server 未連接',
1299
- },
1300
- 'en': {
1301
- waiting: 'Waiting for AI feedback request...',
1302
- waitingHint: 'The feedback interface will appear when AI needs your input',
1303
- summary: 'AI Work Summary',
1304
- yourFeedback: 'Your Feedback',
1305
- placeholder: 'Enter your feedback...',
1306
- uploadImage: 'Upload Image',
1307
- selectPath: 'Select File/Folder',
1308
- submit: 'Submit Feedback',
1309
- timeout: 'Time remaining',
1310
- expired: 'Expired',
1311
- checking: 'Checking connection...',
1312
- connected: 'MCP Server connected',
1313
- disconnected: 'MCP Server disconnected',
1314
- }
1315
- };
1316
- return translations[lang] || translations['zh-CN'];
518
+ const stylesCssUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'dist', 'webview', 'styles.css'));
519
+ const scriptJsUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'dist', 'webview', 'script.js'));
520
+ // 读取 HTML 模板
521
+ const htmlTemplatePath = path.join(this._extensionUri.fsPath, 'dist', 'webview', 'index.html');
522
+ let htmlTemplate = fs.readFileSync(htmlTemplatePath, 'utf-8');
523
+ // CSP 策略
524
+ const csp = `default-src 'none'; style-src ${webview.cspSource}; script-src 'unsafe-inline' ${webview.cspSource}; img-src data:;`;
525
+ // 替换占位符
526
+ htmlTemplate = htmlTemplate
527
+ .replace(/\{\{CSP\}\}/g, csp)
528
+ .replace(/\{\{MARKED_JS_URI\}\}/g, markedJsUri.toString())
529
+ .replace(/\{\{STYLES_CSS_URI\}\}/g, stylesCssUri.toString())
530
+ .replace(/\{\{SCRIPT_JS_URI\}\}/g, scriptJsUri.toString());
531
+ return htmlTemplate;
1317
532
  }
1318
533
  }
1319
534
  FeedbackViewProvider.viewType = 'cursorFeedback.feedbackView';