diff --git a/api-samples/history/historyOverride/README.md b/api-samples/history/historyOverride/README.md new file mode 100644 index 0000000000..9b3a983489 --- /dev/null +++ b/api-samples/history/historyOverride/README.md @@ -0,0 +1,13 @@ +# chrome.history - History Override + +A sample that demonstrates how to override the default history page. + +## Overview + +Once this extension is installed, `chrome://history` will be overridden by the extension's custom page. + +## Running this extension + +1. Clone this repository. +2. Load this directory in Chrome as an [unpacked extension](https://developer.chrome.com/docs/extensions/mv3/getstarted/development-basics/#load-unpacked). +3. Open `chrome://history` and you will find the custom history page. diff --git a/api-samples/history/historyOverride/history.html b/api-samples/history/historyOverride/history.html new file mode 100644 index 0000000000..ad39a1dcab --- /dev/null +++ b/api-samples/history/historyOverride/history.html @@ -0,0 +1,43 @@ + + + History + + + + + +
+ + + + diff --git a/api-samples/history/historyOverride/history128.png b/api-samples/history/historyOverride/history128.png new file mode 100644 index 0000000000..e53641aac2 Binary files /dev/null and b/api-samples/history/historyOverride/history128.png differ diff --git a/api-samples/history/historyOverride/history16.png b/api-samples/history/historyOverride/history16.png new file mode 100644 index 0000000000..cdb6e8aea7 Binary files /dev/null and b/api-samples/history/historyOverride/history16.png differ diff --git a/api-samples/history/historyOverride/history32.png b/api-samples/history/historyOverride/history32.png new file mode 100644 index 0000000000..004630abc1 Binary files /dev/null and b/api-samples/history/historyOverride/history32.png differ diff --git a/api-samples/history/historyOverride/history48.png b/api-samples/history/historyOverride/history48.png new file mode 100644 index 0000000000..c16dfe8c31 Binary files /dev/null and b/api-samples/history/historyOverride/history48.png differ diff --git a/api-samples/history/historyOverride/logic.js b/api-samples/history/historyOverride/logic.js new file mode 100644 index 0000000000..3ced6c6a38 --- /dev/null +++ b/api-samples/history/historyOverride/logic.js @@ -0,0 +1,85 @@ +const kMillisecondsPerWeek = 1000 * 60 * 60 * 24 * 7; +const kOneWeekAgo = new Date().getTime() - kMillisecondsPerWeek; +const historyDiv = document.getElementById('historyDiv'); + +function faviconURL(u) { + const url = new URL(chrome.runtime.getURL('/_favicon/')); + url.searchParams.set('pageUrl', u); + url.searchParams.set('size', '24'); + return url.toString(); +} + +function constructHistory(historyItems) { + const template = document.getElementById('historyTemplate'); + for (let item of historyItems) { + const clone = document.importNode(template.content, true); + const pageLinkEl = clone.querySelector('.page-link'); + const pageTitleEl = clone.querySelector('.page-title'); + const pageVisitTimeEl = clone.querySelector('.page-visit-time'); + const imageWrapperEl = clone.querySelector('.image-wrapper'); + const checkbox = clone.querySelector('.removeCheck, input'); + checkbox.setAttribute('value', item.url); + const favicon = document.createElement('img'); + pageLinkEl.href = item.url; + favicon.src = faviconURL(item.url); + pageLinkEl.textContent = item.url; + imageWrapperEl.prepend(favicon); + pageVisitTimeEl.textContent = new Date(item.lastVisitTime).toLocaleString(); + if (!item.title) { + pageTitleEl.style.display = 'none'; + } + pageTitleEl.innerText = item.title; + + clone + .querySelector('.removeButton, button') + .addEventListener('click', async function () { + await chrome.history.deleteUrl({ url: item.url }); + location.reload(); + }); + + clone + .querySelector('.history') + .addEventListener('click', async function (event) { + // fix double click + if (event.target.className === 'removeCheck') { + return; + } + + checkbox.checked = !checkbox.checked; + }); + historyDiv.appendChild(clone); + } +} + +document.getElementById('searchSubmit').onclick = async function () { + historyDiv.innerHTML = ' '; + const searchQuery = document.getElementById('searchInput').value; + const historyItems = await chrome.history.search({ + text: searchQuery, + startTime: kOneWeekAgo + }); + constructHistory(historyItems); +}; + +document.getElementById('deleteSelected').onclick = async function () { + const checkboxes = document.getElementsByTagName('input'); + for (let checkbox of checkboxes) { + if (checkbox.checked == true) { + await chrome.history.deleteUrl({ url: checkbox.value }); + } + } + location.reload(); +}; + +document.getElementById('removeAll').onclick = async function () { + await chrome.history.deleteAll(); + location.reload(); +}; + +chrome.history + .search({ + text: '', + startTime: kOneWeekAgo, + maxResults: 99 + }) + .then(constructHistory); diff --git a/api-samples/history/historyOverride/manifest.json b/api-samples/history/historyOverride/manifest.json new file mode 100644 index 0000000000..8b57f2b09d --- /dev/null +++ b/api-samples/history/historyOverride/manifest.json @@ -0,0 +1,16 @@ +{ + "manifest_version": 3, + "name": "History Override", + "description": "Overrides the History Page", + "version": "1.0", + "chrome_url_overrides": { + "history": "history.html" + }, + "permissions": ["history", "favicon"], + "icons": { + "16": "history16.png", + "32": "history32.png", + "48": "history48.png", + "128": "history128.png" + } +} diff --git a/api-samples/history/historyOverride/style.css b/api-samples/history/historyOverride/style.css new file mode 100644 index 0000000000..43f126b70d --- /dev/null +++ b/api-samples/history/historyOverride/style.css @@ -0,0 +1,88 @@ +small { + font-size: 12px; + font-weight: normal; +} + +#historyDiv { + margin-top: 10px; + display: flex; + flex-direction: column; + gap: 10px; + max-width: 1000px; +} + +.history { + box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.2); + border-radius: 10px; + display: flex; + height: 60px; + padding: 10px; + cursor: pointer; +} + +.image-wrapper { + width: 24px; + height: 100%; + margin-right: 10px; + flex-grow: 0; + flex-shrink: 0; +} + +.image-wrapper img { + width: 100%; +} + +.page-meta { + flex-grow: 1; + display: flex; + flex-direction: column; + justify-content: space-between; + max-width: calc(100% - 180px); +} + +.page-title { + font-weight: bold; + font-size: 0.8rem; +} + +.page-detail { + display: flex; + flex-direction: column; + align-items: start; +} + +.page-link, +.page-title { + overflow: hidden; + white-space: nowrap; + max-width: 100%; + display: inline-block; + text-overflow: ellipsis; + margin: 0; +} + +.page-link { + font-size: 0.8rem; + color: #8c8c8c; +} + +.page-link:hover { + color: #000; +} + +.page-visit-time { + font-size: 0.8rem; + color: #a5a5a5; +} + +.actions { + flex-grow: 0; + flex-shrink: 0; + display: flex; + flex-grow: 1; + flex-shrink: 0; + flex-direction: column; + justify-content: space-between; + align-items: end; + gap: 5px; +}