- Create individual CSS files: monitoring.css, alerts.css, health.css, logs.css, metrics.css, resources.css - Create individual JS files: monitoring.js, alerts.js, health.js, logs.js, metrics.js, resources.js - Update HTML files to reference external CSS/JS files - Add CSS/JS files for other modules (analytics, chat, mail, meet, tasks, etc.) - Remove obsolete implementation plan files
333 lines
9.8 KiB
JavaScript
333 lines
9.8 KiB
JavaScript
/**
|
|
* Sources Module JavaScript
|
|
* Prompts, Templates, MCP Servers & AI Models
|
|
*/
|
|
(function() {
|
|
'use strict';
|
|
|
|
/**
|
|
* Initialize the Sources module
|
|
*/
|
|
function init() {
|
|
setupTabNavigation();
|
|
setupCategoryNavigation();
|
|
setupViewToggle();
|
|
setupKeyboardShortcuts();
|
|
setupHTMXEvents();
|
|
}
|
|
|
|
/**
|
|
* Set active tab
|
|
*/
|
|
window.setActiveTab = function(btn) {
|
|
document.querySelectorAll('.tab-btn').forEach(t => {
|
|
t.classList.remove('active');
|
|
t.setAttribute('aria-selected', 'false');
|
|
});
|
|
btn.classList.add('active');
|
|
btn.setAttribute('aria-selected', 'true');
|
|
};
|
|
|
|
/**
|
|
* Setup tab navigation
|
|
*/
|
|
function setupTabNavigation() {
|
|
document.querySelectorAll('.tab-btn').forEach(btn => {
|
|
btn.addEventListener('click', function() {
|
|
setActiveTab(this);
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Setup category navigation
|
|
*/
|
|
function setupCategoryNavigation() {
|
|
document.addEventListener('click', function(e) {
|
|
const categoryItem = e.target.closest('.category-item');
|
|
if (categoryItem) {
|
|
document.querySelectorAll('.category-item').forEach(c => c.classList.remove('active'));
|
|
categoryItem.classList.add('active');
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Setup view toggle (grid/list)
|
|
*/
|
|
function setupViewToggle() {
|
|
document.addEventListener('click', function(e) {
|
|
const viewBtn = e.target.closest('.view-btn');
|
|
if (viewBtn) {
|
|
const controls = viewBtn.closest('.view-controls');
|
|
if (controls) {
|
|
controls.querySelectorAll('.view-btn').forEach(b => b.classList.remove('active'));
|
|
viewBtn.classList.add('active');
|
|
|
|
const grid = document.querySelector('.prompts-grid, .templates-grid, .servers-grid, .models-grid, .news-grid');
|
|
if (grid) {
|
|
if (viewBtn.title === 'List view') {
|
|
grid.classList.add('list-view');
|
|
} else {
|
|
grid.classList.remove('list-view');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Setup keyboard shortcuts
|
|
*/
|
|
function setupKeyboardShortcuts() {
|
|
document.addEventListener('keydown', function(e) {
|
|
// Ctrl+K to focus search
|
|
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
|
|
e.preventDefault();
|
|
const searchInput = document.querySelector('.search-box input');
|
|
if (searchInput) searchInput.focus();
|
|
}
|
|
|
|
// Tab navigation with number keys
|
|
if (!e.ctrlKey && !e.metaKey && !e.altKey && !e.target.matches('input, textarea')) {
|
|
const tabs = document.querySelectorAll('.tab-btn');
|
|
const num = parseInt(e.key);
|
|
if (num >= 1 && num <= tabs.length) {
|
|
tabs[num - 1].click();
|
|
}
|
|
}
|
|
|
|
// Escape to close modals
|
|
if (e.key === 'Escape') {
|
|
closeModals();
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Setup HTMX events
|
|
*/
|
|
function setupHTMXEvents() {
|
|
if (typeof htmx === 'undefined') return;
|
|
|
|
document.body.addEventListener('htmx:beforeRequest', function(e) {
|
|
if (e.detail.target && e.detail.target.id === 'content-area') {
|
|
e.detail.target.innerHTML = `
|
|
<div class="loading-spinner">
|
|
<div class="spinner"></div>
|
|
<p>Loading...</p>
|
|
</div>
|
|
`;
|
|
}
|
|
});
|
|
|
|
document.body.addEventListener('htmx:afterSwap', function(e) {
|
|
// Re-initialize any dynamic content handlers after content swap
|
|
setupPromptCards();
|
|
setupServerCards();
|
|
setupModelCards();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Setup prompt card interactions
|
|
*/
|
|
function setupPromptCards() {
|
|
document.querySelectorAll('.prompt-card').forEach(card => {
|
|
card.addEventListener('click', function(e) {
|
|
// Don't trigger if clicking on action buttons
|
|
if (e.target.closest('.prompt-action-btn')) return;
|
|
|
|
const promptId = this.dataset.id;
|
|
if (promptId) {
|
|
showPromptDetail(promptId);
|
|
}
|
|
});
|
|
});
|
|
|
|
document.querySelectorAll('.prompt-action-btn').forEach(btn => {
|
|
btn.addEventListener('click', function(e) {
|
|
e.stopPropagation();
|
|
const action = this.title.toLowerCase();
|
|
const card = this.closest('.prompt-card');
|
|
const promptId = card?.dataset.id;
|
|
|
|
switch (action) {
|
|
case 'use':
|
|
usePrompt(promptId);
|
|
break;
|
|
case 'copy':
|
|
copyPrompt(promptId);
|
|
break;
|
|
case 'save':
|
|
savePrompt(promptId);
|
|
break;
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Setup server card interactions
|
|
*/
|
|
function setupServerCards() {
|
|
document.querySelectorAll('.server-card').forEach(card => {
|
|
card.addEventListener('click', function() {
|
|
const serverId = this.dataset.id;
|
|
if (serverId) {
|
|
showServerDetail(serverId);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Setup model card interactions
|
|
*/
|
|
function setupModelCards() {
|
|
document.querySelectorAll('.model-card').forEach(card => {
|
|
card.addEventListener('click', function() {
|
|
const modelId = this.dataset.id;
|
|
if (modelId) {
|
|
showModelDetail(modelId);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Show prompt detail modal/panel
|
|
*/
|
|
function showPromptDetail(promptId) {
|
|
if (typeof htmx !== 'undefined') {
|
|
htmx.ajax('GET', `/api/sources/prompts/${promptId}`, {
|
|
target: '#prompt-detail-panel',
|
|
swap: 'innerHTML'
|
|
}).then(() => {
|
|
document.getElementById('prompt-detail-panel')?.classList.remove('hidden');
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Use a prompt
|
|
*/
|
|
function usePrompt(promptId) {
|
|
if (typeof htmx !== 'undefined') {
|
|
htmx.ajax('POST', `/api/sources/prompts/${promptId}/use`, {
|
|
swap: 'none'
|
|
}).then(() => {
|
|
// Navigate to the appropriate module
|
|
window.location.hash = '#research';
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Copy prompt to clipboard
|
|
*/
|
|
function copyPrompt(promptId) {
|
|
if (typeof htmx !== 'undefined') {
|
|
htmx.ajax('GET', `/api/sources/prompts/${promptId}/content`, {
|
|
swap: 'none'
|
|
}).then(response => {
|
|
// Parse response and copy to clipboard
|
|
navigator.clipboard.writeText(response || '');
|
|
showToast('Prompt copied to clipboard');
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Save prompt to collection
|
|
*/
|
|
function savePrompt(promptId) {
|
|
const collectionName = prompt('Enter collection name:');
|
|
if (collectionName && typeof htmx !== 'undefined') {
|
|
htmx.ajax('POST', '/api/sources/prompts/save', {
|
|
values: {
|
|
promptId,
|
|
collection: collectionName
|
|
}
|
|
}).then(() => {
|
|
showToast('Prompt saved to collection');
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Show server detail
|
|
*/
|
|
function showServerDetail(serverId) {
|
|
if (typeof htmx !== 'undefined') {
|
|
htmx.ajax('GET', `/api/sources/mcp-servers/${serverId}`, {
|
|
target: '#server-detail-panel',
|
|
swap: 'innerHTML'
|
|
}).then(() => {
|
|
document.getElementById('server-detail-panel')?.classList.remove('hidden');
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Show model detail
|
|
*/
|
|
function showModelDetail(modelId) {
|
|
if (typeof htmx !== 'undefined') {
|
|
htmx.ajax('GET', `/api/sources/models/${modelId}`, {
|
|
target: '#model-detail-panel',
|
|
swap: 'innerHTML'
|
|
}).then(() => {
|
|
document.getElementById('model-detail-panel')?.classList.remove('hidden');
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Close all modals
|
|
*/
|
|
function closeModals() {
|
|
document.querySelectorAll('.modal, .detail-panel').forEach(modal => {
|
|
modal.classList.add('hidden');
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Show toast notification
|
|
*/
|
|
function showToast(message, type = 'success') {
|
|
const toast = document.createElement('div');
|
|
toast.className = `toast toast-${type}`;
|
|
toast.textContent = message;
|
|
document.body.appendChild(toast);
|
|
|
|
// Trigger animation
|
|
requestAnimationFrame(() => {
|
|
toast.classList.add('show');
|
|
});
|
|
|
|
// Remove after delay
|
|
setTimeout(() => {
|
|
toast.classList.remove('show');
|
|
setTimeout(() => toast.remove(), 300);
|
|
}, 3000);
|
|
}
|
|
|
|
// Initialize on DOM ready
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', init);
|
|
} else {
|
|
init();
|
|
}
|
|
|
|
// Expose for external use
|
|
window.Sources = {
|
|
setActiveTab,
|
|
showPromptDetail,
|
|
usePrompt,
|
|
copyPrompt,
|
|
savePrompt,
|
|
showToast
|
|
};
|
|
})();
|