diff --git a/app/components/chat/Chat.client.tsx b/app/components/chat/Chat.client.tsx
index a5d2ca786..008971819 100644
--- a/app/components/chat/Chat.client.tsx
+++ b/app/components/chat/Chat.client.tsx
@@ -54,6 +54,8 @@ export function Chat() {
position="bottom-right"
pauseOnFocusLoss
transition={toastAnimation}
+ hideProgressBar
+ autoClose={false}
/>
>
) : null;
diff --git a/app/entry.client.tsx b/app/entry.client.tsx
index 62917e70d..36a3d6023 100644
--- a/app/entry.client.tsx
+++ b/app/entry.client.tsx
@@ -1,7 +1,134 @@
import { RemixBrowser } from '@remix-run/react';
import { startTransition } from 'react';
import { hydrateRoot } from 'react-dom/client';
+import { createScopedLogger } from '~/utils/logger';
-startTransition(() => {
- hydrateRoot(document.getElementById('root')!, );
+const logger = createScopedLogger('Client');
+
+function isChrome(): boolean {
+ return /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor);
+}
+
+// Initialize IndexedDB before hydration
+async function initIndexedDB() {
+ if (typeof window === 'undefined' || !window.indexedDB) {
+ logger.debug('IndexedDB not available');
+ window.__BOLT_PERSISTENCE_AVAILABLE__ = false;
+ return false;
+ }
+
+ return new Promise((resolve) => {
+ try {
+ // For Chrome, we need to be more careful with initialization
+ if (isChrome()) {
+ // First, try to open a test database
+ const testRequest = window.indexedDB.open('test', 1);
+ testRequest.onerror = () => {
+ logger.error('Test database failed');
+ window.__BOLT_PERSISTENCE_AVAILABLE__ = false;
+ resolve(false);
+ };
+
+ testRequest.onsuccess = () => {
+ // Close and delete test database
+ const testDb = testRequest.result;
+ testDb.close();
+ const deleteRequest = window.indexedDB.deleteDatabase('test');
+
+ deleteRequest.onsuccess = () => {
+ // Now try to open the actual database
+ const request = window.indexedDB.open('boltHistory', 1);
+
+ request.onerror = () => {
+ logger.error('Failed to open database');
+ window.__BOLT_PERSISTENCE_AVAILABLE__ = false;
+ resolve(false);
+ };
+
+ request.onupgradeneeded = (event) => {
+ const db = (event.target as IDBOpenDBRequest).result;
+ if (!db.objectStoreNames.contains('chats')) {
+ const store = db.createObjectStore('chats', { keyPath: 'id' });
+ store.createIndex('id', 'id', { unique: true });
+ store.createIndex('urlId', 'urlId', { unique: true });
+ }
+ };
+
+ request.onsuccess = (event) => {
+ const db = (event.target as IDBOpenDBRequest).result;
+
+ // Test if we can actually use the database
+ try {
+ const transaction = db.transaction(['chats'], 'readonly');
+ transaction.oncomplete = () => {
+ logger.debug('Database test successful');
+ window.__BOLT_PERSISTENCE_AVAILABLE__ = true;
+ resolve(true);
+ };
+ transaction.onerror = () => {
+ logger.error('Database test failed');
+ window.__BOLT_PERSISTENCE_AVAILABLE__ = false;
+ resolve(false);
+ };
+ } catch (error) {
+ logger.error('Error testing database:', error);
+ window.__BOLT_PERSISTENCE_AVAILABLE__ = false;
+ resolve(false);
+ }
+ };
+ };
+
+ deleteRequest.onerror = () => {
+ logger.error('Failed to delete test database');
+ window.__BOLT_PERSISTENCE_AVAILABLE__ = false;
+ resolve(false);
+ };
+ };
+ } else {
+ // For other browsers, use the standard approach
+ const request = window.indexedDB.open('boltHistory', 1);
+ request.onerror = () => {
+ logger.error('Failed to open database');
+ window.__BOLT_PERSISTENCE_AVAILABLE__ = false;
+ resolve(false);
+ };
+
+ request.onupgradeneeded = (event) => {
+ const db = (event.target as IDBOpenDBRequest).result;
+ if (!db.objectStoreNames.contains('chats')) {
+ const store = db.createObjectStore('chats', { keyPath: 'id' });
+ store.createIndex('id', 'id', { unique: true });
+ store.createIndex('urlId', 'urlId', { unique: true });
+ }
+ };
+
+ request.onsuccess = () => {
+ logger.debug('Database initialized');
+ window.__BOLT_PERSISTENCE_AVAILABLE__ = true;
+ resolve(true);
+ };
+ }
+ } catch (error) {
+ logger.error('Error initializing database:', error);
+ window.__BOLT_PERSISTENCE_AVAILABLE__ = false;
+ resolve(false);
+ }
+ });
+}
+
+// Set initial persistence state
+window.__BOLT_PERSISTENCE_AVAILABLE__ = false;
+
+// Initialize IndexedDB before hydrating the app
+initIndexedDB().then(() => {
+ startTransition(() => {
+ hydrateRoot(document.getElementById('root')!, );
+ });
});
+
+// Add type declaration
+declare global {
+ interface Window {
+ __BOLT_PERSISTENCE_AVAILABLE__: boolean;
+ }
+}
diff --git a/app/lib/persistence/db.ts b/app/lib/persistence/db.ts
index 1199ff7b8..bfbb6fcd9 100644
--- a/app/lib/persistence/db.ts
+++ b/app/lib/persistence/db.ts
@@ -37,62 +37,78 @@ export async function openDatabase(): Promise {
return;
}
- const request = indexedDB.open('boltHistory', 1);
+ // Test if we can actually open IndexedDB
+ const testRequest = window.indexedDB.open('test');
+ testRequest.onerror = () => {
+ logger.error('IndexedDB test failed');
+ dbInitAttempted = true;
+ dbInitializing = false;
+ resolve(undefined);
+ };
- request.onupgradeneeded = (event: IDBVersionChangeEvent) => {
- const db = (event.target as IDBOpenDBRequest).result;
- logger.debug('Upgrading database');
+ testRequest.onsuccess = () => {
+ // Close and delete test database
+ const db = testRequest.result;
+ db.close();
+ window.indexedDB.deleteDatabase('test');
- if (!db.objectStoreNames.contains('chats')) {
- const store = db.createObjectStore('chats', { keyPath: 'id' });
- store.createIndex('id', 'id', { unique: true });
- store.createIndex('urlId', 'urlId', { unique: true });
- logger.debug('Created chats store');
- }
- };
+ // Now open the actual database
+ const request = window.indexedDB.open('boltHistory', 1);
- request.onsuccess = (event: Event) => {
- const db = (event.target as IDBOpenDBRequest).result;
- logger.debug('Successfully opened database');
-
- // Test if we can actually use the database
- try {
- const transaction = db.transaction(['chats'], 'readonly');
- transaction.oncomplete = () => {
- logger.debug('Database test successful');
- dbInitAttempted = true;
- dbInitializing = false;
- resolve(db);
- };
- transaction.onerror = () => {
- logger.error('Database test failed');
+ request.onupgradeneeded = (event: IDBVersionChangeEvent) => {
+ const db = (event.target as IDBOpenDBRequest).result;
+ logger.debug('Upgrading database');
+
+ if (!db.objectStoreNames.contains('chats')) {
+ const store = db.createObjectStore('chats', { keyPath: 'id' });
+ store.createIndex('id', 'id', { unique: true });
+ store.createIndex('urlId', 'urlId', { unique: true });
+ logger.debug('Created chats store');
+ }
+ };
+
+ request.onsuccess = (event: Event) => {
+ const db = (event.target as IDBOpenDBRequest).result;
+ logger.debug('Successfully opened database');
+
+ // Test if we can actually use the database
+ try {
+ const transaction = db.transaction(['chats'], 'readonly');
+ transaction.oncomplete = () => {
+ logger.debug('Database test successful');
+ dbInitAttempted = true;
+ dbInitializing = false;
+ resolve(db);
+ };
+ transaction.onerror = () => {
+ logger.error('Database test failed');
+ dbInitAttempted = true;
+ dbInitializing = false;
+ resolve(undefined);
+ };
+ } catch (error) {
+ logger.error('Error testing database:', error);
dbInitAttempted = true;
dbInitializing = false;
resolve(undefined);
- };
- } catch (error) {
- logger.error('Error testing database:', error);
+ }
+ };
+
+ request.onerror = (event: Event) => {
+ const error = (event.target as IDBOpenDBRequest).error;
+ logger.error('Failed to open database:', error?.message || 'Unknown error');
dbInitAttempted = true;
dbInitializing = false;
resolve(undefined);
- }
- };
+ };
- request.onerror = (event: Event) => {
- const error = (event.target as IDBOpenDBRequest).error;
- logger.error('Failed to open database:', error?.message || 'Unknown error');
- dbInitAttempted = true;
- dbInitializing = false;
- resolve(undefined);
- };
-
- request.onblocked = () => {
- logger.error('Database blocked');
- dbInitAttempted = true;
- dbInitializing = false;
- resolve(undefined);
+ request.onblocked = () => {
+ logger.error('Database blocked');
+ dbInitAttempted = true;
+ dbInitializing = false;
+ resolve(undefined);
+ };
};
-
} catch (error) {
logger.error('Error initializing database:', error);
dbInitAttempted = true;
diff --git a/app/lib/persistence/useChatHistory.ts b/app/lib/persistence/useChatHistory.ts
index eddf17509..b3ac5e6cf 100644
--- a/app/lib/persistence/useChatHistory.ts
+++ b/app/lib/persistence/useChatHistory.ts
@@ -31,9 +31,23 @@ async function initializeDb() {
dbInitializing = true;
try {
+ // Check if we're in a browser environment
+ if (typeof window === 'undefined') {
+ logger.debug('Not in browser environment');
+ return undefined;
+ }
+
+ // Check if persistence is available
+ if (!window.__BOLT_PERSISTENCE_AVAILABLE__) {
+ logger.debug('Persistence not available');
+ return undefined;
+ }
+
db = await openDatabase();
- dbInitialized = true;
- logger.debug('Database initialized successfully');
+ if (db) {
+ dbInitialized = true;
+ logger.debug('Database initialized successfully');
+ }
} catch (error) {
logger.error('Failed to initialize database:', error);
} finally {
@@ -54,23 +68,39 @@ export function useChatHistory() {
useEffect(() => {
const init = async () => {
try {
+ // Always try to initialize the database
const database = await initializeDb();
+
+ // If we have a mixedId but no database, navigate home silently
+ if (mixedId && !database) {
+ navigate('/', { replace: true });
+ setReady(true);
+ return;
+ }
+ // If we have both mixedId and database, try to load messages
if (mixedId && database) {
- const storedMessages = await getMessages(database, mixedId);
- if (storedMessages && storedMessages.messages.length > 0) {
- setInitialMessages(storedMessages.messages);
- setUrlId(storedMessages.urlId);
- description.set(storedMessages.description);
- chatId.set(storedMessages.id);
- } else {
+ try {
+ const storedMessages = await getMessages(database, mixedId);
+ if (storedMessages && storedMessages.messages.length > 0) {
+ setInitialMessages(storedMessages.messages);
+ setUrlId(storedMessages.urlId);
+ description.set(storedMessages.description);
+ chatId.set(storedMessages.id);
+ } else {
+ navigate('/', { replace: true });
+ }
+ } catch (error) {
+ logger.error('Failed to load messages:', error);
navigate('/', { replace: true });
}
}
+
+ setReady(true);
} catch (error) {
logger.error('Failed to initialize:', error);
+ setReady(true);
}
- setReady(true);
};
init();
@@ -119,3 +149,10 @@ function navigateChat(nextId: string) {
url.pathname = `/chat/${nextId}`;
window.history.replaceState({}, '', url);
}
+
+// Add type declaration
+declare global {
+ interface Window {
+ __BOLT_PERSISTENCE_AVAILABLE__: boolean;
+ }
+}
diff --git a/app/root.tsx b/app/root.tsx
index 31eb387e0..c653d358c 100644
--- a/app/root.tsx
+++ b/app/root.tsx
@@ -50,6 +50,85 @@ const inlineThemeCode = stripIndents`
document.querySelector('html')?.setAttribute('data-theme', theme);
}
+
+ // Initialize IndexedDB early
+ if (typeof window !== 'undefined' && window.indexedDB) {
+ window.__BOLT_PERSISTENCE_AVAILABLE__ = false;
+
+ // Check if we're in Chrome
+ const isChrome = /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor);
+
+ if (isChrome) {
+ // For Chrome, we need to be more careful with initialization
+ const testRequest = window.indexedDB.open('test', 1);
+ testRequest.onerror = () => {
+ window.__BOLT_PERSISTENCE_AVAILABLE__ = false;
+ };
+
+ testRequest.onsuccess = () => {
+ // Close and delete test database
+ const testDb = testRequest.result;
+ testDb.close();
+ const deleteRequest = window.indexedDB.deleteDatabase('test');
+
+ deleteRequest.onsuccess = () => {
+ // Now try to open the actual database
+ const request = window.indexedDB.open('boltHistory', 1);
+
+ request.onupgradeneeded = (event) => {
+ const db = event.target.result;
+ if (!db.objectStoreNames.contains('chats')) {
+ const store = db.createObjectStore('chats', { keyPath: 'id' });
+ store.createIndex('id', 'id', { unique: true });
+ store.createIndex('urlId', 'urlId', { unique: true });
+ }
+ };
+
+ request.onsuccess = (event) => {
+ const db = event.target.result;
+
+ // Test if we can actually use the database
+ try {
+ const transaction = db.transaction(['chats'], 'readonly');
+ transaction.oncomplete = () => {
+ window.__BOLT_PERSISTENCE_AVAILABLE__ = true;
+ };
+ transaction.onerror = () => {
+ window.__BOLT_PERSISTENCE_AVAILABLE__ = false;
+ };
+ } catch (error) {
+ window.__BOLT_PERSISTENCE_AVAILABLE__ = false;
+ }
+ };
+
+ request.onerror = () => {
+ window.__BOLT_PERSISTENCE_AVAILABLE__ = false;
+ };
+ };
+
+ deleteRequest.onerror = () => {
+ window.__BOLT_PERSISTENCE_AVAILABLE__ = false;
+ };
+ };
+ } else {
+ // For other browsers, use the standard approach
+ const request = window.indexedDB.open('boltHistory', 1);
+ request.onupgradeneeded = (event) => {
+ const db = event.target.result;
+ if (!db.objectStoreNames.contains('chats')) {
+ const store = db.createObjectStore('chats', { keyPath: 'id' });
+ store.createIndex('id', 'id', { unique: true });
+ store.createIndex('urlId', 'urlId', { unique: true });
+ }
+ };
+ request.onsuccess = () => {
+ window.__BOLT_PERSISTENCE_AVAILABLE__ = true;
+ };
+ request.onerror = () => {
+ window.__BOLT_PERSISTENCE_AVAILABLE__ = false;
+ };
+ }
+ }
`;
export const Head = createHead(() => (
@@ -81,3 +160,10 @@ export function Layout({ children }: { children: React.ReactNode }) {
export default function App() {
return ;
}
+
+// Add type declaration
+declare global {
+ interface Window {
+ __BOLT_PERSISTENCE_AVAILABLE__: boolean;
+ }
+}