// settings.js - Settings Management
(function() {
console.log("[settings] Loading Settings interface...");
// --- Model Configuration ---
const MODEL_CONFIG = {
'grok-code-fast-1': {
provider: 'xai',
name: 'Grok Code Fast 1',
icon: '🤖',
color: '#8b5cf6'
},
'deepseek-chat': {
provider: 'deepseek',
name: 'DeepSeek Chat',
icon: '🧠',
color: '#3b82f6'
},
'deepseek-reasoner': {
provider: 'deepseek',
name: 'DeepSeek Reasoner',
icon: '🧠',
color: '#3b82f6'
},
'gpt-4o': {
provider: 'openai',
name: 'GPT-4o',
icon: '🌟',
color: '#10b981'
},
'gpt-4o-mini': {
provider: 'openai',
name: 'GPT-4o Mini',
icon: '🌟',
color: '#10b981'
},
'grok-3': {
provider: 'xai',
name: 'Grok 3',
icon: '🤖',
color: '#8b5cf6'
}
};
const API_ENDPOINT = 'api.php';
// Load settings from localStorage
let settings = {
defaultModel: localStorage.getItem('settings_defaultModel') || 'grok-code-fast-1',
maxTokens: parseInt(localStorage.getItem('settings_maxTokens')) || 2000,
temperature: parseFloat(localStorage.getItem('settings_temperature')) || 0.7,
chatPrompt: localStorage.getItem('settings_chatPrompt') || 'You are a helpful AI assistant for web development.',
snippetsPrompt: localStorage.getItem('settings_snippetsPrompt') || 'You are an expert at writing small, focused code snippets. Provide concise, working code examples.',
fullCodePrompt: localStorage.getItem('settings_fullCodePrompt') || 'You are an expert full-stack developer. Provide complete, production-ready code solutions.',
responseMode: localStorage.getItem('settings_responseMode') || 'normal',
currentMode: localStorage.getItem('settings_currentMode') || 'chat'
};
// Save settings to localStorage
function saveSettings() {
localStorage.setItem('settings_defaultModel', settings.defaultModel);
localStorage.setItem('settings_maxTokens', settings.maxTokens);
localStorage.setItem('settings_temperature', settings.temperature);
localStorage.setItem('settings_chatPrompt', settings.chatPrompt);
localStorage.setItem('settings_snippetsPrompt', settings.snippetsPrompt);
localStorage.setItem('settings_fullCodePrompt', settings.fullCodePrompt);
localStorage.setItem('settings_responseMode', settings.responseMode);
localStorage.setItem('settings_currentMode', settings.currentMode);
console.log('[settings] Settings saved:', settings);
// Dispatch event so other modules can react
window.dispatchEvent(new CustomEvent('settingsUpdated', { detail: settings }));
}
// --- Settings Slide Configuration ---
const settingsSlide = {
title: 'Settings',
html: `
<div style="height: 100%; overflow-y: auto; padding: 20px; background: #0a0a0a;">
<h2 style="color: #fff; font-size: 24px; margin-bottom: 24px; font-weight: 700;">⚙️ Settings</h2>
<!-- AI Configuration Section -->
<div style="background: #1a1a1a; border: 2px solid #2a2a2a; border-radius: 8px; padding: 20px; margin-bottom: 20px;">
<h3 style="color: #fff; font-size: 18px; margin-bottom: 16px; font-weight: 600;">🤖 AI Configuration</h3>
<div style="margin-bottom: 20px;">
<label style="display: block; color: #888; font-size: 13px; font-weight: 600; margin-bottom: 8px;">Default AI Model:</label>
<select id="settingsDefaultModel" style="width: 100%; padding: 10px 12px; background: #2a2a2a; border: 1px solid #3a3a3a; border-radius: 4px; color: #e0e0e0; font-size: 14px; font-family: monospace; cursor: pointer; outline: none;">
<option value="grok-code-fast-1">🤖 Grok Code Fast 1</option>
<option value="deepseek-chat">🧠 DeepSeek Chat</option>
<option value="deepseek-reasoner">🧠 DeepSeek Reasoner</option>
<option value="gpt-4o">🌟 GPT-4o</option>
<option value="gpt-4o-mini">🌟 GPT-4o Mini</option>
<option value="grok-3">🤖 Grok 3</option>
</select>
</div>
<div style="margin-bottom: 20px;">
<label style="display: block; color: #888; font-size: 13px; font-weight: 600; margin-bottom: 8px;">Max Tokens: <span id="maxTokensValue" style="color: #16a34a;">2000</span></label>
<input type="range" id="settingsMaxTokens" min="500" max="8000" step="100" value="2000" style="width: 100%; cursor: pointer;" />
<div style="display: flex; justify-content: space-between; color: #666; font-size: 11px; margin-top: 4px;">
<span>500</span>
<span>8000</span>
</div>
</div>
<div style="margin-bottom: 20px;">
<label style="display: block; color: #888; font-size: 13px; font-weight: 600; margin-bottom: 8px;">Temperature: <span id="temperatureValue" style="color: #16a34a;">0.7</span></label>
<input type="range" id="settingsTemperature" min="0" max="2" step="0.1" value="0.7" style="width: 100%; cursor: pointer;" />
<div style="display: flex; justify-content: space-between; color: #666; font-size: 11px; margin-top: 4px;">
<span>0.0 (Focused)</span>
<span>2.0 (Creative)</span>
</div>
</div>
<button id="saveSettingsBtn" style="width: 100%; padding: 12px; background: #16a34a; border: 1px solid #15803d; border-radius: 4px; color: #fff; font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.2s;">💾 Save Settings</button>
</div>
<!-- System Prompts Section -->
<div style="background: #1a1a1a; border: 2px solid #2a2a2a; border-radius: 8px; padding: 20px; margin-bottom: 20px;">
<h3 style="color: #fff; font-size: 18px; margin-bottom: 16px; font-weight: 600;">📝 System Prompts & Mode</h3>
<div style="margin-bottom: 20px;">
<label style="display: block; color: #888; font-size: 13px; font-weight: 600; margin-bottom: 8px;">🎨 Active Mode:</label>
<select id="settingsCurrentMode" style="width: 100%; padding: 10px 12px; background: #2a2a2a; border: 1px solid #3a3a3a; border-radius: 4px; color: #e0e0e0; font-size: 14px; font-family: monospace; cursor: pointer; outline: none;">
<option value="chat">💬 Chat Mode</option>
<option value="snippets">✂️ Snippets Mode</option>
<option value="fullcode">🔧 Full Code Mode</option>
</select>
<div style="margin-top: 8px; padding: 8px 12px; background: rgba(139, 92, 246, 0.1); border: 1px solid #8b5cf6; border-radius: 4px; color: #c4b5fd; font-size: 11px; line-height: 1.4;">
This determines which system prompt is used for your conversations.
</div>
</div>
<div style="margin-bottom: 20px;">
<label style="display: block; color: #888; font-size: 13px; font-weight: 600; margin-bottom: 8px;">💬 Chat Mode Prompt:</label>
<textarea id="settingsChatPrompt" placeholder="System prompt for general chat..." style="width: 100%; min-height: 80px; padding: 10px 12px; background: #2a2a2a; border: 1px solid #3a3a3a; border-radius: 4px; color: #e0e0e0; font-size: 13px; font-family: 'Segoe UI', sans-serif; resize: vertical; outline: none;"></textarea>
</div>
<div style="margin-bottom: 20px;">
<label style="display: block; color: #888; font-size: 13px; font-weight: 600; margin-bottom: 8px;">✂️ Snippets Mode Prompt:</label>
<textarea id="settingsSnippetsPrompt" placeholder="System prompt for code snippets..." style="width: 100%; min-height: 80px; padding: 10px 12px; background: #2a2a2a; border: 1px solid #3a3a3a; border-radius: 4px; color: #e0e0e0; font-size: 13px; font-family: 'Segoe UI', sans-serif; resize: vertical; outline: none;"></textarea>
</div>
<div style="margin-bottom: 20px;">
<label style="display: block; color: #888; font-size: 13px; font-weight: 600; margin-bottom: 8px;">🔧 Full Code Mode Prompt:</label>
<textarea id="settingsFullCodePrompt" placeholder="System prompt for full code solutions..." style="width: 100%; min-height: 80px; padding: 10px 12px; background: #2a2a2a; border: 1px solid #3a3a3a; border-radius: 4px; color: #e0e0e0; font-size: 13px; font-family: 'Segoe UI', sans-serif; resize: vertical; outline: none;"></textarea>
</div>
<div style="margin-bottom: 0;">
<label style="display: block; color: #888; font-size: 13px; font-weight: 600; margin-bottom: 8px;">📤 Response Mode:</label>
<select id="settingsResponseMode" style="width: 100%; padding: 10px 12px; background: #2a2a2a; border: 1px solid #3a3a3a; border-radius: 4px; color: #e0e0e0; font-size: 14px; font-family: monospace; cursor: pointer; outline: none;">
<option value="normal">📋 Normal (Formatted response)</option>
<option value="raw">🔤 Raw (Plain text only)</option>
</select>
<div style="margin-top: 8px; padding: 8px 12px; background: rgba(59, 130, 246, 0.1); border: 1px solid #3b82f6; border-radius: 4px; color: #93c5fd; font-size: 11px; line-height: 1.4;">
<strong>Normal:</strong> AI provides explanations with code.<br>
<strong>Raw:</strong> AI returns only code without commentary.
</div>
</div>
</div>
<!-- Model Information -->
<div style="background: #1a1a1a; border: 2px solid #2a2a2a; border-radius: 8px; padding: 20px;">
<h3 style="color: #fff; font-size: 18px; margin-bottom: 16px; font-weight: 600;">📊 Model Information</h3>
<div id="modelInfoContainer" style="font-size: 13px; line-height: 1.6;"></div>
</div>
</div>
`,
onRender(el) {
console.log('[settings] Rendering settings interface');
const defaultModelSelect = el.querySelector('#settingsDefaultModel');
const maxTokensSlider = el.querySelector('#settingsMaxTokens');
const maxTokensValue = el.querySelector('#maxTokensValue');
const temperatureSlider = el.querySelector('#settingsTemperature');
const temperatureValue = el.querySelector('#temperatureValue');
const chatPrompt = el.querySelector('#settingsChatPrompt');
const snippetsPrompt = el.querySelector('#settingsSnippetsPrompt');
const fullCodePrompt = el.querySelector('#settingsFullCodePrompt');
const responseMode = el.querySelector('#settingsResponseMode');
const currentMode = el.querySelector('#settingsCurrentMode');
const saveBtn = el.querySelector('#saveSettingsBtn');
const modelInfoContainer = el.querySelector('#modelInfoContainer');
// Load current settings
defaultModelSelect.value = settings.defaultModel;
maxTokensSlider.value = settings.maxTokens;
maxTokensValue.textContent = settings.maxTokens;
temperatureSlider.value = settings.temperature;
temperatureValue.textContent = settings.temperature.toFixed(1);
chatPrompt.value = settings.chatPrompt;
snippetsPrompt.value = settings.snippetsPrompt;
fullCodePrompt.value = settings.fullCodePrompt;
responseMode.value = settings.responseMode;
currentMode.value = settings.currentMode;
// Update max tokens display
maxTokensSlider.addEventListener('input', () => {
maxTokensValue.textContent = maxTokensSlider.value;
});
// Update temperature display
temperatureSlider.addEventListener('input', () => {
temperatureValue.textContent = parseFloat(temperatureSlider.value).toFixed(1);
});
// Display model info
function updateModelInfo() {
const modelKey = defaultModelSelect.value;
const model = MODEL_CONFIG[modelKey];
if (model) {
const r = parseInt(model.color.slice(1,3), 16);
const g = parseInt(model.color.slice(3,5), 16);
const b = parseInt(model.color.slice(5,7), 16);
modelInfoContainer.innerHTML = `
<div style="padding: 12px; background: rgba(${r}, ${g}, ${b}, 0.1); border: 1px solid ${model.color}; border-radius: 6px; margin-bottom: 12px;">
<div style="font-size: 16px; margin-bottom: 8px;">${model.icon} <strong style="color: #fff;">${model.name}</strong></div>
<div style="color: #888;">Provider: <span style="color: #e0e0e0; font-family: monospace;">${model.provider}</span></div>
</div>
<div style="color: #666; font-size: 12px; line-height: 1.6;">
<p style="margin-bottom: 8px;"><strong style="color: #888;">Max Tokens:</strong> Controls the maximum length of the response.</p>
<p style="margin-bottom: 8px;"><strong style="color: #888;">Temperature:</strong> Controls creativity (0 = focused, 2 = creative).</p>
</div>
`;
}
}
defaultModelSelect.addEventListener('change', updateModelInfo);
updateModelInfo();
// Save settings
saveBtn.addEventListener('click', () => {
settings.defaultModel = defaultModelSelect.value;
settings.maxTokens = parseInt(maxTokensSlider.value);
settings.temperature = parseFloat(temperatureSlider.value);
settings.chatPrompt = chatPrompt.value.trim();
settings.snippetsPrompt = snippetsPrompt.value.trim();
settings.fullCodePrompt = fullCodePrompt.value.trim();
settings.responseMode = responseMode.value;
settings.currentMode = currentMode.value;
saveSettings();
// Visual feedback
saveBtn.textContent = '✅ Saved!';
saveBtn.style.background = '#15803d';
setTimeout(() => {
saveBtn.textContent = '💾 Save Settings';
saveBtn.style.background = '#16a34a';
}, 1500);
});
// Button hover effect
saveBtn.addEventListener('mouseenter', () => {
if (saveBtn.textContent === '💾 Save Settings') {
saveBtn.style.background = '#15803d';
}
});
saveBtn.addEventListener('mouseleave', () => {
if (saveBtn.textContent === '💾 Save Settings') {
saveBtn.style.background = '#16a34a';
}
});
}
};
// --- Expose Settings API ---
window.Settings = {
open: () => {
if (window.AppOverlay) {
window.AppOverlay.open([settingsSlide]);
} else {
console.error('[settings] AppOverlay not available');
}
},
slide: settingsSlide,
get: () => ({ ...settings }),
getModelConfig: (modelKey) => MODEL_CONFIG[modelKey],
getAllModels: () => MODEL_CONFIG,
getApiEndpoint: () => API_ENDPOINT
};
// --- Register as AppItems component ---
/* if (window.AppItems) {
window.AppItems.push({
title: 'Settings',
html: settingsSlide.html,
onRender: settingsSlide.onRender
});
console.log('[settings] Registered as AppItems component');
}*/
console.log('[settings] Settings interface loaded with settings:', settings);
})();