@vacantthinker/firefox-addon-framework-easy 2026.609.1038 → 2026.610.749

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.
package/README.md CHANGED
@@ -69,13 +69,16 @@ export function browserRuntimeOnMessageCommon(act, message, sendResponse) { }
69
69
 
70
70
  ### 📄 File: `src/browserTab.js`
71
71
  ```javascript
72
- export async function browserTabSendMessage(tabId, message) { }
72
+ export async function browserTabSendMessage(
73
+ tabId,
74
+ message,
75
+ ) { }
73
76
 
74
77
  export function browserTabWaitReloadThenSendMessageToContentJs(message) { }
75
78
 
76
79
  export async function browserTabCreateToDownload(message) { }
77
80
 
78
- export async function browserTabCreateNearSendMessageToContentJs(message) { }
81
+ export async function browserTabCreateNearSendMessageToContentJs(message = { }
79
82
 
80
83
  export function browserTabWaitReloadThenRemoveIt({ }
81
84
 
@@ -89,12 +92,6 @@ export class DomainORM extends BaseORM { }
89
92
 
90
93
  ### 📄 File: `src/generate.js`
91
94
  ```javascript
92
- export async function generateHtmlByUserSettings(
93
- userSettings,
94
- radioItemClickCallback,
95
- checkboxItemClickCallback,
96
- ) { }
97
-
98
95
  export function generateMkvScriptForSystemWindows({ }
99
96
 
100
97
  export function generateMkvScriptForSystemFedora({ }
@@ -121,7 +118,7 @@ export async function stoOpSetNull(k) { }
121
118
 
122
119
  ### 📄 File: `src/opTab.js`
123
120
  ```javascript
124
- export async function tabOpEnhance(tab) { }
121
+ export function tabOpEnhance(tab) { }
125
122
 
126
123
  export async function tabOpCreate(properties) { }
127
124
 
@@ -131,31 +128,40 @@ export async function tabOpCreateActiveFalse(properties) { }
131
128
 
132
129
  export async function tabOpCreateByWindow(url) { }
133
130
 
134
- export async function tabOpGet(tabId) { }
131
+ export function tabOpGet(tabId) { }
135
132
 
136
- export async function tabOpQueryAll() { }
133
+ export function tabOpQueryAll() { }
137
134
 
138
135
  export async function tabOpQueryUrl(urlQuery) { }
139
136
 
140
137
  export async function tabOpQueryUrlThenRemove(urlQuery) { }
141
138
 
142
- export async function tabOpReload(tabId) { }
139
+ export function tabOpReload(tabId) { }
143
140
 
144
- export async function tabOpReloadByPassCacheTrue(tabId) { }
141
+ export function tabOpReloadByPassCacheTrue(tabId) { }
145
142
 
146
- export async function tabOpRemove(tabId) { }
143
+ export function tabOpRemove(tabId) { }
147
144
 
148
- export async function tabOpHide(tabId) { }
145
+ export function tabOpHide(tabId) { }
149
146
 
150
- export async function tabOpUpdate(tabId, updateProperties) { }
147
+ export async function tabOpUpdate(
148
+ tabId,
149
+ updateProperties,
150
+ ) { }
151
151
 
152
- export async function tabOpUpdateActiveFalse(tabId) { }
152
+ export function tabOpUpdateActiveFalse(tabId) { }
153
153
 
154
154
  export async function tabOpFocus(tabId) { }
155
155
 
156
- export async function tabOpInsertCssCode(tabId, code) { }
156
+ export function tabOpInsertCssCode(
157
+ tabId,
158
+ code,
159
+ ) { }
157
160
 
158
- export async function tabOpRemoveCssCode(tabId, code) { }
161
+ export function tabOpRemoveCssCode(
162
+ tabId,
163
+ code,
164
+ ) { }
159
165
 
160
166
  ```
161
167
 
@@ -227,11 +233,3 @@ export async function serviceUpdataALLNodeBackgroundColor(message) { }
227
233
 
228
234
  ```
229
235
 
230
- ### 📄 File: `src/serviceUserSettings.js`
231
- ```javascript
232
- export async function serviceInitUserSettings(userSettings) { }
233
-
234
- export async function serviceGetUserSettings(userSettings) { }
235
-
236
- ```
237
-
package/index.js CHANGED
@@ -15,4 +15,3 @@ export * from './src/serviceOpContent.js'
15
15
  export * from './src/serviceOpJavascript.js'
16
16
  export * from './src/servicePure.js'
17
17
  export * from './src/serviceUpdateTabStyle.js'
18
- export * from './src/serviceUserSettings.js'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vacantthinker/firefox-addon-framework-easy",
3
- "version": "2026.0609.1038",
3
+ "version": "2026.0610.0749",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "publishConfig": {
package/src/browserTab.js CHANGED
@@ -1,16 +1,21 @@
1
1
  // @ts-check
2
2
 
3
- import {browserNotificationCreate} from './browserNotification.js';
4
3
  import {tabOpCreateNear, tabOpGet, tabOpRemove} from './opTab.js';
5
4
 
6
- export async function browserTabSendMessage(tabId, message) {
5
+ export async function browserTabSendMessage(
6
+ tabId,
7
+ message,
8
+ ) {
7
9
  await browser.tabs.sendMessage(tabId, message);
8
10
  }
9
11
 
10
12
  export function browserTabWaitReloadThenSendMessageToContentJs(message) {
11
13
  let tabId = message.tabId;
12
14
  browser.tabs.onUpdated.addListener(
13
- async function lis(tabId, changeInfo) {
15
+ async function lis(
16
+ tabId,
17
+ changeInfo,
18
+ ) {
14
19
  if (changeInfo.status === 'complete') {
15
20
  browser.tabs.onUpdated.removeListener(lis);
16
21
  await browserTabSendMessage(tabId, message);
@@ -47,7 +52,10 @@ export async function browserTabCreateToDownload(message) {
47
52
 
48
53
  let {tabId} = await tabOpCreateNear(properties);
49
54
  browser.tabs.onUpdated.addListener(
50
- async function lis(tabId, changeInfo) {
55
+ async function lis(
56
+ tabId,
57
+ changeInfo,
58
+ ) {
51
59
  if (changeInfo.status === 'complete') {
52
60
  browser.tabs.onUpdated.removeListener(lis);
53
61
  // todo code here
@@ -58,18 +66,21 @@ export async function browserTabCreateToDownload(message) {
58
66
  }
59
67
 
60
68
  /**
61
- * must has, tabId, url
62
- * @param message
63
- * @param message.tabId{number}
64
- * @param message.url{string}
65
- * @param message.options.focusNewTab{boolean}
69
+ * Safely handles incoming messages to create tabs and send messages to content scripts.
70
+ * * @param {Object} message - The message payload.
71
+ * @param {number} [message.tabId] - Optional Tab ID to attach to.
72
+ * @param {string} [message.url] - The URL to open.
73
+ * @param {Object} [message.options] - Additional options.
74
+ * @param {boolean} [message.options.focusNewTab] - Whether the newly created tab should be active.
66
75
  * @returns {Promise<void>}
67
76
  */
68
- export async function browserTabCreateNearSendMessageToContentJs(message) {
77
+ export async function browserTabCreateNearSendMessageToContentJs(message = {}) {
69
78
  let properties = {
70
79
  url: message.url,
71
80
  };
72
- if (message.tabId) {
81
+
82
+ // Strictly check for tabId presence to prevent passing undefined.
83
+ if (message.tabId !== undefined) {
73
84
  let tabId = message.tabId;
74
85
  try {
75
86
  await tabOpGet(tabId);
@@ -78,22 +89,32 @@ export async function browserTabCreateNearSendMessageToContentJs(message) {
78
89
  delete message.tabId;
79
90
  }
80
91
  }
81
- if (message.options) {
82
- let focusNewTab = message.options.focusNewTab;
83
- Object.assign(properties, {active: focusNewTab});
92
+
93
+ // Use optional chaining to safely extract nested properties.
94
+ // Only assign 'active' if 'focusNewTab' was explicitly provided.
95
+ if (message?.options?.focusNewTab !== undefined) {
96
+ Object.assign(properties, {active: message.options.focusNewTab});
84
97
  }
85
98
 
86
99
  let {tabId} = await tabOpCreateNear(properties);
100
+
87
101
  browser.tabs.onUpdated.addListener(
88
- async function lis(tabId, changeInfo) {
102
+ async function lis(
103
+ updatedTabId,
104
+ changeInfo,
105
+ ) {
89
106
  if (changeInfo.status === 'complete') {
90
107
  browser.tabs.onUpdated.removeListener(lis);
91
- // todo code here
108
+
109
+ // Execute further logic once the tab is completely loaded.
92
110
  await browserTabSendMessage(
93
- tabId, Object.assign({}, message, {tabId}));
111
+ updatedTabId,
112
+ Object.assign({}, message, {tabId: updatedTabId}),
113
+ );
94
114
  }
95
- }
96
- , {tabId, properties: ['status']});
115
+ },
116
+ {tabId, properties: ['status']},
117
+ );
97
118
  }
98
119
 
99
120
  /**
@@ -103,7 +124,10 @@ export async function browserTabCreateNearSendMessageToContentJs(message) {
103
124
  */
104
125
  export function browserTabWaitReloadThenRemoveIt({tabId}) {
105
126
  browser.tabs.onUpdated.addListener(
106
- async function lis(tabId, changeInfo) {
127
+ async function lis(
128
+ tabId,
129
+ changeInfo,
130
+ ) {
107
131
  if (changeInfo.status === 'complete') {
108
132
  browser.tabs.onUpdated.removeListener(lis);
109
133
  // todo code here
package/src/generate.js CHANGED
@@ -1,204 +1,3 @@
1
- import {stoOpGet, stoOpSet} from './opStorage.js';
2
-
3
- // [Optimization 1] Introduce a debounce function to prevent triggering storage writes on every keystroke.
4
- function debounce(func, wait) {
5
- let timeout;
6
- return function(...args) {
7
- clearTimeout(timeout);
8
- timeout = setTimeout(() => func.apply(this, args), wait);
9
- };
10
- }
11
-
12
- /**
13
- * Generates HTML elements based on a user settings schema object.
14
- *
15
- * @param {Object} userSettings
16
- * @param {Function} radioItemClickCallback
17
- * @returns {Promise<DocumentFragment>}
18
- */
19
- export async function generateHtmlByUserSettings(
20
- userSettings,
21
- radioItemClickCallback,
22
- checkboxItemClickCallback,
23
- ) {
24
- const elementsMap = {};
25
-
26
- // [Optimization 2] Use a DocumentFragment for off-screen DOM construction to improve rendering performance.
27
- const fragment = document.createDocumentFragment();
28
-
29
- const keys = Object.keys(userSettings);
30
-
31
- // [Optimization 3] Batch fetch all stored values.
32
- // Wait for all queries to complete to prevent async callbacks from interrupting the rendering pipeline.
33
- const valuesArray = await Promise.all(keys.map((key) => stoOpGet(key)));
34
- const storageData = {};
35
- keys.forEach((key, index) => {
36
- storageData[key] = valuesArray[index];
37
- });
38
-
39
- // First pass: Pre-create all container wrappers.
40
- // This ensures all target elements exist in the DOM references before executing triggerVisibility later.
41
- keys.forEach((storageKey) => {
42
- const eleWrap = document.createElement('fieldset');
43
- const eleTitle = document.createElement('legend');
44
- eleTitle.textContent = storageKey;
45
- eleWrap.append(eleTitle);
46
-
47
- elementsMap[storageKey] = eleWrap;
48
- fragment.append(eleWrap);
49
- });
50
-
51
- // Second pass: Synchronously populate form content and attach event listeners.
52
- keys.forEach((storageKey) => {
53
- const storageValue = userSettings[storageKey];
54
- const type = storageValue.type || 'text';
55
- const eleWrap = elementsMap[storageKey];
56
-
57
- // Retrieve pre-loaded value; fallback to the schema's default value if undefined.
58
- const storedValue = storageData[storageKey];
59
- const initialValue =
60
- storedValue !== undefined && storedValue !== null
61
- ? storedValue
62
- : storageValue.selected;
63
-
64
- // --- CONDITION 1: CHECKBOX & RADIO ---
65
- if (type === 'checkbox' || type === 'radio') {
66
- /** @type {string[]} */
67
- const options = storageValue.options || [];
68
-
69
- options.forEach((option) => {
70
- const eleLabel = document.createElement('label');
71
- eleLabel.textContent = option;
72
- const eleInput = document.createElement('input');
73
- eleInput.name = storageKey;
74
- eleInput.type = type;
75
- eleInput.value = option;
76
-
77
- if (type === 'checkbox') {
78
- const initialArray = Array.from(initialValue || []);
79
- const set = new Set(initialArray);
80
- eleInput.checked = set.has(option); // Apply state synchronously
81
-
82
- eleInput.addEventListener('change', async () => {
83
- // Derive state in real-time from the DOM (faster than reading from storage again)
84
- const allCheckboxes = eleWrap.querySelectorAll(
85
- 'input[type="checkbox"]',
86
- );
87
- const valueNew = Array.from(allCheckboxes)
88
- .filter((cb) => cb.checked)
89
- .map((cb) => cb.value);
90
-
91
- await stoOpSet(storageKey, valueNew);
92
- if (typeof checkboxItemClickCallback === 'function') {
93
- checkboxItemClickCallback(storageKey, option);
94
- }
95
- triggerVisibility(storageKey, valueNew);
96
- });
97
- }
98
- else if (type === 'radio') {
99
- if (option === initialValue) {
100
- eleInput.checked = true; // Apply state synchronously
101
- }
102
-
103
- // It is recommended to bind the 'change' event to the input rather than 'onclick' to the label
104
- eleInput.addEventListener('change', () => {
105
- stoOpSet(storageKey, option).then(() => {
106
- if (typeof radioItemClickCallback === 'function') {
107
- radioItemClickCallback(storageKey, option);
108
- }
109
- triggerVisibility(storageKey, option);
110
- });
111
- });
112
- }
113
-
114
- // Fix: Native HTML typically places the input element before the text node inside a label.
115
- eleLabel.prepend(eleInput);
116
- eleWrap.append(eleLabel);
117
- });
118
- }
119
-
120
- // --- CONDITION 2: TOGGLE BUTTON ---
121
- else if (type === 'button') {
122
- const eleButton = document.createElement('button');
123
- eleButton.type = 'button';
124
-
125
- let currentStatus = initialValue === true || initialValue === 'true';
126
- eleButton.textContent = String(currentStatus); // Apply state synchronously
127
-
128
- eleButton.addEventListener('click', async () => {
129
- currentStatus = !currentStatus;
130
- eleButton.textContent = String(currentStatus);
131
- await stoOpSet(storageKey, currentStatus);
132
- triggerVisibility(storageKey, currentStatus);
133
- });
134
-
135
- eleWrap.append(eleButton);
136
- }
137
-
138
- // --- CONDITION 3: NUMBER & TEXT INPUTS ---
139
- else if (type === 'number' || type === 'text') {
140
- const eleInput = document.createElement('input');
141
- eleInput.type = type;
142
- eleInput.name = storageKey;
143
- eleInput.value = initialValue !== undefined ? initialValue : ''; // Apply state synchronously
144
-
145
- // [Optimization 4] Wrap the write operation with debounce, delaying storage writes by 500ms.
146
- // This eliminates UI freezing regardless of typing speed.
147
- const debouncedSave = debounce(async (val) => {
148
- const finalizedValue = type === 'number' ? Number(val) : val;
149
- await stoOpSet(storageKey, finalizedValue);
150
- }, 500);
151
-
152
- eleInput.addEventListener('input', () => {
153
- const rawValue = eleInput.value;
154
- debouncedSave(rawValue);
155
- triggerVisibility(storageKey, rawValue); // Visual UI updates do not require a delay
156
- });
157
-
158
- eleWrap.append(eleInput);
159
- }
160
-
161
- // --- CONDITION 4: SPAN / READ-ONLY TEXT ---
162
- else if (type === 'span') {
163
- const eleSpan = document.createElement('span');
164
- eleSpan.textContent = String(initialValue); // Apply state synchronously
165
- eleWrap.append(eleSpan);
166
- }
167
- });
168
-
169
- // [Optimization 5] Third pass: Globally trigger a single visibility check.
170
- // At this point, all DOM elements exist and all initialValues are fully derived,
171
- // preventing undefined errors or invalid target hiding.
172
- keys.forEach((storageKey) => {
173
- const storedValue = storageData[storageKey];
174
- const initialValue =
175
- storedValue !== undefined && storedValue !== null
176
- ? storedValue
177
- : userSettings[storageKey].selected;
178
-
179
- triggerVisibility(storageKey, initialValue);
180
- });
181
-
182
- /**
183
- * Evaluates the visibility rules for a given source key based on its current
184
- * value.
185
- */
186
- function triggerVisibility(sourceKey, currentValue) {
187
- const config = userSettings[sourceKey];
188
- if (config && config.visibilityControl) {
189
- const {targetField, expectedValue} = config.visibilityControl;
190
- const targetElement = elementsMap[targetField];
191
-
192
- if (targetElement) {
193
- const shouldBeVisible = String(currentValue) === String(expectedValue);
194
- targetElement.style.display = shouldBeVisible ? '' : 'none';
195
- }
196
- }
197
- }
198
-
199
- return fragment;
200
- }
201
-
202
1
  export function generateMkvScriptForSystemWindows({vid, title}) {
203
2
  let args = {vid, title};
204
3
 
package/src/opTab.js CHANGED
@@ -1,198 +1,237 @@
1
1
  /**
2
- * Creates a normal tab using either a URL string or a properties object.
2
+ * Enhances a tab object by explicitly attaching its ID to the 'tabId' property.
3
3
  *
4
- * @param tab{browser.tabs.Tab}
5
- * @returns {Promise<(browser.tabs.Tab & {tabId: number})>}
4
+ * @param {browser.tabs.Tab} tab
5
+ * @returns {browser.tabs.Tab & {tabId: number}|null}
6
6
  */
7
- export async function tabOpEnhance(tab) {
8
- return Object.assign({}, tab, {tabId: tab.id});
7
+ export function tabOpEnhance(tab) {
8
+ if (!tab || typeof tab.id !== 'number') return null;
9
+ return {...tab, tabId: tab.id};
9
10
  }
10
11
 
11
12
  /**
12
- * Creates a normal tab using either a URL string or a properties object.
13
+ * Creates a normal tab using a properties object.
13
14
  *
14
- * @param {browser.tabs._CreateCreateProperties} properties
15
- * @returns {Promise<(browser.tabs.Tab & {tabId: number})>}
15
+ * @param {Object} properties
16
+ * @returns {Promise<browser.tabs.Tab & {tabId: number}>}
16
17
  */
17
18
  export async function tabOpCreate(properties) {
18
- // Otherwise, assume it is already a properties object
19
- let tab = await browser.tabs.create(properties);
19
+ const tab = await browser.tabs.create(properties);
20
20
  return tabOpEnhance(tab);
21
21
  }
22
22
 
23
23
  /**
24
+ * Creates a new tab positioned immediately after a specified existing tab.
25
+ * Does not mutate the original properties object.
24
26
  *
25
- * @param {browser.tabs._CreateCreateProperties & {tabId: number} } properties
26
- * @returns {Promise<(browser.tabs.Tab & {tabId: number})>}
27
+ * @param {Object & {tabId?: number}} properties
28
+ * @returns {Promise<browser.tabs.Tab & {tabId: number}>}
27
29
  */
28
30
  export async function tabOpCreateNear(properties) {
29
- if (properties.tabId) {
30
- let tabPrev = await tabOpGet(properties.tabId);
31
- delete properties.tabId;
32
- Object.assign(properties, {
33
- index: tabPrev.index + 1,
34
- openerTabId: tabPrev.id,
35
- });
31
+ // Destructure tabId out to avoid mutating the original properties object
32
+ const {tabId, ...restProperties} = properties;
33
+
34
+ if (tabId) {
35
+ const tabPrev = await tabOpGet(tabId);
36
+ if (tabPrev) {
37
+ Object.assign(restProperties, {
38
+ index: tabPrev.index + 1,
39
+ openerTabId: tabPrev.id,
40
+ });
41
+ }
36
42
  }
37
43
 
38
- let tab = await tabOpCreate(properties);
44
+ const tab = await browser.tabs.create(restProperties);
39
45
  return tabOpEnhance(tab);
40
46
  }
41
47
 
42
48
  /**
43
- * {active: false, muted: true}
44
- * @param properties{browser.tabs._CreateCreateProperties}
45
- * @returns {Promise<(browser.tabs.Tab & {tabId: number})>}
49
+ * Creates a tab in the background (inactive and muted).
50
+ *
51
+ * @param {Object} properties
52
+ * @returns {Promise<browser.tabs.Tab & {tabId: number}>}
46
53
  */
47
54
  export async function tabOpCreateActiveFalse(properties) {
48
- /**
49
- * @type {browser.tabs._CreateCreateProperties}
50
- */
51
- let source = {active: false, muted: true};
52
- Object.assign(properties, source);
53
- let tab = await browser.tabs.create(properties);
55
+ const mergedProperties = {
56
+ ...properties,
57
+ active: false,
58
+ muted: true,
59
+ };
60
+ const tab = await browser.tabs.create(mergedProperties);
54
61
  return tabOpEnhance(tab);
55
62
  }
56
63
 
57
64
  /**
58
- * Creates a normal tab using either a URL string or a properties object.
65
+ * Creates a completely new window and returns its initial tab.
59
66
  *
60
67
  * @param {string} url
61
- * @returns {Promise<(browser.tabs.Tab & {tabId: number})>}
68
+ * @returns {Promise<(browser.tabs.Tab & {tabId: number})|undefined>}
62
69
  */
63
70
  export async function tabOpCreateByWindow(url) {
64
- // If it's a string, wrap it in an object
65
- if (typeof url === 'string') {
66
- let window = await browser.windows.create({
67
- url,
68
- });
69
- let tab = window.tabs.shift();
70
- return tabOpEnhance(tab);
71
+ if (typeof url !== 'string') return undefined;
72
+
73
+ const window = await browser.windows.create({url});
74
+ if (window && window.tabs && window.tabs.length > 0) {
75
+ return tabOpEnhance(window.tabs[0]);
71
76
  }
77
+ return undefined;
72
78
  }
73
79
 
74
80
  /**
81
+ * Retrieves details about the specified tab.
75
82
  *
76
- * @param tabId{number}
77
- * @return {Promise<browser.tabs.Tab>}
83
+ * @param {number} tabId
84
+ * @returns {Promise<browser.tabs.Tab>}
78
85
  */
79
- export async function tabOpGet(tabId) {
80
- return await browser.tabs.get(tabId);
86
+ export function tabOpGet(tabId) {
87
+ return browser.tabs.get(tabId);
81
88
  }
82
89
 
83
90
  /**
91
+ * Retrieves all tabs across all windows.
84
92
  *
85
93
  * @returns {Promise<browser.tabs.Tab[]>}
86
94
  */
87
- export async function tabOpQueryAll() {
88
- return await browser.tabs.query({});
95
+ export function tabOpQueryAll() {
96
+ return browser.tabs.query({});
89
97
  }
90
98
 
91
99
  /**
100
+ * Retrieves an array of tab IDs that match the specified URL pattern.
92
101
  *
93
- * @param urlQuery{string}
102
+ * @param {string} urlQuery
94
103
  * @returns {Promise<number[]>}
95
104
  */
96
105
  export async function tabOpQueryUrl(urlQuery) {
97
- let tabs = await browser.tabs.query({url: urlQuery});
98
- return tabs.map(v => v.id);
106
+ const tabs = await browser.tabs.query({url: urlQuery});
107
+ return tabs.map(tab => tab.id);
99
108
  }
100
109
 
101
110
  /**
111
+ * Finds all tabs matching the URL pattern and removes them simultaneously.
112
+ * [Optimization]: browser.tabs.remove accepts an array of numbers, which is
113
+ * significantly faster than looping and awaiting individually.
102
114
  *
103
- * @param urlQuery{string}
115
+ * @param {string} urlQuery
104
116
  * @returns {Promise<void>}
105
117
  */
106
118
  export async function tabOpQueryUrlThenRemove(urlQuery) {
107
- let ids = await tabOpQueryUrl(urlQuery);
108
- ids.map(id => tabOpRemove(id));
119
+ const ids = await tabOpQueryUrl(urlQuery);
120
+ if (ids.length > 0) {
121
+ await browser.tabs.remove(ids);
122
+ }
109
123
  }
110
124
 
111
125
  /**
126
+ * Reloads the given tab.
112
127
  *
113
- * @param tabId{number}
128
+ * @param {number} tabId
114
129
  * @returns {Promise<void>}
115
130
  */
116
- export async function tabOpReload(tabId) {
117
- await browser.tabs.reload(tabId);
131
+ export function tabOpReload(tabId) {
132
+ return browser.tabs.reload(tabId);
118
133
  }
119
134
 
120
135
  /**
136
+ * Reloads the given tab, bypassing the local web cache.
121
137
  *
122
- * @param tabId{number}
138
+ * @param {number} tabId
123
139
  * @returns {Promise<void>}
124
140
  */
125
- export async function tabOpReloadByPassCacheTrue(tabId) {
126
- await browser.tabs.reload(tabId, {bypassCache: true});
141
+ export function tabOpReloadByPassCacheTrue(tabId) {
142
+ return browser.tabs.reload(tabId, {bypassCache: true});
127
143
  }
128
144
 
129
145
  /**
146
+ * Closes one or more tabs.
130
147
  *
131
- * @param tabId{number}
148
+ * @param {number|number[]} tabId
132
149
  * @returns {Promise<void>}
133
150
  */
134
- export async function tabOpRemove(tabId) {
135
- await browser.tabs.remove(tabId);
151
+ export function tabOpRemove(tabId) {
152
+ return browser.tabs.remove(tabId);
136
153
  }
137
154
 
138
155
  /**
156
+ * Hides one or more tabs (Firefox specific API).
139
157
  *
140
- * @param tabId{number}
141
- * @returns {Promise<void>}
158
+ * @param {number|number[]} tabId
159
+ * @returns {Promise<number[]>}
142
160
  */
143
- export async function tabOpHide(tabId) {
144
- await browser.tabs.hide(tabId);
161
+ export function tabOpHide(tabId) {
162
+ return browser.tabs.hide(tabId);
145
163
  }
146
164
 
147
165
  /**
166
+ * Modifies the properties of a tab.
148
167
  *
149
- * @param {number}tabId
150
- * @param {browser.tabs._UpdateUpdateProperties}updateProperties
151
- * @returns {Promise<browser.tabs.Tab&{tabId: number}>}
152
- */
153
- export async function tabOpUpdate(tabId, updateProperties) {
154
- let tab = await browser.tabs.update(tabId, updateProperties);
168
+ * @param {number} tabId
169
+ * @param {Object} updateProperties
170
+ * @returns {Promise<browser.tabs.Tab & {tabId: number}>}
171
+ */
172
+ export async function tabOpUpdate(
173
+ tabId,
174
+ updateProperties,
175
+ ) {
176
+ const tab = await browser.tabs.update(tabId, updateProperties);
155
177
  return tabOpEnhance(tab);
156
178
  }
157
179
 
158
180
  /**
159
- * active: false, muted: true
160
- * @param tabId{number}
161
- * @returns {Promise<browser.tabs.Tab&{tabId: number}>}
181
+ * Updates a tab to be inactive and muted.
182
+ *
183
+ * @param {number} tabId
184
+ * @returns {Promise<browser.tabs.Tab & {tabId: number}>}
162
185
  */
163
- export async function tabOpUpdateActiveFalse(tabId) {
164
- return await tabOpUpdate(tabId, {active: false, muted: true});
186
+ export function tabOpUpdateActiveFalse(tabId) {
187
+ return tabOpUpdate(tabId, {active: false, muted: true});
165
188
  }
166
189
 
167
190
  /**
168
- * @param tabId
191
+ * Focuses the window containing the tab, then highlights and activates the tab.
192
+ *
193
+ * @param {number} tabId
194
+ * @returns {Promise<browser.tabs.Tab & {tabId: number}>}
169
195
  */
170
196
  export async function tabOpFocus(tabId) {
171
- let tab = await tabOpGet(tabId);
172
- let windowId = tab.windowId;
173
- await browser.windows.update(windowId, {focused: true});
197
+ const tab = await tabOpGet(tabId);
198
+ if (!tab || !tab.windowId) throw new Error(`Tab ${tabId} not found.`);
199
+
200
+ await browser.windows.update(tab.windowId, {focused: true});
201
+
202
+ const tabUpdated = await browser.tabs.update(tabId, {
203
+ active: true,
204
+ highlighted: true,
205
+ });
174
206
 
175
- let updateProperties = {active: true, highlighted: true};
176
- let tabUpdated = await tabOpUpdate(tabId, updateProperties);
177
207
  return tabOpEnhance(tabUpdated);
178
208
  }
179
209
 
180
210
  /**
211
+ * Injects CSS code into a page.
212
+ * Note: Check manifest version compatibility for 'insertCSS'.
181
213
  *
182
- * @param{number} tabId
183
- * @param {string}code
214
+ * @param {number} tabId
215
+ * @param {string} code
184
216
  * @returns {Promise<void>}
185
217
  */
186
- export async function tabOpInsertCssCode(tabId, code) {
187
- await browser.tabs.insertCSS(tabId, {code});
218
+ export function tabOpInsertCssCode(
219
+ tabId,
220
+ code,
221
+ ) {
222
+ return browser.tabs.insertCSS(tabId, {code});
188
223
  }
189
224
 
190
225
  /**
226
+ * Removes CSS code that was previously injected into a page.
191
227
  *
192
- * @param{number} tabId
193
- * @param {string}code
228
+ * @param {number} tabId
229
+ * @param {string} code
194
230
  * @returns {Promise<void>}
195
231
  */
196
- export async function tabOpRemoveCssCode(tabId, code) {
197
- await browser.tabs.removeCSS(tabId, {code});
232
+ export function tabOpRemoveCssCode(
233
+ tabId,
234
+ code,
235
+ ) {
236
+ return browser.tabs.removeCSS(tabId, {code});
198
237
  }
@@ -1,33 +0,0 @@
1
- import {stoOpGet, stoOpSet} from './opStorage.js';
2
-
3
- export async function serviceInitUserSettings(userSettings) {
4
- const initPromises = Object.entries(userSettings)
5
- .map(async ([key, setting]) => {
6
- const oldValue = await stoOpGet(key);
7
-
8
- // FIX: Check strictly for null or undefined.
9
- // This allows `false` and `0` to be recognized as valid saved values.
10
- if (oldValue === null || oldValue === undefined) {
11
- await stoOpSet(key, setting.selected);
12
- }
13
- });
14
-
15
- await Promise.all(initPromises);
16
- }
17
-
18
- /**
19
- * // todo
20
- * @param userSettings{{}}
21
- * @returns {Promise<{Object}>}
22
- */
23
- export async function serviceGetUserSettings(userSettings) {
24
- const red = {};
25
- for (let k of Object.keys(userSettings)) {
26
- red[k] = await stoOpGet(k);
27
- }
28
- return red;
29
- }
30
-
31
-
32
-
33
-