From e7946725b545a4932f60e7fbdbbaa78c72fa6380 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 07:53:10 +0000 Subject: [PATCH 1/3] Initial plan From 0b6f3ab63789e72a64af087f9512ed743d8fe202 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 07:56:47 +0000 Subject: [PATCH 2/3] Add drag and drop support for patch files and upgrade to v1.0.1 Co-authored-by: unknowIfGuestInDream <57802425+unknowIfGuestInDream@users.noreply.github.com> --- package-lock.json | 7 ++- package.json | 2 +- src/chrome/manifest.json | 2 +- src/common/_locales/en/messages.json | 5 +- src/common/_locales/ja/messages.json | 5 +- src/common/_locales/zh/messages.json | 5 +- src/common/diff-viewer.html | 2 +- src/common/diff-viewer.js | 88 ++++++++++++++++++++++++++++ src/common/styles.css | 7 +++ src/edge/manifest.json | 2 +- 10 files changed, 116 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2851112..ebd3454 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,18 @@ { "name": "patchreader-extension", - "version": "1.0.0", + "version": "1.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "patchreader-extension", - "version": "1.0.0", + "version": "1.0.1", "license": "MIT", "devDependencies": { "diff2html": "^3.4.55" + }, + "engines": { + "node": ">=22.0.0" } }, "node_modules/@profoundlogic/hogan": { diff --git a/package.json b/package.json index c77f738..66f5796 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "patchreader-extension", - "version": "1.0.0", + "version": "1.0.1", "description": "Browser extension to read and render patch/diff files using diff2html", "main": "index.js", "engines": { diff --git a/src/chrome/manifest.json b/src/chrome/manifest.json index 0dd091f..b5a01f5 100644 --- a/src/chrome/manifest.json +++ b/src/chrome/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "Patch Reader", - "version": "1.0.0", + "version": "1.0.1", "description": "Read and render patch/diff files with diff2html", "default_locale": "en", "icons": { diff --git a/src/common/_locales/en/messages.json b/src/common/_locales/en/messages.json index ce6313f..958e73b 100644 --- a/src/common/_locales/en/messages.json +++ b/src/common/_locales/en/messages.json @@ -30,7 +30,7 @@ "message": "After entering or uploading patch/diff content, the rendered result will be displayed here" }, "inputPlaceholder": { - "message": "Paste patch/diff content here, or click the upload button to upload .patch or .diff files..." + "message": "Paste patch/diff content here, drag and drop files here, or click the upload button to upload .patch or .diff files..." }, "fileReadFailed": { "message": "File read failed" @@ -40,5 +40,8 @@ }, "viewed": { "message": "Viewed" + }, + "invalidFileType": { + "message": "Please drop .patch, .diff, or .txt files" } } diff --git a/src/common/_locales/ja/messages.json b/src/common/_locales/ja/messages.json index ecff61f..549055e 100644 --- a/src/common/_locales/ja/messages.json +++ b/src/common/_locales/ja/messages.json @@ -30,7 +30,7 @@ "message": "patch/diff 内容を入力またはアップロードすると、レンダリング結果がここに表示されます" }, "inputPlaceholder": { - "message": "ここに patch/diff 内容を貼り付けるか、アップロードボタンをクリックして .patch または .diff ファイルをアップロードしてください..." + "message": "ここに patch/diff 内容を貼り付けるか、ファイルをここにドラッグアンドドロップするか、アップロードボタンをクリックして .patch または .diff ファイルをアップロードしてください..." }, "fileReadFailed": { "message": "ファイルの読み取りに失敗しました" @@ -40,5 +40,8 @@ }, "viewed": { "message": "確認済み" + }, + "invalidFileType": { + "message": ".patch、.diff、または .txt ファイルをドロップしてください" } } diff --git a/src/common/_locales/zh/messages.json b/src/common/_locales/zh/messages.json index fb9e27b..15027e0 100644 --- a/src/common/_locales/zh/messages.json +++ b/src/common/_locales/zh/messages.json @@ -30,7 +30,7 @@ "message": "输入或上传 patch/diff 内容后,渲染结果将显示在这里" }, "inputPlaceholder": { - "message": "在此粘贴 patch/diff 内容,或点击上传文件按钮上传 .patch 或 .diff 文件..." + "message": "在此粘贴 patch/diff 内容,拖拽文件到此处,或点击上传文件按钮上传 .patch 或 .diff 文件..." }, "fileReadFailed": { "message": "文件读取失败" @@ -40,5 +40,8 @@ }, "viewed": { "message": "已查看" + }, + "invalidFileType": { + "message": "请拖拽 .patch、.diff 或 .txt 文件" } } diff --git a/src/common/diff-viewer.html b/src/common/diff-viewer.html index 2fb911c..05e9985 100644 --- a/src/common/diff-viewer.html +++ b/src/common/diff-viewer.html @@ -30,7 +30,7 @@

Input Patch/Diff Content

- +
diff --git a/src/common/diff-viewer.js b/src/common/diff-viewer.js index c1bbfce..18cb0ca 100644 --- a/src/common/diff-viewer.js +++ b/src/common/diff-viewer.js @@ -53,6 +53,94 @@ // Delegate click events for viewed checkboxes diffOutput.addEventListener('change', handleViewedCheckboxChange); + + // Drag and drop event listeners for the textarea + diffInput.addEventListener('dragover', handleDragOver); + diffInput.addEventListener('dragenter', handleDragEnter); + diffInput.addEventListener('dragleave', handleDragLeave); + diffInput.addEventListener('drop', handleDrop); + } + + // Handle drag over event + function handleDragOver(event) { + event.preventDefault(); + event.stopPropagation(); + } + + // Handle drag enter event + function handleDragEnter(event) { + event.preventDefault(); + event.stopPropagation(); + diffInput.classList.add('drag-over'); + } + + // Handle drag leave event + function handleDragLeave(event) { + event.preventDefault(); + event.stopPropagation(); + diffInput.classList.remove('drag-over'); + } + + // Handle drop event + function handleDrop(event) { + event.preventDefault(); + event.stopPropagation(); + diffInput.classList.remove('drag-over'); + + const files = event.dataTransfer.files; + if (!files || files.length === 0) return; + + // Filter for valid file types + const validFiles = Array.from(files).filter(file => { + const ext = file.name.toLowerCase(); + return ext.endsWith('.patch') || ext.endsWith('.diff') || ext.endsWith('.txt'); + }); + + if (validFiles.length === 0) { + const errorMsg = window.i18n ? window.i18n.getMessage('invalidFileType') : 'Please drop .patch, .diff, or .txt files'; + showError(errorMsg); + return; + } + + processDroppedFiles(validFiles); + } + + // Process dropped files + function processDroppedFiles(files) { + const readPromises = files.map(file => { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = (e) => resolve({ + name: file.name, + content: e.target.result + }); + reader.onerror = () => reject(new Error(`Failed to read file: ${file.name}`)); + reader.readAsText(file); + }); + }); + + Promise.all(readPromises) + .then(results => { + // Combine all file contents + const combinedContent = results.map(r => { + // Add file header comment if multiple files + if (results.length > 1) { + return `# File: ${r.name}\n${r.content}`; + } + return r.content; + }).join('\n\n'); + + diffInput.value = combinedContent; + // Clear viewed files when new content is loaded + viewedFiles.clear(); + saveViewedFiles(); + renderDiff(); + saveState(); + }) + .catch(error => { + const errorMsg = window.i18n ? window.i18n.getMessage('fileReadFailed') : 'File read failed'; + showError(`${errorMsg}: ${error.message}`); + }); } // Handle viewed checkbox change diff --git a/src/common/styles.css b/src/common/styles.css index 4084d1d..75f155a 100644 --- a/src/common/styles.css +++ b/src/common/styles.css @@ -173,6 +173,13 @@ body { color: #6a737d; } +/* Drag and drop state */ +#diff-input.drag-over { + background-color: #dafbe1; + border: 2px dashed #238636; + outline: none; +} + /* Output Section */ .output-section { flex: 1; diff --git a/src/edge/manifest.json b/src/edge/manifest.json index 0dd091f..b5a01f5 100644 --- a/src/edge/manifest.json +++ b/src/edge/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "Patch Reader", - "version": "1.0.0", + "version": "1.0.1", "description": "Read and render patch/diff files with diff2html", "default_locale": "en", "icons": { From ede899e320a1866d280bb1f215a970ac9f9ab33c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 07:59:55 +0000 Subject: [PATCH 3/3] Refactor drag and drop code to address code review feedback Co-authored-by: unknowIfGuestInDream <57802425+unknowIfGuestInDream@users.noreply.github.com> --- src/common/diff-viewer.js | 57 +++++++++++---------------------------- 1 file changed, 16 insertions(+), 41 deletions(-) diff --git a/src/common/diff-viewer.js b/src/common/diff-viewer.js index 18cb0ca..4397879 100644 --- a/src/common/diff-viewer.js +++ b/src/common/diff-viewer.js @@ -61,6 +61,9 @@ diffInput.addEventListener('drop', handleDrop); } + // Track drag enter/leave to handle nested elements + let dragCounter = 0; + // Handle drag over event function handleDragOver(event) { event.preventDefault(); @@ -71,6 +74,7 @@ function handleDragEnter(event) { event.preventDefault(); event.stopPropagation(); + dragCounter++; diffInput.classList.add('drag-over'); } @@ -78,13 +82,17 @@ function handleDragLeave(event) { event.preventDefault(); event.stopPropagation(); - diffInput.classList.remove('drag-over'); + dragCounter--; + if (dragCounter === 0) { + diffInput.classList.remove('drag-over'); + } } // Handle drop event function handleDrop(event) { event.preventDefault(); event.stopPropagation(); + dragCounter = 0; diffInput.classList.remove('drag-over'); const files = event.dataTransfer.files; @@ -92,8 +100,8 @@ // Filter for valid file types const validFiles = Array.from(files).filter(file => { - const ext = file.name.toLowerCase(); - return ext.endsWith('.patch') || ext.endsWith('.diff') || ext.endsWith('.txt'); + const fileName = file.name.toLowerCase(); + return fileName.endsWith('.patch') || fileName.endsWith('.diff') || fileName.endsWith('.txt'); }); if (validFiles.length === 0) { @@ -102,12 +110,12 @@ return; } - processDroppedFiles(validFiles); + processFiles(validFiles); } - // Process dropped files - function processDroppedFiles(files) { - const readPromises = files.map(file => { + // Process files (shared by drag-drop and file upload) + function processFiles(files) { + const readPromises = Array.from(files).map(file => { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = (e) => resolve({ @@ -235,40 +243,7 @@ const files = event.target.files; if (!files || files.length === 0) return; - const readPromises = Array.from(files).map(file => { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onload = (e) => resolve({ - name: file.name, - content: e.target.result - }); - reader.onerror = () => reject(new Error(`Failed to read file: ${file.name}`)); - reader.readAsText(file); - }); - }); - - Promise.all(readPromises) - .then(results => { - // Combine all file contents - const combinedContent = results.map(r => { - // Add file header comment if multiple files - if (results.length > 1) { - return `# File: ${r.name}\n${r.content}`; - } - return r.content; - }).join('\n\n'); - - diffInput.value = combinedContent; - // Clear viewed files when new content is loaded - viewedFiles.clear(); - saveViewedFiles(); - renderDiff(); - saveState(); - }) - .catch(error => { - const errorMsg = window.i18n ? window.i18n.getMessage('fileReadFailed') : 'File read failed'; - showError(`${errorMsg}: ${error.message}`); - }); + processFiles(files); // Reset file input fileInput.value = '';