diff --git a/app/[lang]/page.tsx b/app/[lang]/page.tsx
index 9d6acc8..b44c9fe 100644
--- a/app/[lang]/page.tsx
+++ b/app/[lang]/page.tsx
@@ -12,7 +12,6 @@ import {
import { useDiagram } from "@/contexts/diagram-context"
import { type DrawioTheme, isDrawioTheme } from "@/lib/drawio-themes"
import { i18n, type Locale } from "@/lib/i18n/config"
-import { isIndexedDBUsable } from "@/lib/session-storage"
export default function Home() {
const {
@@ -33,8 +32,6 @@ export default function Home() {
const [isLoaded, setIsLoaded] = useState(false)
const [isDrawioReady, setIsDrawioReady] = useState(false)
const [isElectron, setIsElectron] = useState(false)
- const [canPersist, setCanPersist] = useState(false)
- const [canPersistChecked, setCanPersistChecked] = useState(false)
const [drawioBaseUrl, setDrawioBaseUrl] = useState(
process.env.NEXT_PUBLIC_DRAWIO_BASE_URL || "https://embed.diagrams.net",
)
@@ -85,11 +82,6 @@ export default function Home() {
setDrawioBaseUrl(`${window.location.origin}/drawio/index.html`)
}
- void (async () => {
- const usable = await isIndexedDBUsable()
- setCanPersist(usable)
- setCanPersistChecked(true)
- })()
setIsLoaded(true)
}, [pathname, router])
@@ -98,13 +90,6 @@ export default function Home() {
onDrawioLoad()
}, [onDrawioLoad])
- const handleDrawioAutoSave = useCallback(
- (data: { xml?: string }) => {
- handleDiagramAutoSave(data)
- },
- [handleDiagramAutoSave],
- )
-
const handleDarkModeChange = () => {
const newValue = !darkMode
setDarkMode(newValue)
@@ -187,7 +172,7 @@ export default function Home() {
}`}
>
- {isLoaded && canPersistChecked && (
+ {isLoaded && (
@@ -195,24 +180,14 @@ export default function Home() {
key={`${drawioUi}-${darkMode}-${currentLang}-${isElectron}`}
ref={drawioRef}
autosave
- onAutoSave={handleDrawioAutoSave}
+ onAutoSave={handleDiagramAutoSave}
onExport={handleDiagramExport}
onLoad={handleDrawioLoad}
baseUrl={drawioBaseUrl}
- configuration={
- canPersist
- ? { confirmExit: false }
- : undefined
- }
urlParameters={{
ui: drawioUi,
spin: false,
libraries: false,
- // Disable modified tracking only when persistence is available
- ...(canPersist && {
- modified: false,
- keepmodified: false,
- }),
saveAndExit: false,
noSaveBtn: true,
noExitBtn: true,
diff --git a/lib/session-storage.ts b/lib/session-storage.ts
index 4a51d66..8450375 100644
--- a/lib/session-storage.ts
+++ b/lib/session-storage.ts
@@ -58,33 +58,6 @@ interface ChatSessionDB extends DBSchema {
// Database singleton
let dbPromise: Promise> | null = null
-const resetDBPromise = () => {
- dbPromise = null
-}
-
-const isClosingError = (error: unknown): boolean => {
- return (
- error instanceof DOMException &&
- error.name === "InvalidStateError" &&
- /closing/i.test(error.message)
- )
-}
-
-const withDB = async (
- action: (db: IDBPDatabase) => Promise,
-): Promise => {
- try {
- const db = await getDB()
- return await action(db)
- } catch (error) {
- if (isClosingError(error)) {
- resetDBPromise()
- const db = await getDB()
- return await action(db)
- }
- throw error
- }
-}
async function getDB(): Promise> {
if (!dbPromise) {
@@ -115,23 +88,7 @@ async function getDB(): Promise> {
}
}
},
- terminated() {
- resetDBPromise()
- },
})
- dbPromise
- .then((db) => {
- db.onversionchange = () => {
- db.close()
- resetDBPromise()
- }
- db.onclose = () => {
- resetDBPromise()
- }
- })
- .catch(() => {
- resetDBPromise()
- })
}
return dbPromise
}
@@ -146,46 +103,31 @@ export function isIndexedDBAvailable(): boolean {
}
}
-// Check if IndexedDB is actually usable (not just present).
-// Note: Do NOT close the db here - getDB() returns a shared singleton connection
-// that other code depends on.
-export async function isIndexedDBUsable(): Promise {
- if (!isIndexedDBAvailable()) return false
- try {
- await getDB()
- return true
- } catch {
- return false
- }
-}
-
// CRUD Operations
export async function getAllSessionMetadata(): Promise {
if (!isIndexedDBAvailable()) return []
try {
- return await withDB(async (db) => {
- const tx = db.transaction(STORE_NAME, "readonly")
- const index = tx.store.index("by-updated")
- const metadata: SessionMetadata[] = []
+ const db = await getDB()
+ const tx = db.transaction(STORE_NAME, "readonly")
+ const index = tx.store.index("by-updated")
+ const metadata: SessionMetadata[] = []
- // Use cursor to read only metadata fields (avoids loading full messages/XML)
- let cursor = await index.openCursor(null, "prev") // newest first
- while (cursor) {
- const s = cursor.value
- metadata.push({
- id: s.id,
- title: s.title,
- createdAt: s.createdAt,
- updatedAt: s.updatedAt,
- messageCount: s.messages.length,
- hasDiagram:
- !!s.diagramXml && s.diagramXml.trim().length > 0,
- thumbnailDataUrl: s.thumbnailDataUrl,
- })
- cursor = await cursor.continue()
- }
- return metadata
- })
+ // Use cursor to read only metadata fields (avoids loading full messages/XML)
+ let cursor = await index.openCursor(null, "prev") // newest first
+ while (cursor) {
+ const s = cursor.value
+ metadata.push({
+ id: s.id,
+ title: s.title,
+ createdAt: s.createdAt,
+ updatedAt: s.updatedAt,
+ messageCount: s.messages.length,
+ hasDiagram: !!s.diagramXml && s.diagramXml.trim().length > 0,
+ thumbnailDataUrl: s.thumbnailDataUrl,
+ })
+ cursor = await cursor.continue()
+ }
+ return metadata
} catch (error) {
console.error("Failed to get session metadata:", error)
return []
@@ -195,9 +137,8 @@ export async function getAllSessionMetadata(): Promise {
export async function getSession(id: string): Promise {
if (!isIndexedDBAvailable()) return null
try {
- return await withDB(async (db) => {
- return (await db.get(STORE_NAME, id)) || null
- })
+ const db = await getDB()
+ return (await db.get(STORE_NAME, id)) || null
} catch (error) {
console.error("Failed to get session:", error)
return null
@@ -207,9 +148,8 @@ export async function getSession(id: string): Promise {
export async function saveSession(session: ChatSession): Promise {
if (!isIndexedDBAvailable()) return false
try {
- await withDB(async (db) => {
- await db.put(STORE_NAME, session)
- })
+ const db = await getDB()
+ await db.put(STORE_NAME, session)
return true
} catch (error) {
// Handle quota exceeded
@@ -221,9 +161,8 @@ export async function saveSession(session: ChatSession): Promise {
await deleteOldestSession()
// Retry once
try {
- await withDB(async (db) => {
- await db.put(STORE_NAME, session)
- })
+ const db = await getDB()
+ await db.put(STORE_NAME, session)
return true
} catch (retryError) {
console.error(
@@ -242,9 +181,8 @@ export async function saveSession(session: ChatSession): Promise {
export async function deleteSession(id: string): Promise {
if (!isIndexedDBAvailable()) return
try {
- await withDB(async (db) => {
- await db.delete(STORE_NAME, id)
- })
+ const db = await getDB()
+ await db.delete(STORE_NAME, id)
} catch (error) {
console.error("Failed to delete session:", error)
}
@@ -253,9 +191,8 @@ export async function deleteSession(id: string): Promise {
export async function getSessionCount(): Promise {
if (!isIndexedDBAvailable()) return 0
try {
- return await withDB(async (db) => {
- return await db.count(STORE_NAME)
- })
+ const db = await getDB()
+ return await db.count(STORE_NAME)
} catch (error) {
console.error("Failed to get session count:", error)
return 0
@@ -265,15 +202,14 @@ export async function getSessionCount(): Promise {
export async function deleteOldestSession(): Promise {
if (!isIndexedDBAvailable()) return
try {
- await withDB(async (db) => {
- const tx = db.transaction(STORE_NAME, "readwrite")
- const index = tx.store.index("by-updated")
- const cursor = await index.openCursor()
- if (cursor) {
- await cursor.delete()
- }
- await tx.done
- })
+ const db = await getDB()
+ const tx = db.transaction(STORE_NAME, "readwrite")
+ const index = tx.store.index("by-updated")
+ const cursor = await index.openCursor()
+ if (cursor) {
+ await cursor.delete()
+ }
+ await tx.done
} catch (error) {
console.error("Failed to delete oldest session:", error)
}
diff --git a/lib/template-storage.ts b/lib/template-storage.ts
index 65c533a..8b0e451 100644
--- a/lib/template-storage.ts
+++ b/lib/template-storage.ts
@@ -57,33 +57,6 @@ export function generateDefaultTitle(prompt: string): string {
// Database singleton
let dbPromise: Promise> | null = null
-const resetDBPromise = () => {
- dbPromise = null
-}
-
-const isClosingError = (error: unknown): boolean => {
- return (
- error instanceof DOMException &&
- error.name === "InvalidStateError" &&
- /closing/i.test(error.message)
- )
-}
-
-const withDB = async (
- action: (db: IDBPDatabase) => Promise,
-): Promise => {
- try {
- const db = await getDB()
- return await action(db)
- } catch (error) {
- if (isClosingError(error)) {
- resetDBPromise()
- const db = await getDB()
- return await action(db)
- }
- throw error
- }
-}
async function getDB(): Promise> {
if (!dbPromise) {
@@ -101,23 +74,7 @@ async function getDB(): Promise> {
}
}
},
- terminated() {
- resetDBPromise()
- },
})
- dbPromise
- .then((db) => {
- db.onversionchange = () => {
- db.close()
- resetDBPromise()
- }
- db.onclose = () => {
- resetDBPromise()
- }
- })
- .catch(() => {
- resetDBPromise()
- })
}
return dbPromise
}
@@ -137,10 +94,9 @@ export function isIndexedDBAvailable(): boolean {
export async function getAllTemplates(): Promise {
if (!isIndexedDBAvailable()) return []
try {
- return await withDB(async (db) => {
- const templates = await db.getAll(STORE_NAME)
- return sortTemplates(templates)
- })
+ const db = await getDB()
+ const templates = await db.getAll(STORE_NAME)
+ return sortTemplates(templates)
} catch (error) {
console.error("Failed to get templates:", error)
return []
@@ -150,9 +106,8 @@ export async function getAllTemplates(): Promise {
export async function getTemplate(id: string): Promise {
if (!isIndexedDBAvailable()) return null
try {
- return await withDB(async (db) => {
- return (await db.get(STORE_NAME, id)) || null
- })
+ const db = await getDB()
+ return (await db.get(STORE_NAME, id)) || null
} catch (error) {
console.error("Failed to get template:", error)
return null
@@ -182,9 +137,8 @@ export async function createTemplate(
}
try {
- await withDB(async (db) => {
- await db.put(STORE_NAME, template)
- })
+ const db = await getDB()
+ await db.put(STORE_NAME, template)
return template
} catch (error) {
console.error("Failed to create template:", error)
@@ -198,20 +152,19 @@ export async function updateTemplate(
): Promise {
if (!isIndexedDBAvailable()) return null
try {
- return await withDB(async (db) => {
- const existing = await db.get(STORE_NAME, id)
- if (!existing) return null
+ const db = await getDB()
+ const existing = await db.get(STORE_NAME, id)
+ if (!existing) return null
- const updated: Template = {
- ...existing,
- ...updates,
- id: existing.id,
- createdAt: existing.createdAt,
- updatedAt: Date.now(),
- }
- await db.put(STORE_NAME, updated)
- return updated
- })
+ const updated: Template = {
+ ...existing,
+ ...updates,
+ id: existing.id,
+ createdAt: existing.createdAt,
+ updatedAt: Date.now(),
+ }
+ await db.put(STORE_NAME, updated)
+ return updated
} catch (error) {
console.error("Failed to update template:", error)
return null
@@ -221,9 +174,8 @@ export async function updateTemplate(
export async function deleteTemplate(id: string): Promise {
if (!isIndexedDBAvailable()) return false
try {
- await withDB(async (db) => {
- await db.delete(STORE_NAME, id)
- })
+ const db = await getDB()
+ await db.delete(STORE_NAME, id)
return true
} catch (error) {
console.error("Failed to delete template:", error)
@@ -237,25 +189,24 @@ export async function duplicateTemplate(
): Promise {
if (!isIndexedDBAvailable()) return null
try {
- return await withDB(async (db) => {
- const existing = await db.get(STORE_NAME, id)
- if (!existing) return null
+ const db = await getDB()
+ const existing = await db.get(STORE_NAME, id)
+ if (!existing) return null
- const now = Date.now()
- const duplicate: Template = {
- ...existing,
- id: nanoid(),
- title: `${existing.title} ${copySuffix}`,
- createdAt: now,
- updatedAt: now,
- clickCount: 0,
- runCount: 0,
- lastUsedAt: 0,
- pinned: false,
- }
- await db.put(STORE_NAME, duplicate)
- return duplicate
- })
+ const now = Date.now()
+ const duplicate: Template = {
+ ...existing,
+ id: nanoid(),
+ title: `${existing.title} ${copySuffix}`,
+ createdAt: now,
+ updatedAt: now,
+ clickCount: 0,
+ runCount: 0,
+ lastUsedAt: 0,
+ pinned: false,
+ }
+ await db.put(STORE_NAME, duplicate)
+ return duplicate
} catch (error) {
console.error("Failed to duplicate template:", error)
return null
@@ -267,13 +218,12 @@ export async function duplicateTemplate(
export async function incrementClickCount(id: string): Promise {
if (!isIndexedDBAvailable()) return
try {
- await withDB(async (db) => {
- const template = await db.get(STORE_NAME, id)
- if (!template) return
- template.clickCount += 1
- template.updatedAt = Date.now()
- await db.put(STORE_NAME, template)
- })
+ const db = await getDB()
+ const template = await db.get(STORE_NAME, id)
+ if (!template) return
+ template.clickCount += 1
+ template.updatedAt = Date.now()
+ await db.put(STORE_NAME, template)
} catch (error) {
console.error("Failed to increment click count:", error)
}
@@ -282,15 +232,14 @@ export async function incrementClickCount(id: string): Promise {
export async function incrementRunCount(id: string): Promise {
if (!isIndexedDBAvailable()) return
try {
- await withDB(async (db) => {
- const template = await db.get(STORE_NAME, id)
- if (!template) return
- const now = Date.now()
- template.runCount += 1
- template.lastUsedAt = now
- template.updatedAt = now
- await db.put(STORE_NAME, template)
- })
+ const db = await getDB()
+ const template = await db.get(STORE_NAME, id)
+ if (!template) return
+ const now = Date.now()
+ template.runCount += 1
+ template.lastUsedAt = now
+ template.updatedAt = now
+ await db.put(STORE_NAME, template)
} catch (error) {
console.error("Failed to increment run count:", error)
}
@@ -423,9 +372,8 @@ export async function importTemplates(
pinned: typeof t.pinned === "boolean" ? t.pinned : false,
}
try {
- await withDB(async (db) => {
- await db.put(STORE_NAME, newTemplate)
- })
+ const db = await getDB()
+ await db.put(STORE_NAME, newTemplate)
existingKeys.add(key)
imported++
} catch (error) {