botui/ui/suite/research/research.js

348 lines
12 KiB
JavaScript
Raw Permalink Normal View History

/**
* Research Module JavaScript
* AI-Powered Search & Discovery
*/
(function() {
'use strict';
// DOM Elements
const elements = {
searchInput: document.getElementById('search-input'),
searchForm: document.getElementById('research-form'),
suggestionsPanel: document.getElementById('suggestions-panel'),
resultsContainer: document.getElementById('main-results'),
sourcesPanel: document.getElementById('sources-panel'),
sidebar: document.getElementById('research-sidebar'),
focusModeInput: document.getElementById('focus-mode'),
proModeInput: document.getElementById('pro-mode')
};
/**
* Initialize the Research module
*/
function init() {
setupSearchInput();
setupFocusModes();
setupProSearch();
setupSuggestionCards();
setupPromptChips();
setupSidebar();
setupSourcesPanel();
setupResultHandlers();
}
/**
* Setup search input auto-resize and submit behavior
*/
function setupSearchInput() {
if (!elements.searchInput) return;
// Auto-resize textarea
elements.searchInput.addEventListener('input', function() {
this.style.height = 'auto';
this.style.height = Math.min(this.scrollHeight, 120) + 'px';
});
// Handle Enter to submit (Shift+Enter for new line)
elements.searchInput.addEventListener('keydown', function(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
elements.searchForm?.requestSubmit();
}
});
}
/**
* Setup focus mode buttons
*/
function setupFocusModes() {
document.querySelectorAll('.focus-btn').forEach(btn => {
btn.addEventListener('click', function() {
document.querySelectorAll('.focus-btn').forEach(b => b.classList.remove('active'));
this.classList.add('active');
if (elements.focusModeInput) {
elements.focusModeInput.value = this.dataset.focus;
}
});
});
}
/**
* Setup pro search toggle
*/
function setupProSearch() {
const toggle = document.getElementById('pro-search-toggle');
if (toggle && elements.proModeInput) {
toggle.addEventListener('change', function() {
elements.proModeInput.value = this.checked;
});
}
}
/**
* Setup suggestion cards click behavior
*/
function setupSuggestionCards() {
document.querySelectorAll('.suggestion-card').forEach(card => {
card.addEventListener('click', function() {
if (elements.searchInput) {
elements.searchInput.value = this.dataset.query;
elements.searchInput.style.height = 'auto';
elements.searchInput.style.height = elements.searchInput.scrollHeight + 'px';
elements.searchForm?.requestSubmit();
}
});
});
}
/**
* Setup prompt chip buttons
*/
function setupPromptChips() {
const prefixes = {
'explain': 'Explain ',
'compare': 'Compare ',
'summarize': 'Summarize ',
'analyze': 'Analyze ',
'pros-cons': 'What are the pros and cons of ',
'how-to': 'How to '
};
document.querySelectorAll('.prompt-chip').forEach(chip => {
chip.addEventListener('click', function() {
const prompt = this.dataset.prompt;
const currentValue = elements.searchInput?.value.trim() || '';
if (elements.searchInput && prefixes[prompt]) {
elements.searchInput.value = prefixes[prompt] + currentValue;
elements.searchInput.focus();
}
});
});
}
/**
* Setup sidebar toggle
*/
function setupSidebar() {
const toggleBtn = document.getElementById('toggle-research-sidebar');
if (toggleBtn && elements.sidebar) {
toggleBtn.addEventListener('click', function() {
elements.sidebar.classList.toggle('collapsed');
});
}
// Collection item click
document.addEventListener('click', function(e) {
const collectionItem = e.target.closest('.collection-item');
if (collectionItem) {
const collectionId = collectionItem.dataset.id;
if (typeof htmx !== 'undefined') {
htmx.ajax('GET', `/api/research/collections/${collectionId}`, {
target: '#main-results'
});
}
}
});
// Recent item click
document.addEventListener('click', function(e) {
const recentItem = e.target.closest('.recent-item');
if (recentItem && elements.searchInput) {
elements.searchInput.value = recentItem.dataset.query;
elements.searchForm?.requestSubmit();
}
});
// Source category click
document.querySelectorAll('.source-category').forEach(cat => {
cat.addEventListener('click', function() {
const category = this.dataset.category;
if (typeof htmx !== 'undefined') {
htmx.ajax('GET', `/api/research/sources?category=${category}`, {
target: '#sources-list'
});
}
elements.sourcesPanel?.classList.remove('hidden');
});
});
}
/**
* Setup sources panel interactions
*/
function setupSourcesPanel() {
// View all sources
document.addEventListener('click', function(e) {
if (e.target.id === 'view-all-sources' || e.target.closest('#view-all-sources')) {
elements.sourcesPanel?.classList.remove('hidden');
}
});
// Close sources panel
const closeBtn = document.getElementById('close-sources');
if (closeBtn) {
closeBtn.addEventListener('click', function() {
elements.sourcesPanel?.classList.add('hidden');
});
}
// Citation click handler
document.addEventListener('click', function(e) {
if (e.target.classList.contains('citation')) {
const sourceNum = e.target.textContent;
elements.sourcesPanel?.classList.remove('hidden');
// Scroll to source in panel
const sourceCard = elements.sourcesPanel?.querySelector(`[data-source="${sourceNum}"]`);
if (sourceCard) {
sourceCard.scrollIntoView({ behavior: 'smooth', block: 'center' });
sourceCard.classList.add('highlight');
setTimeout(() => sourceCard.classList.remove('highlight'), 2000);
}
}
});
}
/**
* Setup result interaction handlers
*/
function setupResultHandlers() {
// Related question click
document.addEventListener('click', function(e) {
const relatedItem = e.target.closest('.related-item');
if (relatedItem && elements.searchInput) {
elements.searchInput.value = relatedItem.textContent.trim();
elements.searchForm?.requestSubmit();
window.scrollTo({ top: 0, behavior: 'smooth' });
}
});
// Trending tag click
document.addEventListener('click', function(e) {
const trendingTag = e.target.closest('.trending-tag');
if (trendingTag && elements.searchInput) {
elements.searchInput.value = trendingTag.dataset.query || trendingTag.textContent.trim();
elements.searchForm?.requestSubmit();
}
});
// Copy answer
document.addEventListener('click', function(e) {
const copyBtn = e.target.closest('.action-btn[title="Copy"]');
if (copyBtn) {
const content = document.getElementById('answer-content');
if (content) {
navigator.clipboard.writeText(content.innerText);
// Show feedback
const originalTitle = copyBtn.title;
copyBtn.title = 'Copied!';
setTimeout(() => copyBtn.title = originalTitle, 2000);
}
}
});
// Save to collection
document.addEventListener('click', function(e) {
const saveBtn = e.target.closest('.action-btn[title="Save to Collection"]');
if (saveBtn) {
showSaveToCollectionModal();
}
});
// Export to Paper
document.addEventListener('click', function(e) {
const exportBtn = e.target.closest('.action-btn[title="Export to Paper"]');
if (exportBtn) {
const content = document.getElementById('answer-content');
if (content && typeof htmx !== 'undefined') {
htmx.ajax('POST', '/api/paper/import', {
values: {
content: content.innerHTML,
title: elements.searchInput?.value || 'Research Export'
}
}).then(() => {
// Navigate to Paper
window.location.hash = '#paper';
});
}
}
});
// Handle search results display
if (typeof htmx !== 'undefined') {
htmx.on('#main-results', 'htmx:afterSwap', function() {
// Hide suggestions when results are shown
elements.suggestionsPanel?.classList.add('hidden');
// Update source counts
updateSourceCounts();
});
}
}
/**
* Update source counts in sidebar
*/
function updateSourceCounts() {
if (typeof htmx !== 'undefined') {
htmx.ajax('GET', '/api/research/source-counts', {
swap: 'none'
}).then(response => {
// Update counts in sidebar if response contains them
});
}
}
/**
* Show save to collection modal
*/
function showSaveToCollectionModal() {
// This would typically show a modal with collection options
// For now, we'll use a simple prompt
const collectionName = prompt('Enter collection name:');
if (collectionName && typeof htmx !== 'undefined') {
const content = document.getElementById('answer-content');
htmx.ajax('POST', '/api/research/collections/save', {
values: {
collection: collectionName,
content: content?.innerHTML || '',
query: elements.searchInput?.value || ''
}
});
}
}
/**
* Toggle sidebar on mobile
*/
window.toggleResearchSidebar = function() {
elements.sidebar?.classList.toggle('open');
};
// Initialize on DOM ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
// Expose for external use
window.Research = {
search: function(query) {
if (elements.searchInput) {
elements.searchInput.value = query;
elements.searchForm?.requestSubmit();
}
},
setFocusMode: function(mode) {
const btn = document.querySelector(`.focus-btn[data-focus="${mode}"]`);
if (btn) btn.click();
},
toggleSidebar: function() {
elements.sidebar?.classList.toggle('collapsed');
}
};
})();