diff --git a/app/components/settings/data/DataTab.tsx b/app/components/settings/data/DataTab.tsx index 756abaadb..aac2fe0fb 100644 --- a/app/components/settings/data/DataTab.tsx +++ b/app/components/settings/data/DataTab.tsx @@ -5,7 +5,6 @@ import { toast } from 'react-toastify'; import { db, deleteById, getAll } from '~/lib/persistence'; import { logStore } from '~/lib/stores/logs'; import { classNames } from '~/utils/classNames'; -import styles from '~/components/settings/Settings.module.scss'; // List of supported providers that can have API keys const API_KEY_PROVIDERS = [ @@ -25,8 +24,6 @@ const API_KEY_PROVIDERS = [ 'AzureOpenAI', ] as const; -type Provider = typeof API_KEY_PROVIDERS[number]; - interface ApiKeys { [key: string]: string; } @@ -52,6 +49,7 @@ export default function DataTab() { const error = new Error('Database is not available'); logStore.logError('Failed to export chats - DB unavailable', error); toast.error('Database is not available'); + return; } @@ -83,11 +81,13 @@ export default function DataTab() { const error = new Error('Database is not available'); logStore.logError('Failed to delete chats - DB unavailable', error); toast.error('Database is not available'); + return; } try { setIsDeleting(true); + const allChats = await getAll(db); await Promise.all(allChats.map((chat) => deleteById(db!, chat.id))); logStore.logSystem('All chats deleted successfully', { count: allChats.length }); @@ -125,16 +125,22 @@ export default function DataTab() { const handleImportSettings = (event: React.ChangeEvent) => { const file = event.target.files?.[0]; - if (!file) return; + + if (!file) { + return; + } const reader = new FileReader(); + reader.onload = (e) => { try { const settings = JSON.parse(e.target?.result as string); - + Object.entries(settings).forEach(([key, value]) => { if (key === 'bolt_theme') { - if (value) localStorage.setItem(key, value as string); + if (value) { + localStorage.setItem(key, value as string); + } } else if (value) { Cookies.set(key, value as string); } @@ -152,14 +158,14 @@ export default function DataTab() { const handleExportApiKeyTemplate = () => { const template: ApiKeys = {}; - API_KEY_PROVIDERS.forEach(provider => { + API_KEY_PROVIDERS.forEach((provider) => { template[`${provider}_API_KEY`] = ''; }); - template['OPENAI_LIKE_API_BASE_URL'] = ''; - template['LMSTUDIO_API_BASE_URL'] = ''; - template['OLLAMA_API_BASE_URL'] = ''; - template['TOGETHER_API_BASE_URL'] = ''; + template.OPENAI_LIKE_API_BASE_URL = ''; + template.LMSTUDIO_API_BASE_URL = ''; + template.OLLAMA_API_BASE_URL = ''; + template.TOGETHER_API_BASE_URL = ''; downloadAsJson(template, 'api-keys-template.json'); toast.success('API keys template exported successfully'); @@ -167,17 +173,22 @@ export default function DataTab() { const handleImportApiKeys = (event: React.ChangeEvent) => { const file = event.target.files?.[0]; - if (!file) return; + + if (!file) { + return; + } const reader = new FileReader(); + reader.onload = (e) => { try { const apiKeys = JSON.parse(e.target?.result as string); let importedCount = 0; const consolidatedKeys: Record = {}; - API_KEY_PROVIDERS.forEach(provider => { + API_KEY_PROVIDERS.forEach((provider) => { const keyName = `${provider}_API_KEY`; + if (apiKeys[keyName]) { consolidatedKeys[provider] = apiKeys[keyName]; importedCount++; @@ -187,13 +198,14 @@ export default function DataTab() { if (importedCount > 0) { // Store all API keys in a single cookie as JSON Cookies.set('apiKeys', JSON.stringify(consolidatedKeys)); - + // Also set individual cookies for backward compatibility Object.entries(consolidatedKeys).forEach(([provider, key]) => { Cookies.set(`${provider}_API_KEY`, key); }); toast.success(`Successfully imported ${importedCount} API keys/URLs. Refreshing page to apply changes...`); + // Reload the page after a short delay to allow the toast to be seen setTimeout(() => { window.location.reload(); @@ -203,12 +215,13 @@ export default function DataTab() { } // Set base URLs if they exist - ['OPENAI_LIKE_API_BASE_URL', 'LMSTUDIO_API_BASE_URL', 'OLLAMA_API_BASE_URL', 'TOGETHER_API_BASE_URL'].forEach(baseUrl => { - if (apiKeys[baseUrl]) { - Cookies.set(baseUrl, apiKeys[baseUrl]); - } - }); - + ['OPENAI_LIKE_API_BASE_URL', 'LMSTUDIO_API_BASE_URL', 'OLLAMA_API_BASE_URL', 'TOGETHER_API_BASE_URL'].forEach( + (baseUrl) => { + if (apiKeys[baseUrl]) { + Cookies.set(baseUrl, apiKeys[baseUrl]); + } + }, + ); } catch (error) { toast.error('Failed to import API keys. Make sure the file is a valid JSON file.'); console.error('Failed to import API keys:', error); @@ -226,9 +239,7 @@ export default function DataTab() {

Chat History

-

- Export or delete all your chat history. -

+

Export or delete all your chat history.

@@ -287,12 +293,7 @@ export default function DataTab() {
@@ -301,4 +302,4 @@ export default function DataTab() { ); -} \ No newline at end of file +} diff --git a/app/lib/runtime/message-parser.ts b/app/lib/runtime/message-parser.ts index ab6b69548..f2ee44dee 100644 --- a/app/lib/runtime/message-parser.ts +++ b/app/lib/runtime/message-parser.ts @@ -95,6 +95,16 @@ export class StreamingMessageParser { let content = currentAction.content.trim(); if ('type' in currentAction && currentAction.type === 'file') { + // Remove markdown code block syntax if present and file is not markdown + if (!currentAction.filePath.endsWith('.md')) { + const codeBlockRegex = /^\s*```\w*\n([\s\S]*?)\n\s*```\s*$/; + const match = content.match(codeBlockRegex); + + if (match) { + content = match[1].replace(/^[ ]{4}/gm, '').trim(); // Remove common leading 4-space indent + } + } + content += '\n'; }