@opentabs-dev/browser-extension 0.0.51 → 0.0.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/dist/background-message-handlers.d.ts +24 -3
  2. package/dist/background-message-handlers.d.ts.map +1 -1
  3. package/dist/background-message-handlers.js +251 -9
  4. package/dist/background-message-handlers.js.map +1 -1
  5. package/dist/background.js +593 -117
  6. package/dist/background.js.map +1 -1
  7. package/dist/confirmation-badge.d.ts +2 -2
  8. package/dist/confirmation-badge.d.ts.map +1 -1
  9. package/dist/confirmation-badge.js +49 -17
  10. package/dist/confirmation-badge.js.map +1 -1
  11. package/dist/constants.d.ts +8 -0
  12. package/dist/constants.d.ts.map +1 -1
  13. package/dist/constants.js +8 -0
  14. package/dist/constants.js.map +1 -1
  15. package/dist/extension-messages.d.ts +49 -6
  16. package/dist/extension-messages.d.ts.map +1 -1
  17. package/dist/known-methods.d.ts +2 -2
  18. package/dist/known-methods.d.ts.map +1 -1
  19. package/dist/known-methods.js +1 -0
  20. package/dist/known-methods.js.map +1 -1
  21. package/dist/message-router.d.ts.map +1 -1
  22. package/dist/message-router.js +119 -11
  23. package/dist/message-router.js.map +1 -1
  24. package/dist/offscreen/index.js +11 -2
  25. package/dist/offscreen/index.js.map +1 -1
  26. package/dist/server-request.d.ts +19 -0
  27. package/dist/server-request.d.ts.map +1 -0
  28. package/dist/server-request.js +79 -0
  29. package/dist/server-request.js.map +1 -0
  30. package/dist/server-state-cache.d.ts +41 -0
  31. package/dist/server-state-cache.d.ts.map +1 -0
  32. package/dist/server-state-cache.js +75 -0
  33. package/dist/server-state-cache.js.map +1 -0
  34. package/dist/side-panel/App.d.ts.map +1 -1
  35. package/dist/side-panel/App.js +28 -59
  36. package/dist/side-panel/App.js.map +1 -1
  37. package/dist/side-panel/bridge.d.ts +19 -33
  38. package/dist/side-panel/bridge.d.ts.map +1 -1
  39. package/dist/side-panel/bridge.js +27 -91
  40. package/dist/side-panel/bridge.js.map +1 -1
  41. package/dist/side-panel/components/BrowserToolsCard.d.ts.map +1 -1
  42. package/dist/side-panel/components/BrowserToolsCard.js +2 -1
  43. package/dist/side-panel/components/BrowserToolsCard.js.map +1 -1
  44. package/dist/side-panel/components/PluginCard.d.ts.map +1 -1
  45. package/dist/side-panel/components/PluginCard.js +2 -1
  46. package/dist/side-panel/components/PluginCard.js.map +1 -1
  47. package/dist/side-panel/components/PluginIcon.d.ts +2 -1
  48. package/dist/side-panel/components/PluginIcon.d.ts.map +1 -1
  49. package/dist/side-panel/components/PluginIcon.js +23 -5
  50. package/dist/side-panel/components/PluginIcon.js.map +1 -1
  51. package/dist/side-panel/components/ToolIcon.d.ts +2 -1
  52. package/dist/side-panel/components/ToolIcon.d.ts.map +1 -1
  53. package/dist/side-panel/components/ToolIcon.js +17 -3
  54. package/dist/side-panel/components/ToolIcon.js.map +1 -1
  55. package/dist/side-panel/components/ToolRow.d.ts.map +1 -1
  56. package/dist/side-panel/components/ToolRow.js +1 -2
  57. package/dist/side-panel/components/ToolRow.js.map +1 -1
  58. package/dist/side-panel/components/retro/Button.d.ts +1 -1
  59. package/dist/side-panel/hooks/useServerNotifications.d.ts +1 -3
  60. package/dist/side-panel/hooks/useServerNotifications.d.ts.map +1 -1
  61. package/dist/side-panel/hooks/useServerNotifications.js +2 -8
  62. package/dist/side-panel/hooks/useServerNotifications.js.map +1 -1
  63. package/dist/side-panel/side-panel.js +1201 -1210
  64. package/dist/side-panel/styles.css +1 -1
  65. package/dist/side-panel-toggle.d.ts +6 -0
  66. package/dist/side-panel-toggle.d.ts.map +1 -1
  67. package/dist/side-panel-toggle.js +6 -0
  68. package/dist/side-panel-toggle.js.map +1 -1
  69. package/dist/tab-state.d.ts +19 -1
  70. package/dist/tab-state.d.ts.map +1 -1
  71. package/dist/tab-state.js +128 -3
  72. package/dist/tab-state.js.map +1 -1
  73. package/manifest.json +1 -1
  74. package/package.json +1 -1
@@ -1,5 +1,99 @@
1
+ // dist/constants.js
2
+ var KEEPALIVE_ALARM = "opentabs-keepalive";
3
+ var KEEPALIVE_INTERVAL_MINUTES = 0.5;
4
+ var PLUGINS_META_KEY = "plugins_meta";
5
+ var WS_CONNECTED_KEY = "wsConnected";
6
+ var SIDE_PANEL_OPEN_WINDOWS_KEY = "sidePanelOpenWindows";
7
+ var SCRIPT_TIMEOUT_MS = 25e3;
8
+ var MAX_SCRIPT_TIMEOUT_MS = 295e3;
9
+ var IS_READY_TIMEOUT_MS = 5e3;
10
+ var READINESS_POLL_INTERVAL_MS = 3e4;
11
+ var RELOAD_FLUSH_DELAY_MS = 100;
12
+ var INJECTION_RETRY_DELAY_MS = 200;
13
+ var SCREENSHOT_RENDER_DELAY_MS = 100;
14
+ var WS_FLUSH_DELAY_MS = 50;
15
+ var SERVER_PORT_KEY = "serverPort";
16
+ var DEFAULT_SERVER_PORT = 9515;
17
+ var TEXT_PREVIEW_MAX_LENGTH = 200;
18
+ var DEFAULT_WAIT_TIMEOUT_MS = 1e4;
19
+ var POLL_INTERVAL_MS = 100;
20
+ var DEFAULT_QUERY_LIMIT = 100;
21
+ var MAX_INPUT_SIZE = 10 * 1024 * 1024;
22
+ var SIDE_PANEL_TIMEOUT_MS = 3e3;
23
+ var CDP_VERSION = "1.3";
24
+ var EXEC_MAX_ASYNC_WAIT_MS = 1e4;
25
+ var EXEC_POLL_INTERVAL_MS = 50;
26
+ var EXEC_RESULT_TRUNCATION_LIMIT = 5e4;
27
+ var DEFAULT_LOG_LIMIT = 100;
28
+ var VALID_PLUGIN_NAME = /^[a-z0-9]+(-[a-z0-9]+)*$/;
29
+ var isValidPluginName = (name) => VALID_PLUGIN_NAME.test(name);
30
+ var buildWsUrl = (port) => `ws://localhost:${port}/ws`;
31
+
32
+ // dist/side-panel-toggle.js
33
+ var openWindows = /* @__PURE__ */ new Set();
34
+ var persistOpenWindows = () => {
35
+ chrome.storage.session.set({ [SIDE_PANEL_OPEN_WINDOWS_KEY]: Array.from(openWindows) }).catch(() => {
36
+ });
37
+ };
38
+ var restoreOpenWindows = () => {
39
+ chrome.storage.session.get(SIDE_PANEL_OPEN_WINDOWS_KEY).then((data) => {
40
+ const stored = data[SIDE_PANEL_OPEN_WINDOWS_KEY];
41
+ if (Array.isArray(stored)) {
42
+ for (const id of stored) {
43
+ if (typeof id === "number") {
44
+ openWindows.add(id);
45
+ }
46
+ }
47
+ }
48
+ }).catch(() => {
49
+ });
50
+ };
51
+ var isSidePanelOpen = () => openWindows.size > 0;
52
+ var initSidePanelToggle = () => {
53
+ chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: false }).catch(() => {
54
+ });
55
+ const canToggle = "onOpened" in chrome.sidePanel;
56
+ if (canToggle) {
57
+ restoreOpenWindows();
58
+ chrome.sidePanel.onOpened.addListener(({ windowId }) => {
59
+ openWindows.add(windowId);
60
+ persistOpenWindows();
61
+ });
62
+ chrome.sidePanel.onClosed.addListener(({ windowId }) => {
63
+ openWindows.delete(windowId);
64
+ persistOpenWindows();
65
+ });
66
+ chrome.windows.onRemoved.addListener((windowId) => {
67
+ openWindows.delete(windowId);
68
+ persistOpenWindows();
69
+ });
70
+ }
71
+ chrome.action.onClicked.addListener(({ windowId }) => {
72
+ void (async () => {
73
+ if (canToggle && openWindows.has(windowId)) {
74
+ try {
75
+ await chrome.windows.get(windowId);
76
+ } catch {
77
+ openWindows.delete(windowId);
78
+ persistOpenWindows();
79
+ await chrome.sidePanel.open({ windowId }).catch(() => {
80
+ });
81
+ return;
82
+ }
83
+ chrome.sidePanel.close({ windowId }).catch(() => {
84
+ });
85
+ } else {
86
+ chrome.sidePanel.open({ windowId }).catch(() => {
87
+ });
88
+ }
89
+ })();
90
+ });
91
+ };
92
+
1
93
  // dist/confirmation-badge.js
2
94
  var pendingConfirmationCount = 0;
95
+ var NOTIFICATION_ID = "opentabs-confirmation";
96
+ var pendingConfirmationInfo = /* @__PURE__ */ new Map();
3
97
  var confirmationTimeouts = /* @__PURE__ */ new Map();
4
98
  var clearedConfirmationIds = /* @__PURE__ */ new Set();
5
99
  var CONFIRMATION_BACKGROUND_TIMEOUT_BUFFER_MS = 2e3;
@@ -15,9 +109,34 @@ var updateConfirmationBadge = () => {
15
109
  });
16
110
  }
17
111
  };
112
+ var syncConfirmationNotification = () => {
113
+ if (pendingConfirmationCount <= 0 || isSidePanelOpen()) {
114
+ chrome.notifications.clear(NOTIFICATION_ID).catch(() => {
115
+ });
116
+ return;
117
+ }
118
+ let message;
119
+ if (pendingConfirmationCount === 1 && pendingConfirmationInfo.size === 1) {
120
+ const info = pendingConfirmationInfo.values().next().value;
121
+ message = info.domain ? `${info.tool} on ${info.domain}` : info.tool;
122
+ } else if (pendingConfirmationCount > 1) {
123
+ message = `${pendingConfirmationCount} tools awaiting approval`;
124
+ } else {
125
+ message = "1 tool awaiting approval";
126
+ }
127
+ chrome.notifications.create(NOTIFICATION_ID, {
128
+ type: "basic",
129
+ iconUrl: chrome.runtime.getURL("icons/icon-128.png"),
130
+ title: "OpenTabs \u2014 Approval Required",
131
+ message,
132
+ priority: 2,
133
+ requireInteraction: true
134
+ }).catch(() => {
135
+ });
136
+ };
18
137
  var notifyConfirmationRequest = (params) => {
19
138
  const tool = typeof params.tool === "string" ? params.tool : "unknown tool";
20
- const domain = typeof params.domain === "string" ? params.domain : "unknown domain";
139
+ const domain = typeof params.domain === "string" ? params.domain : null;
21
140
  const id = typeof params.id === "string" ? params.id : String(Date.now());
22
141
  const timeoutMs = typeof params.timeoutMs === "number" ? params.timeoutMs : 0;
23
142
  const existingTimeoutId = confirmationTimeouts.get(id);
@@ -28,6 +147,7 @@ var notifyConfirmationRequest = (params) => {
28
147
  pendingConfirmationCount++;
29
148
  updateConfirmationBadge();
30
149
  }
150
+ pendingConfirmationInfo.set(id, { tool, domain });
31
151
  const effectiveTimeoutMs = timeoutMs > 0 ? timeoutMs : CONFIRMATION_FALLBACK_TIMEOUT_MS;
32
152
  const bgTimeoutId = setTimeout(() => {
33
153
  confirmationTimeouts.delete(id);
@@ -35,15 +155,7 @@ var notifyConfirmationRequest = (params) => {
35
155
  clearedConfirmationIds.delete(id);
36
156
  }, effectiveTimeoutMs + CONFIRMATION_BACKGROUND_TIMEOUT_BUFFER_MS);
37
157
  confirmationTimeouts.set(id, bgTimeoutId);
38
- chrome.notifications.create(`opentabs-confirm-${id}`, {
39
- type: "basic",
40
- iconUrl: chrome.runtime.getURL("icons/icon-128.png"),
41
- title: "OpenTabs: Approval Required",
42
- message: `${tool} on ${domain} \u2014 Click to open side panel`,
43
- priority: 2,
44
- requireInteraction: true
45
- }).catch(() => {
46
- });
158
+ syncConfirmationNotification();
47
159
  };
48
160
  var clearConfirmationBadge = (id) => {
49
161
  if (id !== void 0) {
@@ -51,11 +163,11 @@ var clearConfirmationBadge = (id) => {
51
163
  return;
52
164
  }
53
165
  clearedConfirmationIds.add(id);
54
- chrome.notifications.clear(`opentabs-confirm-${id}`).catch(() => {
55
- });
166
+ pendingConfirmationInfo.delete(id);
56
167
  }
57
168
  pendingConfirmationCount = Math.max(0, pendingConfirmationCount - 1);
58
169
  updateConfirmationBadge();
170
+ syncConfirmationNotification();
59
171
  if (id !== void 0 && !confirmationTimeouts.has(id)) {
60
172
  clearedConfirmationIds.delete(id);
61
173
  }
@@ -69,19 +181,20 @@ var clearConfirmationBackgroundTimeout = (id) => {
69
181
  }
70
182
  };
71
183
  var clearAllConfirmationBadges = () => {
72
- for (const [id, timeoutId] of confirmationTimeouts.entries()) {
184
+ for (const [, timeoutId] of confirmationTimeouts.entries()) {
73
185
  clearTimeout(timeoutId);
74
- chrome.notifications.clear(`opentabs-confirm-${id}`).catch(() => {
75
- });
76
186
  }
77
187
  confirmationTimeouts.clear();
78
188
  clearedConfirmationIds.clear();
189
+ pendingConfirmationInfo.clear();
79
190
  pendingConfirmationCount = 0;
80
191
  updateConfirmationBadge();
192
+ chrome.notifications.clear(NOTIFICATION_ID).catch(() => {
193
+ });
81
194
  };
82
195
  var initConfirmationBadge = () => {
83
196
  chrome.notifications.onClicked.addListener((notificationId) => {
84
- if (notificationId.startsWith("opentabs-confirm-")) {
197
+ if (notificationId === NOTIFICATION_ID) {
85
198
  chrome.windows.getCurrent().then((w) => {
86
199
  if (w.id !== void 0) {
87
200
  chrome.sidePanel.open({ windowId: w.id }).catch(() => {
@@ -95,36 +208,6 @@ var initConfirmationBadge = () => {
95
208
  });
96
209
  };
97
210
 
98
- // dist/constants.js
99
- var KEEPALIVE_ALARM = "opentabs-keepalive";
100
- var KEEPALIVE_INTERVAL_MINUTES = 0.5;
101
- var PLUGINS_META_KEY = "plugins_meta";
102
- var WS_CONNECTED_KEY = "wsConnected";
103
- var SIDE_PANEL_OPEN_WINDOWS_KEY = "sidePanelOpenWindows";
104
- var SCRIPT_TIMEOUT_MS = 25e3;
105
- var MAX_SCRIPT_TIMEOUT_MS = 295e3;
106
- var IS_READY_TIMEOUT_MS = 5e3;
107
- var RELOAD_FLUSH_DELAY_MS = 100;
108
- var INJECTION_RETRY_DELAY_MS = 200;
109
- var SCREENSHOT_RENDER_DELAY_MS = 100;
110
- var WS_FLUSH_DELAY_MS = 50;
111
- var SERVER_PORT_KEY = "serverPort";
112
- var DEFAULT_SERVER_PORT = 9515;
113
- var TEXT_PREVIEW_MAX_LENGTH = 200;
114
- var DEFAULT_WAIT_TIMEOUT_MS = 1e4;
115
- var POLL_INTERVAL_MS = 100;
116
- var DEFAULT_QUERY_LIMIT = 100;
117
- var MAX_INPUT_SIZE = 10 * 1024 * 1024;
118
- var SIDE_PANEL_TIMEOUT_MS = 3e3;
119
- var CDP_VERSION = "1.3";
120
- var EXEC_MAX_ASYNC_WAIT_MS = 1e4;
121
- var EXEC_POLL_INTERVAL_MS = 50;
122
- var EXEC_RESULT_TRUNCATION_LIMIT = 5e4;
123
- var DEFAULT_LOG_LIMIT = 100;
124
- var VALID_PLUGIN_NAME = /^[a-z0-9]+(-[a-z0-9]+)*$/;
125
- var isValidPluginName = (name) => VALID_PLUGIN_NAME.test(name);
126
- var buildWsUrl = (port) => `ws://localhost:${port}/ws`;
127
-
128
211
  // dist/json-rpc-errors.js
129
212
  var JSONRPC_METHOD_NOT_FOUND = -32601;
130
213
  var JSONRPC_INVALID_PARAMS = -32602;
@@ -1185,6 +1268,20 @@ var findAllMatchingTabs = async (plugin) => {
1185
1268
  // dist/tab-state.js
1186
1269
  var serializeTabState = (info) => JSON.stringify({ state: info.state, tabs: info.tabs });
1187
1270
  var lastKnownState = /* @__PURE__ */ new Map();
1271
+ var LAST_KNOWN_STATE_SESSION_KEY = "lastKnownState";
1272
+ var lastKnownStatePersistTimer;
1273
+ var persistLastKnownStateToSession = () => {
1274
+ chrome.storage.session.set({ [LAST_KNOWN_STATE_SESSION_KEY]: Object.fromEntries(lastKnownState) }).catch(() => {
1275
+ });
1276
+ };
1277
+ var scheduleLastKnownStatePersist = () => {
1278
+ if (lastKnownStatePersistTimer !== void 0)
1279
+ clearTimeout(lastKnownStatePersistTimer);
1280
+ lastKnownStatePersistTimer = setTimeout(() => {
1281
+ lastKnownStatePersistTimer = void 0;
1282
+ persistLastKnownStateToSession();
1283
+ }, 500);
1284
+ };
1188
1285
  var pluginLocks = /* @__PURE__ */ new Map();
1189
1286
  var withPluginLock = (pluginName, fn) => {
1190
1287
  const prev = pluginLocks.get(pluginName) ?? Promise.resolve();
@@ -1281,15 +1378,20 @@ var sendTabSyncAll = async () => {
1281
1378
  pluginNamesInSync.add(pluginName);
1282
1379
  return withPluginLock(pluginName, () => {
1283
1380
  lastKnownState.set(pluginName, serializeTabState(stateInfo));
1381
+ scheduleLastKnownStatePersist();
1284
1382
  return Promise.resolve();
1285
1383
  });
1286
1384
  }));
1385
+ let removedStale = false;
1287
1386
  for (const key of lastKnownState.keys()) {
1288
1387
  if (!pluginNamesInSync.has(key)) {
1289
1388
  lastKnownState.delete(key);
1290
1389
  pluginLocks.delete(key);
1390
+ removedStale = true;
1291
1391
  }
1292
1392
  }
1393
+ if (removedStale)
1394
+ scheduleLastKnownStatePersist();
1293
1395
  sendToServer({
1294
1396
  jsonrpc: "2.0",
1295
1397
  method: "tab.syncAll",
@@ -1309,16 +1411,40 @@ var sendTabSyncAll = async () => {
1309
1411
  var clearTabStateCache = () => {
1310
1412
  lastKnownState.clear();
1311
1413
  pluginLocks.clear();
1414
+ if (lastKnownStatePersistTimer !== void 0) {
1415
+ clearTimeout(lastKnownStatePersistTimer);
1416
+ lastKnownStatePersistTimer = void 0;
1417
+ }
1418
+ chrome.storage.session.remove(LAST_KNOWN_STATE_SESSION_KEY).catch(() => {
1419
+ });
1312
1420
  };
1313
1421
  var clearPluginTabState = (pluginName) => {
1422
+ const had = lastKnownState.has(pluginName);
1314
1423
  lastKnownState.delete(pluginName);
1315
1424
  pluginLocks.delete(pluginName);
1425
+ if (had)
1426
+ scheduleLastKnownStatePersist();
1316
1427
  };
1317
1428
  var updateLastKnownState = (pluginName, stateInfo) => withPluginLock(pluginName, () => {
1318
1429
  lastKnownState.set(pluginName, serializeTabState(stateInfo));
1430
+ scheduleLastKnownStatePersist();
1319
1431
  return Promise.resolve();
1320
1432
  });
1321
1433
  var getLastKnownStates = () => lastKnownState;
1434
+ var loadLastKnownStateFromSession = async () => {
1435
+ try {
1436
+ const data = await chrome.storage.session.get(LAST_KNOWN_STATE_SESSION_KEY);
1437
+ const stored = data[LAST_KNOWN_STATE_SESSION_KEY];
1438
+ if (stored && typeof stored === "object" && !Array.isArray(stored)) {
1439
+ for (const [key, value] of Object.entries(stored)) {
1440
+ if (typeof value === "string") {
1441
+ lastKnownState.set(key, value);
1442
+ }
1443
+ }
1444
+ }
1445
+ } catch {
1446
+ }
1447
+ };
1322
1448
  var getAggregateState = (serialized) => {
1323
1449
  try {
1324
1450
  const parsed = JSON.parse(serialized);
@@ -1335,6 +1461,7 @@ var notifyAffectedPlugins = async (affectedPlugins) => {
1335
1461
  if (previous === serialized)
1336
1462
  return;
1337
1463
  lastKnownState.set(plugin.name, serialized);
1464
+ scheduleLastKnownStatePersist();
1338
1465
  sendTabStateNotification(plugin.name, newState);
1339
1466
  })));
1340
1467
  };
@@ -1382,6 +1509,46 @@ var checkTabChanged = async (changedTabId, changeInfo) => {
1382
1509
  return;
1383
1510
  await notifyAffectedPlugins(affectedPlugins);
1384
1511
  };
1512
+ var readinessPollTimer;
1513
+ var readinessPollRunning = false;
1514
+ var runReadinessPoll = async () => {
1515
+ if (readinessPollRunning)
1516
+ return;
1517
+ readinessPollRunning = true;
1518
+ try {
1519
+ const index = await getAllPluginMeta();
1520
+ const plugins = Object.values(index);
1521
+ if (plugins.length === 0)
1522
+ return;
1523
+ const activePlugins = plugins.filter((p) => {
1524
+ const cached = lastKnownState.get(p.name);
1525
+ return cached !== void 0 && getAggregateState(cached) !== "closed";
1526
+ });
1527
+ if (activePlugins.length === 0)
1528
+ return;
1529
+ await notifyAffectedPlugins(activePlugins);
1530
+ } catch (err2) {
1531
+ console.warn("[opentabs] readiness poll failed:", err2);
1532
+ } finally {
1533
+ readinessPollRunning = false;
1534
+ }
1535
+ };
1536
+ var startReadinessPoll = () => {
1537
+ if (readinessPollTimer !== void 0)
1538
+ return;
1539
+ readinessPollTimer = setInterval(() => {
1540
+ runReadinessPoll().catch((err2) => {
1541
+ console.warn("[opentabs] readiness poll tick failed:", err2);
1542
+ });
1543
+ }, READINESS_POLL_INTERVAL_MS);
1544
+ };
1545
+ var stopReadinessPoll = () => {
1546
+ if (readinessPollTimer !== void 0) {
1547
+ clearInterval(readinessPollTimer);
1548
+ readinessPollTimer = void 0;
1549
+ }
1550
+ readinessPollRunning = false;
1551
+ };
1385
1552
 
1386
1553
  // dist/browser-commands/extension-commands.js
1387
1554
  var handleExtensionGetState = async (id) => {
@@ -3309,6 +3476,106 @@ var handlePromptGet = async (params, id) => {
3309
3476
  }
3310
3477
  };
3311
3478
 
3479
+ // dist/server-request.js
3480
+ var REQUEST_TIMEOUT_MS = 3e4;
3481
+ var pendingRequests = /* @__PURE__ */ new Map();
3482
+ var nextId = 1;
3483
+ var sendServerRequest = (method, params = {}) => {
3484
+ const id = nextId++;
3485
+ const data = { jsonrpc: "2.0", method, params, id };
3486
+ return new Promise((resolve, reject) => {
3487
+ const timerId = setTimeout(() => {
3488
+ if (pendingRequests.has(id)) {
3489
+ pendingRequests.delete(id);
3490
+ reject(new Error(`Request ${method} timed out after ${REQUEST_TIMEOUT_MS}ms`));
3491
+ }
3492
+ }, REQUEST_TIMEOUT_MS);
3493
+ pendingRequests.set(id, { resolve, reject, timerId });
3494
+ sendToServer(data);
3495
+ });
3496
+ };
3497
+ var consumeServerResponse = (data) => {
3498
+ if (data.method !== void 0)
3499
+ return false;
3500
+ const rawId = data.id;
3501
+ if (rawId === void 0 || rawId === null)
3502
+ return false;
3503
+ const id = typeof rawId === "number" ? rawId : void 0;
3504
+ if (id === void 0)
3505
+ return false;
3506
+ const pending = pendingRequests.get(id);
3507
+ if (!pending)
3508
+ return false;
3509
+ pendingRequests.delete(id);
3510
+ clearTimeout(pending.timerId);
3511
+ if ("error" in data) {
3512
+ const err2 = data.error;
3513
+ pending.reject(new Error(err2.message ?? "Unknown server error"));
3514
+ } else {
3515
+ pending.resolve(data.result);
3516
+ }
3517
+ return true;
3518
+ };
3519
+ var rejectAllPendingServerRequests = () => {
3520
+ for (const [id, pending] of pendingRequests) {
3521
+ pendingRequests.delete(id);
3522
+ clearTimeout(pending.timerId);
3523
+ pending.reject(new Error("Server disconnected"));
3524
+ }
3525
+ };
3526
+
3527
+ // dist/server-state-cache.js
3528
+ var SESSION_KEY = "serverStateCache";
3529
+ var EMPTY_CACHE = {
3530
+ plugins: [],
3531
+ failedPlugins: [],
3532
+ browserTools: [],
3533
+ serverVersion: void 0
3534
+ };
3535
+ var cache = { ...EMPTY_CACHE };
3536
+ var persistTimer;
3537
+ var persistToSession = () => {
3538
+ chrome.storage.session.set({ [SESSION_KEY]: cache }).catch(() => {
3539
+ });
3540
+ };
3541
+ var schedulePersist = () => {
3542
+ if (persistTimer !== void 0)
3543
+ clearTimeout(persistTimer);
3544
+ persistTimer = setTimeout(() => {
3545
+ persistTimer = void 0;
3546
+ persistToSession();
3547
+ }, 500);
3548
+ };
3549
+ var getServerStateCache = () => cache;
3550
+ var updateServerStateCache = (partial) => {
3551
+ cache = { ...cache, ...partial };
3552
+ schedulePersist();
3553
+ };
3554
+ var clearServerStateCache = () => {
3555
+ cache = { ...EMPTY_CACHE };
3556
+ if (persistTimer !== void 0) {
3557
+ clearTimeout(persistTimer);
3558
+ persistTimer = void 0;
3559
+ }
3560
+ chrome.storage.session.remove(SESSION_KEY).catch(() => {
3561
+ });
3562
+ };
3563
+ var loadServerStateCacheFromSession = async () => {
3564
+ try {
3565
+ const data = await chrome.storage.session.get(SESSION_KEY);
3566
+ const stored = data[SESSION_KEY];
3567
+ if (stored && typeof stored === "object") {
3568
+ cache = {
3569
+ plugins: Array.isArray(stored.plugins) ? stored.plugins : [],
3570
+ failedPlugins: Array.isArray(stored.failedPlugins) ? stored.failedPlugins : [],
3571
+ browserTools: Array.isArray(stored.browserTools) ? stored.browserTools : [],
3572
+ serverVersion: typeof stored.serverVersion === "string" ? stored.serverVersion : void 0
3573
+ };
3574
+ }
3575
+ } catch {
3576
+ }
3577
+ };
3578
+
3312
3579
  // dist/tool-dispatch.js
3313
3580
  var progressCallbacks = /* @__PURE__ */ new Map();
3314
3581
  var notifyDispatchProgress = (dispatchId) => {
@@ -3707,11 +3974,45 @@ var handleSyncFull = async (params) => {
3707
3974
  console.warn("[opentabs] Plugin injection failed during sync.full:", result.reason);
3708
3975
  }
3709
3976
  }
3710
- await sendTabSyncAll();
3977
+ const rawPluginMap = /* @__PURE__ */ new Map();
3978
+ for (const raw of rawPlugins) {
3979
+ if (typeof raw === "object" && raw !== null && typeof raw.name === "string") {
3980
+ rawPluginMap.set(raw.name, raw);
3981
+ }
3982
+ }
3983
+ const cachePlugins = uniquePlugins.map((p) => {
3984
+ const raw = rawPluginMap.get(p.name);
3985
+ return {
3986
+ name: p.name,
3987
+ displayName: p.displayName,
3988
+ version: p.version,
3989
+ trustTier: p.trustTier,
3990
+ source: raw?.source === "npm" || raw?.source === "local" ? raw.source : "local",
3991
+ tabState: "closed",
3992
+ urlPatterns: p.urlPatterns,
3993
+ tools: p.tools,
3994
+ iconSvg: p.iconSvg,
3995
+ iconInactiveSvg: p.iconInactiveSvg,
3996
+ ...typeof raw?.sdkVersion === "string" ? { sdkVersion: raw.sdkVersion } : {},
3997
+ ...raw?.update && typeof raw.update === "object" ? { update: raw.update } : {}
3998
+ };
3999
+ });
4000
+ const rawFailedPlugins = Array.isArray(params.failedPlugins) ? params.failedPlugins : void 0;
4001
+ const rawBrowserTools = Array.isArray(params.browserTools) ? params.browserTools : void 0;
4002
+ const rawServerVersion = typeof params.serverVersion === "string" ? params.serverVersion : void 0;
4003
+ updateServerStateCache({
4004
+ plugins: cachePlugins,
4005
+ ...rawFailedPlugins ? { failedPlugins: rawFailedPlugins } : {},
4006
+ ...rawBrowserTools ? { browserTools: rawBrowserTools } : {},
4007
+ ...rawServerVersion !== void 0 ? { serverVersion: rawServerVersion } : {}
4008
+ });
3711
4009
  forwardToSidePanel({
3712
4010
  type: "sp:serverMessage",
3713
4011
  data: { jsonrpc: "2.0", method: "plugins.changed" }
3714
4012
  });
4013
+ void sendTabSyncAll().then(() => {
4014
+ startReadinessPoll();
4015
+ });
3715
4016
  };
3716
4017
  var handlePluginUpdate = async (params) => {
3717
4018
  const validated = validatePluginPayload(params);
@@ -3723,6 +4024,23 @@ var handlePluginUpdate = async (params) => {
3723
4024
  const newState = await computePluginTabState(meta);
3724
4025
  await updateLastKnownState(meta.name, newState);
3725
4026
  sendTabStateNotification(meta.name, newState);
4027
+ const existingCache = getServerStateCache();
4028
+ const updatedPlugin = {
4029
+ name: validated.name,
4030
+ displayName: validated.displayName,
4031
+ version: validated.version,
4032
+ trustTier: validated.trustTier,
4033
+ source: params.source === "npm" || params.source === "local" ? params.source : "local",
4034
+ tabState: newState.state,
4035
+ urlPatterns: validated.urlPatterns,
4036
+ tools: validated.tools,
4037
+ iconSvg: validated.iconSvg,
4038
+ iconInactiveSvg: validated.iconInactiveSvg,
4039
+ ...typeof params.sdkVersion === "string" ? { sdkVersion: params.sdkVersion } : {},
4040
+ ...params.update && typeof params.update === "object" ? { update: params.update } : {}
4041
+ };
4042
+ const otherPlugins = existingCache.plugins.filter((p) => p.name !== validated.name);
4043
+ updateServerStateCache({ plugins: [...otherPlugins, updatedPlugin] });
3726
4044
  forwardToSidePanel({
3727
4045
  type: "sp:serverMessage",
3728
4046
  data: { jsonrpc: "2.0", method: "plugins.changed" }
@@ -3763,6 +4081,18 @@ var handlePluginUninstall = async (params, id) => {
3763
4081
  id
3764
4082
  });
3765
4083
  };
4084
+ var handleExtensionGetTabState = (_params, id) => {
4085
+ const states = getLastKnownStates();
4086
+ const tabStates = {};
4087
+ for (const [pluginName, serialized] of states) {
4088
+ try {
4089
+ tabStates[pluginName] = JSON.parse(serialized);
4090
+ } catch {
4091
+ tabStates[pluginName] = { state: "closed", tabs: [] };
4092
+ }
4093
+ }
4094
+ sendToServer({ jsonrpc: "2.0", result: { tabStates }, id });
4095
+ };
3766
4096
  var methodHandlers = /* @__PURE__ */ new Map([
3767
4097
  ["extension.reload", handleExtensionReload],
3768
4098
  ["sync.full", wrapNotification("sync.full", handleSyncFull)],
@@ -3809,14 +4139,26 @@ var methodHandlers = /* @__PURE__ */ new Map([
3809
4139
  [
3810
4140
  "extension.forceReconnect",
3811
4141
  wrapAsync("extension.forceReconnect", (_params, id) => handleExtensionForceReconnect(id))
3812
- ]
4142
+ ],
4143
+ ["extension.getTabState", wrapSync("extension.getTabState", handleExtensionGetTabState)]
3813
4144
  ]);
3814
4145
  var handleServerMessage = (message) => {
3815
4146
  const method = message.method;
3816
4147
  const id = message.id;
3817
4148
  const params = message.params ?? {};
3818
- const isResponse = id !== void 0 && !method;
3819
- if (isResponse || method && SIDE_PANEL_METHODS.has(method)) {
4149
+ if (!method && consumeServerResponse(message)) {
4150
+ return;
4151
+ }
4152
+ if (method === "plugins.changed") {
4153
+ const payload = params;
4154
+ updateServerStateCache({
4155
+ ...payload.plugins ? { plugins: payload.plugins } : {},
4156
+ ...payload.failedPlugins ? { failedPlugins: payload.failedPlugins } : {},
4157
+ ...payload.browserTools ? { browserTools: payload.browserTools } : {},
4158
+ ...payload.serverVersion !== void 0 ? { serverVersion: payload.serverVersion } : {}
4159
+ });
4160
+ }
4161
+ if (method && SIDE_PANEL_METHODS.has(method)) {
3820
4162
  forwardToSidePanel({ type: "sp:serverMessage", data: message });
3821
4163
  }
3822
4164
  if (method === "confirmation.request") {
@@ -3888,7 +4230,10 @@ var handleWsState = (message, sendResponse) => {
3888
4230
  }
3889
4231
  });
3890
4232
  if (!nowConnected) {
4233
+ stopReadinessPoll();
3891
4234
  clearTabStateCache();
4235
+ clearServerStateCache();
4236
+ rejectAllPendingServerRequests();
3892
4237
  clearAllConfirmationBadges();
3893
4238
  }
3894
4239
  sendResponse({ ok: true });
@@ -3897,16 +4242,78 @@ var handleWsMessage = (message, sendResponse) => {
3897
4242
  handleServerMessage(message.data);
3898
4243
  sendResponse({ ok: true });
3899
4244
  };
3900
- var handleBgSend = (message, sendResponse) => {
3901
- sendToServer(message.data);
3902
- sendResponse({ ok: true });
3903
- };
3904
4245
  var handleBgGetConnectionState = (_message, sendResponse) => {
3905
4246
  sendResponse({
3906
4247
  connected: wsConnected,
3907
4248
  disconnectReason: wsConnected ? void 0 : lastDisconnectReason
3908
4249
  });
3909
4250
  };
4251
+ var handleBgGetFullState = (_message, sendResponse) => {
4252
+ (async () => {
4253
+ let tabStates = getLastKnownStates();
4254
+ let serverCache = getServerStateCache();
4255
+ if (wsConnected && tabStates.size === 0 && serverCache.plugins.length === 0) {
4256
+ await Promise.all([loadLastKnownStateFromSession(), loadServerStateCacheFromSession()]);
4257
+ tabStates = getLastKnownStates();
4258
+ serverCache = getServerStateCache();
4259
+ }
4260
+ const metaIndex = await getAllPluginMeta();
4261
+ const serverPluginMap = /* @__PURE__ */ new Map();
4262
+ for (const sp of serverCache.plugins) {
4263
+ serverPluginMap.set(sp.name, sp);
4264
+ }
4265
+ const plugins = Object.values(metaIndex).map((meta) => {
4266
+ const serverPlugin = serverPluginMap.get(meta.name);
4267
+ let tabState = "closed";
4268
+ const serialized = tabStates.get(meta.name);
4269
+ if (serialized) {
4270
+ try {
4271
+ const parsed = JSON.parse(serialized);
4272
+ tabState = parsed.state;
4273
+ } catch {
4274
+ }
4275
+ }
4276
+ const tools = meta.tools.map((metaTool) => {
4277
+ const serverTool = serverPlugin?.tools.find((st) => st.name === metaTool.name);
4278
+ return {
4279
+ ...metaTool,
4280
+ enabled: serverTool?.enabled ?? true
4281
+ };
4282
+ });
4283
+ return {
4284
+ name: meta.name,
4285
+ displayName: meta.displayName,
4286
+ version: meta.version,
4287
+ trustTier: meta.trustTier,
4288
+ urlPatterns: meta.urlPatterns,
4289
+ iconSvg: meta.iconSvg,
4290
+ iconInactiveSvg: meta.iconInactiveSvg,
4291
+ tools,
4292
+ tabState,
4293
+ source: serverPlugin?.source ?? "local",
4294
+ sdkVersion: serverPlugin?.sdkVersion,
4295
+ update: serverPlugin?.update
4296
+ };
4297
+ });
4298
+ sendResponse({
4299
+ connected: wsConnected,
4300
+ disconnectReason: wsConnected ? void 0 : lastDisconnectReason,
4301
+ plugins,
4302
+ failedPlugins: serverCache.failedPlugins,
4303
+ browserTools: serverCache.browserTools,
4304
+ serverVersion: serverCache.serverVersion
4305
+ });
4306
+ })().catch(() => {
4307
+ sendResponse({
4308
+ connected: wsConnected,
4309
+ disconnectReason: wsConnected ? void 0 : lastDisconnectReason,
4310
+ plugins: [],
4311
+ failedPlugins: [],
4312
+ browserTools: [],
4313
+ serverVersion: void 0
4314
+ });
4315
+ });
4316
+ };
3910
4317
  var handlePluginLogs = (message, sendResponse) => {
3911
4318
  const entries = message.entries;
3912
4319
  if (wsConnected && Array.isArray(entries)) {
@@ -3975,6 +4382,117 @@ var handleSpConfirmationTimeout = (message, sendResponse) => {
3975
4382
  clearConfirmationBadge(id);
3976
4383
  sendResponse({ ok: true });
3977
4384
  };
4385
+ var handleBgSetToolEnabled = (message, sendResponse) => {
4386
+ const plugin = message.plugin;
4387
+ const tool = message.tool;
4388
+ const enabled = message.enabled;
4389
+ const cache2 = getServerStateCache();
4390
+ const updatedPlugins = cache2.plugins.map((p) => {
4391
+ if (p.name !== plugin)
4392
+ return p;
4393
+ return {
4394
+ ...p,
4395
+ tools: p.tools.map((t) => t.name === tool ? { ...t, enabled } : t)
4396
+ };
4397
+ });
4398
+ updateServerStateCache({ plugins: updatedPlugins });
4399
+ sendServerRequest("config.setToolEnabled", { plugin, tool, enabled }).then((result) => {
4400
+ sendResponse(result);
4401
+ }).catch((err2) => {
4402
+ const revertCache = getServerStateCache();
4403
+ const revertPlugins = revertCache.plugins.map((p) => {
4404
+ if (p.name !== plugin)
4405
+ return p;
4406
+ return {
4407
+ ...p,
4408
+ tools: p.tools.map((t) => t.name === tool ? { ...t, enabled: !enabled } : t)
4409
+ };
4410
+ });
4411
+ updateServerStateCache({ plugins: revertPlugins });
4412
+ sendResponse({ error: err2 instanceof Error ? err2.message : String(err2) });
4413
+ });
4414
+ };
4415
+ var handleBgSetAllToolsEnabled = (message, sendResponse) => {
4416
+ const plugin = message.plugin;
4417
+ const enabled = message.enabled;
4418
+ const cache2 = getServerStateCache();
4419
+ const originalPlugins = cache2.plugins;
4420
+ const updatedPlugins = cache2.plugins.map((p) => {
4421
+ if (p.name !== plugin)
4422
+ return p;
4423
+ return {
4424
+ ...p,
4425
+ tools: p.tools.map((t) => ({ ...t, enabled }))
4426
+ };
4427
+ });
4428
+ updateServerStateCache({ plugins: updatedPlugins });
4429
+ sendServerRequest("config.setAllToolsEnabled", { plugin, enabled }).then((result) => {
4430
+ sendResponse(result);
4431
+ }).catch((err2) => {
4432
+ updateServerStateCache({ plugins: originalPlugins });
4433
+ sendResponse({ error: err2 instanceof Error ? err2.message : String(err2) });
4434
+ });
4435
+ };
4436
+ var handleBgSetBrowserToolEnabled = (message, sendResponse) => {
4437
+ const tool = message.tool;
4438
+ const enabled = message.enabled;
4439
+ const cache2 = getServerStateCache();
4440
+ const updatedBrowserTools = cache2.browserTools.map((bt) => bt.name === tool ? { ...bt, enabled } : bt);
4441
+ updateServerStateCache({ browserTools: updatedBrowserTools });
4442
+ sendServerRequest("config.setBrowserToolEnabled", { tool, enabled }).then((result) => {
4443
+ sendResponse(result);
4444
+ }).catch((err2) => {
4445
+ const revertCache = getServerStateCache();
4446
+ const revertBrowserTools = revertCache.browserTools.map((bt) => bt.name === tool ? { ...bt, enabled: !enabled } : bt);
4447
+ updateServerStateCache({ browserTools: revertBrowserTools });
4448
+ sendResponse({ error: err2 instanceof Error ? err2.message : String(err2) });
4449
+ });
4450
+ };
4451
+ var handleBgSetAllBrowserToolsEnabled = (message, sendResponse) => {
4452
+ const enabled = message.enabled;
4453
+ const cache2 = getServerStateCache();
4454
+ const originalBrowserTools = cache2.browserTools;
4455
+ const updatedBrowserTools = cache2.browserTools.map((bt) => ({ ...bt, enabled }));
4456
+ updateServerStateCache({ browserTools: updatedBrowserTools });
4457
+ sendServerRequest("config.setAllBrowserToolsEnabled", { enabled }).then((result) => {
4458
+ sendResponse(result);
4459
+ }).catch((err2) => {
4460
+ updateServerStateCache({ browserTools: originalBrowserTools });
4461
+ sendResponse({ error: err2 instanceof Error ? err2.message : String(err2) });
4462
+ });
4463
+ };
4464
+ var handleBgSearchPlugins = (message, sendResponse) => {
4465
+ const query = message.query;
4466
+ sendServerRequest("plugin.search", { query }).then((result) => {
4467
+ sendResponse(result);
4468
+ }).catch((err2) => {
4469
+ sendResponse({ error: err2 instanceof Error ? err2.message : String(err2) });
4470
+ });
4471
+ };
4472
+ var handleBgInstallPlugin = (message, sendResponse) => {
4473
+ const name = message.name;
4474
+ sendServerRequest("plugin.install", { name }).then((result) => {
4475
+ sendResponse(result);
4476
+ }).catch((err2) => {
4477
+ sendResponse({ error: err2 instanceof Error ? err2.message : String(err2) });
4478
+ });
4479
+ };
4480
+ var handleBgRemovePlugin = (message, sendResponse) => {
4481
+ const name = message.name;
4482
+ sendServerRequest("plugin.remove", { name }).then((result) => {
4483
+ sendResponse(result);
4484
+ }).catch((err2) => {
4485
+ sendResponse({ error: err2 instanceof Error ? err2.message : String(err2) });
4486
+ });
4487
+ };
4488
+ var handleBgUpdatePlugin = (message, sendResponse) => {
4489
+ const name = message.name;
4490
+ sendServerRequest("plugin.updateFromRegistry", { name }).then((result) => {
4491
+ sendResponse(result);
4492
+ }).catch((err2) => {
4493
+ sendResponse({ error: err2 instanceof Error ? err2.message : String(err2) });
4494
+ });
4495
+ };
3978
4496
  var handlePortChanged = (message, sendResponse) => {
3979
4497
  chrome.runtime.sendMessage(message).catch(() => {
3980
4498
  });
@@ -3984,8 +4502,16 @@ var backgroundHandlers = /* @__PURE__ */ new Map([
3984
4502
  ["offscreen:getUrl", handleOffscreenGetUrl],
3985
4503
  ["ws:state", handleWsState],
3986
4504
  ["ws:message", handleWsMessage],
3987
- ["bg:send", handleBgSend],
3988
4505
  ["bg:getConnectionState", handleBgGetConnectionState],
4506
+ ["bg:getFullState", handleBgGetFullState],
4507
+ ["bg:setToolEnabled", handleBgSetToolEnabled],
4508
+ ["bg:setAllToolsEnabled", handleBgSetAllToolsEnabled],
4509
+ ["bg:setBrowserToolEnabled", handleBgSetBrowserToolEnabled],
4510
+ ["bg:setAllBrowserToolsEnabled", handleBgSetAllBrowserToolsEnabled],
4511
+ ["bg:searchPlugins", handleBgSearchPlugins],
4512
+ ["bg:installPlugin", handleBgInstallPlugin],
4513
+ ["bg:removePlugin", handleBgRemovePlugin],
4514
+ ["bg:updatePlugin", handleBgUpdatePlugin],
3989
4515
  ["plugin:logs", handlePluginLogs],
3990
4516
  ["tool:progress", handleToolProgress],
3991
4517
  ["sp:confirmationResponse", handleSpConfirmationResponse],
@@ -3996,8 +4522,16 @@ var EXTENSION_ONLY_TYPES = /* @__PURE__ */ new Set([
3996
4522
  "offscreen:getUrl",
3997
4523
  "ws:state",
3998
4524
  "ws:message",
3999
- "bg:send",
4000
4525
  "bg:getConnectionState",
4526
+ "bg:getFullState",
4527
+ "bg:setToolEnabled",
4528
+ "bg:setAllToolsEnabled",
4529
+ "bg:setBrowserToolEnabled",
4530
+ "bg:setAllBrowserToolsEnabled",
4531
+ "bg:searchPlugins",
4532
+ "bg:installPlugin",
4533
+ "bg:removePlugin",
4534
+ "bg:updatePlugin",
4001
4535
  "offscreen:getLogs",
4002
4536
  "sp:confirmationResponse",
4003
4537
  "sp:confirmationTimeout",
@@ -4019,66 +4553,6 @@ var initBackgroundMessageHandlers = () => {
4019
4553
  };
4020
4554
  var backgroundHandlerNames = [...backgroundHandlers.keys()];
4021
4555
 
4022
- // dist/side-panel-toggle.js
4023
- var openWindows = /* @__PURE__ */ new Set();
4024
- var persistOpenWindows = () => {
4025
- chrome.storage.session.set({ [SIDE_PANEL_OPEN_WINDOWS_KEY]: Array.from(openWindows) }).catch(() => {
4026
- });
4027
- };
4028
- var restoreOpenWindows = () => {
4029
- chrome.storage.session.get(SIDE_PANEL_OPEN_WINDOWS_KEY).then((data) => {
4030
- const stored = data[SIDE_PANEL_OPEN_WINDOWS_KEY];
4031
- if (Array.isArray(stored)) {
4032
- for (const id of stored) {
4033
- if (typeof id === "number") {
4034
- openWindows.add(id);
4035
- }
4036
- }
4037
- }
4038
- }).catch(() => {
4039
- });
4040
- };
4041
- var initSidePanelToggle = () => {
4042
- chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: false }).catch(() => {
4043
- });
4044
- const canToggle = "onOpened" in chrome.sidePanel;
4045
- if (canToggle) {
4046
- restoreOpenWindows();
4047
- chrome.sidePanel.onOpened.addListener(({ windowId }) => {
4048
- openWindows.add(windowId);
4049
- persistOpenWindows();
4050
- });
4051
- chrome.sidePanel.onClosed.addListener(({ windowId }) => {
4052
- openWindows.delete(windowId);
4053
- persistOpenWindows();
4054
- });
4055
- chrome.windows.onRemoved.addListener((windowId) => {
4056
- openWindows.delete(windowId);
4057
- persistOpenWindows();
4058
- });
4059
- }
4060
- chrome.action.onClicked.addListener(({ windowId }) => {
4061
- void (async () => {
4062
- if (canToggle && openWindows.has(windowId)) {
4063
- try {
4064
- await chrome.windows.get(windowId);
4065
- } catch {
4066
- openWindows.delete(windowId);
4067
- persistOpenWindows();
4068
- await chrome.sidePanel.open({ windowId }).catch(() => {
4069
- });
4070
- return;
4071
- }
4072
- chrome.sidePanel.close({ windowId }).catch(() => {
4073
- });
4074
- } else {
4075
- chrome.sidePanel.open({ windowId }).catch(() => {
4076
- });
4077
- }
4078
- })();
4079
- });
4080
- };
4081
-
4082
4556
  // dist/background.js
4083
4557
  initSidePanelToggle();
4084
4558
  restoreWsConnectedState();
@@ -4108,7 +4582,9 @@ var setupKeepaliveAlarm = async () => {
4108
4582
  }
4109
4583
  };
4110
4584
  chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
4111
- if (changeInfo.status === "complete" && tab.url) {
4585
+ if (changeInfo.status === "loading" && tab.url) {
4586
+ injectPluginsIntoTab(tabId, tab.url).catch((err2) => console.warn("[opentabs] early tab injection failed:", err2));
4587
+ } else if (changeInfo.status === "complete" && tab.url) {
4112
4588
  injectPluginsIntoTab(tabId, tab.url).then(() => checkTabChanged(tabId, changeInfo)).catch((err2) => console.warn("[opentabs] tab injection failed:", err2));
4113
4589
  } else if (changeInfo.url) {
4114
4590
  checkTabChanged(tabId, changeInfo).catch((err2) => console.warn("[opentabs] tab state check failed:", err2));