-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbackground.js
More file actions
141 lines (123 loc) · 5.39 KB
/
background.js
File metadata and controls
141 lines (123 loc) · 5.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// Stash - Background Service Worker
chrome.commands.onCommand.addListener(async (command) => {
if (command === 'save-all-tabs') await saveAllTabsBackground();
});
chrome.runtime.onInstalled.addListener((details) => {
chrome.contextMenus.create({ id: 'save-current-tab', title: 'Save this tab to Stash', contexts: ['page'] });
chrome.contextMenus.create({ id: 'save-all-tabs', title: 'Save all tabs to Stash', contexts: ['page'] });
chrome.contextMenus.create({ id: 'open-dashboard', title: 'Open Stash', contexts: ['page'] });
if (details.reason === 'install') {
chrome.tabs.create({ url: 'onboarding.html' });
}
});
chrome.contextMenus.onClicked.addListener(async (info, tab) => {
if (info.menuItemId === 'save-current-tab') await saveCurrentTab(tab);
else if (info.menuItemId === 'save-all-tabs') await saveAllTabsBackground();
else if (info.menuItemId === 'open-dashboard') chrome.tabs.create({ url: chrome.runtime.getURL('dashboard.html') });
});
// Message Listener for Centralized Logic
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.type === 'SAVE_ALL_TABS') {
saveAllTabsBackground().then(() => sendResponse({ success: true }));
return true; // Keep channel open for async response
}
});
// Alarm Listener for Auto Save
chrome.alarms.onAlarm.addListener(async (alarm) => {
if (alarm.name === 'autoSave') {
await saveAllTabsBackground(true); // true = isAutoSave
}
});
// Watch for settings changes to update Alarm
chrome.storage.onChanged.addListener((changes, namespace) => {
if (namespace === 'local' && changes.settings) {
const newSettings = changes.settings.newValue;
if (newSettings.autoSave) {
const interval = parseInt(newSettings.autoSaveInterval) || 30;
chrome.alarms.create('autoSave', { periodInMinutes: interval });
} else {
chrome.alarms.clear('autoSave');
}
}
});
// Window Removed Listener for Close Auto Save
chrome.windows.onRemoved.addListener(async (windowId) => {
// See previous note: robust "on close" saving is limited in extensions without persistent background state.
// We will rely on periodic auto-save.
});
async function saveCurrentTab(tab) {
if (!tab.url || tab.url.startsWith('chrome://') || tab.url.startsWith('chrome-extension://')) return;
const stored = await chrome.storage.local.get(['tabGroups', 'settings']);
let tabGroups = stored.tabGroups || [];
const settings = stored.settings || { closeAfterSave: true };
// Duplicate Check
if (settings.preventDuplicates) {
const isDuplicate = tabGroups.some(group => !group.deletedAt && group.tabs.some(t => t.url === tab.url));
if (isDuplicate) return;
}
const group = {
id: crypto.randomUUID(),
createdAt: new Date().toISOString(),
favorite: false,
type: 'single',
tabs: [{
id: crypto.randomUUID(),
title: tab.title || 'Untitled',
url: tab.url,
favicon: getFaviconUrl(tab.url)
}]
};
tabGroups.unshift(group);
await chrome.storage.local.set({ tabGroups });
if (settings.closeAfterSave) await chrome.tabs.remove(tab.id);
}
async function saveAllTabsBackground(isAutoSave = false) {
try {
const tabs = await chrome.tabs.query({ currentWindow: true });
const stored = await chrome.storage.local.get(['tabGroups', 'settings']);
let tabGroups = stored.tabGroups || [];
const settings = stored.settings || { closeAfterSave: true, includePinned: false };
let filteredTabs = tabs.filter(tab => !tab.url.startsWith('chrome://') && !tab.url.startsWith('chrome-extension://') && (settings.includePinned || !tab.pinned));
if (filteredTabs.length === 0) return;
if (isAutoSave) {
// Check if identical to last group to avoid spam
const lastGroup = tabGroups.find(g => !g.deletedAt); // Find first non-deleted
if (lastGroup) {
const currentUrls = filteredTabs.map(t => t.url).sort().join(',');
const lastUrls = lastGroup.tabs.map(t => t.url).sort().join(',');
if (currentUrls === lastUrls) return;
}
}
const group = {
id: crypto.randomUUID(),
createdAt: new Date().toISOString(),
favorite: false,
type: isAutoSave ? 'auto' : 'manual',
tabs: filteredTabs.map(tab => ({
id: crypto.randomUUID(),
title: tab.title || 'Untitled',
url: tab.url,
favicon: getFaviconUrl(tab.url)
}))
};
tabGroups.unshift(group);
await chrome.storage.local.set({ tabGroups });
if (settings.closeAfterSave && !isAutoSave) {
const tabIds = filteredTabs.map(tab => tab.id);
await chrome.tabs.create({ url: 'chrome://newtab' });
await chrome.tabs.remove(tabIds);
}
} catch (e) {
console.error(e);
}
}
// Helper: Get reliable favicon URL using Google's favicon service
function getFaviconUrl(pageUrl) {
try {
const url = new URL(pageUrl);
// Use Google's favicon service for more reliable favicon loading
return `https://www.google.com/s2/favicons?domain=${url.hostname}&sz=64`;
} catch {
return null;
}
}