-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbackground.js
More file actions
119 lines (106 loc) · 3.8 KB
/
background.js
File metadata and controls
119 lines (106 loc) · 3.8 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
// Use session storage for state. It's async but persists across service worker restarts.
const clickState = new Map(); // Use a map to handle timeouts for each tab individually.
const DOUBLE_CLICK_THRESHOLD = 400; // ms
chrome.action.onClicked.addListener((tab) => {
const tabId = tab.id;
if (!tabId) return;
if (clickState.has(tabId)) {
// This is a double click
clearTimeout(clickState.get(tabId));
clickState.delete(tabId);
handleDoubleClick(tab);
} else {
// This is a single click, wait to see if it becomes a double
const timeout = setTimeout(() => {
handleSingleClick(tab);
clickState.delete(tabId);
}, DOUBLE_CLICK_THRESHOLD);
clickState.set(tabId, timeout);
}
});
async function handleSingleClick(tab) {
const tabStorage = await chrome.storage.session.get(tab.id.toString());
const isEnabled = tabStorage[tab.id];
if (isEnabled) {
sendMessageWithRetry(tab.id, { action: "toggleSplit" });
} else {
triggerSplitView(tab.id, 2);
}
}
async function handleDoubleClick(tab) {
const tabStorage = await chrome.storage.session.get(tab.id.toString());
const isEnabled = tabStorage[tab.id];
if (isEnabled) {
sendMessageWithRetry(tab.id, { action: "toggleSplit" });
} else {
triggerSplitView(tab.id, 3);
}
}
// The content script reports its state, which we store reliably.
chrome.runtime.onMessage.addListener((request, sender) => {
if (request.type === 'splitState' && sender.tab) {
chrome.storage.session.set({ [sender.tab.id]: request.isSplit });
}
});
// Make the listener async to handle await for storage.
chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => {
if (changeInfo.status !== 'complete' || !tab.url || !tab.url.startsWith('http')) {
return;
}
const tabStorage = await chrome.storage.session.get(tabId.toString());
if (tabStorage[tabId]) {
return; // Already active or pending, don't auto-trigger.
}
const settings = await chrome.storage.sync.get(['urlMemory', 'autoSplitRules']);
const urlMemory = settings.urlMemory || {};
const autoSplitRules = settings.autoSplitRules || [];
let paneCount = 0;
if (urlMemory[tab.url]) {
paneCount = urlMemory[tab.url];
} else {
const matchedRule = autoSplitRules.find(rule => tab.url.includes(rule));
if (matchedRule) {
paneCount = 2;
}
}
if (paneCount > 0) {
triggerSplitView(tabId, paneCount);
}
});
async function triggerSplitView(tabId, paneCount) {
// Final check to prevent race conditions.
const tabStorage = await chrome.storage.session.get(tabId.toString());
if (tabStorage[tabId]) {
return;
}
// Set state immediately to act as a lock.
await chrome.storage.session.set({ [tabId]: true });
try {
await chrome.scripting.executeScript({
target: { tabId: tabId },
files: ["content.js"],
});
// Give the content script a moment to set up its message listener
setTimeout(() => {
sendMessageWithRetry(tabId, { action: "toggleSplit", paneCount: paneCount });
}, 50);
} catch (err) {
// If injection fails, unlock the state.
await chrome.storage.session.set({ [tabId]: false });
if (!err.message.includes('Cannot access a chrome:// URL') && !err.message.includes('No tab with id')) {
console.error(`Failed to inject script into tab ${tabId}: `, err);
}
}
}
function sendMessageWithRetry(tabId, message, retries = 3) {
chrome.tabs.sendMessage(tabId, message, function(response) {
if (chrome.runtime.lastError && retries > 0) {
console.warn(`2Pane: Message failed, retrying... (${retries} left)`);
setTimeout(() => {
sendMessageWithRetry(tabId, message, retries - 1);
}, 100);
} else if (chrome.runtime.lastError) {
console.error(`2Pane: Message failed after multiple retries:`, chrome.runtime.lastError.message);
}
});
}