/** * Hot Topics + Blog Pipeline UX Enhancement (v0.2.5) * Loaded after main dashboard script. * Overrides generateBlog + pollBlogLlm with improved versions. */ (function() { var API = window.API || ''; var blogPipelineRunning = false; var STEP_NAMES = [ 'Topic Expansion', 'Angle Selection', 'Outline Generation', 'Master Draft (writing...)', 'Reality Injection', 'Technical Deepening', 'Opinion Layer', 'Kill AI Tone', 'QA Check', 'Quality Score' ]; // Override generateBlog with pipeline lock + progress UI window.generateBlog = function(topic, speed, customTitle, customAngle) { if (blogPipelineRunning) { if (typeof showToast === 'function') showToast('Pipeline Busy', 'A blog is already being generated. Please wait.'); return; } blogPipelineRunning = true; var pipelineEl = document.getElementById('blog-pipeline-status'); if (pipelineEl) { pipelineEl.innerHTML = '
' + '
Generating Blog with AI...
' + '
Starting 10-step Flexoptix Style pipeline...
' + '
Connecting to LLM (qwen2.5:14b)
' + '
' + '
' + '
0%
' + '
'; } var body = { topic: topic }; if (speed) body.speed = speed; fetch((API || '') + '/api/blog/generate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }).then(function(r) { return r.json(); }).then(function(data) { if (data.success && data.draft) { var s = document.getElementById('bp-status'); if (s) s.textContent = 'Template created. LLM pipeline running (~10 min)...'; var b = document.getElementById('bp-bar'); if (b) b.style.width = '5%'; pollPipeline(data.draft.id, 0); } else { blogPipelineRunning = false; if (typeof showToast === 'function') showToast('Error', (data.error || 'Generation failed'), true); if (typeof loadBlogDrafts === 'function') loadBlogDrafts(); } }).catch(function(err) { blogPipelineRunning = false; if (typeof showToast === 'function') showToast('Network Error', err.message, true); if (typeof loadBlogDrafts === 'function') loadBlogDrafts(); }); }; function pollPipeline(id, attempt) { if (attempt > 80) { blogPipelineRunning = false; var pipelineEl = document.getElementById('blog-pipeline-status'); if (pipelineEl) pipelineEl.innerHTML = ''; if (typeof showToast === 'function') showToast('Timeout', 'Pipeline took too long. Check the blog list.'); if (typeof loadBlogDrafts === 'function') loadBlogDrafts(); return; } // Poll progress endpoint (fast) + blog status endpoint (to detect completion) setTimeout(function() { // 1) Fetch real-time progress from server fetch((API || '') + '/api/blog/' + id + '/progress') .then(function(r) { return r.json(); }) .then(function(prog) { if (prog.running) { var bar = document.getElementById('bp-bar'); var pct = document.getElementById('bp-pct'); var status = document.getElementById('bp-status'); var step = document.getElementById('bp-step'); if (bar) bar.style.width = prog.pct + '%'; if (pct) pct.textContent = prog.pct + '%'; if (status) status.textContent = prog.label || ('Step ' + prog.step + '/10'); if (step) step.textContent = 'Running on qwen2.5:14b via Ollama...'; } }).catch(function() {}); // 2) Fetch blog draft to detect pipeline completion fetch((API || '') + '/api/blog/' + id) .then(function(r) { return r.json(); }) .then(function(data) { var d = data.draft || data; var gen = d.generated_by || ''; var done = gen && gen !== 'tip-blog-engine-template' && gen.length > 0; if (done) { // Pipeline complete! var bar = document.getElementById('bp-bar'); var pct = document.getElementById('bp-pct'); var status = document.getElementById('bp-status'); var step = document.getElementById('bp-step'); if (bar) bar.style.width = '100%'; if (pct) pct.textContent = '100%'; if (status) { status.textContent = '✓ Blog fertig! ' + (d.word_count || '?') + ' Wörter'; status.style.color = '#2d6a4f'; } if (step) step.textContent = 'Engine: ' + gen; blogPipelineRunning = false; if (typeof showToast === 'function') showToast('Blog Ready!', (d.title || 'Article') + ' — ' + (d.word_count || '?') + ' words'); setTimeout(function() { var pipelineEl = document.getElementById('blog-pipeline-status'); if (pipelineEl) pipelineEl.innerHTML = ''; if (typeof loadBlogDrafts === 'function') loadBlogDrafts(); if (typeof viewBlogDraft === 'function') viewBlogDraft(id); }, 2000); } else { pollPipeline(id, attempt + 1); } }).catch(function() { pollPipeline(id, attempt + 1); }); }, 8000); // Poll every 8s instead of 15s for snappier UI } // Hot topics loader window.loadHotTopics = function() { var grid = document.getElementById('hot-topics-grid'); var subtitle = document.getElementById('hot-topics-subtitle'); if (!grid) return; grid.innerHTML = '
Discovering hot topics...
'; fetch((API || '') + '/api/hot-topics').then(function(r) { return r.json(); }).then(function(data) { if (!data.topics || data.topics.length === 0) { grid.innerHTML = '
Hype Cycle Analysis
'; return; } // Update subtitle with next-refresh countdown if (subtitle && data.refreshes_at) { var nextRefresh = new Date(data.refreshes_at); var hoursLeft = Math.round((nextRefresh - new Date()) / 3600000); subtitle.textContent = 'auto-discovered · rotates daily · next refresh in ' + hoursLeft + 'h'; } var colors = { breaking: '#c1121f', hot: '#FF8100', trending: '#e6a800', emerging: '#2d6a4f' }; grid.innerHTML = data.topics.map(function(t) { var c = colors[t.urgency] || '#888'; // Pass full topic title and angle as data attributes to avoid quote-escaping hell var cardId = 'ht-' + Math.random().toString(36).slice(2, 8); // Store topic data for onclick window['_ht_' + cardId] = t; return '
' + '
' + '' + (t.urgency || '') + '' + '' + (t.source_type || '') + '
' + '
' + (t.title || '') + '
' + '
' + (t.suggested_angle || t.description || '').slice(0, 90) + '
' + '
'; }).join(''); }).catch(function() { grid.innerHTML = '
Hype Cycle
' + '
Comparison
' + '
Tutorial
'; }); }; // Generate blog from hot topic card — uses title + angle from stored topic object window._generateFromHotTopic = function(cardId) { var t = window['_ht_' + cardId]; if (!t) return; generateBlog(t.blog_type || 'hype_cycle', null, t.title, t.suggested_angle || t.description); }; // Auto-load hot topics when blog tab activates var origActivateTab = window.activateTab; if (origActivateTab) { window.activateTab = function(tabName) { origActivateTab(tabName); if (tabName === 'blog') loadHotTopics(); }; } })(); // Called via data attributes to avoid quote-escaping issues in onclick window.blogDeleteClick = function(el) { var id = el.getAttribute('data-blog-id'); var title = el.getAttribute('data-blog-title'); if (id) window.deleteBlogDraft(id, title); }; // Delete a single blog draft window.deleteBlogDraft = function(id, title) { if (!confirm('Delete "' + title + '"?')) return; fetch((window.API || '') + '/api/blog/' + id, { method: 'DELETE' }) .then(function(r) { return r.json(); }) .then(function(data) { if (data.success) { if (typeof showToast === 'function') showToast('Deleted', title); if (typeof loadBlogDrafts === 'function') loadBlogDrafts(); } }); }; // Delete all template drafts (keep LLM-generated ones) window.deleteAllTemplateDrafts = function() { if (!confirm('Delete ALL template drafts? LLM-generated articles will be kept.')) return; fetch((window.API || '') + '/api/blog').then(function(r) { return r.json(); }).then(function(data) { var templates = (data.drafts || []).filter(function(d) { return d.generated_by === 'tip-blog-engine-template' || !d.generated_by; }); var count = 0; templates.forEach(function(d) { fetch((window.API || '') + '/api/blog/' + d.id, { method: 'DELETE' }).then(function() { count++; if (count === templates.length && typeof loadBlogDrafts === 'function') loadBlogDrafts(); }); }); if (typeof showToast === 'function') showToast('Cleaning', 'Deleting ' + templates.length + ' template drafts...'); }); };