Alph4Centauri/email-summerization-ui
0
1const BACKEND_URL = 'https://Alph4Centauri-email-summarization-api.hf.space';2 3Office.onReady((info) => {4 if (info.host === Office.HostType.Outlook) {5 if (document.readyState === 'loading') {6 document.addEventListener('DOMContentLoaded', init);7 } else {8 init();9 }10 }11});12 13function init() {14 const summarizeBtn = document.getElementById('summarize-btn');15 const copyBtn = document.getElementById('copy-btn');16 17 if (summarizeBtn) summarizeBtn.onclick = generateSummary;18 if (copyBtn) copyBtn.onclick = copySummary;19 20 initParsingOptions();21}22 23function initParsingOptions() {24 const includeAttachments = document.getElementById('include-attachments');25 const includeEmail = document.getElementById('include-email');26 const attachmentOptions = document.getElementById('attachment-options');27 const sectionOptions = document.getElementById('section-options');28 29 if (includeAttachments && attachmentOptions) {30 includeAttachments.addEventListener('change', function() {31 attachmentOptions.classList.toggle('disabled', !this.checked);32 });33 }34 35 if (includeEmail && includeAttachments && sectionOptions) {36 function updateSectionOptions() {37 const bothSelected = includeEmail.checked && includeAttachments.checked;38 sectionOptions.classList.toggle('disabled', !bothSelected);39 40 if (!bothSelected) {41 document.querySelector('input[name="output-mode"][value="combined"]').checked = true;42 }43 }44 45 includeEmail.addEventListener('change', updateSectionOptions);46 includeAttachments.addEventListener('change', updateSectionOptions);47 updateSectionOptions();48 }49}50 51function showElement(id) {52 document.getElementById(id).style.display = 'block';53}54 55function hideElement(id) {56 document.getElementById(id).style.display = 'none';57}58 59function showMessage(type, message) {60 ['info-bar', 'error-message', 'success-message'].forEach(hideElement);61 62 const elementId = type === 'info' ? 'info-bar' : type + '-message';63 const element = document.getElementById(elementId);64 65 if (element) {66 element.textContent = message;67 showElement(elementId);68 69 if (type === 'success') {70 setTimeout(() => hideElement(elementId), 3000);71 }72 }73}74 75function getEmailContent() {76 return new Promise((resolve, reject) => {77 Office.context.mailbox.item.body.getAsync(Office.CoercionType.Text, (result) => {78 if (result.status === Office.AsyncResultStatus.Succeeded) {79 getAttachments()80 .then(attachments => resolve({ emailBody: result.value, attachments }))81 .catch(reject);82 } else {83 reject(new Error('Failed to get email body: ' + result.error.message));84 }85 });86 });87}88 89function getAttachments() {90 return new Promise((resolve) => {91 const attachments = Office.context.mailbox.item.attachments;92 93 if (!attachments || attachments.length === 0) {94 resolve([]);95 return;96 }97 98 const promises = attachments.map(attachment => {99 return new Promise((resolveAtt) => {100 if (attachment.attachmentType === Office.MailboxEnums.AttachmentType.File) {101 Office.context.mailbox.item.getAttachmentContentAsync(attachment.id, (result) => {102 if (result.status === Office.AsyncResultStatus.Succeeded) {103 resolveAtt({104 name: attachment.name,105 content: result.value.content,106 contentType: result.value.format107 });108 } else {109 resolveAtt({110 name: attachment.name,111 content: null,112 error: result.error.message113 });114 }115 });116 } else {117 resolveAtt({118 name: attachment.name,119 content: null,120 type: 'item'121 });122 }123 });124 });125 126 Promise.all(promises).then(resolve);127 });128}129 130async function sendToBackend(emailData) {131 const summaryType = document.getElementById('summary-type').value;132 const includeEmail = document.getElementById('include-email').checked;133 const includeAttachments = document.getElementById('include-attachments').checked;134 const attachmentMode = document.querySelector('input[name="attachment-mode"]:checked')?.value || 'combined';135 const outputMode = document.querySelector('input[name="output-mode"]:checked')?.value || 'combined';136 137 const requestData = {138 ...emailData,139 summaryType,140 parsingOptions: { includeEmail, includeAttachments, attachmentMode, outputMode }141 };142 143 const response = await fetch(`${BACKEND_URL}/summarize`, {144 method: 'POST',145 headers: { 'Content-Type': 'application/json' },146 body: JSON.stringify(requestData)147 });148 149 const result = await response.json();150 151 if (!response.ok) {152 throw new Error(result.error);153 }154 155 if (!result.success) {156 throw new Error(result.error || 'Unknown error occurred');157 }158 159 return result;160}161 162async function generateSummary() {163 const summarizeBtn = document.getElementById('summarize-btn');164 const summaryContainer = document.getElementById('summary-container');165 const summaryContent = document.getElementById('summary-content');166 const summaryTypeDropdown = document.getElementById('summary-type');167 168 if (!summarizeBtn || !summaryContent || !summaryTypeDropdown) {169 showMessage('error', 'UI elements not found. Please refresh the add-in.');170 return;171 }172 173 const includeEmail = document.getElementById('include-email')?.checked || false;174 const includeAttachments = document.getElementById('include-attachments')?.checked || false;175 176 if (!includeEmail && !includeAttachments) {177 showMessage('error', 'Please select at least one content type to summarize.');178 return;179 }180 181 try {182 const selectedOption = summaryTypeDropdown.options[summaryTypeDropdown.selectedIndex];183 const summaryTypeName = selectedOption.text;184 const outputMode = document.querySelector('input[name="output-mode"]:checked')?.value || 'combined';185 const attachmentMode = document.querySelector('input[name="attachment-mode"]:checked')?.value || 'combined';186 187 summarizeBtn.disabled = true;188 summarizeBtn.textContent = 'Processing...';189 summaryTypeDropdown.disabled = true;190 toggleParsingControls(true);191 192 showElement('loading');193 ['summary-container', 'info-bar', 'error-message', 'success-message'].forEach(hideElement);194 195 showMessage('info', 'Extracting content...');196 const emailData = await getEmailContent();197 198 const contentDesc = [];199 if (includeEmail) contentDesc.push('email body');200 if (includeAttachments) {201 const attachCount = emailData.attachments.length;202 if (attachCount > 0) {203 const mode = attachmentMode === 'separate' ? 'individually' : 'combined';204 contentDesc.push(`${attachCount} attachments (${mode})`);205 } else {206 contentDesc.push('attachments (none found)');207 }208 }209 210 const processingMsg = `Generating ${summaryTypeName} for: ${contentDesc.join(' + ')}${outputMode === 'sections' ? ' (separate sections)' : ''}...`;211 showMessage('info', processingMsg);212 213 const result = await sendToBackend(emailData);214 215 if (result.summaryData?.sections) {216 displaySections(result.summaryData.sections);217 } else {218 summaryContent.textContent = result.summary || 'No summary generated.';219 }220 221 if (summaryContainer) showElement('summary-container');222 223 let successMsg = `${summaryTypeName} generated successfully`;224 if (result.attachmentsProcessed > 0) {225 successMsg += ` (${result.attachmentsProcessed} attachments processed)`;226 }227 showMessage('success', successMsg);228 229 } catch (error) {230 showMessage('error', error.message);231 } finally {232 summarizeBtn.disabled = false;233 summarizeBtn.textContent = 'Generate Summary';234 summaryTypeDropdown.disabled = false;235 toggleParsingControls(false);236 hideElement('loading');237 }238}239 240function toggleParsingControls(disabled) {241 const controls = [242 'include-email',243 'include-attachments',244 ...document.querySelectorAll('input[name="attachment-mode"]'),245 ...document.querySelectorAll('input[name="output-mode"]')246 ];247 248 controls.forEach(ctrl => {249 const element = typeof ctrl === 'string' ? document.getElementById(ctrl) : ctrl;250 if (element) element.disabled = disabled;251 });252}253 254function displaySections(sections) {255 const summaryContent = document.getElementById('summary-content');256 if (!summaryContent) return;257 258 const output = sections.map((section, index) => {259 let text = '';260 if (section.title) {261 text += `\n${'='.repeat(50)}\n${section.title.toUpperCase()}\n${'='.repeat(50)}\n\n`;262 }263 text += section.content;264 return text;265 }).join('\n\n');266 267 summaryContent.textContent = output.trim();268}269 270async function copySummary() {271 const summaryContent = document.getElementById('summary-content');272 const copyBtn = document.getElementById('copy-btn');273 274 if (!summaryContent || !copyBtn) {275 showMessage('error', 'Copy elements not found');276 return;277 }278 279 const text = summaryContent.textContent;280 if (!text?.trim()) {281 showMessage('error', 'No summary content to copy');282 return;283 }284 285 try {286 if (navigator.clipboard?.writeText) {287 await navigator.clipboard.writeText(text);288 showCopySuccess(copyBtn);289 return;290 }291 292 const textarea = document.createElement('textarea');293 Object.assign(textarea.style, {294 position: 'fixed',295 left: '-9999px',296 opacity: '0'297 });298 textarea.value = text;299 300 document.body.appendChild(textarea);301 textarea.select();302 303 const success = document.execCommand('copy');304 document.body.removeChild(textarea);305 306 if (success) {307 showCopySuccess(copyBtn);308 } else {309 selectTextForManualCopy(summaryContent, copyBtn);310 }311 312 } catch (error) {313 showCopyModal(text);314 }315}316 317function showCopySuccess(btn) {318 const originalText = btn.textContent;319 const originalColor = btn.style.backgroundColor;320 321 btn.textContent = 'Copied!';322 btn.style.backgroundColor = '#28a745';323 324 setTimeout(() => {325 btn.textContent = originalText;326 btn.style.backgroundColor = originalColor || '#28a745';327 }, 2000);328 329 showMessage('success', 'Summary copied to clipboard!');330}331 332function selectTextForManualCopy(content, btn) {333 const range = document.createRange();334 range.selectNodeContents(content);335 const selection = window.getSelection();336 selection.removeAllRanges();337 selection.addRange(range);338 339 const originalText = btn.textContent;340 btn.textContent = 'Selected!';341 btn.style.backgroundColor = '#17a2b8';342 343 setTimeout(() => {344 btn.textContent = originalText;345 btn.style.backgroundColor = '#28a745';346 if (window.getSelection) window.getSelection().removeAllRanges();347 }, 3000);348 349 showMessage('success', 'Text selected! Press Ctrl+C to copy.');350}351 352function showCopyModal(text) {353 const existingModal = document.getElementById('copy-modal');354 if (existingModal) existingModal.remove();355 356 const modal = document.createElement('div');357 modal.id = 'copy-modal';358 modal.style.cssText = `359 position: fixed; top: 0; left: 0; right: 0; bottom: 0;360 background: rgba(0,0,0,0.7); display: flex; align-items: center;361 justify-content: center; z-index: 10000; padding: 20px;362 `;363 364 modal.innerHTML = `365 <div style="background: white; border-radius: 8px; padding: 20px; max-width: 90%; max-height: 80%; overflow: auto; box-shadow: 0 4px 20px rgba(0,0,0,0.3);">366 <h3 style="margin-top: 0; color: #2c3e50;">Copy Summary</h3>367 <p style="color: #666; margin-bottom: 15px;">Select all text below and copy manually:</p>368 <textarea readonly style="width: 100%; height: 200px; padding: 10px; border: 1px solid #ddd; border-radius: 4px; font-family: inherit; resize: vertical; box-sizing: border-box;">${text}</textarea>369 <div style="text-align: right; margin-top: 15px;">370 <button onclick="this.closest('#copy-modal').remove()" style="background: #0078d4; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer;">Close</button>371 </div>372 </div>373 `;374 375 document.body.appendChild(modal);376 377 setTimeout(() => {378 const textarea = modal.querySelector('textarea');379 if (textarea) {380 textarea.select();381 textarea.focus();382 }383 }, 100);384 385 modal.addEventListener('click', (e) => {386 if (e.target === modal) modal.remove();387 });388}389 390function displaySections(sections) {391 const summaryContent = document.getElementById('summary-content');392 if (!summaryContent) return;393 394 const output = sections.map(section => {395 let text = '';396 if (section.title) {397 text += `\n${'='.repeat(50)}\n${section.title.toUpperCase()}\n${'='.repeat(50)}\n\n`;398 }399 text += section.content;400 return text;401 }).join('\n\n');402 403 summaryContent.textContent = output.trim();404}