feat: Add alerts for attachment extraction status and improve UI state handling after processing.

feat: add page and document level support for adding atttachments
This commit is contained in:
abdullahalam123 committed 2025-11-19 13:19:14 +05:30
1 parent 98467f4809
commit bd985f06f8
4 files changed
+477 -280

No files matched your search

+59 -7
View File
@@ -43,6 +43,22 @@ export async function addAttachments() {
return;
}
const attachmentLevel = (
document.querySelector('input[name="attachment-level"]:checked') as HTMLInputElement
)?.value || 'document';
let pageRange: string = '';
if (attachmentLevel === 'page') {
const pageRangeInput = document.getElementById('attachment-page-range') as HTMLInputElement;
pageRange = pageRangeInput?.value?.trim() || '';
if (!pageRange) {
showAlert('Error', 'Please specify a page range for page-level attachments.');
return;
}
}
showLoader('Embedding files into PDF...');
try {
const pdfFile = state.files[0];
@@ -54,7 +70,7 @@ export async function addAttachments() {
for (let i = 0; i < attachments.length; i++) {
const file = attachments[i];
showLoader(`Reading ${file.name} (${i + 1}/${attachments.length})...`);
const fileBuffer = (await readFileAsArrayBuffer(file)) as ArrayBuffer;
attachmentBuffers.push(fileBuffer);
attachmentNames.push(file.name);
@@ -66,7 +82,9 @@ export async function addAttachments() {
command: 'add-attachments',
pdfBuffer: pdfBuffer,
attachmentBuffers: attachmentBuffers,
attachmentNames: attachmentNames
attachmentNames: attachmentNames,
attachmentLevel: attachmentLevel,
pageRange: pageRange
};
const transferables = [pdfBuffer, ...attachmentBuffers];
@@ -89,6 +107,8 @@ function clearAttachments() {
const processBtn = document.getElementById(
'process-btn'
) as HTMLButtonElement;
const attachmentLevelOptions = document.getElementById('attachment-level-options');
const pageRangeWrapper = document.getElementById('page-range-wrapper');
if (fileListDiv) fileListDiv.innerHTML = '';
if (attachmentInput) attachmentInput.value = '';
@@ -96,14 +116,20 @@ function clearAttachments() {
processBtn.disabled = true;
processBtn.classList.add('hidden');
}
if (attachmentLevelOptions) {
attachmentLevelOptions.classList.add('hidden');
}
if (pageRangeWrapper) {
pageRangeWrapper.classList.add('hidden');
}
const documentRadio = document.querySelector('input[name="attachment-level"][value="document"]') as HTMLInputElement;
if (documentRadio) {
documentRadio.checked = true;
}
}
let isSetup = false; // Prevent duplicate setup
export function setupAddAttachmentsTool() {
if (isSetup) return; // Already set up
isSetup = true;
const optionsDiv = document.getElementById('attachment-options');
const attachmentInput = document.getElementById(
'attachment-files-input'
@@ -112,6 +138,9 @@ export function setupAddAttachmentsTool() {
const processBtn = document.getElementById(
'process-btn'
) as HTMLButtonElement;
const attachmentLevelOptions = document.getElementById('attachment-level-options');
const pageRangeWrapper = document.getElementById('page-range-wrapper');
const totalPagesSpan = document.getElementById('attachment-total-pages');
if (!optionsDiv || !attachmentInput || !fileListDiv || !processBtn) {
console.error('Attachment tool UI elements not found.');
@@ -125,6 +154,13 @@ export function setupAddAttachmentsTool() {
optionsDiv.classList.remove('hidden');
if (totalPagesSpan && state.pdfDoc) {
totalPagesSpan.textContent = state.pdfDoc.getPageCount().toString();
}
if (attachmentInput.dataset.listenerAttached) return;
attachmentInput.dataset.listenerAttached = 'true';
attachmentInput.addEventListener('change', (e) => {
const files = (e.target as HTMLInputElement).files;
if (files && files.length > 0) {
@@ -149,6 +185,10 @@ export function setupAddAttachmentsTool() {
fileListDiv.appendChild(div);
});
if (attachmentLevelOptions) {
attachmentLevelOptions.classList.remove('hidden');
}
processBtn.disabled = false;
processBtn.classList.remove('hidden');
} else {
@@ -156,5 +196,17 @@ export function setupAddAttachmentsTool() {
}
});
const attachmentLevelRadios = document.querySelectorAll('input[name="attachment-level"]');
attachmentLevelRadios.forEach((radio) => {
radio.addEventListener('change', (e) => {
const value = (e.target as HTMLInputElement).value;
if (value === 'page' && pageRangeWrapper) {
pageRangeWrapper.classList.remove('hidden');
} else if (pageRangeWrapper) {
pageRangeWrapper.classList.add('hidden');
}
});
});
processBtn.onclick = addAttachments;
}
+69 -14
View File
@@ -1,5 +1,6 @@
import { downloadFile, formatBytes } from '../utils/helpers.js';
import { state } from '../state.js';
import { showAlert } from '../ui.js';
import JSZip from 'jszip';
const worker = new Worker('/workers/extract-attachments.worker.js');
@@ -24,7 +25,7 @@ export async function extractAttachments() {
document.getElementById('process-btn')?.classList.add('opacity-50', 'cursor-not-allowed');
document.getElementById('process-btn')?.setAttribute('disabled', 'true');
showStatus('Reading files (Main Thread)...', 'info');
try {
@@ -66,9 +67,33 @@ worker.onmessage = (e: MessageEvent<ExtractAttachmentResponse>) => {
if (e.data.status === 'success') {
const attachments = e.data.attachments;
if (attachments.length === 0) {
showAlert('No Attachments', 'The PDF file(s) do not contain any attachments to extract.');
state.files = [];
state.pdfDoc = null;
const fileDisplayArea = document.getElementById('file-display-area');
if (fileDisplayArea) {
fileDisplayArea.innerHTML = '';
}
const fileInput = document.getElementById('file-input') as HTMLInputElement;
if (fileInput) {
fileInput.value = '';
}
const fileControls = document.getElementById('file-controls');
if (fileControls) {
fileControls.classList.add('hidden');
}
return;
}
const zip = new JSZip();
let totalSize = 0;
for (const attachment of attachments) {
zip.file(attachment.name, new Uint8Array(attachment.data));
totalSize += attachment.data.byteLength;
@@ -76,28 +101,59 @@ worker.onmessage = (e: MessageEvent<ExtractAttachmentResponse>) => {
zip.generateAsync({ type: 'blob' }).then((zipBlob) => {
downloadFile(zipBlob, 'extracted-attachments.zip');
showAlert('Success', `${attachments.length} attachment(s) extracted successfully!`);
showStatus(
`Extraction completed! ${attachments.length} attachment(s) in zip file (${formatBytes(totalSize)}). Download started.`,
'success'
);
state.files = [];
state.pdfDoc = null;
const fileDisplayArea = document.getElementById('file-display-area');
if (fileDisplayArea) {
fileDisplayArea.innerHTML = '';
fileDisplayArea.classList.add('hidden');
}
const fileInput = document.getElementById('file-input') as HTMLInputElement;
if (fileInput) {
fileInput.value = '';
}
document.getElementById('process-btn')?.classList.add('opacity-50', 'cursor-not-allowed');
document.getElementById('process-btn')?.setAttribute('disabled', 'true');
const fileControls = document.getElementById('file-controls');
if (fileControls) {
fileControls.classList.add('hidden');
}
});
} else if (e.data.status === 'error') {
const errorMessage = e.data.message || 'Unknown error occurred in worker.';
console.error('Worker Error:', errorMessage);
showStatus(`Error: ${errorMessage}`, 'error');
if (errorMessage.includes('No attachments were found')) {
showAlert('No Attachments', 'The PDF file(s) do not contain any attachments to extract.');
state.files = [];
state.pdfDoc = null;
const fileDisplayArea = document.getElementById('file-display-area');
if (fileDisplayArea) {
fileDisplayArea.innerHTML = '';
}
const fileInput = document.getElementById('file-input') as HTMLInputElement;
if (fileInput) {
fileInput.value = '';
}
const fileControls = document.getElementById('file-controls');
if (fileControls) {
fileControls.classList.add('hidden');
}
} else {
showStatus(`Error: ${errorMessage}`, 'error');
}
}
};
@@ -111,15 +167,14 @@ worker.onerror = (error) => {
function showStatus(message: string, type: 'success' | 'error' | 'info' = 'info') {
const statusMessage = document.getElementById('status-message') as HTMLElement;
if (!statusMessage) return;
statusMessage.textContent = message;
statusMessage.className = `mt-4 p-3 rounded-lg text-sm ${
type === 'success'
? 'bg-green-900 text-green-200'
: type === 'error'
? 'bg-red-900 text-red-200'
: 'bg-blue-900 text-blue-200'
}`;
statusMessage.className = `mt-4 p-3 rounded-lg text-sm ${type === 'success'
? 'bg-green-900 text-green-200'
: type === 'error'
? 'bg-red-900 text-red-200'
: 'bg-blue-900 text-blue-200'
}`;
statusMessage.classList.remove('hidden');
}