feat: improve Clipboard Sniffer extension integration and UI fixes

This commit is contained in:
2026-02-27 03:37:33 +00:00
parent 1d7fd9a5bc
commit 8e8bf47297
14 changed files with 872 additions and 181 deletions

156
extension/background.js Normal file
View File

@@ -0,0 +1,156 @@
// background.js
// Listen for messages from content scripts or offscreen document
let isSniffing = false;
let lastClipboardContent = '';
let creatingOffscreenDocument;
// Hot-reconnect: Inject content script into existing tabs upon installation/update/restart
const injectContentScriptIfNeeded = async () => {
const tabs = await chrome.tabs.query({ url: ['http://localhost/*', 'http://localhost:*/*', 'https://tools.7u.pl/*'] });
for (const tab of tabs) {
try {
// Try to ping the tab first
try {
await chrome.tabs.sendMessage(tab.id, { action: 'ping' });
// console.log('Content script already active in tab:', tab.id);
} catch (e) {
// If ping fails (no listener), inject script
await chrome.scripting.executeScript({
target: { tabId: tab.id },
files: ['content.js']
});
// console.log('Injected content script into existing tab:', tab.id);
}
} catch (err) {
// console.error('Failed to handle tab:', tab.id, err);
}
}
};
chrome.runtime.onInstalled.addListener(injectContentScriptIfNeeded);
// Also run on startup (when extension is enabled/reloaded)
injectContentScriptIfNeeded();
// Listen for alarms
try {
if (chrome.alarms) {
chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name === 'keepAlive') {
refreshOffscreenDocument();
}
});
} else {
// console.warn('chrome.alarms API is not available.');
}
} catch (e) {
// console.error('Error initializing alarms:', e);
}
// Setup offscreen document
async function setupOffscreenDocument(path) {
// Check if an offscreen document already exists
const existingContexts = await chrome.runtime.getContexts({
contextTypes: ['OFFSCREEN_DOCUMENT'],
});
if (existingContexts.length > 0) {
return;
}
// Create an offscreen document
if (creatingOffscreenDocument) {
await creatingOffscreenDocument;
} else {
creatingOffscreenDocument = chrome.offscreen.createDocument({
url: path,
reasons: ['CLIPBOARD', 'AUDIO_PLAYBACK'],
justification: 'To read clipboard content in the background and play notification sounds',
});
await creatingOffscreenDocument;
creatingOffscreenDocument = null;
}
}
// Lifecycle management: Refresh offscreen document every 25s to avoid 30s timeout
async function refreshOffscreenDocument() {
if (isSniffing) {
await chrome.offscreen.closeDocument();
await setupOffscreenDocument('offscreen.html');
}
}
// Start sniffing when requested
chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => {
if (request.action === 'startSniffing') {
if (isSniffing) {
sendResponse({ status: 'already_started' });
return true;
}
isSniffing = true;
// console.log('Starting sniffing process...');
await setupOffscreenDocument('offscreen.html');
// Setup interval to keep offscreen alive - more aggressive
chrome.alarms.create('keepAlive', { periodInMinutes: 0.1 }); // every 6 seconds
sendResponse({ status: 'started' });
return true;
}
if (request.action === 'stopSniffing') {
if (!isSniffing) {
sendResponse({ status: 'not_running' });
return true;
}
isSniffing = false;
// console.log('Stopping sniffing process...');
// Stop alarm
chrome.alarms.clear('keepAlive');
// Close offscreen document
if (creatingOffscreenDocument) {
await creatingOffscreenDocument;
}
await chrome.offscreen.closeDocument().catch(() => {});
creatingOffscreenDocument = null;
sendResponse({ status: 'stopped' });
return true;
}
if (request.type === 'clipboard-data' && request.target === 'background') {
// Received data from offscreen document
if (isSniffing && request.data && request.data !== lastClipboardContent) {
lastClipboardContent = request.data;
// console.log('Clipboard changed:', request.data.substring(0, 20) + '...');
// Check if sound should be played
chrome.storage.local.get(['playSound'], (result) => {
if (result.playSound !== false) {
// Send message to offscreen document to play sound
chrome.runtime.sendMessage({
target: 'offscreen',
type: 'play-sound'
});
}
});
// Broadcast to all active tabs (content scripts)
// We could filter by sender.tab.id if we knew which tab started sniffing,
// but broadcasting is simpler for now and covers multiple open tabs of the app.
const tabs = await chrome.tabs.query({ url: ['http://localhost/*', 'http://localhost:*/*', 'https://tools.7u.pl/*'] });
for (const tab of tabs) {
chrome.tabs.sendMessage(tab.id, {
action: 'clipboardUpdate',
content: request.data
}).catch(() => {
// Tab might be closed or content script not injected yet
});
}
}
}
});