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..fa3b4a367 100644 --- a/app/lib/runtime/message-parser.ts +++ b/app/lib/runtime/message-parser.ts @@ -52,6 +52,17 @@ interface MessageState { actionId: number; } +function cleanoutMarkdownSyntax(content: string) { + const codeBlockRegex = /^\s*```\w*\n([\s\S]*?)\n\s*```\s*$/; + const match = content.match(codeBlockRegex); + console.log('matching', !!match, content); + + if (match) { + return match[1]; // Remove common leading 4-space indent + } else { + return content; + } +} export class StreamingMessageParser { #messages = new Map(); @@ -95,6 +106,12 @@ 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')) { + content = cleanoutMarkdownSyntax(content); + console.log('content after cleanup', content); + } + content += '\n'; } @@ -120,7 +137,11 @@ export class StreamingMessageParser { i = closeIndex + ARTIFACT_ACTION_TAG_CLOSE.length; } else { if ('type' in currentAction && currentAction.type === 'file') { - const content = input.slice(i); + let content = input.slice(i); + + if (!currentAction.filePath.endsWith('.md')) { + content = cleanoutMarkdownSyntax(content); + } this._options.callbacks?.onActionStream?.({ artifactId: currentArtifact.id,