Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Blog Writer</title> | |
| <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> | |
| <script src="https://cdn.plot.ly/plotly-latest.min.js"></script> | |
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.css"> | |
| <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> | |
| <style> | |
| .container { | |
| text-rendering: optimizeLegibility; | |
| -webkit-font-smoothing: antialiased; | |
| color: rgba(0,0,0,0.8); | |
| position: relative; | |
| min-height: 100vh; | |
| font-family: source-serif-pro, Georgia, Cambria, "Times New Roman", Times, serif; | |
| font-size: 20px; | |
| line-height: 1.58; | |
| color: rgba(0, 0, 0, 0.8); | |
| background-color: #fff; | |
| max-width: 800px; | |
| margin: 0 auto; | |
| padding: 0 20px; | |
| } | |
| /* Typography */ | |
| h1, h2, h3, h4, h5, h6 { | |
| font-family: sohne, "Helvetica Neue", Helvetica, Arial, sans-serif; | |
| font-weight: 700; | |
| color: rgba(0, 0, 0, 0.84); | |
| letter-spacing: -0.022em; | |
| line-height: 1.2; | |
| } | |
| h1 { | |
| font-size: 40px; | |
| margin-bottom: 0.5em; | |
| } | |
| h2 { | |
| font-size: 32px; | |
| margin-top: 1.5em; | |
| margin-bottom: 0.5em; | |
| } | |
| h3 { | |
| font-size: 26px; | |
| margin-top: 1.5em; | |
| margin-bottom: 0.5em; | |
| } | |
| p { | |
| margin-bottom: 32px; | |
| } | |
| /* Links */ | |
| a { | |
| color: #1a8917; | |
| text-decoration: none; | |
| } | |
| a:hover { | |
| text-decoration: underline; | |
| } | |
| /* Input container */ | |
| #input-container { | |
| margin-bottom: 40px; | |
| background-color: #f9f9f9; | |
| padding: 32px; | |
| border-radius: 5px; | |
| } | |
| textarea { | |
| width: 100%; | |
| padding: 12px; | |
| margin-bottom: 20px; | |
| border: 1px solid rgba(0, 0, 0, 0.15); | |
| border-radius: 4px; | |
| font-size: 18px; | |
| resize: vertical; | |
| box-sizing: border-box; | |
| font-family: inherit; | |
| } | |
| button { | |
| padding: 12px 24px; | |
| background-color: #1a8917; | |
| color: white; | |
| border: none; | |
| border-radius: 4px; | |
| cursor: pointer; | |
| font-size: 18px; | |
| transition: background-color 0.3s; | |
| } | |
| button:hover { | |
| background-color: #0f6b0f; | |
| } | |
| /* Output container */ | |
| #output-container { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 40px; | |
| } | |
| #report-container, #sources-container { | |
| background-color: #fff; | |
| padding: 32px; | |
| border-radius: 5px; | |
| box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1); | |
| overflow-wrap: break-word; | |
| word-wrap: break-word; | |
| word-break: break-word; | |
| } | |
| /* Sources */ | |
| .source-item { | |
| margin-bottom: 32px; | |
| padding: 24px; | |
| background-color: #f9f9f9; | |
| border: 1px solid #e0e0e0; | |
| border-radius: 5px; | |
| position: relative; | |
| cursor: pointer; | |
| transition: box-shadow 0.3s; | |
| } | |
| .source-item:hover { | |
| box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); | |
| } | |
| .source-url { | |
| color: #1a8917; | |
| text-decoration: none; | |
| word-break: break-all; | |
| font-weight: bold; | |
| display: block; | |
| margin-bottom: 16px; | |
| font-size: 18px; | |
| } | |
| .source-content { | |
| margin-top: 16px; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .source-snippet { | |
| max-height: 150px; | |
| overflow: hidden; | |
| } | |
| .source-full { | |
| display: none; | |
| } | |
| .expand-indicator { | |
| position: absolute; | |
| bottom: 0; | |
| left: 0; | |
| right: 0; | |
| height: 40px; | |
| background: linear-gradient(to bottom, rgba(249,249,249,0), rgba(249,249,249,1)); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .expand-indicator::after { | |
| content: '▼'; | |
| font-size: 14px; | |
| color: #666; | |
| } | |
| .expanded .expand-indicator::after { | |
| content: '▲'; | |
| } | |
| /* Responsive adjustments */ | |
| @media (max-width: 768px) { | |
| .container { | |
| padding: 0 16px; | |
| } | |
| h1 { | |
| font-size: 32px; | |
| } | |
| h2 { | |
| font-size: 24px; | |
| } | |
| #input-container, #report-container, #sources-container { | |
| padding: 24px; | |
| } | |
| } | |
| </style> | |
| <link rel="stylesheet" href="/css/ai-sidebar.css"> | |
| </head> | |
| <body> | |
| <div id="app" class="ai-sidebar__container"> | |
| <sidebar-component></sidebar-component> | |
| <main class="ai-sidebar__content"> | |
| <div class="container"> | |
| <div id="input-container"> | |
| <h1>Blog Post Generator</h1> | |
| <textarea id="description" rows="4" placeholder="Enter description">write a medium article on nvidia stock performance</textarea> | |
| <button onclick="generateReport()">Generate Blog</button> | |
| </div> | |
| <div id="output-container"> | |
| <div id="report-container"></div> | |
| <div id="sources-container"></div> | |
| <div class="button-container"> | |
| <button id="downloadBtn" onclick="downloadHTML()" style="display: none;" title="Download HTML Blog"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <path d="M3 15v4c0 1.1.9 2 2 2h14a2 2 0 0 0 2-2v-4M17 9l-5 5-5-5M12 12.8V2.5"/> | |
| </svg> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </main> | |
| </div> | |
| <script> | |
| async function generateReport() { | |
| const description = document.getElementById('description').value; | |
| const reportContainer = document.getElementById('report-container'); | |
| const sourcesContainer = document.getElementById('sources-container'); | |
| reportContainer.innerHTML = 'Searching results...'; | |
| sourcesContainer.innerHTML = ''; | |
| try { | |
| const response = await fetch('https://iresearcher-api.elevatics.cloud/generate_blog', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'Accept': 'text/plain' | |
| }, | |
| body: JSON.stringify({ | |
| description: description, | |
| user_id: "", | |
| user_name: "multi-agent-research-generate-blog", | |
| internet: true, | |
| output_format: "report_table", | |
| data_format: "Structured data", | |
| generate_charts: true, | |
| output_as_md: true | |
| }) | |
| }); | |
| if (!response.ok) { | |
| throw new Error(`HTTP error! status: ${response.status}`); | |
| } | |
| const reader = response.body.getReader(); | |
| const decoder = new TextDecoder(); | |
| let markdown = ''; | |
| let metadata = ''; | |
| let isReadingMetadata = false; | |
| while (true) { | |
| const { value, done } = await reader.read(); | |
| if (done) break; | |
| const chunk = decoder.decode(value, { stream: true }); | |
| if (chunk.includes('<report-metadata>')) { | |
| isReadingMetadata = true; | |
| metadata = ''; | |
| } | |
| if (isReadingMetadata) { | |
| metadata += chunk; | |
| if (chunk.includes('</report-metadata>')) { | |
| isReadingMetadata = false; | |
| processMetadata(metadata); | |
| } | |
| } else { | |
| markdown += chunk; | |
| renderMarkdown(markdown); | |
| } | |
| } | |
| document.getElementById('downloadBtn').style.display = 'block'; | |
| } catch (error) { | |
| reportContainer.innerHTML = `Error generating blog: ${error.message}`; | |
| } | |
| } | |
| function renderMarkdown(markdown) { | |
| const reportContainer = document.getElementById('report-container'); | |
| const reportContent = markdown.match(/<report>([\s\S]*)<\/report>/); | |
| if (reportContent) { | |
| reportContainer.innerHTML = marked.parse(reportContent[1]); | |
| } else { | |
| reportContainer.innerHTML = marked.parse(markdown); | |
| } | |
| const scripts = reportContainer.getElementsByTagName('script'); | |
| Array.from(scripts).forEach(script => { | |
| const newScript = document.createElement('script'); | |
| newScript.textContent = script.textContent; | |
| script.parentNode.replaceChild(newScript, script); | |
| }); | |
| // Make Plotly charts responsive | |
| const plots = reportContainer.querySelectorAll('.js-plotly-plot'); | |
| plots.forEach(plot => { | |
| Plotly.Plots.resize(plot); | |
| }); | |
| } | |
| function processMetadata(metadata) { | |
| const sourcesContainer = document.getElementById('sources-container'); | |
| const metadataMatch = metadata.match(/all-text-with-urls: (.+)/); | |
| if (metadataMatch) { | |
| const metadataObj = JSON.parse(metadataMatch[1]); | |
| sourcesContainer.innerHTML = '<h2>Sources</h2>'; | |
| metadataObj.forEach(([content, url]) => { | |
| if (content.trim() !== "") { | |
| const sourceItem = document.createElement('div'); | |
| sourceItem.className = 'source-item'; | |
| const snippet = content.length > 400 ? content.substring(0, 400) + '...' : content; | |
| sourceItem.innerHTML = ` | |
| <a href="${url}" target="_blank" class="source-url">${url}</a> | |
| <div class="source-content"> | |
| <div class="source-snippet">${marked.parse(snippet)}</div> | |
| <div class="source-full">${marked.parse(content)}</div> | |
| <div class="expand-indicator"></div> | |
| </div> | |
| `; | |
| sourcesContainer.appendChild(sourceItem); | |
| const sourceUrl = sourceItem.querySelector('.source-url'); | |
| const sourceContent = sourceItem.querySelector('.source-content'); | |
| const snippetDiv = sourceItem.querySelector('.source-snippet'); | |
| const fullDiv = sourceItem.querySelector('.source-full'); | |
| sourceContent.addEventListener('click', function(e) { | |
| if (!sourceItem.classList.contains('expanded')) { | |
| sourceItem.classList.add('expanded'); | |
| snippetDiv.style.display = 'none'; | |
| fullDiv.style.display = 'block'; | |
| } else if (e.clientY > sourceContent.getBoundingClientRect().bottom - 30) { | |
| sourceItem.classList.remove('expanded'); | |
| snippetDiv.style.display = 'block'; | |
| fullDiv.style.display = 'none'; | |
| } | |
| }); | |
| sourceUrl.addEventListener('click', function(e) { | |
| e.stopPropagation(); | |
| }); | |
| } | |
| }); | |
| } else { | |
| sourcesContainer.innerHTML = '<h2>Sources</h2><p>No source information available.</p>'; | |
| } | |
| } | |
| // Make Plotly charts responsive on window resize | |
| window.addEventListener('resize', function() { | |
| const plots = document.querySelectorAll('.js-plotly-plot'); | |
| plots.forEach(plot => { | |
| Plotly.Plots.resize(plot); | |
| }); | |
| }); | |
| function sanitizeFileName(name) { | |
| // Keep only alphanumeric characters and spaces | |
| name = name.replace(/[^a-z0-9\s]/gi, ''); | |
| // Convert to lowercase | |
| name = name.toLowerCase(); | |
| // Replace spaces with underscores | |
| name = name.replace(/\s+/g, '_'); | |
| // Limit the length to 50 characters | |
| name = name.substring(0, 50); | |
| // If the name is empty after sanitization, use a default name | |
| return name || 'generated_report'; | |
| } | |
| async function downloadHTML() { | |
| try { | |
| // Get styles from current document | |
| let css = ''; | |
| const styleTags = document.getElementsByTagName('style'); | |
| for (let styleTag of styleTags) { | |
| css += styleTag.innerHTML; | |
| } | |
| // Add additional styles for expanded sources and body max-width | |
| css += ` | |
| body.report-body { | |
| max-width: 804px; | |
| margin: 0 auto; | |
| } | |
| .source-item { | |
| margin-bottom: 20px; | |
| } | |
| .source-content { | |
| margin-top: 10px; | |
| } | |
| .source-snippet, .expand-indicator { | |
| display: none; | |
| } | |
| .source-full { | |
| display: block; | |
| } | |
| `; | |
| // Clone the document | |
| const htmlContent = document.implementation.createHTMLDocument('Report'); | |
| // Set up the basic structure | |
| htmlContent.documentElement.lang = 'en'; | |
| const head = htmlContent.head; | |
| const body = htmlContent.body; | |
| body.className = 'report-body'; | |
| // Add meta tags | |
| const meta = htmlContent.createElement('meta'); | |
| meta.charset = 'UTF-8'; | |
| head.appendChild(meta); | |
| const viewport = htmlContent.createElement('meta'); | |
| viewport.name = 'viewport'; | |
| viewport.content = 'width=device-width, initial-scale=1.0'; | |
| head.appendChild(viewport); | |
| // Copy the report content | |
| const reportContainer = document.getElementById('report-container'); | |
| body.innerHTML = reportContainer.innerHTML; | |
| // Generate file name from content | |
| let fileName = 'generatedreport'; | |
| const headings = body.querySelector('h1, h2, h3'); | |
| if (headings) { | |
| fileName = sanitizeFileName(headings.textContent); | |
| } | |
| // Add title | |
| const title = htmlContent.createElement('title'); | |
| title.textContent = fileName; | |
| head.appendChild(title); | |
| // Add style | |
| const style = htmlContent.createElement('style'); | |
| style.textContent = css; | |
| head.appendChild(style); | |
| // Add necessary scripts | |
| const markedScript = htmlContent.createElement('script'); | |
| markedScript.src = 'https://cdn.jsdelivr.net/npm/marked/marked.min.js'; | |
| head.appendChild(markedScript); | |
| const plotlyScript = htmlContent.createElement('script'); | |
| plotlyScript.src = 'https://cdn.plot.ly/plotly-latest.min.js'; | |
| head.appendChild(plotlyScript); | |
| // Copy and modify the sources content | |
| const sourcesContainer = document.getElementById('sources-container'); | |
| const sourcesDiv = htmlContent.createElement('div'); | |
| sourcesDiv.innerHTML = sourcesContainer.innerHTML; | |
| // Expand all sources | |
| const sourceItems = sourcesDiv.querySelectorAll('.source-item'); | |
| sourceItems.forEach(item => { | |
| item.classList.add('expanded'); | |
| const snippetDiv = item.querySelector('.source-snippet'); | |
| const fullDiv = item.querySelector('.source-full'); | |
| if (snippetDiv) snippetDiv.style.display = 'none'; | |
| if (fullDiv) fullDiv.style.display = 'block'; | |
| }); | |
| body.appendChild(sourcesDiv); | |
| // Create blob and download | |
| const blob = new Blob([htmlContent.documentElement.outerHTML], { type: 'text/html' }); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = `${fileName}.html`; | |
| document.body.appendChild(a); | |
| a.click(); | |
| document.body.removeChild(a); | |
| URL.revokeObjectURL(url); | |
| } catch (error) { | |
| console.error('Error downloading HTML:', error); | |
| } | |
| } | |
| </script> | |
| </body> | |
| </html> |