From 10286c2924041b0bf1a5726aeffecb42a3644a27 Mon Sep 17 00:00:00 2001 From: Grzegorz Kucmierz Date: Wed, 4 Mar 2026 02:45:53 +0000 Subject: [PATCH] chore(lint): enforce 2-space indent & add gpg pre-commit hook --- .husky/pre-commit | 10 ++ eslint.config.js | 1 + src/composables/useExtension.js | 2 +- src/composables/useUrlCleaner.js | 202 +++++++++++++++---------------- src/directives/ripple.js | 2 +- src/style.css | 6 + src/workers/qrcode.worker.js | 30 ++--- 7 files changed, 135 insertions(+), 118 deletions(-) diff --git a/.husky/pre-commit b/.husky/pre-commit index 3867a0f..64e394f 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1 +1,11 @@ +# Check if GPG signing is enabled +gpg_sign=$(git config --get commit.gpgsign || echo "false") + +if [ "$gpg_sign" != "true" ]; then + echo "Error: GPG signing is not enabled or properly configured!" + echo "Please enable it globally using: git config --global commit.gpgsign true" + echo "Or locally by running: git config commit.gpgsign true" + exit 1 +fi + npm run lint diff --git a/eslint.config.js b/eslint.config.js index 097869a..2e602ea 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -33,6 +33,7 @@ export default [ 'no-unused-vars': 'off', 'no-undef': 'off', 'no-debugger': 'off', + 'indent': ['error', 2] } }, { diff --git a/src/composables/useExtension.js b/src/composables/useExtension.js index 37dcd2a..03a5bbe 100644 --- a/src/composables/useExtension.js +++ b/src/composables/useExtension.js @@ -31,7 +31,7 @@ export function useExtension() { extensionCheckInterval = setInterval(() => { window.postMessage({ type: 'TOOLS_APP_PING' }, '*') if (Date.now() - lastPongTime > TIMEOUT_THRESHOLD) { - isExtensionReady.value = false + isExtensionReady.value = false } }, PING_INTERVAL) } diff --git a/src/composables/useUrlCleaner.js b/src/composables/useUrlCleaner.js index fa9acde..62aae68 100644 --- a/src/composables/useUrlCleaner.js +++ b/src/composables/useUrlCleaner.js @@ -1,115 +1,115 @@ import { useLocalStorage } from './useLocalStorage' export function useUrlCleaner() { - const cleanedHistory = useLocalStorage('url-cleaner-history', []) - const isWatchEnabled = useLocalStorage('url-cleaner-watch-enabled', false) + const cleanedHistory = useLocalStorage('url-cleaner-history', []) + const isWatchEnabled = useLocalStorage('url-cleaner-watch-enabled', false) - const defaultExceptions = [ - { id: 'yt', domainPattern: '*.youtube.com', keepParams: ['v', 't'], keepHash: false, keepAllParams: false, isEnabled: true, isDefault: true }, - { id: 'yt-short', domainPattern: 'youtu.be', keepParams: ['t'], keepHash: false, keepAllParams: false, isEnabled: true, isDefault: true } - ] - const exceptions = useLocalStorage('url-cleaner-exceptions', defaultExceptions) + const defaultExceptions = [ + { id: 'yt', domainPattern: '*.youtube.com', keepParams: ['v', 't'], keepHash: false, keepAllParams: false, isEnabled: true, isDefault: true }, + { id: 'yt-short', domainPattern: 'youtu.be', keepParams: ['t'], keepHash: false, keepAllParams: false, isEnabled: true, isDefault: true } + ] + const exceptions = useLocalStorage('url-cleaner-exceptions', defaultExceptions) - const matchDomain = (pattern, domain) => { - // Escape regex chars except * - const regexString = '^' + pattern.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*') + '$' - return new RegExp(regexString, 'i').test(domain) - } + const matchDomain = (pattern, domain) => { + // Escape regex chars except * + const regexString = '^' + pattern.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*') + '$' + return new RegExp(regexString, 'i').test(domain) + } - const processUrl = (text, autoClipboard = false, writeClipboardFn = null) => { - try { - // Basic URL validation - if (!text.match(/^https?:\/\//i)) { - if (autoClipboard) return text + const processUrl = (text, autoClipboard = false, writeClipboardFn = null) => { + try { + // Basic URL validation + if (!text.match(/^https?:\/\//i)) { + if (autoClipboard) return text + } + + const originalLength = text.length + let cleanedUrl = text + + try { + const urlObj = new URL(text) + const hostname = urlObj.hostname + + const matchedRule = exceptions.value.find(rule => + rule.isEnabled && matchDomain(rule.domainPattern, hostname) + ) + + if (matchedRule) { + if (!matchedRule.keepAllParams) { + const params = new URLSearchParams(urlObj.search) + const keys = Array.from(params.keys()) + + for (const key of keys) { + if (!matchedRule.keepParams.includes(key)) { + params.delete(key) + } } + urlObj.search = params.toString() + } - const originalLength = text.length - let cleanedUrl = text - - try { - const urlObj = new URL(text) - const hostname = urlObj.hostname - - const matchedRule = exceptions.value.find(rule => - rule.isEnabled && matchDomain(rule.domainPattern, hostname) - ) - - if (matchedRule) { - if (!matchedRule.keepAllParams) { - const params = new URLSearchParams(urlObj.search) - const keys = Array.from(params.keys()) - - for (const key of keys) { - if (!matchedRule.keepParams.includes(key)) { - params.delete(key) - } - } - urlObj.search = params.toString() - } - - if (!matchedRule.keepHash) { - urlObj.hash = '' - } - } else { - if (urlObj.search || urlObj.hash) { - urlObj.search = '' - urlObj.hash = '' - } - } - - cleanedUrl = urlObj.toString() - } catch (e) { - return text - } - - if (cleanedUrl === text && autoClipboard) { - return text - } - - const newLength = cleanedUrl.length - const savedChars = originalLength - newLength - const savedPercent = originalLength > 0 ? Math.round((savedChars / originalLength) * 100) : 0 - - const entry = { - id: Date.now(), - original: text, - cleaned: cleanedUrl, - savedPercent, - timestamp: new Date().toLocaleTimeString() - } - - cleanedHistory.value.unshift(entry) - - if (cleanedHistory.value.length > 50) { - cleanedHistory.value.pop() - } - - if (autoClipboard && savedChars > 0 && writeClipboardFn) { - writeClipboardFn(cleanedUrl) - } - - return cleanedUrl - } catch (e) { - console.error('Error processing URL:', e) - return text + if (!matchedRule.keepHash) { + urlObj.hash = '' + } + } else { + if (urlObj.search || urlObj.hash) { + urlObj.search = '' + urlObj.hash = '' + } } - } - const removeEntry = (id) => { - cleanedHistory.value = cleanedHistory.value.filter(item => item.id !== id) - } + cleanedUrl = urlObj.toString() + } catch (e) { + return text + } - const clearHistory = () => { - cleanedHistory.value = [] - } + if (cleanedUrl === text && autoClipboard) { + return text + } - return { - cleanedHistory, - isWatchEnabled, - exceptions, - defaultExceptions, - processUrl, - removeEntry, - clearHistory + const newLength = cleanedUrl.length + const savedChars = originalLength - newLength + const savedPercent = originalLength > 0 ? Math.round((savedChars / originalLength) * 100) : 0 + + const entry = { + id: Date.now(), + original: text, + cleaned: cleanedUrl, + savedPercent, + timestamp: new Date().toLocaleTimeString() + } + + cleanedHistory.value.unshift(entry) + + if (cleanedHistory.value.length > 50) { + cleanedHistory.value.pop() + } + + if (autoClipboard && savedChars > 0 && writeClipboardFn) { + writeClipboardFn(cleanedUrl) + } + + return cleanedUrl + } catch (e) { + console.error('Error processing URL:', e) + return text } + } + + const removeEntry = (id) => { + cleanedHistory.value = cleanedHistory.value.filter(item => item.id !== id) + } + + const clearHistory = () => { + cleanedHistory.value = [] + } + + return { + cleanedHistory, + isWatchEnabled, + exceptions, + defaultExceptions, + processUrl, + removeEntry, + clearHistory + } } diff --git a/src/directives/ripple.js b/src/directives/ripple.js index 7c8a69a..a7a8e59 100644 --- a/src/directives/ripple.js +++ b/src/directives/ripple.js @@ -19,7 +19,7 @@ const Ripple = { // Allow custom color via directive value if (binding.value && typeof binding.value === 'string') { - circle.style.backgroundColor = binding.value; + circle.style.backgroundColor = binding.value; } el.appendChild(circle); diff --git a/src/style.css b/src/style.css index ffc7151..0e29b6f 100644 --- a/src/style.css +++ b/src/style.css @@ -212,6 +212,12 @@ body { box-sizing: border-box; } +::placeholder { + color: var(--text-muted); + opacity: 1; + /* Override Firefox default opacity */ +} + .tool-textarea { font-family: monospace; resize: none; diff --git a/src/workers/qrcode.worker.js b/src/workers/qrcode.worker.js index f08b696..4caa0ea 100644 --- a/src/workers/qrcode.worker.js +++ b/src/workers/qrcode.worker.js @@ -1,22 +1,22 @@ import QRCode from 'qrcode' self.onmessage = async (e) => { - const { id, text, ecc } = e.data + const { id, text, ecc } = e.data - if (!text) { - self.postMessage({ id, svgContent: '' }) - return - } + if (!text) { + self.postMessage({ id, svgContent: '' }) + return + } - try { - const svgContent = await QRCode.toString(text, { - type: 'svg', - errorCorrectionLevel: ecc, - margin: 1, - }) + try { + const svgContent = await QRCode.toString(text, { + type: 'svg', + errorCorrectionLevel: ecc, + margin: 1, + }) - self.postMessage({ id, svgContent }) - } catch (err) { - self.postMessage({ id, error: err.message }) - } + self.postMessage({ id, svgContent }) + } catch (err) { + self.postMessage({ id, error: err.message }) + } }