diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index d3e7fc3..d51f23e 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -13,5 +13,5 @@ contact_links: url: https://discord.gg/wXy6m2X8wY - name: '📝 Submit Feedback' - about: Send General Feedback Anonymously. + about: Send General Feedback. url: https://cssnr.github.io/feedback/?app=ASN%20Plus diff --git a/package-lock.json b/package-lock.json index 1224a03..3f6b93a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "eslint": "^8.57.0", "gulp": "^4.0.2", "json-merger": "^1.1.10", - "prettier": "^3.3.2", + "prettier": "^3.3.3", "web-ext": "^8.2.0" } }, @@ -3314,9 +3314,9 @@ } }, "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -5245,17 +5245,14 @@ } }, "node_modules/jackspeak": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.1.tgz", - "integrity": "sha512-U23pQPDnmYybVkYjObcuYMk43VRlMLLqLI+RdZy8s8WV8WsxO9SnqSroKaluuvcNOdCAlauKszDwd+umbot5Mg==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, - "engines": { - "node": ">=18" - }, "funding": { "url": "https://github.com/sponsors/isaacs" }, @@ -5689,14 +5686,11 @@ } }, "node_modules/lru-cache": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.3.1.tgz", - "integrity": "sha512-9/8QXrtbGeMB6LxwQd4x1tIMnsmUxMvIH/qWGsccz6bt9Uln3S+sgAaqfQNhbGA8ufzs2fHuP/yqapGgP9Hh2g==", + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, - "license": "ISC", - "engines": { - "node": ">=18" - } + "license": "ISC" }, "node_modules/make-error": { "version": "1.3.6", @@ -7308,9 +7302,9 @@ } }, "node_modules/prettier": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz", - "integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", "dev": true, "license": "MIT", "bin": { diff --git a/package.json b/package.json index b3b217e..69f55cb 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "eslint": "^8.57.0", "gulp": "^4.0.2", "json-merger": "^1.1.10", - "prettier": "^3.3.2", + "prettier": "^3.3.3", "web-ext": "^8.2.0" } } diff --git a/src/css/options.css b/src/css/options.css index d749f57..4b1b224 100644 --- a/src/css/options.css +++ b/src/css/options.css @@ -1,5 +1,33 @@ /* CSS for options.html */ -.card { - min-width: 360px; +body { + min-width: 400px; +} + +#options-wrapper { + width: 100%; + max-width: 800px; + backdrop-filter: blur(6px); + filter: drop-shadow(15px 15px 12px #000000); +} + +[data-bs-theme='dark'] #options-wrapper { + background: rgba(0 0 0 / 50%); +} +[data-bs-theme='light'] #options-wrapper { + background: rgba(255 255 255 / 60%); +} + +[data-bs-theme='dark'] .form-control { + background: rgba(0 0 0 / 50%); +} +[data-bs-theme='light'] .form-control { + background: rgba(255 255 255 / 50%); +} + +[data-bs-theme='dark'] #table-wrapper { + background: rgba(0 0 0 / 50%); +} +[data-bs-theme='light'] #table-wrapper { + background: rgba(255 255 255 / 50%); } diff --git a/src/html/options.html b/src/html/options.html index 1c417d8..da7b125 100644 --- a/src/html/options.html +++ b/src/html/options.html @@ -13,11 +13,10 @@ -
-
-
-
-
+
+
+
+
ASN Plus @@ -25,20 +24,18 @@

ASN Plus

v

- - - - - - - - - - - - - -
Keyboard Shortcuts
DescriptionShortcut
Show Popup Action Not Set
Open Home Page Not Set
+
+ + + + + + + + + +
Keyboard Shortcuts
DescriptionShortcut
Unknown
+

- -
-
+ +
+
+
+ + +
diff --git a/src/js/export.js b/src/js/export.js index ac0aef6..6c3dcec 100644 --- a/src/js/export.js +++ b/src/js/export.js @@ -104,6 +104,9 @@ export async function saveOptions(event) { options['countryCode'] = 'N' key = 'countryDisplay' value = 'USA' + } else if (key === 'reset-background') { + key = 'pictureURL' + value = 'https://images.cssnr.com/aviation' } else if (event.target.type === 'radio') { key = event.target.name const radios = document.getElementsByName(key) @@ -116,7 +119,16 @@ export async function saveOptions(event) { } else if (event.target.type === 'checkbox') { value = event.target.checked } else if (event.target.type === 'number') { - value = event.target.value.toString() + const number = parseFloat(event.target.value) + let min = 0.5 + let max = 2.0 + if (!isNaN(number) && number >= min && number <= max) { + event.target.value = number.toString() + value = number + } else { + event.target.value = options[event.target.id] + return + } } else { value = event.target.value?.trim() } @@ -147,13 +159,12 @@ export function updateOptions(options) { } // console.debug(`${key}: ${value}`) const el = document.getElementById(key) - // console.debug('el:', el) if (!el) { continue } if (!['INPUT', 'SELECT'].includes(el.tagName)) { el.textContent = value.toString() - } else if (el.type === 'checkbox') { + } else if (['checkbox', 'radio'].includes(el.type)) { el.checked = value } else { el.value = value diff --git a/src/js/options.js b/src/js/options.js index 94aa847..67bad99 100644 --- a/src/js/options.js +++ b/src/js/options.js @@ -3,7 +3,6 @@ import { activateOrOpen, checkPerms, - onChanged, requestPerms, saveOptions, showToast, @@ -15,10 +14,15 @@ chrome.storage.onChanged.addListener(onChanged) chrome.permissions.onAdded.addListener(onAdded) chrome.permissions.onRemoved.addListener(onRemoved) +window.addEventListener('keydown', handleKeyboard) + document.addEventListener('DOMContentLoaded', initOptions) -document.getElementById('reset-country').addEventListener('click', resetCountry) document.getElementById('test-voice').addEventListener('click', testVoice) document.getElementById('copy-support').addEventListener('click', copySupport) +document.getElementById('reset-country').addEventListener('click', resetCountry) +document + .getElementById('reset-background') + .addEventListener('click', resetBackground) document .querySelectorAll('#options-form input,select') .forEach((el) => el.addEventListener('change', saveOptions)) @@ -44,14 +48,12 @@ document */ async function initOptions() { console.debug('initOptions') - await setShortcuts({ - mainKey: '_execute_action', - openHome: 'openHome', - }) + await setShortcuts('#keyboard-shortcuts') updateManifest() const { options } = await chrome.storage.sync.get(['options']) console.debug('options:', options) updateOptions(options) + setBackground(options) await checkPerms() if (typeof speechSynthesis !== 'undefined') { @@ -69,8 +71,34 @@ async function initOptions() { } } +// /** +// * Login Background Change Callback +// * @function loginBackgroundChange +// * @param {InputEvent} event +// */ +// function loginBackgroundChange(event) { +// console.debug('loginBackgroundChange:', event.target.id) +// updateBackgroundInput(event.target.id) +// } + +/** + * Set Background + * @function setBackground + * @param {Object} options + */ +function setBackground(options) { + console.debug('setBackground:', options.radioBackground, options.pictureURL) + if (options.radioBackground === 'bgPicture') { + const url = options.pictureURL || 'https://images.cssnr.com/aviation' + document.body.style.background = `url('${url}') no-repeat center fixed` + document.body.style.backgroundSize = 'cover' + } else { + document.body.style.cssText = '' + } +} + function addSpeechVoices(options, voices) { - console.debug('addSpeechVoices:', options, voices) + console.debug('addSpeechVoices:', options.speechVoice, voices) const voiceSelect = document.getElementById('speechVoice') voices.sort((a, b) => a.lang.localeCompare(b.lang)) voices.forEach((voice) => { @@ -104,6 +132,23 @@ async function resetCountry(event) { showToast('Country Display and Code Reset.') } +/** + * Reset Background Option Callback + * @function resetBackground + * @param {InputEvent} event + */ +async function resetBackground(event) { + console.log('resetBackground:', event) + event.preventDefault() + const pictureURL = document.getElementById('pictureURL') + pictureURL.value = 'https://images.cssnr.com/aviation' + pictureURL.focus() + // const form = document.getElementById('options-form') + // form.submit() + await saveOptions(event) + showToast('Background Image URL Reset.') +} + /** * Test Voice Click Callback * @function testVoice @@ -140,6 +185,39 @@ function getUtterance(text, options) { return utterance } +/** + * Handle Keyboard Shortcuts Callback + * @function handleKeyboard + * @param {KeyboardEvent} e + */ +function handleKeyboard(e) { + // console.debug('handleKeyboard:', e) + // console.debug('type:', e.target.type) + if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || e.repeat) { + return + } + if ( + [ + 'date', + 'email', + 'number', + 'password', + 'search', + 'tel', + 'text', + 'url', + ].includes(e.target.type) + ) { + return + } + if (!document.getElementById('enableKeyboard').checked) { + return + } + if (['KeyZ', 'KeyK'].includes(e.code)) { + bootstrap.Modal.getOrCreateInstance('#keybinds-modal').toggle() + } +} + /** * Copy Support/Debugging Information * @function copySupport @@ -215,18 +293,52 @@ async function openPermissions(event) { /** * Set Keyboard Shortcuts * @function setShortcuts - * @param {Object} mapping { elementID: name } + * @param {String} selector */ -async function setShortcuts(mapping) { +async function setShortcuts(selector = '#keyboard-shortcuts') { + if (!chrome.commands) { + return console.debug('Skipping: chrome.commands') + } + document.getElementById('table-wrapper').classList.remove('d-none') + const table = document.querySelector(selector) + const tbody = table.querySelector('tbody') + const source = tbody.querySelector('tr.d-none').cloneNode(true) + source.classList.remove('d-none') const commands = await chrome.commands.getAll() - for (const [elementID, name] of Object.entries(mapping)) { - // console.debug(`${elementID}: ${name}`) - const command = commands.find((x) => x.name === name) - if (command?.shortcut) { - console.debug(`${elementID}: ${command.shortcut}`) - const el = document.getElementById(elementID) - if (el) { - el.textContent = command.shortcut + for (const command of commands) { + // console.debug('command:', command) + const row = source.cloneNode(true) + // TODO: Chrome does not parse the description for _execute_action in manifest.json + let description = command.description + if (!description && command.name === '_execute_action') { + description = 'Show Popup' + } + row.querySelector('.description').textContent = description + row.querySelector('kbd').textContent = command.shortcut || 'Not Set' + tbody.appendChild(row) + } +} + +/** + * On Changed Callback + * @function onChanged + * @param {Object} changes + * @param {String} namespace + */ +export function onChanged(changes, namespace) { + console.debug('onChanged:', changes, namespace) + for (const [key, { oldValue, newValue }] of Object.entries(changes)) { + if (namespace === 'sync' && key === 'options') { + console.debug('newValue:', newValue) + updateOptions(newValue) + if (oldValue.radioBackground !== newValue.radioBackground) { + setBackground(newValue) + } + if ( + oldValue.pictureURL !== newValue.pictureURL || + oldValue.videoURL !== newValue.videoURL + ) { + setBackground(newValue) } } } diff --git a/src/js/service-worker.js b/src/js/service-worker.js index e472c7d..74b6b4d 100644 --- a/src/js/service-worker.js +++ b/src/js/service-worker.js @@ -59,6 +59,8 @@ async function onInstalled(details) { autoFill: false, asnUsername: '', asnEmail: '', + radioBackground: 'bgPicture', + pictureURL: 'https://images.cssnr.com/aviation', contextMenu: true, showUpdate: false, })