diff options
| author | sameerasw <[email protected]> | 2025-04-10 15:09:26 +0530 | 
|---|---|---|
| committer | sameerasw <[email protected]> | 2025-04-10 15:09:26 +0530 | 
| commit | 6e0a2f92e25b927c559a9b532fe1b087df921e74 (patch) | |
| tree | f6f74e13215b56cb9b116cff819a5d19eb54bba1 | |
| parent | 7f45e1729c777de8f37e2f98bd58957a13d7027b (diff) | |
New data viewer page and ability to reset forced sites and all data
| -rw-r--r-- | data-viewer/data-viewer.css | 402 | ||||
| -rw-r--r-- | data-viewer/data-viewer.html | 69 | ||||
| -rw-r--r-- | data-viewer/data-viewer.js | 405 | ||||
| -rw-r--r-- | manifest.json | 5 | ||||
| -rw-r--r-- | popup/popup.html | 6 | ||||
| -rw-r--r-- | popup/popup.js | 7 | 
6 files changed, 894 insertions, 0 deletions
diff --git a/data-viewer/data-viewer.css b/data-viewer/data-viewer.css new file mode 100644 index 0000000..7e4d8b3 --- /dev/null +++ b/data-viewer/data-viewer.css @@ -0,0 +1,402 @@ +/* Data Viewer specific styles */ + +/* General layout adjustments */ +.app-content { +  max-width: 960px; +  margin: 0 auto; +  padding: 20px; +} + +body { +  width: auto; +  min-width: 600px; +} + +/* Data section styling */ +.data-section { +  margin-bottom: 24px; +  background-color: var(--secondary-bg); +  border-radius: var(--radius-md); +  padding: 16px; +} + +.data-container { +  border-radius: var(--radius-sm); +  overflow: hidden; +} + +/* Table styling */ +.data-table { +  width: 100%; +  border-collapse: collapse; +  font-size: 14px; +  background-color: var(--bg-color); +} + +.data-table th, +.data-table td { +  padding: 10px 12px; +  text-align: left; +  border-bottom: 1px solid var(--border-color); +} + +.data-table th { +  background-color: var(--bg-color); +  font-weight: 600; +  color: var(--accent-color); +  position: sticky; +  top: 0; +} + +.data-table tr:hover { +  background-color: rgba(255, 255, 255, 0.05); +} + +/* Status badges */ +.badge { +  display: inline-block; +  padding: 2px 8px; +  border-radius: 12px; +  font-size: 12px; +  font-weight: 500; +} + +.badge.enabled { +  background-color: var(--success-color); +  color: white; +} + +.badge.disabled { +  background-color: var(--border-color); +  color: var(--text-secondary); +} + +/* No data message */ +.no-data { +  padding: 16px; +  text-align: center; +  color: var(--text-secondary); +  font-style: italic; +  background-color: var(--bg-color); +  border-radius: var(--radius-sm); +} + +/* Website list styling */ +.websites-container { +  margin-top: 16px; +} + +.website-panel { +  margin-bottom: 8px; +  background-color: var(--bg-color); +  border-radius: var(--radius-md); +  overflow: hidden; +} + +.website-header { +  cursor: pointer; +  padding: 12px 16px; +  font-weight: 500; +  position: relative; +  display: flex; +  align-items: center; +  justify-content: space-between; +} + +.website-header::after { +  content: "\f078"; /* chevron-down icon */ +  font-family: "Font Awesome 5 Free"; +  font-weight: 900; +  transition: transform 0.3s ease; +} + +.website-header.active::after { +  transform: rotate(180deg); +} + +.website-header-content { +  display: flex; +  align-items: center; +  flex: 1; +} + +.website-name { +  font-weight: 600; +  color: var(--text-primary); +} + +.feature-count { +  font-size: 12px; +  color: var(--text-secondary); +  margin-left: 12px; +} + +.settings-badge { +  background-color: var(--accent-color); +  color: white; +  font-size: 11px; +  padding: 2px 6px; +  border-radius: 4px; +  margin-left: 8px; +} + +.website-content { +  max-height: 0; +  overflow: hidden; +  transition: max-height 0.3s ease; +  background-color: var(--secondary-bg); +} + +/* Search functionality */ +.search-container { +  position: relative; +  margin-bottom: 16px; +} + +.search-input { +  width: 100%; +  padding: 10px 16px 10px 36px; +  border: 1px solid var(--border-color); +  border-radius: var(--radius-md); +  background-color: var(--bg-color); +  color: var(--text-primary); +  font-size: 14px; +} + +.search-input:focus { +  outline: none; +  border-color: var(--accent-color); +} + +.search-icon { +  position: absolute; +  left: 12px; +  top: 12px; +  color: var(--text-secondary); +} + +/* Tab system */ +.tabs-container { +  display: flex; +  background-color: var(--bg-color); +  border-bottom: 1px solid var(--border-color); +} + +.tab { +  padding: 10px 16px; +  cursor: pointer; +  font-weight: 500; +  border-bottom: 2px solid transparent; +  transition: all 0.2s ease; +} + +.tab.active { +  border-bottom-color: var(--accent-color); +  color: var(--accent-color); +} + +.tab-contents { +  padding: 16px; +} + +.tab-content { +  display: none; +} + +.tab-content.active { +  display: block; +} + +/* Features list */ +.features-list { +  display: grid; +  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); +  gap: 8px; +} + +.feature-item { +  padding: 8px 12px; +  background-color: var(--bg-color); +  border-radius: var(--radius-sm); +  font-size: 13px; +} + +/* CSS blocks */ +.css-block { +  border-bottom: 1px solid rgba(255, 255, 255, 0.1); +} + +.css-block:last-child { +  border-bottom: none; +} + +.css-block-header { +  padding: 12px 16px; +  cursor: pointer; +  display: flex; +  justify-content: space-between; +  align-items: center; +  background-color: rgba(0, 0, 0, 0.2); +  position: relative; +} + +.css-block-header::after { +  content: "\f078"; /* chevron-down icon */ +  font-family: "Font Awesome 5 Free"; +  font-weight: 900; +  transition: transform 0.3s ease; +} + +.css-block-header.active::after { +  transform: rotate(180deg); +} + +.feature-name { +  font-weight: 500; +} + +.feature-status { +  padding: 2px 8px; +  border-radius: 12px; +  font-size: 12px; +  margin-right: 24px; +} + +.feature-status.enabled { +  background-color: var(--success-color); +  color: white; +} + +.feature-status.disabled { +  background-color: var(--border-color); +  color: var(--text-secondary); +} + +.css-content { +  max-height: 0; +  overflow: hidden; +  transition: max-height 0.3s ease; +} + +/* CSS code display */ +.css-code { +  background-color: rgba(0, 0, 0, 0.2); +  padding: 16px; +  margin: 0; +  overflow-x: auto; +  font-family: "Monaco", "Courier New", monospace; +  font-size: 12px; +  line-height: 1.5; +  color: var(--text-primary); +  max-height: 400px; +  overflow-y: auto; +  white-space: pre-wrap; +} + +.show-more { +  background: none; +  border: none; +  color: var(--accent-color); +  cursor: pointer; +  font-size: 12px; +  padding: 0; +  margin-top: 8px; +  text-decoration: underline; +} + +.show-more:hover { +  color: var(--hover-color); +} + +/* Mode info box */ +.mode-info { +  padding: 12px; +  margin-bottom: 16px; +  background-color: var(--bg-color); +  border-radius: var(--radius-sm); +  border-left: 3px solid var(--accent-color); +} + +/* Expand/collapse all button */ +.view-all-button { +  margin: 8px 0; +} + +/* Clear list button styling */ +.clear-list-button { +  margin-top: 8px; +  margin-bottom: 16px; +  background-color: var(--secondary-bg); +  border-color: var(--border-color); +  font-size: 12px; +  padding: 6px 12px; +} + +.clear-list-button:hover { +  background-color: rgba(220, 53, 69, 0.1); +  border-color: var(--danger-color); +  color: var(--danger-color); +} + +/* Null and object value styling */ +.null-value { +  color: #999; +  font-style: italic; +} + +.object-value { +  color: var(--accent-color); +  font-style: italic; +} + +/* Danger zone styling */ +.danger-zone { +  margin-top: 24px; +  padding: 16px; +  background-color: rgba(220, 53, 69, 0.1); +  border: 1px solid rgba(220, 53, 69, 0.3); +  border-radius: var(--radius-md); +} + +.danger-title { +  color: var(--danger-color); +  font-size: 16px; +  font-weight: 600; +  margin-top: 0; +  margin-bottom: 12px; +} + +.danger-actions { +  display: flex; +  justify-content: center; +} + +.action-button.danger { +  background-color: var(--danger-color); +  color: white; +} + +.action-button.danger:hover { +  background-color: #e03444; +  box-shadow: 0 0 8px rgba(220, 53, 69, 0.5); +} + +/* Responsive adjustments */ +@media (max-width: 768px) { +  .app-content { +    padding: 15px; +  } + +  body { +    min-width: 320px; +  } + +  .data-table th, +  .data-table td { +    padding: 8px 10px; +  } + +  .features-list { +    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); +  } +} diff --git a/data-viewer/data-viewer.html b/data-viewer/data-viewer.html new file mode 100644 index 0000000..12af92c --- /dev/null +++ b/data-viewer/data-viewer.html @@ -0,0 +1,69 @@ +<!DOCTYPE html> +<html lang="en"> + +<head> +    <meta charset="UTF-8"> +    <meta name="viewport" content="width=device-width, initial-scale=1.0"> +    <title>ZenInternet - Data Viewer</title> +    <link rel="stylesheet" href="../popup/popup.css"> +    <link rel="stylesheet" href="data-viewer.css"> +    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> +</head> + +<body> +    <div class="container"> +        <header class="app-header"> +            <div class="logo-container"> +                <img src="../assets/images/logo_48.png" alt="ZenInternet Logo" class="logo-img"> +                <h1 class="app-title">ZenInternet Data Viewer</h1> +            </div> +            <div class="author">by <a href="https://www.sameerasw.com/" target="_blank">@sameerasw</a></div> +            <div id="addon-version" class="addon-version"></div> +        </header> + +        <main class="app-content"> +            <div class="data-section"> +                <h2 class="section-title">Global Settings</h2> +                <div id="global-settings-data" class="data-container"></div> +            </div> + +            <div class="data-section"> +                <h2 class="section-title">Skip/Enable List</h2> +                <div id="skip-list-data" class="data-container"></div> +            </div> + +            <div class="data-section"> +                <h2 class="section-title">Websites and CSS</h2> +                <div id="combined-websites-data" class="data-container"></div> +            </div> + +            <div class="actions"> +                <button id="back-button" class="action-button secondary"> +                    <i class="fas fa-arrow-left"></i> Back to Extension +                </button> +            </div> +             +            <div class="danger-zone"> +                <h3 class="danger-title">Danger Zone</h3> +                <div class="danger-actions"> +                    <button id="delete-all-data" class="action-button danger"> +                        <i class="fas fa-trash"></i> Delete All Data +                    </button> +                </div> +            </div> +        </main> + +        <footer class="app-footer"> +            <a href="https://sameerasw.github.io/my-internet/" class="footer-link" target="_blank"> +                <i class="fas fa-book"></i> Styles Repository +            </a> +            <div class="footer-divider"></div> +            <a href="https://github.com/sameerasw/zeninternet" class="footer-link" target="_blank"> +                <i class="fa-brands fa-github"></i> Project Repository +            </a> +        </footer> +    </div> +    <script src="data-viewer.js"></script> +</body> + +</html>
\ No newline at end of file diff --git a/data-viewer/data-viewer.js b/data-viewer/data-viewer.js new file mode 100644 index 0000000..f7a7043 --- /dev/null +++ b/data-viewer/data-viewer.js @@ -0,0 +1,405 @@ +document.addEventListener("DOMContentLoaded", function () { +  const BROWSER_STORAGE_KEY = "transparentZenSettings"; +  const SKIP_FORCE_THEMING_KEY = "skipForceThemingList"; + +  const globalSettingsElement = document.getElementById("global-settings-data"); +  const skipListElement = document.getElementById("skip-list-data"); +  const combinedWebsitesElement = document.getElementById( +    "combined-websites-data" +  ); +  const backButton = document.getElementById("back-button"); +  const refreshButton = document.getElementById("refresh-data"); +  const deleteAllButton = document.getElementById("delete-all-data"); +  const versionElement = document.getElementById("addon-version"); + +  // Load and display the data +  loadAllData(); + +  // Display addon version +  displayAddonVersion(); + +  // Event listener for the back button +  backButton.addEventListener("click", function () { +    window.close(); +  }); + +  // Event listener for refresh button +  refreshButton.addEventListener("click", function () { +    loadAllData(); +  }); + +  // Event listener for delete all data button +  deleteAllButton.addEventListener("click", function() { +    if (confirm("WARNING: This will delete ALL extension data including settings, website styles, and preferences. This action cannot be undone!\n\nAre you sure you want to proceed?")) { +      deleteAllData(); +    } +  }); + +  async function deleteAllData() { +    try { +      // Clear all storage data +      await browser.storage.local.clear(); +       +      // Show confirmation message +      alert("All data has been deleted successfully. The page will now reload."); +       +      // Reload the page to show empty state +      window.location.reload(); +    } catch (error) { +      console.error("Error deleting data:", error); +      alert("An error occurred while trying to delete data: " + error.message); +    } +  } + +  async function displayAddonVersion() { +    const manifest = browser.runtime.getManifest(); +    versionElement.textContent = `Version: ${manifest.version}`; +  } + +  async function loadAllData() { +    try { +      // Load all data from storage +      const data = await browser.storage.local.get(null); + +      // Display global settings +      const globalSettings = data[BROWSER_STORAGE_KEY] || {}; +      displayGlobalSettings(globalSettings); + +      // Display skip/enable list +      const skipList = data[SKIP_FORCE_THEMING_KEY] || []; +      displaySkipList(skipList, globalSettings.whitelistMode); + +      // Display combined websites and settings +      displayCombinedWebsiteData(data); + +      console.info("Data loaded successfully"); +    } catch (error) { +      console.error("Error loading data:", error); +    } +  } + +  function displayGlobalSettings(settings) { +    globalSettingsElement.innerHTML = ""; + +    const table = document.createElement("table"); +    table.classList.add("data-table"); + +    // Table header +    const thead = document.createElement("thead"); +    const headerRow = document.createElement("tr"); +    headerRow.innerHTML = `<th>Setting</th><th>Value</th>`; +    thead.appendChild(headerRow); +    table.appendChild(thead); + +    // Table body +    const tbody = document.createElement("tbody"); + +    for (const [key, value] of Object.entries(settings)) { +      // Skip lastFetchedTime as it will be formatted differently +      if (key === "lastFetchedTime") continue; + +      const row = document.createElement("tr"); +      row.innerHTML = ` +        <td>${formatSettingName(key)}</td> +        <td>${formatSettingValue(value)}</td> +      `; +      tbody.appendChild(row); +    } + +    // Add last fetched time with formatted date if available +    if (settings.lastFetchedTime) { +      const row = document.createElement("tr"); +      row.innerHTML = ` +        <td>${formatSettingName("lastFetchedTime")}</td> +        <td>${new Date(settings.lastFetchedTime).toLocaleString()}</td> +      `; +      tbody.appendChild(row); +    } + +    table.appendChild(tbody); +    globalSettingsElement.appendChild(table); +  } + +  function displaySkipList(skipList, isWhitelistMode) { +    skipListElement.innerHTML = ""; + +    const modeType = isWhitelistMode ? "Whitelist" : "Blacklist"; +    const actionType = isWhitelistMode ? "Enabled" : "Skipped"; + +    const modeInfo = document.createElement("div"); +    modeInfo.classList.add("mode-info"); +    modeInfo.innerHTML = `<strong>Current Mode:</strong> ${modeType} Mode - ${ +      isWhitelistMode +        ? "Only apply forced styling to sites in the list" +        : "Apply forced styling to all sites except those in the list" +    }`; +    skipListElement.appendChild(modeInfo); + +    // Add Clear List button +    if (skipList.length > 0) { +      const clearListButton = document.createElement("button"); +      clearListButton.classList.add("action-button", "secondary", "clear-list-button"); +      clearListButton.innerHTML = '<i class="fas fa-trash"></i> Clear List'; +      clearListButton.addEventListener("click", function() { +        if (confirm(`Are you sure you want to clear the entire ${modeType} list? This will affect how styling is applied to websites.`)) { +          clearSkipList(); +        } +      }); +      skipListElement.appendChild(clearListButton); +    } + +    if (skipList.length === 0) { +      skipListElement.innerHTML += +        '<div class="no-data">No websites in the list.</div>'; +      return; +    } + +    const table = document.createElement("table"); +    table.classList.add("data-table"); + +    // Table header +    const thead = document.createElement("thead"); +    const headerRow = document.createElement("tr"); +    headerRow.innerHTML = `<th>${actionType} Websites</th>`; +    thead.appendChild(headerRow); +    table.appendChild(thead); + +    // Table body +    const tbody = document.createElement("tbody"); + +    for (const site of skipList) { +      const row = document.createElement("tr"); +      row.innerHTML = `<td>${site}</td>`; +      tbody.appendChild(row); +    } + +    table.appendChild(tbody); +    skipListElement.appendChild(table); +  } + +  async function clearSkipList() { +    try { +      await browser.storage.local.remove(SKIP_FORCE_THEMING_KEY); +      alert(`${SKIP_FORCE_THEMING_KEY} has been cleared successfully.`); +      loadAllData(); // Reload data to reflect changes +    } catch (error) { +      console.error("Error clearing skip list:", error); +      alert("An error occurred while trying to clear the list: " + error.message); +    } +  } + +  function displayCombinedWebsiteData(data) { +    combinedWebsitesElement.innerHTML = ""; + +    const styles = data.styles || {}; +    const websites = styles.website || {}; +    const websiteKeys = Object.keys(websites); + +    // Find all site-specific settings +    const siteSettings = {}; +    for (const [key, value] of Object.entries(data)) { +      if (key.startsWith(BROWSER_STORAGE_KEY + ".")) { +        const siteName = key.substring(BROWSER_STORAGE_KEY.length + 1); +        siteSettings[siteName] = value; +      } +    } + +    if (websiteKeys.length === 0) { +      combinedWebsitesElement.innerHTML = +        '<div class="no-data">No websites found. Try fetching styles first.</div>'; +      return; +    } + +    // Create search filter +    const searchContainer = document.createElement("div"); +    searchContainer.classList.add("search-container"); + +    const searchInput = document.createElement("input"); +    searchInput.type = "text"; +    searchInput.placeholder = "Search websites..."; +    searchInput.classList.add("search-input"); +    searchInput.addEventListener("input", function () { +      filterWebsites(this.value.toLowerCase()); +    }); + +    const searchIcon = document.createElement("i"); +    searchIcon.className = "fas fa-search search-icon"; + +    searchContainer.appendChild(searchIcon); +    searchContainer.appendChild(searchInput); + +    combinedWebsitesElement.appendChild(searchContainer); + +    // Create expand all button +    const expandAllButton = document.createElement("button"); +    expandAllButton.classList.add( +      "action-button", +      "secondary", +      "view-all-button" +    ); +    expandAllButton.textContent = "Expand All"; +    expandAllButton.addEventListener("click", function () { +      const expanded = this.textContent === "Collapse All"; +      const panels = document.querySelectorAll(".website-panel"); + +      panels.forEach((panel) => { +        const header = panel.querySelector(".website-header"); +        const content = panel.querySelector(".website-content"); + +        if (expanded) { +          header.classList.remove("active"); +          content.style.maxHeight = null; + +          // Also collapse all CSS blocks +          content.querySelectorAll(".css-block-header").forEach((cssHeader) => { +            cssHeader.classList.remove("active"); +            const cssContent = cssHeader.nextElementSibling; +            if (cssContent) cssContent.style.maxHeight = null; +          }); +        } else { +          header.classList.add("active"); +          content.style.maxHeight = content.scrollHeight + "px"; +        } +      }); + +      this.textContent = expanded ? "Expand All" : "Collapse All"; +    }); + +    combinedWebsitesElement.appendChild(expandAllButton); + +    const websitesContainer = document.createElement("div"); +    websitesContainer.classList.add("websites-container"); +    combinedWebsitesElement.appendChild(websitesContainer); + +    // Sort websites alphabetically +    websiteKeys.sort(); + +    // Create panels for each website +    for (const website of websiteKeys) { +      const websitePanel = document.createElement("div"); +      websitePanel.classList.add("website-panel"); +      websitePanel.dataset.website = website.toLowerCase(); + +      const header = document.createElement("div"); +      header.classList.add("website-header"); + +      // Create website name with feature count +      const features = websites[website]; +      const featureCount = Object.keys(features).length; + +      // Get site settings if available +      const siteName = website.replace(".css", ""); +      const domainName = siteName.startsWith("+") +        ? siteName.slice(1) +        : siteName; +      const settingsData =  +        siteSettings[domainName] || siteSettings[`www.${domainName}`] || {}; + +      header.innerHTML = ` +        <div class="website-header-content"> +          <span class="website-name">${website}</span> +          <span class="feature-count">${featureCount} features</span> +        </div> +      `; + +      header.addEventListener("click", function () { +        this.classList.toggle("active"); +        const content = this.nextElementSibling; +        if (content.style.maxHeight) { +          content.style.maxHeight = null; +        } else { +          content.style.maxHeight = content.scrollHeight + "px"; +        } +      }); + +      const content = document.createElement("div"); +      content.classList.add("website-content"); + +      // Create CSS blocks for each feature +      for (const [feature, css] of Object.entries(features)) { +        const cssBlock = document.createElement("div"); +        cssBlock.classList.add("css-block"); + +        // Get the feature's enabled status from site settings +        const isEnabled = settingsData[feature] !== false; // true by default + +        // Create the block header with feature name and status +        const cssBlockHeader = document.createElement("div"); +        cssBlockHeader.classList.add("css-block-header"); +        cssBlockHeader.innerHTML = ` +          <span class="feature-name">${feature}</span> +          <span class="feature-status ${isEnabled ? "enabled" : "disabled"}">${ +          isEnabled ? "Enabled" : "Disabled" +        }</span> +        `; + +        // Make the CSS block header toggleable +        cssBlockHeader.addEventListener("click", function (e) { +          // Don't expand if clicking on status badge +          if (e.target.classList.contains("feature-status")) return; + +          this.classList.toggle("active"); +          const cssContent = this.nextElementSibling; +          if (cssContent.style.maxHeight) { +            cssContent.style.maxHeight = null; +          } else { +            cssContent.style.maxHeight = cssContent.scrollHeight + "px"; +          } +        }); + +        // Create the CSS content area +        const cssContent = document.createElement("div"); +        cssContent.classList.add("css-content"); + +        const cssCode = document.createElement("pre"); +        cssCode.classList.add("css-code"); +        cssCode.textContent = css; +        cssContent.appendChild(cssCode); + +        cssBlock.appendChild(cssBlockHeader); +        cssBlock.appendChild(cssContent); +        content.appendChild(cssBlock); +      } + +      websitePanel.appendChild(header); +      websitePanel.appendChild(content); +      websitesContainer.appendChild(websitePanel); +    } + +    // Filter function for search +    function filterWebsites(query) { +      const panels = websitesContainer.querySelectorAll(".website-panel"); + +      panels.forEach((panel) => { +        const website = panel.dataset.website; +        if (website.includes(query)) { +          panel.style.display = ""; +        } else { +          panel.style.display = "none"; +        } +      }); +    } +  } + +  // Helper Functions +  function formatSettingName(name) { +    // Convert camelCase to Title Case with spaces +    return name +      .replace(/([A-Z])/g, " $1") +      .replace(/^./, (str) => str.toUpperCase()); +  } + +  function formatSettingValue(value) { +    if (typeof value === "boolean") { +      return value +        ? '<span class="badge enabled">Enabled</span>' +        : '<span class="badge disabled">Disabled</span>'; +    } else if (value === null) { +      return '<span class="null-value">null</span>'; +    } else if (typeof value === "object") { +      return '<span class="object-value">{Object}</span>'; +    } else { +      return value; +    } +  } +}); diff --git a/manifest.json b/manifest.json index 356ff24..92a8578 100644 --- a/manifest.json +++ b/manifest.json @@ -33,5 +33,10 @@        "js": ["inject-css.js"],        "run_at": "document_start"      } +  ], +  "web_accessible_resources": [ +    "data-viewer/data-viewer.html", +    "data-viewer/data-viewer.js", +    "data-viewer/data-viewer.css"    ]  } diff --git a/popup/popup.html b/popup/popup.html index 8ffa03d..1c5c84c 100644 --- a/popup/popup.html +++ b/popup/popup.html @@ -87,6 +87,12 @@          </div>        </div> +      <div class="actions"> +        <button id="view-data" class="action-button secondary"> +          <i class="fas fa-database"></i> View All Stored Data +        </button> +      </div> +      </main> diff --git a/popup/popup.js b/popup/popup.js index d81ed7d..82fc942 100644 --- a/popup/popup.js +++ b/popup/popup.js @@ -54,6 +54,13 @@ new (class ExtensionPopup {        this.handleWhitelistModeChange.bind(this)      ); +    // Add event listener for the data viewer button +    document.getElementById("view-data")?.addEventListener("click", () => { +      browser.tabs.create({ +        url: browser.runtime.getURL("data-viewer/data-viewer.html"), +      }); +    }); +      // Setup auto-update and display last fetched time      this.setupAutoUpdate();      this.displayLastFetchedTime();  | 
