diff --git a/manifest.json b/manifest.json index df24ab9..e86b09c 100644 --- a/manifest.json +++ b/manifest.json @@ -1,11 +1,21 @@ { "manifest_version": 2, "name": "Embermarks", - "version": "1.0.0", + "version": "1.0.1", "description": "Rediscover your forgotten bookmarks. Surface the ones you never visit.", "author": "Bastian Gruber", "homepage_url": "https://github.com/gruberb/embermarks", + "browser_specific_settings": { + "gecko": { + "id": "embermarks@gruberb.dev", + "strict_min_version": "109.0", + "data_collection_permissions": { + "required": false + } + } + }, + "icons": { "48": "icons/ember-48.svg", "96": "icons/ember-96.svg" diff --git a/options/options.js b/options/options.js index c2263a1..42676a0 100644 --- a/options/options.js +++ b/options/options.js @@ -20,7 +20,10 @@ document.addEventListener("DOMContentLoaded", async () => { action: "getBookmarkFolders", }); - folderList.innerHTML = ""; + // Clear loading text + while (folderList.firstChild) { + folderList.removeChild(folderList.firstChild); + } for (const folder of folders) { const item = document.createElement("div"); @@ -28,11 +31,18 @@ document.addEventListener("DOMContentLoaded", async () => { const isExcluded = settings.excludedFolders.includes(folder.id); - item.innerHTML = ` - - - `; + const checkbox = document.createElement("input"); + checkbox.type = "checkbox"; + checkbox.id = `folder-${folder.id}`; + checkbox.value = folder.id; + checkbox.checked = isExcluded; + const label = document.createElement("label"); + label.htmlFor = `folder-${folder.id}`; + label.textContent = `๐Ÿ“ ${folder.title || "(unnamed)"}`; + + item.appendChild(checkbox); + item.appendChild(label); folderList.appendChild(item); } diff --git a/shared/ui.js b/shared/ui.js index 26bdb5b..04968bb 100644 --- a/shared/ui.js +++ b/shared/ui.js @@ -20,13 +20,6 @@ function formatDaysAgo(days) { return `${Math.floor(days / 365)}y ago`; } -// Escape HTML -function escapeHtml(text) { - const div = document.createElement("div"); - div.textContent = text; - return div.innerHTML; -} - // Create a bookmark card element function createBookmarkCard(bookmark) { const card = document.createElement("a"); @@ -35,20 +28,47 @@ function createBookmarkCard(bookmark) { card.target = "_blank"; card.rel = "noopener"; - const faviconUrl = getFaviconUrl(bookmark.url); + // Favicon container + const favicon = document.createElement("div"); + favicon.className = "bookmark-favicon"; - card.innerHTML = ` -
- ${faviconUrl ? `` : "๐Ÿ”–"} -
-
-
${escapeHtml(bookmark.title)}
-
- ${bookmark.visitCount} visit${bookmark.visitCount !== 1 ? "s" : ""} ยท Added ${formatDaysAgo(bookmark.daysSinceAdded)} -
- ${bookmark.path ? `
${escapeHtml(bookmark.path)}
` : ""} -
- `; + const faviconUrl = getFaviconUrl(bookmark.url); + if (faviconUrl) { + const img = document.createElement("img"); + img.src = faviconUrl; + img.alt = ""; + favicon.appendChild(img); + } else { + favicon.textContent = "๐Ÿ”–"; + } + + // Info container + const info = document.createElement("div"); + info.className = "bookmark-info"; + + const title = document.createElement("div"); + title.className = "bookmark-title"; + title.title = bookmark.title; + title.textContent = bookmark.title; + + const meta = document.createElement("div"); + meta.className = "bookmark-meta"; + const visitText = + bookmark.visitCount === 1 ? "1 visit" : `${bookmark.visitCount} visits`; + meta.textContent = `${visitText} ยท Added ${formatDaysAgo(bookmark.daysSinceAdded)}`; + + info.appendChild(title); + info.appendChild(meta); + + if (bookmark.path) { + const folder = document.createElement("div"); + folder.className = "bookmark-folder"; + folder.textContent = bookmark.path; + info.appendChild(folder); + } + + card.appendChild(favicon); + card.appendChild(info); return card; } @@ -70,18 +90,66 @@ function showEmptyState(container, reason = "none") { const msg = messages[reason] || messages.none; - container.innerHTML = ` -
-
${msg.icon}
-

${msg.title}

-

${msg.text}

-
- `; + // Clear container + while (container.firstChild) { + container.removeChild(container.firstChild); + } + + const emptyState = document.createElement("div"); + emptyState.className = "empty-state"; + + const icon = document.createElement("div"); + icon.className = "icon"; + icon.textContent = msg.icon; + + const heading = document.createElement("h2"); + heading.textContent = msg.title; + + const paragraph = document.createElement("p"); + paragraph.textContent = msg.text; + + emptyState.appendChild(icon); + emptyState.appendChild(heading); + emptyState.appendChild(paragraph); + + container.appendChild(emptyState); } // Show loading state function showLoading(container) { - container.innerHTML = '
'; + while (container.firstChild) { + container.removeChild(container.firstChild); + } + + const loading = document.createElement("div"); + loading.className = "loading"; + container.appendChild(loading); +} + +// Show error state +function showError(container) { + while (container.firstChild) { + container.removeChild(container.firstChild); + } + + const emptyState = document.createElement("div"); + emptyState.className = "empty-state"; + + const icon = document.createElement("div"); + icon.className = "icon"; + icon.textContent = "!"; + + const heading = document.createElement("h2"); + heading.textContent = "Oops"; + + const paragraph = document.createElement("p"); + paragraph.textContent = "Failed to load bookmarks"; + + emptyState.appendChild(icon); + emptyState.appendChild(heading); + emptyState.appendChild(paragraph); + + container.appendChild(emptyState); } // Load and display bookmarks @@ -93,7 +161,9 @@ async function loadBookmarks(container) { action: "getForgottenBookmarks", }); - container.innerHTML = ""; + while (container.firstChild) { + container.removeChild(container.firstChild); + } if (!bookmarks || bookmarks.length === 0) { showEmptyState(container, "none"); @@ -105,12 +175,6 @@ async function loadBookmarks(container) { } } catch (error) { console.error("Failed to load bookmarks:", error); - container.innerHTML = ` -
-
!
-

Oops

-

Failed to load bookmarks

-
- `; + showError(container); } }