117 [category: string]: { task: string; is_high_priority: boolean }[];
118}
119function generateHtmlShell(initialQuery, sourceUrl) { // initialUrl and initialText removed
120 const escapedQuery = initialQuery.replace(/</g, "<").replace(/>/g, ">");
121 return `
146.hamburger-box { width: 30px; height: 22px; display: inline-block; position: relative; }
147.hamburger-inner { display: block; top: 50%; margin-top: -1px; }
148.hamburger-inner, .hamburger-inner::before, .hamburger-inner::after { width: 30px; height: 2px; background-color: #e2e8f0; border-radius: 4px; position: absolute; transition-property: transform; transition-duration: 0.15s; transition-timing-function: ease; }
149.hamburger-inner::before, .hamburger-inner::after { content: ""; display: block; }
150.hamburger-inner::before { top: -8px; }
259</div>
260<script>
261(function() {
262const LS_DOCUMENTS_KEY = 'lexi_documents_v2'; const LS_ANALYSES_KEY = 'lexi_analyses_v2'; const MAX_SUGGESTION_SELECTIONS = 15;
263let currentView = 'dashboard'; let ni_currentAppPhase = 'document_input'; let ni_currentExtractedDocText = null; let ni_currentInputSourceDescription = null; let ni_currentSelectedOrCustomTask = null; let ni_currentDocId = null; let ni_currentAnalysisResult = null;
270if (window.innerWidth <= 768) { sidebar.classList.add('collapsed'); hamburgerToggle.setAttribute('aria-expanded', 'false'); } else { hamburgerToggle.setAttribute('aria-expanded', 'true');}
271});
272function navigateToView(viewId, params = {}) {
273currentView = viewId; for (const key in views) { views[key].classList.remove('active-view'); } if (views[viewId]) { views[viewId].classList.add('active-view'); }
274for (const key in navItems) { navItems[key].classList.remove('active'); } if (navItems[viewId]) { navItems[viewId].classList.add('active'); }
277window.scrollTo(0, 0);
278}
279function setupEventListeners() {
280hamburgerToggle.addEventListener('click', () => {const isExpanded = sidebar.classList.contains('expanded-mobile') || !sidebar.classList.contains('collapsed'); if (window.innerWidth <= 768) { sidebar.classList.toggle('expanded-mobile'); sidebar.classList.toggle('collapsed'); hamburgerToggle.setAttribute('aria-expanded', sidebar.classList.contains('expanded-mobile').toString()); } else { sidebar.classList.toggle('collapsed'); hamburgerToggle.setAttribute('aria-expanded', !sidebar.classList.contains('collapsed').toString()); } });
281document.querySelectorAll('.nav-item').forEach(item => { item.addEventListener('click', () => navigateToView(item.dataset.view)); });
291});
292}
293function getFromLocalStorage(key) { return JSON.parse(localStorage.getItem(key)) || []; }
294function saveToLocalStorage(key, data) { localStorage.setItem(key, JSON.stringify(data)); }
295function generateId() { return Date.now().toString(36) + Math.random().toString(36).substring(2); }
296function escapeHtml(unsafe) { if (unsafe === null || typeof unsafe === 'undefined') return ''; return String(unsafe).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'"); }
297function renderDashboard() {
298const docs = getFromLocalStorage(LS_DOCUMENTS_KEY);
299const analyses = getFromLocalStorage(LS_ANALYSES_KEY);
319}
320let ni_form, ni_fileInput, ni_taskSuggestionSection, ni_suggestedTasksListDiv, ni_customLegalTaskQueryInput, ni_submitButton, ni_submitButtonTextSpan, ni_statusContainer, ni_structuredResultsContainer, ni_saveAnalysisButton, ni_documentInputSection, ni_taskSelectionInfo;
321function renderNewInquiryView(params = {}) {
322views['new-inquiry'].innerHTML = \`<h1>New Inquiry</h1><form id="ni-analysis-form"><div class="card" id="ni-document-input-section"><h2>Step 1: Provide Your Document</h2><p style="margin-bottom:20px; color:#555;">Please upload the legal PDF document you want to analyze.</p><div class="ni-input-method"><h3>Upload PDF Document</h3><label for="ni-doc-file">Select a PDF file (Max 10MB):</label><input type="file" id="ni-doc-file" name="documentFile" accept=".pdf" style="margin-bottom:5px;"></div></div><div class="card" id="ni-task-suggestion-section" style="display:none;"><h2>Step 2: Define Analysis Task(s)</h2><p id="ni-task-selection-info">Select up to \${MAX_SUGGESTION_SELECTIONS} tasks, or write your own below. AI-recommended tasks are pre-selected.</p><div id="ni-suggested-tasks-list-container"><label for="ni-suggested-tasks">Suggested Tasks (click category to select/deselect all within that category):</label><div id="ni-suggested-tasks-list"><p style="text-align:center;color:#777;"><em>Processing document to generate task suggestions...</em></p></div></div><label for="ni-custom-legal-task-query">Your Legal Task(s) / Query (edit or define new):</label><textarea id="ni-custom-legal-task-query" name="customLegalTaskQuery" placeholder="E.g., 'Identify all termination clauses and their conditions. Extract key dates and deadlines. Assess potential risks related to indemnification.'"></textarea></div><div class="card" style="padding-top:10px;"><button type="submit" id="ni-submit-button" class="button button-primary"><span id="ni-submit-button-text">Process Document & Get Task Suggestions</span><div class="spinner"></div></button></div></form><div class="card results-section" id="ni-results-section" style="display:none;"><h2>Analysis Status & Results</h2><div id="ni-status-container"></div><div id="ni-structured-results-container" style="display:none;"></div><button id="ni-save-analysis-button" class="button button-primary" data-action="save-analysis" style="display:none;margin-top:15px;">Save Analysis</button></div>\`;
323ni_form=document.getElementById('ni-analysis-form'); ni_fileInput=document.getElementById('ni-doc-file'); ni_documentInputSection=document.getElementById('ni-document-input-section'); ni_taskSuggestionSection=document.getElementById('ni-task-suggestion-section'); ni_suggestedTasksListDiv=document.getElementById('ni-suggested-tasks-list'); ni_customLegalTaskQueryInput=document.getElementById('ni-custom-legal-task-query'); ni_submitButton=document.getElementById('ni-submit-button'); ni_submitButtonTextSpan=document.getElementById('ni-submit-button-text'); ni_statusContainer=document.getElementById('ni-status-container'); ni_structuredResultsContainer=document.getElementById('ni-structured-results-container'); ni_saveAnalysisButton=document.getElementById('ni-save-analysis-button'); ni_taskSelectionInfo=document.getElementById('ni-task-selection-info');
329updateTaskSelectionInfo();
330}
331function ni_handleFormSubmit(event){event.preventDefault();ni_clearStatusMessages();ni_saveAnalysisButton.style.display='none';ni_currentAnalysisResult=null;document.getElementById('ni-results-section').style.display='block';if(ni_currentAppPhase==='document_input')ni_handleDocumentInputAndSuggestTasks();else if(ni_currentAppPhase==='task_selection')ni_handleFinalAnalysis();}
332function ni_addStatusMessage(message,type='info'){const entry=document.createElement('div');entry.className=\`status-entry \${type}\`;entry.innerHTML=escapeHtml(message);ni_statusContainer.appendChild(entry);ni_statusContainer.scrollTop=ni_statusContainer.scrollHeight;}
333function ni_clearStatusMessages(){ni_statusContainer.innerHTML='';}
334function ni_setUIState(phase){ni_currentAppPhase=phase;ni_submitButton.classList.remove('loading');ni_submitButton.disabled=false;ni_fileInput.disabled=(phase!=='document_input'&&!ni_currentDocId);ni_customLegalTaskQueryInput.disabled=(phase!=='task_selection');if(phase==='document_input'){ni_documentInputSection.style.display='block';ni_taskSuggestionSection.style.display='none';ni_submitButtonTextSpan.textContent='Process Document & Get Task Suggestions';document.getElementById('ni-results-section').style.display='none';}else if(phase==='task_selection'){if(!ni_currentDocId)ni_documentInputSection.style.display='none';ni_taskSuggestionSection.style.display='block';ni_submitButtonTextSpan.textContent='Perform Analysis on Selected Task(s)';}else if(phase==='analysis_pending'){ni_submitButton.classList.add('loading');ni_submitButton.disabled=true;ni_customLegalTaskQueryInput.disabled=true;}}
335async function ni_fetchAndPopulateSuggestions(docSampleText){ni_suggestedTasksListDiv.innerHTML='<p style="text-align:center;color:#777;"><em>Fetching task suggestions...</em></p>';const formData=new FormData();formData.append('documentText',docSampleText);formData.append('inputSourceDescription',"For suggestions");try{const response=await fetch(window.location.pathname+'?action=suggestTasks&format=json',{method:'POST',body:formData});let responseData;try{responseData=await response.json();}catch(e){throw new Error(\`Server returned non-JSON response: \${response.status} \${response.statusText}\`);}(responseData.log||[]).forEach(log=>ni_addStatusMessage(\`[\${log.agent||'S'}] \${typeof log.message==='object'?JSON.stringify(log.message):escapeHtml(log.message)}\`,log.type));if(!response.ok||responseData.error){let mainError=responseData?.error;if(typeof mainError==='object')mainError=JSON.stringify(mainError);let detailError=responseData?.details;if(typeof detailError==='object')detailError=JSON.stringify(detailError);throw new Error(mainError||detailError||\`Server Error: \${response.status} \${response.statusText||''}\`.trim());}if(responseData.suggestions&&Object.keys(responseData.suggestions).length>0){ni_populateSuggestedTasks(responseData.suggestions);}else{ni_populateSuggestedTasks({"General Recommendations":[{"task":"Identify key obligations and responsibilities", "is_high_priority":true},{"task":"Summarize the core purpose of the document", "is_high_priority":false},{"task":"List all defined clauses and sections", "is_high_priority":false}]});}}catch(e){console.error("Fetch suggestions error:",e);ni_addStatusMessage(\`Could not fetch task suggestions: \${e.message}\`,'error');ni_populateSuggestedTasks({"Error":[{"task":"Could not load suggestions. Define task manually.", "is_high_priority":true}]});}}
336function ni_populateSuggestedTasks(groupedSuggestions={}){ni_suggestedTasksListDiv.innerHTML='';updateTaskSelectionInfo();if(Object.keys(groupedSuggestions).length===0){ni_suggestedTasksListDiv.innerHTML='<p style="text-align:center;color:#777;"><em>No specific tasks suggested. Please define your task manually.</em></p>';return;}for(const category in groupedSuggestions){const groupId=\`group-\${category.replace(/\\s+/g,'-')}\`;const groupDiv=document.createElement('div');groupDiv.className='suggestion-group';const groupHeader=document.createElement('div');groupHeader.className='suggestion-group-header';const groupCheckbox=document.createElement('input');groupCheckbox.type='checkbox';groupCheckbox.id=groupId;groupCheckbox.dataset.category=category;groupCheckbox.addEventListener('change',handleGroupCheckboxChange);const groupLabel=document.createElement('label');groupLabel.htmlFor=groupId;groupLabel.textContent=escapeHtml(category);groupHeader.appendChild(groupCheckbox);groupHeader.appendChild(groupLabel);groupDiv.appendChild(groupHeader);const tasksDiv=document.createElement('div');tasksDiv.className='suggestion-group-tasks';let categoryHighPrioritySelected=false;groupedSuggestions[category].forEach((suggestion,index)=>{const checkId=\`ni_task_\${category.replace(/\\s+/g,'-')}_\${index}\`;const label=document.createElement('label');label.htmlFor=checkId;label.className='suggestion-item-label';if(suggestion.is_high_priority){label.classList.add('high-priority-task');}const checkbox=document.createElement('input');checkbox.type='checkbox';checkbox.name='ni_suggested_task';checkbox.id=checkId;checkbox.value=suggestion.task;checkbox.dataset.category=category;if(suggestion.is_high_priority&&!categoryHighPrioritySelected){checkbox.checked=true;categoryHighPrioritySelected=true;}checkbox.addEventListener('change',handleSuggestionCheckboxChange);const text=document.createElement('span');text.className='task-text';text.textContent=escapeHtml(suggestion.task);label.appendChild(checkbox);label.appendChild(text);tasksDiv.appendChild(label);});groupDiv.appendChild(tasksDiv);ni_suggestedTasksListDiv.appendChild(groupDiv);}updateSelectedTasksInTextarea();updateTaskSelectionInfo();updateAllGroupCheckboxesStates();enforceMaxSelections();}
337function handleGroupCheckboxChange(event){const groupCheckbox=event.target;const category=groupCheckbox.dataset.category;const taskCheckboxes=ni_suggestedTasksListDiv.querySelectorAll(\`input[type="checkbox"][data-category="\${category}"][name="ni_suggested_task"]\`);let currentSelectedCount=Array.from(ni_suggestedTasksListDiv.querySelectorAll('input[type="checkbox"][name="ni_suggested_task"]:checked')).length;taskCheckboxes.forEach(cb=>{if(groupCheckbox.checked&&!cb.checked){if(currentSelectedCount<MAX_SUGGESTION_SELECTIONS){cb.checked=true;currentSelectedCount++;}else{groupCheckbox.checked=false;}}else if(!groupCheckbox.checked&&cb.checked){cb.checked=false;currentSelectedCount--;}});updateSelectedTasksInTextarea();updateTaskSelectionInfo();enforceMaxSelections();updateAllGroupCheckboxesStates();}
338function handleSuggestionCheckboxChange(){const category=this.dataset.category;const groupCheckbox=ni_suggestedTasksListDiv.querySelector(\`input[type="checkbox"]#group-\${category.replace(/\\s+/g,'-')}\`);if(this.checked){const selectedCount=Array.from(ni_suggestedTasksListDiv.querySelectorAll('input[type="checkbox"][name="ni_suggested_task"]:checked')).length;if(selectedCount>MAX_SUGGESTION_SELECTIONS){this.checked=false;alert(\`Max \${MAX_SUGGESTION_SELECTIONS} tasks can be selected.\`);}}if(groupCheckbox){const taskCheckboxesInGroup=Array.from(ni_suggestedTasksListDiv.querySelectorAll(\`input[type="checkbox"][data-category="\${category}"][name="ni_suggested_task"]\`));const allCheckedInGroup=taskCheckboxesInGroup.every(cb=>cb.checked);groupCheckbox.checked=allCheckedInGroup;groupCheckbox.indeterminate=!allCheckedInGroup&&taskCheckboxesInGroup.some(cb=>cb.checked);}updateSelectedTasksInTextarea();updateTaskSelectionInfo();enforceMaxSelections();}
339function enforceMaxSelections(){const allTaskCheckboxes=Array.from(ni_suggestedTasksListDiv.querySelectorAll('input[type="checkbox"][name="ni_suggested_task"]'));const selectedTaskCheckboxes=allTaskCheckboxes.filter(cb=>cb.checked);if(selectedTaskCheckboxes.length>=MAX_SUGGESTION_SELECTIONS){allTaskCheckboxes.forEach(cb=>{if(!cb.checked)cb.disabled=true;});ni_suggestedTasksListDiv.querySelectorAll('input[type="checkbox"][id^="group-"]').forEach(gcb=>{if(!gcb.checked){const tasksInItsGroup=Array.from(ni_suggestedTasksListDiv.querySelectorAll(\`input[type="checkbox"][data-category="\${gcb.dataset.category}"][name="ni_suggested_task"]\`));const uncheckedTasksInGroup=tasksInItsGroup.filter(tcb=>!tcb.checked);if(uncheckedTasksInGroup.length>0)gcb.disabled=true;}});}else{allTaskCheckboxes.forEach(cb=>cb.disabled=false);ni_suggestedTasksListDiv.querySelectorAll('input[type="checkbox"][id^="group-"]').forEach(gcb=>gcb.disabled=false);}}
340function updateAllGroupCheckboxesStates(){const groupCheckboxes=ni_suggestedTasksListDiv.querySelectorAll('input[type="checkbox"][id^="group-"]');groupCheckboxes.forEach(gcb=>{const category=gcb.dataset.category;const taskCheckboxesInGroup=Array.from(ni_suggestedTasksListDiv.querySelectorAll(\`input[type="checkbox"][data-category="\${category}"][name="ni_suggested_task"]\`));const allCheckedInGroup=taskCheckboxesInGroup.length>0&&taskCheckboxesInGroup.every(cb=>cb.checked);const someCheckedInGroup=taskCheckboxesInGroup.some(cb=>cb.checked);gcb.checked=allCheckedInGroup;gcb.indeterminate=!allCheckedInGroup&&someCheckedInGroup;});}
341function updateSelectedTasksInTextarea(){const selectedCheckboxes=Array.from(ni_suggestedTasksListDiv.querySelectorAll('input[type="checkbox"][name="ni_suggested_task"]:checked'));const selectedTasks=selectedCheckboxes.map(cb=>cb.value);ni_customLegalTaskQueryInput.value=selectedTasks.join(".\\n");}
342function updateTaskSelectionInfo(){if(!ni_taskSelectionInfo)return;const selectedCount=Array.from(ni_suggestedTasksListDiv.querySelectorAll('input[type="checkbox"][name="ni_suggested_task"]:checked')).length;ni_taskSelectionInfo.textContent=\`Selected: \${selectedCount} / \${MAX_SUGGESTION_SELECTIONS}. Selections populate query box below.\`;}
343async function ni_handleDocumentInputAndSuggestTasks(){const file=ni_fileInput.files[0];if(!file){ni_addStatusMessage('Please upload a PDF file.','error');return;}ni_setUIState('analysis_pending');ni_submitButtonTextSpan.textContent='Processing Document...';ni_addStatusMessage('Processing document for task suggestions...','progress');const formData=new FormData();let clientInputSourceDescription='';let docNameForStorage="Uploaded Document";let sourceTypeForStorage='file';let sourceValueForStorage='Uploaded File';if(file){if(file.type!=='application/pdf'){ni_addStatusMessage('Invalid file: Only PDF files supported.','error');ni_setUIState('document_input');return;}if(file.size>10*1024*1024){ni_addStatusMessage('File too large. Max 10MB.','error');ni_setUIState('document_input');return;}formData.append('documentFile',file,file.name);clientInputSourceDescription=\`File: \${file.name}\`;docNameForStorage=file.name;sourceTypeForStorage='file';sourceValueForStorage=file.name;}else{ni_addStatusMessage('No file selected for processing.','error');ni_setUIState('document_input');return;}formData.append('inputSourceDescription',clientInputSourceDescription);try{const response=await fetch(window.location.pathname+'?action=suggestTasks&format=json',{method:'POST',body:formData});let responseData;try{responseData=await response.json();}catch(jsonError){console.error("Parse JSON error (suggestTasks):",jsonError);throw new Error(\`Server error: \${response.status} \${response.statusText||''}. Invalid JSON response.\`);}(responseData.log||[]).forEach(logEntry=>ni_addStatusMessage(\`[\${logEntry.agent||'Sys'}] \${typeof logEntry.message==='object'?JSON.stringify(logEntry.message):escapeHtml(logEntry.message)}\`,logEntry.type==='error'?'error':(logEntry.type==='step'?'progress':'info')));if(!response.ok||responseData.error){let mainError=responseData?.error;if(typeof mainError==='object')mainError=JSON.stringify(mainError);let detailError=responseData?.details;if(typeof detailError==='object')detailError=JSON.stringify(detailError);const errorToThrow=mainError||detailError||\`Server error: \${response.status} \${response.statusText||''}\`.trim();throw new Error(errorToThrow);}if(responseData.suggestions&&typeof responseData.suggestions==='object'&&Object.keys(responseData.suggestions).length>0){ni_currentExtractedDocText=responseData.actualDocumentText;ni_currentInputSourceDescription=responseData.derivedInputSourceDescription||clientInputSourceDescription;const docs=getFromLocalStorage(LS_DOCUMENTS_KEY);ni_currentDocId=generateId();docs.push({id:ni_currentDocId,name:docNameForStorage,text:ni_currentExtractedDocText,sourceType:sourceTypeForStorage,sourceValue:sourceValueForStorage,dateAdded:new Date().toISOString(), type: 'document'});saveToLocalStorage(LS_DOCUMENTS_KEY,docs);ni_addStatusMessage(\`Document '\${escapeHtml(docNameForStorage)}' processed and saved.\`,'info');ni_populateSuggestedTasks(responseData.suggestions);ni_addStatusMessage('Task suggestions generated. Review or define new tasks.','info');ni_setUIState('task_selection');}else{ni_addStatusMessage('No specific suggestions generated, using fallback.','info');ni_populateSuggestedTasks({"General Recommendations":[{"task":"Identify key obligations and responsibilities", "is_high_priority":true},{"task":"Summarize the core purpose of the document", "is_high_priority":false},{"task":"List all defined clauses and sections", "is_high_priority":false}]});ni_setUIState('task_selection');}}catch(error){console.error('Error during doc processing/suggestion:',error);ni_addStatusMessage(\`Error processing document: \${escapeHtml(error.message)}\`,'error');ni_setUIState('document_input');}}
344async function ni_handleFinalAnalysis(){ni_currentSelectedOrCustomTask=ni_customLegalTaskQueryInput.value.trim();if(!ni_currentSelectedOrCustomTask){ni_addStatusMessage('Please define or select at least one legal task.','error');return;}if(!ni_currentExtractedDocText){ni_addStatusMessage('Document text missing. Please re-upload in Step 1.','error');ni_setUIState('document_input');return;}ni_setUIState('analysis_pending');ni_submitButtonTextSpan.textContent='Analyzing Document...';ni_addStatusMessage('Performing legal analysis...','progress');const formData=new FormData();formData.append('legalTaskQuery',ni_currentSelectedOrCustomTask);formData.append('documentText',ni_currentExtractedDocText);formData.append('inputSourceDescription',ni_currentInputSourceDescription);try{const response=await fetch(window.location.pathname+'?format=json',{method:'POST',body:formData});let responseData;try{responseData=await response.json();}catch(jsonError){console.error("Parse JSON error (analysis):",jsonError);throw new Error(\`Server error: \${response.status} \${response.statusText||''}. Invalid JSON response.\`);}(responseData.log||[]).forEach(logEntry=>{if(logEntry.type!=='final'&&logEntry.type!=='input')ni_addStatusMessage(\`[\${logEntry.agent||'Sys'}] \${typeof logEntry.message==='object'?JSON.stringify(logEntry.message):escapeHtml(logEntry.message)}\`,logEntry.type==='error'?'error':(logEntry.type==='step'?'progress':'info'));});if(!response.ok){let mainError=responseData?.error;if(typeof mainError==='object')mainError=JSON.stringify(mainError);let detailError=responseData?.details;if(typeof detailError==='object')detailError=JSON.stringify(detailError);throw new Error(mainError||detailError||\`Server error: \${response.status} \${response.statusText||''}\`.trim());}if(responseData.error&&!responseData.finalResult){let srvError=responseData.error;if(typeof srvError==='object')srvError=JSON.stringify(srvError);throw new Error(\`Analysis Server Error: \${srvError}\`);}if(responseData.finalResult){ni_addStatusMessage('Analysis complete. Results below.','info');ni_displayAnalysisResults(responseData.finalResult);}else if(responseData.request_details){ni_addStatusMessage('Analysis complete (direct result). Results below.','info');ni_displayAnalysisResults(responseData);}else if(responseData.error){let anError=responseData.error;if(typeof anError==='object')anError=JSON.stringify(anError);ni_addStatusMessage(\`Analysis error: \${escapeHtml(anError.message||anError)}\`,'error');if(responseData.finalResult)ni_displayAnalysisResults(responseData.finalResult);}else throw new Error("Unexpected response structure from analysis.");}catch(error){console.error('Error during final analysis:',error);ni_addStatusMessage(\`Analysis failed: \${escapeHtml(error.message)}\`,'error');ni_structuredResultsContainer.style.display='none';}finally{if(ni_currentAppPhase==='analysis_pending')ni_setUIState('task_selection');}}
345function ni_displayAnalysisResults(data){ni_structuredResultsContainer.innerHTML='';if(!data||typeof data!=='object'){ni_addStatusMessage('Invalid analysis results data.','error');ni_structuredResultsContainer.style.display='none';return;}let html='';if(data.request_details){html+=\`<div class="result-block"><h3>Request Details</h3><p><strong>Operation Performed:</strong> \${escapeHtml(data.request_details.operation_performed)}</p><p><strong>Input Source:</strong> \${escapeHtml(data.request_details.input_source_description)}</p></div>\`;}if(data.analysis_summary){html+=\`<div class="result-block"><h3>Analysis Summary</h3><p><strong>Overview:</strong> \${escapeHtml(data.analysis_summary.overview)}</p><p><strong>Key Findings Count:</strong> \${escapeHtml(data.analysis_summary.key_findings_count)}</p></div>\`;}if(data.detailed_findings&&data.detailed_findings.length>0){html+='<div class="result-block"><h3>Detailed Findings</h3>';data.detailed_findings.forEach(f=>{html+=\`<div class="finding-card"><h4>Finding Type: \${escapeHtml(f.finding_type||'General Finding')} (\${escapeHtml(f.finding_id||'N/A')})</h4><p><strong>Description:</strong> \${escapeHtml(f.description)}</p>\${f.severity_or_importance?\`<p><strong>Severity/Importance:</strong> \${escapeHtml(f.severity_or_importance)}</p>\`:''}\${f.page_or_section_reference?\`<p><strong>Page/Section Reference:</strong> \${escapeHtml(f.page_or_section_reference)}</p>\`:''}\${f.implications?\`<p><strong>Implications:</strong> \${escapeHtml(f.implications)}</p>\`:''}\${f.confidence_score!=null?\`<p><strong>Confidence Score:</strong> \${escapeHtml(f.confidence_score.toFixed(2))}</p>\`:''}\${f.relevant_input_excerpt?\`<p><strong>Relevant Excerpt from Document:</strong></p><pre>\${escapeHtml(f.relevant_input_excerpt)}</pre>\`:''}</div>\`;});html+='</div>';}else if(data.detailed_findings?.length===0){html+='<div class="result-block"><h3>Detailed Findings</h3><p>No specific detailed findings were extracted.</p></div>';}if(data.execution_metadata){html+=\`<div class="result-block"><h3>Execution Metadata</h3><p><strong>Status:</strong><span style="font-weight:bold;color:\${data.execution_metadata.status==='Completed'?'green':'orange'}">\${escapeHtml(data.execution_metadata.status)}</span></p><p><strong>Timestamp:</strong> \${escapeHtml(new Date(data.execution_metadata.timestamp).toLocaleString())}</p>\${data.execution_metadata.limitations_or_warnings&&data.execution_metadata.limitations_or_warnings!=="N/A"?\`<p><strong>Limitations or Warnings:</strong></p><pre>\${escapeHtml(data.execution_metadata.limitations_or_warnings)}</pre>\`:''}\${data.execution_metadata.disclaimer?\`<div class="disclaimer">\${escapeHtml(data.execution_metadata.disclaimer)}</div>\`:''}</div>\`;}ni_structuredResultsContainer.innerHTML=html;ni_structuredResultsContainer.style.display='block';document.getElementById('ni-results-section').style.display='block';ni_currentAnalysisResult=data;if(data&&data.request_details&&ni_currentDocId){ni_saveAnalysisButton.style.display='inline-block';}else{ni_saveAnalysisButton.style.display='none';}}
346function saveCurrentAnalysis(){if(ni_currentAnalysisResult&&ni_currentDocId&&ni_currentSelectedOrCustomTask){const analyses=getFromLocalStorage(LS_ANALYSES_KEY);analyses.push({id:generateId(),docId:ni_currentDocId,query:ni_currentSelectedOrCustomTask,result:ni_currentAnalysisResult,datePerformed:new Date().toISOString(), type: 'analysis'});saveToLocalStorage(LS_ANALYSES_KEY,analyses);ni_addStatusMessage('Analysis saved to History.','info');ni_saveAnalysisButton.style.display='none';}else{ni_addStatusMessage('Could not save analysis: data missing.','error');console.error("Save analysis failed - missing data:",{ni_currentAnalysisResult,ni_currentDocId,ni_currentSelectedOrCustomTask});}}
347function renderHistoryView() {
348const docs = getFromLocalStorage(LS_DOCUMENTS_KEY);
349const analyses = getFromLocalStorage(LS_ANALYSES_KEY);
377views['history'].innerHTML = html;
378}
379function deleteDocument(docId){let docs=getFromLocalStorage(LS_DOCUMENTS_KEY);let analyses=getFromLocalStorage(LS_ANALYSES_KEY);saveToLocalStorage(LS_DOCUMENTS_KEY,docs.filter(doc=>doc.id!==docId));saveToLocalStorage(LS_ANALYSES_KEY,analyses.filter(an=>an.docId!==docId));if(currentView==='history')renderHistoryView();else if(currentView==='dashboard')renderDashboard();ni_addStatusMessage('Document and associated analyses deleted.','info');}
380function deleteAnalysis(analysisId){let analyses=getFromLocalStorage(LS_ANALYSES_KEY);saveToLocalStorage(LS_ANALYSES_KEY,analyses.filter(an=>an.id!==analysisId));if(currentView==='history')renderHistoryView();else if(currentView==='dashboard')renderDashboard();ni_addStatusMessage('Analysis deleted.','info');}
381function renderDocumentDetailView(docId){const doc=getFromLocalStorage(LS_DOCUMENTS_KEY).find(d=>d.id===docId);const analyses=getFromLocalStorage(LS_ANALYSES_KEY).filter(a=>a.docId===docId).sort((a,b)=>new Date(b.datePerformed).getTime()-new Date(a.datePerformed).getTime());const dv=views['document-detail'];if(!doc){dv.innerHTML=\`<p>Document not found.</p><button class="button" data-action="back-to-history">Back to History</button>\`;return;}let analysesHtml='<h3>Analyses Performed on This Document:</h3>';if(analyses.length>0){analysesHtml+='<ul class="history-list" style="padding-left:0;">';analyses.forEach(an=>{analysesHtml+=\`<li class="history-item" style="margin-bottom:10px;padding:15px;"><strong>Query: \${escapeHtml(an.query)}</strong><p class="meta">Date Performed: \${new Date(an.datePerformed).toLocaleString()}</p><div class="actions"><button class="button button-secondary" data-action="view-analysis-detail" data-id="\${an.id}">View This Analysis</button></div></li>\`;});analysesHtml+='</ul>';}else{analysesHtml+='<p>No analyses performed on this document yet.</p>';}dv.innerHTML=\`<button class="button button-secondary" data-action="back-to-history" style="margin-bottom:20px;">← Back to History</button><h1>Document Details: \${escapeHtml(doc.name)}</h1><div class="card"><p><strong>Source Type:</strong> \${escapeHtml(doc.sourceType)}</p><p><strong>Source File:</strong> \${escapeHtml(doc.sourceValue)}</p><p><strong>Date Added:</strong> \${new Date(doc.dateAdded).toLocaleString()}</p><h3>Full Document Text:</h3><textarea readonly style="width:100%;height:300px;border:1px solid #ccc;padding:10px;font-family:monospace;font-size:0.9em;background-color:#f9f9f9;">\${escapeHtml(doc.text)}</textarea></div><div class="card">\${analysesHtml}</div><button class="button button-primary" data-action="analyze-doc-again" data-id="\${doc.id}" style="margin-top:10px;">Analyze This Document Again</button>\`;}
382function renderAnalysisDetailView(analysisId){const an=getFromLocalStorage(LS_ANALYSES_KEY).find(a=>a.id===analysisId);const dv=views['analysis-detail'];if(!an){dv.innerHTML=\`<p>Analysis not found.</p><button class="button" data-action="back-to-history">Back to History</button>\`;return;}const doc=getFromLocalStorage(LS_DOCUMENTS_KEY).find(d=>d.id===an.docId);const tempDiv=document.createElement('div');const old_s=ni_statusContainer,old_str=ni_structuredResultsContainer,old_save=ni_saveAnalysisButton;ni_statusContainer=document.createElement('div');ni_structuredResultsContainer=tempDiv;ni_saveAnalysisButton=document.createElement('button');ni_displayAnalysisResults(an.result);ni_statusContainer=old_s;ni_structuredResultsContainer=old_str;ni_saveAnalysisButton=old_save;dv.innerHTML=\`<button class="button button-secondary" data-action="back-to-history" style="margin-bottom:20px;">← Back to History</button><h1>Analysis Details</h1><div class="card"><p><strong>Analysis Query:</strong> \${escapeHtml(an.query)}</p><p><strong>Original Document:</strong> \${doc?\`<button class="button button-link" data-action="view-doc-detail" data-id="\${doc.id}" style="padding:2px 5px; font-size:1em;">\${escapeHtml(doc.name)}</button>\`:'Document N/A'}</p><p><strong>Date Performed:</strong> \${new Date(an.datePerformed).toLocaleString()}</p></div><div class="card" id="analysis-res-content">\${tempDiv.innerHTML}</div>\`;}
383function renderSettingsView(){views.settings.innerHTML=\`<h1>Settings</h1><div class="card"><h3>Manage Local Data Storage</h3><p>Lexi stores documents and analysis results in your browser's local storage. Data doesn't leave your computer unless a document is sent for AI analysis.</p><p><strong>Warning: Clearing data is irreversible and deletes all saved documents and analyses.</strong></p><button class="button button-danger" data-action="clear-local-storage">Clear All Lexi Data From Browser</button><p style="margin-top:15px;"><small>This removes data from this browser only.</small></p></div>\`;}
384})();
385</script>
387</html>`;
388}
389export default async function(req: Request) {
390 const { OpenAI } = await import("https://esm.town/v/std/openai");
391 const { fetch } = await import("https://esm.town/v/std/fetch"); // Required for OpenAI SDK, not for user URL fetching
404 const MAX_TEXT_LENGTH_SUGGESTION = 20000;
405 const MAX_TEXT_LENGTH_ANALYSIS = 30000;
406 async function extractPdfTextNative(data: ArrayBuffer, fileName: string, log: LogEntry[]): Promise<string | null> {
407 const agent = "PDF Extraction (Val Server)";
408 log.push({ agent, type: "step", message: `Starting PDF processing: ${fileName}` });
425 }
426 }
427 async function callOpenAI(
428 openaiInstance: OpenAI,
429 systemPrompt: string,
474 }
475 }
476 async function ingestDocumentInput(
477 input: { documentFile?: File; clientInputSourceDescription: string },
478 log: LogEntry[],
643 );
644 }
645 async function runAnalysisWithProvidedText(
646 analysisInput: { legalTaskQuery: string; documentText: string; inputSourceDescription: string },
647 log: LogEntry[],