@vacantthinker/firefox-addon-framework-easy 2026.611.751 → 2026.611.1153
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 +0 -217
- package/dist/BaseORM.d.ts +41 -0
- package/dist/BaseORM.d.ts.map +1 -0
- package/dist/BaseORM.js +77 -0
- package/dist/browserDownload.d.ts +9 -0
- package/dist/browserDownload.d.ts.map +1 -0
- package/dist/browserDownload.js +10 -0
- package/dist/browserNotification.d.ts +8 -0
- package/dist/browserNotification.d.ts.map +1 -0
- package/dist/browserNotification.js +17 -0
- package/dist/browserRuntime.d.ts +41 -0
- package/dist/browserRuntime.d.ts.map +1 -0
- package/dist/browserRuntime.js +61 -0
- package/dist/browserTab.d.ts +34 -0
- package/dist/browserTab.d.ts.map +1 -0
- package/dist/browserTab.js +105 -0
- package/dist/generate.d.ts +13 -0
- package/dist/generate.d.ts.map +1 -0
- package/dist/generate.js +99 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/opStorage.d.ts +40 -0
- package/dist/opStorage.d.ts.map +1 -0
- package/dist/opStorage.js +57 -0
- package/dist/opTab.d.ts +82 -0
- package/dist/opTab.d.ts.map +1 -0
- package/dist/opTab.js +152 -0
- package/dist/serviceCommon.d.ts +8 -0
- package/dist/serviceCommon.d.ts.map +1 -0
- package/dist/serviceCommon.js +21 -0
- package/dist/serviceFetch.d.ts +23 -0
- package/dist/serviceFetch.d.ts.map +1 -0
- package/dist/serviceFetch.js +38 -0
- package/dist/serviceGet.d.ts +13 -0
- package/dist/serviceGet.d.ts.map +1 -0
- package/dist/serviceGet.js +24 -0
- package/dist/serviceOpContent.d.ts +24 -0
- package/dist/serviceOpContent.d.ts.map +1 -0
- package/dist/serviceOpContent.js +69 -0
- package/dist/serviceOpJavascript.d.ts +41 -0
- package/dist/serviceOpJavascript.d.ts.map +1 -0
- package/dist/serviceOpJavascript.js +182 -0
- package/dist/servicePure.d.ts +18 -0
- package/dist/servicePure.d.ts.map +1 -0
- package/dist/servicePure.js +51 -0
- package/dist/serviceUpdateTabStyle.d.ts +17 -0
- package/dist/serviceUpdateTabStyle.d.ts.map +1 -0
- package/dist/serviceUpdateTabStyle.js +63 -0
- package/package.json +11 -4
- package/.github/workflows/npm-publish.yml +0 -34
- package/index.js +0 -17
- package/publish.sh +0 -81
- package/src/BaseORM.js +0 -66
- package/src/DomainORM.js +0 -11
- package/src/browserDownload.js +0 -23
- package/src/browserNotification.js +0 -22
- package/src/browserRuntime.js +0 -65
- package/src/browserRuntimeOnMessageCommon.js +0 -50
- package/src/browserTab.js +0 -139
- package/src/generate.js +0 -177
- package/src/opStorage.js +0 -76
- package/src/opTab.js +0 -237
- package/src/serviceCommon.js +0 -22
- package/src/serviceFetch.js +0 -65
- package/src/serviceGet.js +0 -31
- package/src/serviceOpContent.js +0 -83
- package/src/serviceOpJavascript.js +0 -346
- package/src/servicePure.js +0 -42
- package/src/serviceUpdateTabStyle.js +0 -130
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { browserRuntimePlatformInfo } from './browserRuntime';
|
|
2
|
+
import { generateMkvScriptForSystemFedora, generateMkvScriptForSystemWindows, } from './generate';
|
|
3
|
+
/**
|
|
4
|
+
* Maps file extensions to MIME types.
|
|
5
|
+
*/
|
|
6
|
+
const EXT_MIME_MAP = {
|
|
7
|
+
txt: 'text/plain',
|
|
8
|
+
json: 'application/json',
|
|
9
|
+
js: 'application/javascript',
|
|
10
|
+
sh: 'application/x-sh',
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Copies text content to the system clipboard.
|
|
14
|
+
*/
|
|
15
|
+
export async function serviceCopyContentToClipboard(data) {
|
|
16
|
+
return await window.navigator.clipboard.writeText(data);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Saves content to a local file.
|
|
20
|
+
* @param content The content string.
|
|
21
|
+
* @param filename The base filename.
|
|
22
|
+
* @param ext The file extension.
|
|
23
|
+
*/
|
|
24
|
+
export function serviceSaveContentToLocal(content, filename, ext = 'txt') {
|
|
25
|
+
const mimeType = EXT_MIME_MAP[ext] || 'text/plain';
|
|
26
|
+
const blob = new Blob([content], { type: mimeType });
|
|
27
|
+
const url = URL.createObjectURL(blob);
|
|
28
|
+
const eleA = document.createElement('a');
|
|
29
|
+
eleA.href = url;
|
|
30
|
+
eleA.download = `${filename}.${ext}`;
|
|
31
|
+
// Programmatically trigger download
|
|
32
|
+
eleA.click();
|
|
33
|
+
// Cleanup
|
|
34
|
+
URL.revokeObjectURL(url);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Generates an MKVToolNix script based on the current OS.
|
|
38
|
+
* @param videoInfo - The object containing vid and videoTitle.
|
|
39
|
+
*/
|
|
40
|
+
export async function serviceGenerateMkvToolNixScript(videoInfo) {
|
|
41
|
+
const platformInfo = await browserRuntimePlatformInfo();
|
|
42
|
+
if (platformInfo.os === 'win') {
|
|
43
|
+
const content = generateMkvScriptForSystemWindows(videoInfo);
|
|
44
|
+
serviceSaveContentToLocal(content, videoInfo.videoTitle, 'js');
|
|
45
|
+
}
|
|
46
|
+
else if (platformInfo.os === 'linux') {
|
|
47
|
+
const content = generateMkvScriptForSystemFedora(videoInfo);
|
|
48
|
+
serviceSaveContentToLocal(content, videoInfo.videoTitle, 'sh');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Cleans a string to make it a valid filename.
|
|
53
|
+
* @param value The raw string.
|
|
54
|
+
* @returns A cleaned filename string.
|
|
55
|
+
*/
|
|
56
|
+
export function serviceRemoveIllegalWord(value) {
|
|
57
|
+
if (!value)
|
|
58
|
+
return '';
|
|
59
|
+
// Get the first line and trim whitespace
|
|
60
|
+
let name = value.trim().split(/\r?\n/)[0];
|
|
61
|
+
// Replace punctuation, symbols, and control chars with space
|
|
62
|
+
name = name.replace(/[\p{P}\p{S}\p{C}]/gu, ' ');
|
|
63
|
+
// Replace specific legacy forbidden characters
|
|
64
|
+
name = name.replace(/[~"#%&*:<>?/\\{|}]/g, ' ');
|
|
65
|
+
// Normalize whitespaces
|
|
66
|
+
name = name.replace(/[\s\u3000]+/g, ' ').trim();
|
|
67
|
+
// Remove leading/trailing dots or hyphens
|
|
68
|
+
return name.replace(/^[-.]+|[-.]+$/g, '');
|
|
69
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
interface ScreenRect {
|
|
2
|
+
x: number;
|
|
3
|
+
y: number;
|
|
4
|
+
width: number;
|
|
5
|
+
height: number;
|
|
6
|
+
}
|
|
7
|
+
interface ActionMessage {
|
|
8
|
+
tabId: number;
|
|
9
|
+
act: string;
|
|
10
|
+
}
|
|
11
|
+
interface MagnetLinkMessage extends ActionMessage {
|
|
12
|
+
title: string;
|
|
13
|
+
data?: string[];
|
|
14
|
+
handleOption?: 'clipboard' | 'txt' | 'clipboardAndTxt';
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Captures a tab's screenshot and triggers a local download in the target tab.
|
|
18
|
+
*/
|
|
19
|
+
export declare function serviceTakeScreenshot({ tabId, filename, rect }: {
|
|
20
|
+
tabId: number;
|
|
21
|
+
filename: string;
|
|
22
|
+
rect: ScreenRect;
|
|
23
|
+
}): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Enables an element picker overlay in the target tab.
|
|
26
|
+
*/
|
|
27
|
+
export declare function serviceElementPicker(message: ActionMessage): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Gets the bounding rectangle of the full page.
|
|
30
|
+
*/
|
|
31
|
+
export declare function serviceGetFullPageRectData(message: ActionMessage): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Scans the page for magnet links.
|
|
34
|
+
*/
|
|
35
|
+
export declare function serviceFindAllMagnetLink(message: MagnetLinkMessage): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Handles the processing of collected magnet links (clipboard or file).
|
|
38
|
+
*/
|
|
39
|
+
export declare function serviceDealWithMagnetLink(message: MagnetLinkMessage): Promise<void>;
|
|
40
|
+
export {};
|
|
41
|
+
//# sourceMappingURL=serviceOpJavascript.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serviceOpJavascript.d.ts","sourceRoot":"","sources":["../src/serviceOpJavascript.ts"],"names":[],"mappings":"AAUA,UAAU,UAAU;IAClB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,aAAa;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,UAAU,iBAAkB,SAAQ,aAAa;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,YAAY,CAAC,EAAE,WAAW,GAAG,KAAK,GAAG,iBAAiB,CAAC;CACxD;AAID;;GAEG;AACH,wBAAsB,qBAAqB,CAAC,EAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAC,EAAE;IACnE,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,UAAU,CAAA;CACjB,GAAG,OAAO,CAAC,IAAI,CAAC,CAchB;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAsFhF;AAED;;GAEG;AACH,wBAAsB,0BAA0B,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAatF;AAED;;GAEG;AACH,wBAAsB,wBAAwB,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CA8BxF;AAED;;GAEG;AACH,wBAAsB,yBAAyB,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBzF"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { serviceGetCurrentDateYYYYMMDDHHMMSS } from './serviceGet';
|
|
2
|
+
import { serviceCopyContentToClipboard, serviceRemoveIllegalWord, serviceSaveContentToLocal, } from './serviceOpContent';
|
|
3
|
+
import { browserNotificationCreate } from './browserNotification';
|
|
4
|
+
// --- Services ---
|
|
5
|
+
/**
|
|
6
|
+
* Captures a tab's screenshot and triggers a local download in the target tab.
|
|
7
|
+
*/
|
|
8
|
+
export async function serviceTakeScreenshot({ tabId, filename, rect }) {
|
|
9
|
+
const dataURI = await browser.tabs.captureTab(tabId, { rect });
|
|
10
|
+
await browser.scripting.executeScript({
|
|
11
|
+
target: { tabId },
|
|
12
|
+
args: [{ dataURI, filename }],
|
|
13
|
+
func: (message) => {
|
|
14
|
+
const { dataURI, filename } = message;
|
|
15
|
+
const a = document.createElement('a');
|
|
16
|
+
a.href = dataURI;
|
|
17
|
+
a.download = filename;
|
|
18
|
+
a.click();
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Enables an element picker overlay in the target tab.
|
|
24
|
+
*/
|
|
25
|
+
export async function serviceElementPicker(message) {
|
|
26
|
+
await browser.scripting.executeScript({
|
|
27
|
+
target: { tabId: message.tabId },
|
|
28
|
+
args: [message],
|
|
29
|
+
func: (async (message) => {
|
|
30
|
+
const overlayId = 'extension-element-highlighter-overlay';
|
|
31
|
+
let overlay = document.getElementById(overlayId);
|
|
32
|
+
if (!overlay) {
|
|
33
|
+
overlay = document.createElement('div');
|
|
34
|
+
overlay.id = overlayId;
|
|
35
|
+
overlay.style.cssText = `
|
|
36
|
+
position: fixed !important; top: 0 !important; left: 0 !important;
|
|
37
|
+
width: 0 !important; height: 0 !important;
|
|
38
|
+
background-color: rgba(255, 0, 85, 0.2) !important;
|
|
39
|
+
outline: 2px dashed #ff0055 !important; outline-offset: -2px !important;
|
|
40
|
+
z-index: 2147483647 !important; pointer-events: none !important;
|
|
41
|
+
transition: all 0.05s ease-out !important; display: none !important;
|
|
42
|
+
`;
|
|
43
|
+
document.documentElement.appendChild(overlay);
|
|
44
|
+
}
|
|
45
|
+
function getUniqueSelector(el) {
|
|
46
|
+
const path = [];
|
|
47
|
+
let current = el;
|
|
48
|
+
while (current && current.nodeType === Node.ELEMENT_NODE) {
|
|
49
|
+
let selector = current.nodeName.toLowerCase();
|
|
50
|
+
if (current.id) {
|
|
51
|
+
selector += `#${current.id}`;
|
|
52
|
+
path.unshift(selector);
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
let sib = current, nth = 1;
|
|
57
|
+
while ((sib = sib.previousElementSibling) && sib.nodeName.toLowerCase() === selector)
|
|
58
|
+
nth++;
|
|
59
|
+
if (nth !== 1)
|
|
60
|
+
selector += `:nth-of-type(${nth})`;
|
|
61
|
+
}
|
|
62
|
+
path.unshift(selector);
|
|
63
|
+
current = current.parentElement;
|
|
64
|
+
}
|
|
65
|
+
return path.join(' > ');
|
|
66
|
+
}
|
|
67
|
+
function handleMouseOver(e) {
|
|
68
|
+
const target = e.target;
|
|
69
|
+
if (target.id === overlayId || !overlay)
|
|
70
|
+
return;
|
|
71
|
+
const rect = target.getBoundingClientRect();
|
|
72
|
+
Object.assign(overlay.style, {
|
|
73
|
+
display: 'block',
|
|
74
|
+
top: `${rect.top}px`, left: `${rect.left}px`,
|
|
75
|
+
width: `${rect.width}px`, height: `${rect.height}px`,
|
|
76
|
+
});
|
|
77
|
+
document.body.style.setProperty('cursor', 'crosshair', 'important');
|
|
78
|
+
}
|
|
79
|
+
async function handleElementClick(e) {
|
|
80
|
+
e.preventDefault();
|
|
81
|
+
e.stopPropagation();
|
|
82
|
+
const target = e.target;
|
|
83
|
+
stopPickingMode();
|
|
84
|
+
const clientRect = target.getBoundingClientRect();
|
|
85
|
+
const rect = {
|
|
86
|
+
height: clientRect.height,
|
|
87
|
+
width: clientRect.width,
|
|
88
|
+
x: clientRect.left + window.scrollX,
|
|
89
|
+
y: clientRect.top + window.scrollY,
|
|
90
|
+
};
|
|
91
|
+
await browser.runtime.sendMessage({
|
|
92
|
+
...message,
|
|
93
|
+
rect,
|
|
94
|
+
uniqueSelector: getUniqueSelector(target),
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
function stopPickingMode() {
|
|
98
|
+
document.removeEventListener('mouseover', handleMouseOver, true);
|
|
99
|
+
document.removeEventListener('click', handleElementClick, true);
|
|
100
|
+
document.body.style.removeProperty('cursor');
|
|
101
|
+
overlay?.remove();
|
|
102
|
+
}
|
|
103
|
+
document.addEventListener('mouseover', handleMouseOver, true);
|
|
104
|
+
document.addEventListener('click', handleElementClick, true);
|
|
105
|
+
}),
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Gets the bounding rectangle of the full page.
|
|
110
|
+
*/
|
|
111
|
+
export async function serviceGetFullPageRectData(message) {
|
|
112
|
+
await browser.scripting.executeScript({
|
|
113
|
+
target: { tabId: message.tabId },
|
|
114
|
+
args: [message],
|
|
115
|
+
func: (message) => {
|
|
116
|
+
const rect = {
|
|
117
|
+
x: 0, y: 0,
|
|
118
|
+
width: document.documentElement.scrollWidth,
|
|
119
|
+
height: document.documentElement.scrollHeight,
|
|
120
|
+
};
|
|
121
|
+
browser.runtime.sendMessage({ ...message, rect });
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Scans the page for magnet links.
|
|
127
|
+
*/
|
|
128
|
+
export async function serviceFindAllMagnetLink(message) {
|
|
129
|
+
await browser.scripting.executeScript({
|
|
130
|
+
target: { tabId: message.tabId },
|
|
131
|
+
args: [message],
|
|
132
|
+
func: (async (message) => {
|
|
133
|
+
const magnets = new Set();
|
|
134
|
+
// Type 1: Attribute selectors
|
|
135
|
+
const attrElements = document.querySelectorAll('*[href*="magnet:"], *[data-url*="magnet:"], *[data-magnet*="magnet:"], *[data-href*="magnet:"]');
|
|
136
|
+
attrElements.forEach(el => {
|
|
137
|
+
Array.from(el.attributes).forEach(attr => {
|
|
138
|
+
if (attr.value.includes('magnet:?xt='))
|
|
139
|
+
magnets.add(attr.value.trim());
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
// Type 2: Text content
|
|
143
|
+
document.querySelectorAll('div, span, td, p, a, button').forEach(el => {
|
|
144
|
+
if (el.children.length === 0) {
|
|
145
|
+
const text = el.textContent?.trim() || '';
|
|
146
|
+
const match = text.match(/magnet:\?xt=[^\s"'<>]+/);
|
|
147
|
+
if (match)
|
|
148
|
+
magnets.add(match[0]);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
await browser.runtime.sendMessage({
|
|
152
|
+
...message,
|
|
153
|
+
data: Array.from(magnets),
|
|
154
|
+
});
|
|
155
|
+
}),
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Handles the processing of collected magnet links (clipboard or file).
|
|
160
|
+
*/
|
|
161
|
+
export async function serviceDealWithMagnetLink(message) {
|
|
162
|
+
const { title, data, handleOption } = message;
|
|
163
|
+
const titleCleaned = serviceRemoveIllegalWord(title);
|
|
164
|
+
if (!Array.isArray(data) || data.length === 0) {
|
|
165
|
+
await browserNotificationCreate('magnet link not found!');
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const content = `${data.join('\n')}\n`;
|
|
169
|
+
const filename = ['magnet-link', titleCleaned, serviceGetCurrentDateYYYYMMDDHHMMSS()].join(' ');
|
|
170
|
+
switch (handleOption) {
|
|
171
|
+
case 'clipboard':
|
|
172
|
+
await serviceCopyContentToClipboard(content);
|
|
173
|
+
break;
|
|
174
|
+
case 'txt':
|
|
175
|
+
serviceSaveContentToLocal(content, filename);
|
|
176
|
+
break;
|
|
177
|
+
case 'clipboardAndTxt':
|
|
178
|
+
await serviceCopyContentToClipboard(content);
|
|
179
|
+
serviceSaveContentToLocal(content, filename);
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface YoutubeVideoInfo {
|
|
2
|
+
videolink: string;
|
|
3
|
+
vid: string;
|
|
4
|
+
}
|
|
5
|
+
export interface YoutubePlaylistInfo {
|
|
6
|
+
playlistVideolink: string;
|
|
7
|
+
playlistId: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Extracts video ID and creates a clean YouTube URL.
|
|
11
|
+
* Supports: youtube.com/watch?v=, youtu.be/, and youtube.com/shorts/
|
|
12
|
+
*/
|
|
13
|
+
export declare function servicePureVideolinkYTB(videolinkOrigin: string): YoutubeVideoInfo | null;
|
|
14
|
+
/**
|
|
15
|
+
* Extracts playlist ID and creates a clean YouTube playlist URL.
|
|
16
|
+
*/
|
|
17
|
+
export declare function servicePurePlaylistVideolinkYTB(videolinkOrigin: string): YoutubePlaylistInfo | null;
|
|
18
|
+
//# sourceMappingURL=servicePure.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"servicePure.d.ts","sourceRoot":"","sources":["../src/servicePure.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,mBAAmB;IAClC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,eAAe,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CA6BxF;AAED;;GAEG;AACH,wBAAgB,+BAA+B,CAAC,eAAe,EAAE,MAAM,GAAG,mBAAmB,GAAG,IAAI,CAgBnG"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extracts video ID and creates a clean YouTube URL.
|
|
3
|
+
* Supports: youtube.com/watch?v=, youtu.be/, and youtube.com/shorts/
|
|
4
|
+
*/
|
|
5
|
+
export function servicePureVideolinkYTB(videolinkOrigin) {
|
|
6
|
+
if (!videolinkOrigin)
|
|
7
|
+
return null;
|
|
8
|
+
try {
|
|
9
|
+
const url = new URL(videolinkOrigin);
|
|
10
|
+
let vid = null;
|
|
11
|
+
// 1. Standard watch URL: youtube.com/watch?v=ID
|
|
12
|
+
vid = url.searchParams.get('v');
|
|
13
|
+
// 2. Short URL: youtu.be/ID
|
|
14
|
+
if (!vid && url.hostname.includes('youtu.be')) {
|
|
15
|
+
vid = url.pathname.slice(1);
|
|
16
|
+
}
|
|
17
|
+
// 3. Shorts URL: youtube.com/shorts/ID
|
|
18
|
+
if (!vid && url.pathname.startsWith('/shorts/')) {
|
|
19
|
+
vid = url.pathname.split('/')[2];
|
|
20
|
+
}
|
|
21
|
+
if (!vid)
|
|
22
|
+
return null;
|
|
23
|
+
return {
|
|
24
|
+
videolink: `https://www.youtube.com/watch?v=${vid}`,
|
|
25
|
+
vid,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return null; // Invalid URL
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Extracts playlist ID and creates a clean YouTube playlist URL.
|
|
34
|
+
*/
|
|
35
|
+
export function servicePurePlaylistVideolinkYTB(videolinkOrigin) {
|
|
36
|
+
if (!videolinkOrigin)
|
|
37
|
+
return null;
|
|
38
|
+
try {
|
|
39
|
+
const url = new URL(videolinkOrigin);
|
|
40
|
+
const playlistId = url.searchParams.get('list');
|
|
41
|
+
if (!playlistId)
|
|
42
|
+
return null;
|
|
43
|
+
return {
|
|
44
|
+
playlistVideolink: `https://www.youtube.com/playlist?list=${playlistId}`,
|
|
45
|
+
playlistId,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return null; // Invalid URL
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Style management service for browser tabs.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Update the text color for all elements globally.
|
|
6
|
+
*/
|
|
7
|
+
export declare function serviceUpdataALLTextNodeColor(tabId: number, color: string): Promise<void>;
|
|
8
|
+
/**
|
|
9
|
+
* Update the background color for all elements globally.
|
|
10
|
+
*/
|
|
11
|
+
export declare function serviceUpdataALLNodeBackgroundColor(tabId: number, bgColor: string): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Adjust font size globally by a delta value.
|
|
14
|
+
* Uses recursive DOM traversal to support Shadow DOM elements.
|
|
15
|
+
*/
|
|
16
|
+
export declare function serviceIncreaseAllTextFontSize(tabId: number, delta: number): Promise<void>;
|
|
17
|
+
//# sourceMappingURL=serviceUpdateTabStyle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serviceUpdateTabStyle.d.ts","sourceRoot":"","sources":["../src/serviceUpdateTabStyle.ts"],"names":[],"mappings":"AAAA;;GAEG;AAaH;;GAEG;AACH,wBAAsB,6BAA6B,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAI/F;AAED;;GAEG;AACH,wBAAsB,mCAAmC,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIvG;AASD;;;GAGG;AACH,wBAAsB,8BAA8B,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkChG"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Style management service for browser tabs.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Apply a CSS string globally to the target tab.
|
|
6
|
+
* This is the most efficient way to override styles without traversing the DOM.
|
|
7
|
+
*/
|
|
8
|
+
async function applyGlobalCss(tabId, css) {
|
|
9
|
+
await browser.scripting.insertCSS({
|
|
10
|
+
target: { tabId },
|
|
11
|
+
css: css,
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Update the text color for all elements globally.
|
|
16
|
+
*/
|
|
17
|
+
export async function serviceUpdataALLTextNodeColor(tabId, color) {
|
|
18
|
+
// Use "body, body *" to target all elements, with !important to override existing styles.
|
|
19
|
+
const css = `body, body * { color: ${color} !important; }`;
|
|
20
|
+
await applyGlobalCss(tabId, css);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Update the background color for all elements globally.
|
|
24
|
+
*/
|
|
25
|
+
export async function serviceUpdataALLNodeBackgroundColor(tabId, bgColor) {
|
|
26
|
+
// Apply background-color to all elements.
|
|
27
|
+
const css = `body, body * { background-color: ${bgColor} !important; }`;
|
|
28
|
+
await applyGlobalCss(tabId, css);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Adjust font size globally by a delta value.
|
|
32
|
+
* Uses recursive DOM traversal to support Shadow DOM elements.
|
|
33
|
+
*/
|
|
34
|
+
export async function serviceIncreaseAllTextFontSize(tabId, delta) {
|
|
35
|
+
await browser.scripting.executeScript({
|
|
36
|
+
target: { tabId },
|
|
37
|
+
args: [{ delta }],
|
|
38
|
+
func: (({ delta }) => {
|
|
39
|
+
/**
|
|
40
|
+
* Recursively traverse DOM and Shadow DOM to update font sizes.
|
|
41
|
+
*/
|
|
42
|
+
function walk(root) {
|
|
43
|
+
const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
|
|
44
|
+
let node;
|
|
45
|
+
while ((node = walker.nextNode())) {
|
|
46
|
+
const el = node;
|
|
47
|
+
// If the element has a Shadow Root, recurse into it to support web components.
|
|
48
|
+
if (el.shadowRoot) {
|
|
49
|
+
walk(el.shadowRoot);
|
|
50
|
+
}
|
|
51
|
+
// Calculate and update font size
|
|
52
|
+
const style = window.getComputedStyle(el);
|
|
53
|
+
const currentSize = parseFloat(style.fontSize);
|
|
54
|
+
if (!isNaN(currentSize)) {
|
|
55
|
+
el.style.setProperty('font-size', `${currentSize + delta}px`, 'important');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Start traversal from the document body
|
|
60
|
+
walk(document.body);
|
|
61
|
+
}), // Cast to any to bypass strict type checking for the execution closure
|
|
62
|
+
});
|
|
63
|
+
}
|
package/package.json
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vacantthinker/firefox-addon-framework-easy",
|
|
3
|
-
"version": "2026.0611.
|
|
3
|
+
"version": "2026.0611.1153",
|
|
4
4
|
"description": "",
|
|
5
|
-
"
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
6
11
|
"publishConfig": {
|
|
7
12
|
"access": "public",
|
|
8
13
|
"provenance": true
|
|
9
14
|
},
|
|
10
15
|
"scripts": {
|
|
11
|
-
"install_package": "npm install"
|
|
16
|
+
"install_package": "npm install",
|
|
17
|
+
"build": "tsc"
|
|
12
18
|
},
|
|
13
19
|
"author": "VacantThinker",
|
|
14
20
|
"license": "AGPL-3.0-only",
|
|
@@ -20,7 +26,8 @@
|
|
|
20
26
|
"bugs": {
|
|
21
27
|
"url": "https://github.com/VacantThinker/firefox-addon-framework-easy/issues"
|
|
22
28
|
},
|
|
23
|
-
"
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"typescript": "6.0.3",
|
|
24
31
|
"@types/firefox-webext-browser": "^143.0.0"
|
|
25
32
|
}
|
|
26
33
|
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
|
|
2
|
-
# For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages
|
|
3
|
-
|
|
4
|
-
name: Node.js Package
|
|
5
|
-
|
|
6
|
-
on:
|
|
7
|
-
push:
|
|
8
|
-
branches:
|
|
9
|
-
- main #
|
|
10
|
-
|
|
11
|
-
jobs:
|
|
12
|
-
build:
|
|
13
|
-
runs-on: ubuntu-latest
|
|
14
|
-
steps:
|
|
15
|
-
- uses: actions/checkout@v6
|
|
16
|
-
- uses: actions/setup-node@v6
|
|
17
|
-
with:
|
|
18
|
-
node-version: 24
|
|
19
|
-
- run: npm ci
|
|
20
|
-
|
|
21
|
-
publish-npm:
|
|
22
|
-
needs: build
|
|
23
|
-
runs-on: ubuntu-latest
|
|
24
|
-
permissions:
|
|
25
|
-
contents: read
|
|
26
|
-
id-token: write
|
|
27
|
-
steps:
|
|
28
|
-
- uses: actions/checkout@v6
|
|
29
|
-
- uses: actions/setup-node@v6
|
|
30
|
-
with:
|
|
31
|
-
node-version: 24
|
|
32
|
-
registry-url: https://registry.npmjs.org/
|
|
33
|
-
- run: npm ci
|
|
34
|
-
- run: npm publish --provenance --access public
|
package/index.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
export * from './src/BaseORM.js'
|
|
2
|
-
export * from './src/browserDownload.js'
|
|
3
|
-
export * from './src/browserNotification.js'
|
|
4
|
-
export * from './src/browserRuntime.js'
|
|
5
|
-
export * from './src/browserRuntimeOnMessageCommon.js'
|
|
6
|
-
export * from './src/browserTab.js'
|
|
7
|
-
export * from './src/DomainORM.js'
|
|
8
|
-
export * from './src/generate.js'
|
|
9
|
-
export * from './src/opStorage.js'
|
|
10
|
-
export * from './src/opTab.js'
|
|
11
|
-
export * from './src/serviceCommon.js'
|
|
12
|
-
export * from './src/serviceFetch.js'
|
|
13
|
-
export * from './src/serviceGet.js'
|
|
14
|
-
export * from './src/serviceOpContent.js'
|
|
15
|
-
export * from './src/serviceOpJavascript.js'
|
|
16
|
-
export * from './src/servicePure.js'
|
|
17
|
-
export * from './src/serviceUpdateTabStyle.js'
|
package/publish.sh
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
# 1. Get the current time (Format example: 2026.0522.0443)
|
|
4
|
-
# This includes the date, hour, and minute to ensure the version number increments every time you push.
|
|
5
|
-
NEW_VERSION=$(date +'%Y.%m%d.%H%M')
|
|
6
|
-
|
|
7
|
-
echo "🚀 Starting version update to: $NEW_VERSION"
|
|
8
|
-
|
|
9
|
-
# 2. Use jq to automatically modify the version field in package.json and write it back
|
|
10
|
-
jq ".version = \"$NEW_VERSION\"" package.json > package.json.tmp && mv package.json.tmp package.json
|
|
11
|
-
|
|
12
|
-
# 2.5 Automatically scan all js files in the src directory and generate export statements for index.js
|
|
13
|
-
echo "🚀 Dynamically generating index.js exports..."
|
|
14
|
-
true > index.js
|
|
15
|
-
for file in src/*.js; do
|
|
16
|
-
if [ -f "$file" ]; then
|
|
17
|
-
echo "export * from './$file'" >> index.js
|
|
18
|
-
fi
|
|
19
|
-
done
|
|
20
|
-
|
|
21
|
-
# 2.6 Automatically reset README.md and append the scanned public functions
|
|
22
|
-
echo "📝 Updating README.md..."
|
|
23
|
-
|
|
24
|
-
# 1. Write the default fixed content
|
|
25
|
-
cat << 'EOF' > README.md
|
|
26
|
-
# firefox-addon-framework-easy
|
|
27
|
-
|
|
28
|
-
[](https://gnu.org)
|
|
29
|
-
|
|
30
|
-
## License
|
|
31
|
-
|
|
32
|
-
This project is licensed under the GNU Affero General Public License v3.0 - see the [LICENSE](LICENSE) file for details.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
## Source Code Access
|
|
36
|
-
|
|
37
|
-
According to the terms of the AGPL-3.0, the source code for this network service must be made available to all users.
|
|
38
|
-
|
|
39
|
-
You can download, clone, or view the complete source code for this application here: [Insert your GitHub/GitLab URL].
|
|
40
|
-
|
|
41
|
-
## API Reference (Auto-Generated)
|
|
42
|
-
|
|
43
|
-
Below is a list of all public functions found inside the `src` directory:
|
|
44
|
-
|
|
45
|
-
EOF
|
|
46
|
-
|
|
47
|
-
# 2. Iterate through all js files, capture multi-line signatures, and append them to README
|
|
48
|
-
for file in src/*.js; do
|
|
49
|
-
if [ -f "$file" ]; then
|
|
50
|
-
echo "### 📄 File: \`$file\`" >> README.md
|
|
51
|
-
echo "\`\`\`javascript" >> README.md
|
|
52
|
-
|
|
53
|
-
# Reads multi-line signatures up to the opening brace or semicolon
|
|
54
|
-
awk '/^export (async )?(function|const|let|var|class) / {
|
|
55
|
-
line = $0
|
|
56
|
-
# Keep reading lines if there is no opening brace or semicolon yet
|
|
57
|
-
while (line !~ /\{/ && line !~ /;/ && (getline next_line) > 0) {
|
|
58
|
-
line = line "\n" next_line
|
|
59
|
-
}
|
|
60
|
-
# Clean up the function body and replace it with standard closing
|
|
61
|
-
sub(/\{.*/, "{ }", line)
|
|
62
|
-
print line
|
|
63
|
-
print ""
|
|
64
|
-
}' "$file" >> README.md
|
|
65
|
-
|
|
66
|
-
echo "\`\`\`" >> README.md
|
|
67
|
-
echo "" >> README.md
|
|
68
|
-
fi
|
|
69
|
-
done
|
|
70
|
-
|
|
71
|
-
# 3. Check if there are any code changes to commit
|
|
72
|
-
git add .
|
|
73
|
-
|
|
74
|
-
# 4. Automatically commit and log this release version
|
|
75
|
-
git commit -m "chore: bump version to $NEW_VERSION and publish"
|
|
76
|
-
|
|
77
|
-
# 5. Push to GitHub to trigger cloud-based automated publishing
|
|
78
|
-
echo "📤 Pushing to GitHub..."
|
|
79
|
-
git push origin main
|
|
80
|
-
|
|
81
|
-
echo "✅ Push complete! Please check the GitHub Actions page to view the deployment status."
|