diff --git a/package.json b/package.json index 93f5b054..f21bd451 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ "color": "^4.2.3", "dompurify": "^3.0.8", "framer-motion": "^11.0.25", + "idb": "^8.0.0", "kolorist": "^1.8.0", "localforage": "^1.10.0", "lodash": "^4.17.21", diff --git a/src/manifests/manifest.json b/src/manifests/manifest.json index b0cdec0e..0087ca16 100644 --- a/src/manifests/manifest.json +++ b/src/manifests/manifest.json @@ -30,10 +30,6 @@ } ], "web_accessible_resources": [ - { - "resources": ["seqta/ui/background/background.html"], - "matches": ["*://*/*"] - }, { "resources": ["*://*/*"], "matches": ["*://*/*"] diff --git a/src/seqta/ui/ImageBackgrounds.ts b/src/seqta/ui/ImageBackgrounds.ts index 71a6a0f1..25fec6ad 100644 --- a/src/seqta/ui/ImageBackgrounds.ts +++ b/src/seqta/ui/ImageBackgrounds.ts @@ -1,13 +1,104 @@ -import browser from 'webextension-polyfill'; +import { getDataById, isIndexedDBSupported } from '@/svelte-interface/hooks/BackgroundDataLoader'; export async function appendBackgroundToUI() { const parent = document.getElementById('container'); - // embed background.html - const background = document.createElement('iframe'); + // embed background.html - old method + /* const background = document.createElement('iframe'); background.id = 'background'; background.classList.add('imageBackground'); background.setAttribute('excludeDarkCheck', 'true'); background.src = browser.runtime.getURL('seqta/ui/background/background.html'); - parent!.appendChild(background); + parent!.appendChild(background); */ + if (!parent) return; + + const backgroundContainer = document.createElement('div'); + backgroundContainer.classList.add('imageBackground'); + backgroundContainer.setAttribute('excludeDarkCheck', 'true'); + + const mediaContainer = document.createElement('div'); + mediaContainer.id = 'media-container'; + backgroundContainer.appendChild(mediaContainer); + + parent.appendChild(backgroundContainer); + + // Add styles + const style = document.createElement('style'); + style.textContent = ` + #media-container { + width: 100%; + height: 100%; + } + + #media-container video, #media-container img { + width: 100%; + height: 100%; + object-fit: cover; + } + `; + document.head.appendChild(style); + + // Load and display the background + await loadBackground(); } + +export async function loadBackground() { + if (!isIndexedDBSupported()) { + console.error("IndexedDB is not supported. Unable to load background."); + return; + } + + try { + const selectedBackgroundId = localStorage.getItem('selectedBackground'); + if (!selectedBackgroundId) { + const backgroundContainer = document.querySelector('.imageBackground'); + if (backgroundContainer) { + backgroundContainer.remove(); + } + return; + }; + + const background = await getDataById(selectedBackgroundId); + if (!background) return; + + let backgroundContainer = document.querySelector('.imageBackground'); + if (!backgroundContainer) { + backgroundContainer = document.createElement('div'); + backgroundContainer.classList.add('imageBackground'); + backgroundContainer.setAttribute('excludeDarkCheck', 'true'); + const parent = document.getElementById('container'); + if (parent) { + parent.appendChild(backgroundContainer); + } + } + + let mediaContainer = document.getElementById('media-container'); + if (!mediaContainer) { + mediaContainer = document.createElement('div'); + mediaContainer.id = 'media-container'; + backgroundContainer.appendChild(mediaContainer); + }; + + mediaContainer = document.getElementById('media-container'); + if (!mediaContainer) return; + + mediaContainer.innerHTML = ''; + + const mediaElement = background.type === 'video' + ? document.createElement('video') + : document.createElement('img'); + + mediaElement.src = URL.createObjectURL(background.blob); + mediaElement.classList.add('background'); + + if (mediaElement instanceof HTMLVideoElement) { + mediaElement.loop = true; + mediaElement.muted = true; + mediaElement.autoplay = true; + } + + mediaContainer.appendChild(mediaElement); + } catch (error) { + console.error('Error loading background:', error); + } +} \ No newline at end of file diff --git a/src/seqta/ui/background/background.html b/src/seqta/ui/background/background.html deleted file mode 100644 index c78dcf96..00000000 --- a/src/seqta/ui/background/background.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - - Background Fetcher - - - - - -
- - - - diff --git a/src/seqta/ui/background/background.ts b/src/seqta/ui/background/background.ts deleted file mode 100644 index c76c0f75..00000000 --- a/src/seqta/ui/background/background.ts +++ /dev/null @@ -1,115 +0,0 @@ -interface Data { - blob: Blob; - type: 'image' | 'video'; -} - -interface DatabaseEventTarget extends EventTarget { - result: IDBDatabase; -} - -interface DatabaseEvent extends Event { - target: DatabaseEventTarget; -} - -const openDB = (): Promise => { - return new Promise((resolve, reject) => { - const request = indexedDB.open('MyDatabase', 1); - - request.onerror = () => reject(request.error); - request.onsuccess = () => resolve(request.result); - - request.onupgradeneeded = (event: IDBVersionChangeEvent) => { - // @ts-expect-error - The event type is not recognized by TypeScript - event?.target?.result.createObjectStore('backgrounds', { keyPath: 'id' }); - }; - }); -}; - -const readData = async (): Promise => { - const selectedBackground = localStorage.getItem('selectedBackground'); - - //const selectedBackground = localStorage.getItem('selectedBackground'); - if (!selectedBackground || selectedBackground === '') { - return null; - } - - const db = await openDB(); - const tx = db.transaction('backgrounds', 'readonly'); - const store = tx.objectStore('backgrounds'); - const request = store.get(selectedBackground); - - return new Promise((resolve, reject) => { - request.onsuccess = () => resolve(request.result as Data); - request.onerror = () => reject(request.error); - }); -}; - -const updateBackground = async (): Promise => { - try { - const data = await readData(); - if (!data) { - const container = document.getElementById('media-container'); - const currentMedia = container?.querySelector('.current-media'); - if (currentMedia) { - currentMedia.remove(); - } - return; - } - - const url = URL.createObjectURL(data.blob); - const container = document.getElementById('media-container'); - - // Create new element and set properties - let newElement; - if (data.type === 'image') { - newElement = document.createElement('img'); - newElement.src = url; - newElement.alt = 'Uploaded content'; - } else if (data.type === 'video') { - newElement = document.createElement('video'); - newElement.src = url; - newElement.autoplay = true; - newElement.loop = true; - newElement.muted = true; - } - - // Mark the old element for removal - const oldElement = container?.querySelector('.current-media'); - if (oldElement) { - oldElement.classList.remove('current-media'); - oldElement.classList.add('old-media'); - } - - // Add the new element and mark it as current - newElement?.classList.add('current-media'); - container?.appendChild(newElement as Node); - - // Delay removal of old element - setTimeout(() => { - const oldMedia = container?.querySelector('.old-media'); - if (oldMedia) { - oldMedia.remove(); - } - }, 100); // 0.1 second delay - } catch (error) { - console.error('An error occurred:', error); - } -}; - -// Main function to run on page load -const main = async (): Promise => { - await updateBackground(); - - // Listen for changes to local storage - try { - window.addEventListener('storage', async (event) => { - if (event.key === 'selectedBackground') { - await updateBackground(); - } - }); - } catch (error) { - console.error('An error occurred:', error); - } -}; - -main() \ No newline at end of file diff --git a/src/svelte-interface/components/themes/BackgroundItem.svelte b/src/svelte-interface/components/themes/BackgroundItem.svelte new file mode 100644 index 00000000..a6594354 --- /dev/null +++ b/src/svelte-interface/components/themes/BackgroundItem.svelte @@ -0,0 +1,36 @@ + + +
+ {#if isEditMode} +
+
+
+ {/if} + {#if bg.type === 'image'} + swatch + {:else if bg.type === 'video'} + + {/if} +
\ No newline at end of file diff --git a/src/svelte-interface/components/themes/BackgroundSelector.svelte b/src/svelte-interface/components/themes/BackgroundSelector.svelte new file mode 100644 index 00000000..8f24b4b1 --- /dev/null +++ b/src/svelte-interface/components/themes/BackgroundSelector.svelte @@ -0,0 +1,152 @@ + + + +
+

Background Images

+
+ {#if !isEditMode} + handleFileChange(e.detail)} /> + {/if} + {#each imageBackgrounds as bg (bg.id)} + selectBackground(bg.id)} + onDelete={() => deleteBackground(bg.id)} + /> + {/each} +
+ +

Background Videos

+
+ {#if !isEditMode} + handleFileChange(e.detail)} /> + {/if} + {#each videoBackgrounds as bg (bg.id)} + selectBackground(bg.id)} + onDelete={() => deleteBackground(bg.id)} + /> + {/each} +
+
\ No newline at end of file diff --git a/src/svelte-interface/components/themes/BackgroundUploader.svelte b/src/svelte-interface/components/themes/BackgroundUploader.svelte new file mode 100644 index 00000000..37451793 --- /dev/null +++ b/src/svelte-interface/components/themes/BackgroundUploader.svelte @@ -0,0 +1,26 @@ + + +
+
+ +  +
+ +
\ No newline at end of file diff --git a/src/svelte-interface/components/themes/ProgressCircle.svelte b/src/svelte-interface/components/themes/ProgressCircle.svelte new file mode 100644 index 00000000..cd4fdb3b --- /dev/null +++ b/src/svelte-interface/components/themes/ProgressCircle.svelte @@ -0,0 +1,13 @@ + + +
+ + + + +
\ No newline at end of file diff --git a/src/svelte-interface/hooks/BackgroundDataLoader.ts b/src/svelte-interface/hooks/BackgroundDataLoader.ts new file mode 100644 index 00000000..8f5f67a7 --- /dev/null +++ b/src/svelte-interface/hooks/BackgroundDataLoader.ts @@ -0,0 +1,75 @@ +import { openDB, type IDBPDatabase, type DBSchema } from 'idb'; + +interface BackgroundDB extends DBSchema { + backgrounds: { + key: string; + value: { + id: string; + type: string; + blob: Blob; + }; + }; +} + +let db: IDBPDatabase | null = null; + +export async function openDatabase(): Promise> { + if (db) return db; + + db = await openDB('BackgroundDB', 1, { + upgrade(db: IDBPDatabase) { + db.createObjectStore('backgrounds', { keyPath: 'id' }); + }, + }); + + return db; +} + +export async function readAllData(): Promise> { + const db = await openDatabase(); + return db.getAll('backgrounds'); +} + +export async function writeData(id: string, type: string, blob: Blob): Promise { + const db = await openDatabase(); + await db.put('backgrounds', { id, type, blob }); +} + +export async function deleteData(id: string): Promise { + const db = await openDatabase(); + await db.delete('backgrounds', id); +} + +export async function clearAllData(): Promise { + const db = await openDatabase(); + await db.clear('backgrounds'); +} + +export async function getDataById(id: string): Promise<{ id: string; type: string; blob: Blob } | undefined> { + const db = await openDatabase(); + return db.get('backgrounds', id); +} + +export function closeDatabase(): void { + if (db) { + db.close(); + db = null; + } +} + +// Helper function to check if IndexedDB is supported +export function isIndexedDBSupported(): boolean { + return 'indexedDB' in window; +} + +// Helper function to check if there's enough storage space +export async function hasEnoughStorageSpace(requiredSpace: number): Promise { + if ('storage' in navigator && 'estimate' in navigator.storage) { + const { quota, usage } = await navigator.storage.estimate(); + if (quota !== undefined && usage !== undefined) { + return (quota - usage) > requiredSpace; + } + } + // If we can't determine, assume there's enough space + return true; +} \ No newline at end of file diff --git a/src/svelte-interface/hooks/backgroundState.svelte.ts b/src/svelte-interface/hooks/backgroundState.svelte.ts new file mode 100644 index 00000000..3bab4725 --- /dev/null +++ b/src/svelte-interface/hooks/backgroundState.svelte.ts @@ -0,0 +1 @@ +export let selectedBackground = $state(null); \ No newline at end of file diff --git a/src/svelte-interface/pages/settings/theme.svelte b/src/svelte-interface/pages/settings/theme.svelte index 23ec4f05..37cf35cf 100644 --- a/src/svelte-interface/pages/settings/theme.svelte +++ b/src/svelte-interface/pages/settings/theme.svelte @@ -1,11 +1,19 @@ -
+
- +
\ No newline at end of file diff --git a/src/svelte-interface/test.svelte b/src/svelte-interface/test.svelte deleted file mode 100644 index 20d5be8c..00000000 --- a/src/svelte-interface/test.svelte +++ /dev/null @@ -1 +0,0 @@ -

HI THERE!!!!

\ No newline at end of file diff --git a/src/svelte-interface/utils/standalone.svelte.ts b/src/svelte-interface/utils/standalone.svelte.ts index 3a8c9461..163bb8d7 100644 --- a/src/svelte-interface/utils/standalone.svelte.ts +++ b/src/svelte-interface/utils/standalone.svelte.ts @@ -11,4 +11,6 @@ export function createStandalone() { }, setStandalone }; -} \ No newline at end of file +} + +export const standalone = createStandalone(); \ No newline at end of file diff --git a/tailwind.config.js b/tailwind.config.js index bcd2bb97..fe161f23 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -7,11 +7,11 @@ export default { content: [ "./src/**/*.{js,ts,jsx,tsx,html,svelte}", ], - safelist: [ - { - pattern: / */, - } - ], + //safelist: [ + //{ + // pattern: / */, + //} + //], darkMode: "class", theme: { fontSize: { diff --git a/vite.config.ts b/vite.config.ts index 22d8c776..df803936 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -58,8 +58,7 @@ export default defineConfig({ minify: false, rollupOptions: { input: { - settings: join(__dirname, 'src', 'svelte-interface', 'index.html'), - backgrounds: join(__dirname, 'src', 'seqta', 'ui', 'background', 'background.html') + settings: join(__dirname, 'src', 'svelte-interface', 'index.html') } } }