@skrillex1224/chrome-article-publish-extension 1.0.0
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 +226 -0
- package/dist/adapters/index.d.ts +1125 -0
- package/dist/adapters/index.js +1900 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/chunk-7Q3KKWUB.js +602 -0
- package/dist/chunk-7Q3KKWUB.js.map +1 -0
- package/dist/chunk-IUAZAMKF.js +123 -0
- package/dist/chunk-IUAZAMKF.js.map +1 -0
- package/dist/chunk-QM2T4VGM.js +6688 -0
- package/dist/chunk-QM2T4VGM.js.map +1 -0
- package/dist/chunk-UO6CDRC5.js +322 -0
- package/dist/chunk-UO6CDRC5.js.map +1 -0
- package/dist/converter/index.d.ts +2 -0
- package/dist/converter/index.html +10 -0
- package/dist/converter/index.js +432 -0
- package/dist/converter/index.js.map +1 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.js +192 -0
- package/dist/index.js.map +1 -0
- package/dist/interface-BlLeWXzS.d.ts +265 -0
- package/dist/preprocessor/index.html +10 -0
- package/dist/runtime/index.d.ts +90 -0
- package/dist/runtime/index.js +9 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/types-BAnNlLpy.d.ts +140 -0
- package/package.json +54 -0
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
// src/runtime/chrome-extension.ts
|
|
2
|
+
var DEFAULT_FETCH_TIMEOUT = 30 * 1e3;
|
|
3
|
+
var MARKDOWN_CONVERTER_URL = "converter/index.html";
|
|
4
|
+
var ExtensionRuntime = class {
|
|
5
|
+
constructor(config) {
|
|
6
|
+
this.config = config;
|
|
7
|
+
}
|
|
8
|
+
config;
|
|
9
|
+
type = "extension";
|
|
10
|
+
// 使用时间戳+随机数避免规则 ID 冲突(扩展重载或并发场景)
|
|
11
|
+
ruleIdBase = Date.now() % 1e5;
|
|
12
|
+
ruleIdCounter = 0;
|
|
13
|
+
/**
|
|
14
|
+
* HTTP 请求 - 自动携带 cookies,带超时保护
|
|
15
|
+
*/
|
|
16
|
+
async fetch(url, options) {
|
|
17
|
+
const timeout = this.config?.timeout ?? DEFAULT_FETCH_TIMEOUT;
|
|
18
|
+
const controller = new AbortController();
|
|
19
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
20
|
+
try {
|
|
21
|
+
const response = await fetch(url, {
|
|
22
|
+
...options,
|
|
23
|
+
credentials: "include",
|
|
24
|
+
signal: controller.signal
|
|
25
|
+
});
|
|
26
|
+
return response;
|
|
27
|
+
} catch (error) {
|
|
28
|
+
if (error.name === "AbortError") {
|
|
29
|
+
throw new Error(`\u8BF7\u6C42\u8D85\u65F6\uFF08${timeout / 1e3}\u79D2\uFF09: ${url}`);
|
|
30
|
+
}
|
|
31
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
32
|
+
throw new Error(`${message}: ${url}`);
|
|
33
|
+
} finally {
|
|
34
|
+
clearTimeout(timeoutId);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Cookie 管理
|
|
39
|
+
*/
|
|
40
|
+
cookies = {
|
|
41
|
+
async get(domain) {
|
|
42
|
+
const cookies = await chrome.cookies.getAll({ domain });
|
|
43
|
+
return cookies.map((c) => ({
|
|
44
|
+
name: c.name,
|
|
45
|
+
value: c.value,
|
|
46
|
+
domain: c.domain,
|
|
47
|
+
path: c.path,
|
|
48
|
+
secure: c.secure,
|
|
49
|
+
httpOnly: c.httpOnly,
|
|
50
|
+
expirationDate: c.expirationDate
|
|
51
|
+
}));
|
|
52
|
+
},
|
|
53
|
+
async set(cookie) {
|
|
54
|
+
await chrome.cookies.set({
|
|
55
|
+
url: `https://${cookie.domain}${cookie.path || "/"}`,
|
|
56
|
+
name: cookie.name,
|
|
57
|
+
value: cookie.value,
|
|
58
|
+
domain: cookie.domain,
|
|
59
|
+
path: cookie.path,
|
|
60
|
+
secure: cookie.secure,
|
|
61
|
+
httpOnly: cookie.httpOnly,
|
|
62
|
+
expirationDate: cookie.expirationDate
|
|
63
|
+
});
|
|
64
|
+
},
|
|
65
|
+
async remove(name, domain) {
|
|
66
|
+
await chrome.cookies.remove({
|
|
67
|
+
url: `https://${domain}`,
|
|
68
|
+
name
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* 获取单个 Cookie 值(便捷方法)
|
|
74
|
+
*/
|
|
75
|
+
async getCookie(domain, name) {
|
|
76
|
+
const cookies = await chrome.cookies.getAll({ domain, name });
|
|
77
|
+
return cookies.length > 0 ? cookies[0].value : null;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* 持久化存储
|
|
81
|
+
*/
|
|
82
|
+
storage = {
|
|
83
|
+
async get(key) {
|
|
84
|
+
const result = await chrome.storage.local.get(key);
|
|
85
|
+
return result[key] ?? null;
|
|
86
|
+
},
|
|
87
|
+
async set(key, value) {
|
|
88
|
+
await chrome.storage.local.set({ [key]: value });
|
|
89
|
+
},
|
|
90
|
+
async remove(key) {
|
|
91
|
+
await chrome.storage.local.remove(key);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* 会话存储
|
|
96
|
+
*/
|
|
97
|
+
session = {
|
|
98
|
+
async get(key) {
|
|
99
|
+
const result = await chrome.storage.session.get(key);
|
|
100
|
+
return result[key] ?? null;
|
|
101
|
+
},
|
|
102
|
+
async set(key, value) {
|
|
103
|
+
await chrome.storage.session.set({ [key]: value });
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* Header 规则管理 (declarativeNetRequest)
|
|
108
|
+
* 规则只对扩展自身发起的请求生效,不影响其他网页
|
|
109
|
+
*/
|
|
110
|
+
headerRules = {
|
|
111
|
+
add: async (rule) => {
|
|
112
|
+
const ruleId = this.ruleIdBase + this.ruleIdCounter++;
|
|
113
|
+
const id = `rule_${ruleId}`;
|
|
114
|
+
await chrome.declarativeNetRequest.updateDynamicRules({
|
|
115
|
+
removeRuleIds: [ruleId]
|
|
116
|
+
});
|
|
117
|
+
await chrome.declarativeNetRequest.updateDynamicRules({
|
|
118
|
+
addRules: [
|
|
119
|
+
{
|
|
120
|
+
id: ruleId,
|
|
121
|
+
priority: 1,
|
|
122
|
+
action: {
|
|
123
|
+
type: chrome.declarativeNetRequest.RuleActionType.MODIFY_HEADERS,
|
|
124
|
+
requestHeaders: Object.entries(rule.headers).map(
|
|
125
|
+
([header, value]) => ({
|
|
126
|
+
header,
|
|
127
|
+
operation: chrome.declarativeNetRequest.HeaderOperation.SET,
|
|
128
|
+
value
|
|
129
|
+
})
|
|
130
|
+
)
|
|
131
|
+
},
|
|
132
|
+
condition: {
|
|
133
|
+
urlFilter: rule.urlFilter,
|
|
134
|
+
// 只对扩展自身发起的请求生效,不影响其他网页
|
|
135
|
+
initiatorDomains: [chrome.runtime.id],
|
|
136
|
+
resourceTypes: rule.resourceTypes || [
|
|
137
|
+
"xmlhttprequest"
|
|
138
|
+
]
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
]
|
|
142
|
+
});
|
|
143
|
+
return id;
|
|
144
|
+
},
|
|
145
|
+
remove: async (ruleId) => {
|
|
146
|
+
const id = parseInt(ruleId.replace("rule_", ""), 10);
|
|
147
|
+
await chrome.declarativeNetRequest.updateDynamicRules({
|
|
148
|
+
removeRuleIds: [id]
|
|
149
|
+
});
|
|
150
|
+
},
|
|
151
|
+
clear: async () => {
|
|
152
|
+
const rules = await chrome.declarativeNetRequest.getDynamicRules();
|
|
153
|
+
await chrome.declarativeNetRequest.updateDynamicRules({
|
|
154
|
+
removeRuleIds: rules.map((r) => r.id)
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* 文件下载
|
|
160
|
+
* Service Worker 中不支持 URL.createObjectURL,使用 data URL 替代
|
|
161
|
+
*/
|
|
162
|
+
downloads = {
|
|
163
|
+
async download(blob, filename, saveAs = true) {
|
|
164
|
+
const buffer = await blob.arrayBuffer();
|
|
165
|
+
const base64 = btoa(
|
|
166
|
+
new Uint8Array(buffer).reduce((data, byte) => data + String.fromCharCode(byte), "")
|
|
167
|
+
);
|
|
168
|
+
const mimeType = blob.type || "application/octet-stream";
|
|
169
|
+
const dataUrl = `data:${mimeType};base64,${base64}`;
|
|
170
|
+
const downloadId = await chrome.downloads.download({
|
|
171
|
+
url: dataUrl,
|
|
172
|
+
filename,
|
|
173
|
+
saveAs
|
|
174
|
+
});
|
|
175
|
+
return downloadId;
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
async renderMarkdownToImages(markdown, options) {
|
|
179
|
+
let windowId;
|
|
180
|
+
let tabId;
|
|
181
|
+
const requestId = `md-img-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
182
|
+
try {
|
|
183
|
+
const win = await chrome.windows.create({
|
|
184
|
+
url: chrome.runtime.getURL(MARKDOWN_CONVERTER_URL),
|
|
185
|
+
type: "popup",
|
|
186
|
+
width: 1,
|
|
187
|
+
height: 1,
|
|
188
|
+
left: 0,
|
|
189
|
+
top: 0,
|
|
190
|
+
focused: false
|
|
191
|
+
});
|
|
192
|
+
windowId = win.id;
|
|
193
|
+
tabId = win.tabs?.[0]?.id;
|
|
194
|
+
if (!tabId) throw new Error("Markdown \u8F6C\u56FE\u7A97\u53E3\u521B\u5EFA\u5931\u8D25");
|
|
195
|
+
await this.waitForTabComplete(tabId, 1e4);
|
|
196
|
+
const response = await this.sendRuntimeMessageWithTimeout({
|
|
197
|
+
type: "RENDER_MARKDOWN_TO_IMAGES",
|
|
198
|
+
target: "markdown-image-converter",
|
|
199
|
+
requestId,
|
|
200
|
+
payload: { markdown, ...options }
|
|
201
|
+
}, 6e4);
|
|
202
|
+
if (response?.requestId && response.requestId !== requestId) {
|
|
203
|
+
throw new Error("Markdown \u8F6C\u56FE\u54CD\u5E94 ID \u4E0D\u5339\u914D");
|
|
204
|
+
}
|
|
205
|
+
if (response?.error) {
|
|
206
|
+
throw new Error(response.error);
|
|
207
|
+
}
|
|
208
|
+
if (!response?.images?.length) {
|
|
209
|
+
throw new Error("Markdown \u8F6C\u56FE\u672A\u751F\u6210\u56FE\u7247");
|
|
210
|
+
}
|
|
211
|
+
return response.images;
|
|
212
|
+
} finally {
|
|
213
|
+
if (windowId) {
|
|
214
|
+
chrome.windows.remove(windowId).catch(() => {
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Tab 管理
|
|
221
|
+
*/
|
|
222
|
+
tabs = {
|
|
223
|
+
async query(urlPattern) {
|
|
224
|
+
const tabs = await chrome.tabs.query({ url: urlPattern });
|
|
225
|
+
return tabs.filter((t) => t.id !== void 0).map((t) => ({ id: t.id, url: t.url }));
|
|
226
|
+
},
|
|
227
|
+
async create(url, active = false) {
|
|
228
|
+
const tab = await chrome.tabs.create({ url, active });
|
|
229
|
+
return { id: tab.id };
|
|
230
|
+
},
|
|
231
|
+
async waitForLoad(tabId, timeout = 3e4) {
|
|
232
|
+
return new Promise((resolve, reject) => {
|
|
233
|
+
const timeoutId = setTimeout(() => {
|
|
234
|
+
chrome.tabs.onUpdated.removeListener(listener);
|
|
235
|
+
reject(new Error("Tab load timeout"));
|
|
236
|
+
}, timeout);
|
|
237
|
+
const listener = (updatedTabId, info) => {
|
|
238
|
+
if (updatedTabId === tabId && info.status === "complete") {
|
|
239
|
+
clearTimeout(timeoutId);
|
|
240
|
+
chrome.tabs.onUpdated.removeListener(listener);
|
|
241
|
+
setTimeout(resolve, 1e3);
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
chrome.tabs.onUpdated.addListener(listener);
|
|
245
|
+
});
|
|
246
|
+
},
|
|
247
|
+
async executeScript(tabId, func, args) {
|
|
248
|
+
const results = await chrome.scripting.executeScript({
|
|
249
|
+
target: { tabId },
|
|
250
|
+
world: "MAIN",
|
|
251
|
+
func,
|
|
252
|
+
args
|
|
253
|
+
});
|
|
254
|
+
const result = results[0]?.result;
|
|
255
|
+
return result;
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
/**
|
|
259
|
+
* DOM 操作 - 通过 Offscreen Document 实现
|
|
260
|
+
*/
|
|
261
|
+
dom = {
|
|
262
|
+
parseHTML: async (html) => {
|
|
263
|
+
const parser = new DOMParser();
|
|
264
|
+
return parser.parseFromString(html, "text/html");
|
|
265
|
+
},
|
|
266
|
+
querySelector: (doc, selector) => {
|
|
267
|
+
return doc.querySelector(selector);
|
|
268
|
+
},
|
|
269
|
+
querySelectorAll: (doc, selector) => {
|
|
270
|
+
return Array.from(doc.querySelectorAll(selector));
|
|
271
|
+
},
|
|
272
|
+
getTextContent: (element) => {
|
|
273
|
+
return element.textContent || "";
|
|
274
|
+
},
|
|
275
|
+
getInnerHTML: (element) => {
|
|
276
|
+
return element.innerHTML;
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
async waitForTabComplete(tabId, timeout = 1e4) {
|
|
280
|
+
const tab = await chrome.tabs.get(tabId);
|
|
281
|
+
if (tab.status === "complete") return;
|
|
282
|
+
return new Promise((resolve, reject) => {
|
|
283
|
+
const timeoutId = setTimeout(() => {
|
|
284
|
+
chrome.tabs.onUpdated.removeListener(listener);
|
|
285
|
+
reject(new Error("Markdown \u8F6C\u56FE\u9875\u9762\u52A0\u8F7D\u8D85\u65F6"));
|
|
286
|
+
}, timeout);
|
|
287
|
+
const listener = (updatedTabId, info) => {
|
|
288
|
+
if (updatedTabId === tabId && info.status === "complete") {
|
|
289
|
+
chrome.tabs.onUpdated.removeListener(listener);
|
|
290
|
+
clearTimeout(timeoutId);
|
|
291
|
+
resolve();
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
chrome.tabs.onUpdated.addListener(listener);
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
async sendRuntimeMessageWithTimeout(message, timeout = 6e4) {
|
|
298
|
+
return new Promise((resolve, reject) => {
|
|
299
|
+
const timeoutId = setTimeout(() => {
|
|
300
|
+
reject(new Error("Markdown \u8F6C\u56FE\u54CD\u5E94\u8D85\u65F6"));
|
|
301
|
+
}, timeout);
|
|
302
|
+
chrome.runtime.sendMessage(message, (response) => {
|
|
303
|
+
clearTimeout(timeoutId);
|
|
304
|
+
const error = chrome.runtime.lastError;
|
|
305
|
+
if (error) {
|
|
306
|
+
reject(new Error(error.message));
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
resolve(response);
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
function createExtensionRuntime(config) {
|
|
315
|
+
return new ExtensionRuntime(config);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export {
|
|
319
|
+
ExtensionRuntime,
|
|
320
|
+
createExtensionRuntime
|
|
321
|
+
};
|
|
322
|
+
//# sourceMappingURL=chunk-UO6CDRC5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/runtime/chrome-extension.ts"],"sourcesContent":["import type {\n RuntimeInterface,\n RuntimeConfig,\n MarkdownImageRenderOptions,\n MarkdownImageRenderResult,\n} from './interface'\nimport type { Cookie, HeaderRule } from '../types'\n\n/**\n * Chrome 扩展运行时实现\n */\n// 默认请求超时:30 秒\nconst DEFAULT_FETCH_TIMEOUT = 30 * 1000\nconst MARKDOWN_CONVERTER_URL = 'converter/index.html'\n\nexport class ExtensionRuntime implements RuntimeInterface {\n readonly type = 'extension' as const\n // 使用时间戳+随机数避免规则 ID 冲突(扩展重载或并发场景)\n private ruleIdBase = Date.now() % 100000\n private ruleIdCounter = 0\n\n constructor(private config?: RuntimeConfig) {}\n\n /**\n * HTTP 请求 - 自动携带 cookies,带超时保护\n */\n async fetch(url: string, options?: RequestInit): Promise<Response> {\n const timeout = this.config?.timeout ?? DEFAULT_FETCH_TIMEOUT\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), timeout)\n\n try {\n const response = await fetch(url, {\n ...options,\n credentials: 'include',\n signal: controller.signal,\n })\n return response\n } catch (error) {\n if ((error as Error).name === 'AbortError') {\n throw new Error(`请求超时(${timeout / 1000}秒): ${url}`)\n }\n const message = error instanceof Error ? error.message : String(error)\n throw new Error(`${message}: ${url}`)\n } finally {\n clearTimeout(timeoutId)\n }\n }\n\n /**\n * Cookie 管理\n */\n cookies = {\n async get(domain: string): Promise<Cookie[]> {\n const cookies = await chrome.cookies.getAll({ domain })\n return cookies.map(c => ({\n name: c.name,\n value: c.value,\n domain: c.domain,\n path: c.path,\n secure: c.secure,\n httpOnly: c.httpOnly,\n expirationDate: c.expirationDate,\n }))\n },\n\n async set(cookie: Cookie): Promise<void> {\n await chrome.cookies.set({\n url: `https://${cookie.domain}${cookie.path || '/'}`,\n name: cookie.name,\n value: cookie.value,\n domain: cookie.domain,\n path: cookie.path,\n secure: cookie.secure,\n httpOnly: cookie.httpOnly,\n expirationDate: cookie.expirationDate,\n })\n },\n\n async remove(name: string, domain: string): Promise<void> {\n await chrome.cookies.remove({\n url: `https://${domain}`,\n name,\n })\n },\n }\n\n /**\n * 获取单个 Cookie 值(便捷方法)\n */\n async getCookie(domain: string, name: string): Promise<string | null> {\n const cookies = await chrome.cookies.getAll({ domain, name })\n return cookies.length > 0 ? cookies[0].value : null\n }\n\n /**\n * 持久化存储\n */\n storage = {\n async get<T>(key: string): Promise<T | null> {\n const result = await chrome.storage.local.get(key)\n return (result[key] as T) ?? null\n },\n\n async set<T>(key: string, value: T): Promise<void> {\n await chrome.storage.local.set({ [key]: value })\n },\n\n async remove(key: string): Promise<void> {\n await chrome.storage.local.remove(key)\n },\n }\n\n /**\n * 会话存储\n */\n session = {\n async get<T>(key: string): Promise<T | null> {\n const result = await chrome.storage.session.get(key)\n return (result[key] as T) ?? null\n },\n\n async set<T>(key: string, value: T): Promise<void> {\n await chrome.storage.session.set({ [key]: value })\n },\n }\n\n /**\n * Header 规则管理 (declarativeNetRequest)\n * 规则只对扩展自身发起的请求生效,不影响其他网页\n */\n headerRules = {\n add: async (rule: HeaderRule): Promise<string> => {\n // 组合 base + counter 生成唯一 ID,避免冲突\n const ruleId = this.ruleIdBase + this.ruleIdCounter++\n const id = `rule_${ruleId}`\n\n await chrome.declarativeNetRequest.updateDynamicRules({\n removeRuleIds: [ruleId],\n })\n\n await chrome.declarativeNetRequest.updateDynamicRules({\n addRules: [\n {\n id: ruleId,\n priority: 1,\n action: {\n type: chrome.declarativeNetRequest.RuleActionType.MODIFY_HEADERS,\n requestHeaders: Object.entries(rule.headers).map(\n ([header, value]) => ({\n header,\n operation: chrome.declarativeNetRequest.HeaderOperation.SET,\n value,\n })\n ),\n },\n condition: {\n urlFilter: rule.urlFilter,\n // 只对扩展自身发起的请求生效,不影响其他网页\n initiatorDomains: [chrome.runtime.id],\n resourceTypes: (rule.resourceTypes || [\n 'xmlhttprequest',\n ]) as chrome.declarativeNetRequest.ResourceType[],\n },\n },\n ],\n })\n\n return id\n },\n\n remove: async (ruleId: string): Promise<void> => {\n const id = parseInt(ruleId.replace('rule_', ''), 10)\n await chrome.declarativeNetRequest.updateDynamicRules({\n removeRuleIds: [id],\n })\n },\n\n clear: async (): Promise<void> => {\n const rules = await chrome.declarativeNetRequest.getDynamicRules()\n await chrome.declarativeNetRequest.updateDynamicRules({\n removeRuleIds: rules.map(r => r.id),\n })\n },\n }\n\n /**\n * 文件下载\n * Service Worker 中不支持 URL.createObjectURL,使用 data URL 替代\n */\n downloads = {\n async download(blob: Blob, filename: string, saveAs = true): Promise<number> {\n // 将 Blob 转换为 data URL\n const buffer = await blob.arrayBuffer()\n const base64 = btoa(\n new Uint8Array(buffer).reduce((data, byte) => data + String.fromCharCode(byte), '')\n )\n const mimeType = blob.type || 'application/octet-stream'\n const dataUrl = `data:${mimeType};base64,${base64}`\n\n const downloadId = await chrome.downloads.download({\n url: dataUrl,\n filename,\n saveAs,\n })\n return downloadId\n },\n }\n\n async renderMarkdownToImages(\n markdown: string,\n options?: MarkdownImageRenderOptions,\n ): Promise<MarkdownImageRenderResult[]> {\n let windowId: number | undefined\n let tabId: number | undefined\n const requestId = `md-img-${Date.now()}-${Math.random().toString(36).slice(2)}`\n\n try {\n const win = await chrome.windows.create({\n url: chrome.runtime.getURL(MARKDOWN_CONVERTER_URL),\n type: 'popup',\n width: 1,\n height: 1,\n left: 0,\n top: 0,\n focused: false,\n })\n windowId = win.id\n tabId = win.tabs?.[0]?.id\n if (!tabId) throw new Error('Markdown 转图窗口创建失败')\n\n await this.waitForTabComplete(tabId, 10000)\n\n const response = await this.sendRuntimeMessageWithTimeout<{\n requestId?: string\n images?: MarkdownImageRenderResult[]\n error?: string\n }>({\n type: 'RENDER_MARKDOWN_TO_IMAGES',\n target: 'markdown-image-converter',\n requestId,\n payload: { markdown, ...options },\n }, 60000)\n\n if (response?.requestId && response.requestId !== requestId) {\n throw new Error('Markdown 转图响应 ID 不匹配')\n }\n if (response?.error) {\n throw new Error(response.error)\n }\n if (!response?.images?.length) {\n throw new Error('Markdown 转图未生成图片')\n }\n return response.images\n } finally {\n if (windowId) {\n chrome.windows.remove(windowId).catch(() => {})\n }\n }\n }\n\n /**\n * Tab 管理\n */\n tabs = {\n async query(urlPattern: string): Promise<Array<{ id: number; url?: string }>> {\n const tabs = await chrome.tabs.query({ url: urlPattern })\n return tabs.filter(t => t.id !== undefined).map(t => ({ id: t.id!, url: t.url }))\n },\n\n async create(url: string, active = false): Promise<{ id: number }> {\n const tab = await chrome.tabs.create({ url, active })\n return { id: tab.id! }\n },\n\n async waitForLoad(tabId: number, timeout = 30000): Promise<void> {\n return new Promise((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n chrome.tabs.onUpdated.removeListener(listener)\n reject(new Error('Tab load timeout'))\n }, timeout)\n\n const listener = (updatedTabId: number, info: chrome.tabs.TabChangeInfo) => {\n if (updatedTabId === tabId && info.status === 'complete') {\n clearTimeout(timeoutId)\n chrome.tabs.onUpdated.removeListener(listener)\n // 额外等待让页面 JS 初始化\n setTimeout(resolve, 1000)\n }\n }\n chrome.tabs.onUpdated.addListener(listener)\n })\n },\n\n async executeScript<T, A extends unknown[]>(\n tabId: number,\n func: (...args: A) => T | Promise<T>,\n args: A\n ): Promise<T> {\n const results = await chrome.scripting.executeScript({\n target: { tabId },\n world: 'MAIN',\n func: func as (...args: unknown[]) => unknown,\n args: args as unknown[],\n })\n\n const result = results[0]?.result as T\n return result\n },\n }\n\n /**\n * DOM 操作 - 通过 Offscreen Document 实现\n */\n dom = {\n parseHTML: async (html: string): Promise<Document> => {\n const parser = new DOMParser()\n return parser.parseFromString(html, 'text/html')\n },\n\n querySelector: (doc: Document, selector: string): Element | null => {\n return doc.querySelector(selector)\n },\n\n querySelectorAll: (doc: Document, selector: string): Element[] => {\n return Array.from(doc.querySelectorAll(selector))\n },\n\n getTextContent: (element: Element): string => {\n return element.textContent || ''\n },\n\n getInnerHTML: (element: Element): string => {\n return element.innerHTML\n },\n }\n\n private async waitForTabComplete(tabId: number, timeout = 10000): Promise<void> {\n const tab = await chrome.tabs.get(tabId)\n if (tab.status === 'complete') return\n\n return new Promise((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n chrome.tabs.onUpdated.removeListener(listener)\n reject(new Error('Markdown 转图页面加载超时'))\n }, timeout)\n\n const listener = (updatedTabId: number, info: chrome.tabs.TabChangeInfo) => {\n if (updatedTabId === tabId && info.status === 'complete') {\n chrome.tabs.onUpdated.removeListener(listener)\n clearTimeout(timeoutId)\n resolve()\n }\n }\n chrome.tabs.onUpdated.addListener(listener)\n })\n }\n\n private async sendRuntimeMessageWithTimeout<T>(message: unknown, timeout = 60000): Promise<T> {\n return new Promise((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n reject(new Error('Markdown 转图响应超时'))\n }, timeout)\n\n chrome.runtime.sendMessage(message, (response) => {\n clearTimeout(timeoutId)\n const error = chrome.runtime.lastError\n if (error) {\n reject(new Error(error.message))\n return\n }\n resolve(response as T)\n })\n })\n }\n}\n\n/**\n * 创建扩展运行时实例\n */\nexport function createExtensionRuntime(config?: RuntimeConfig): ExtensionRuntime {\n return new ExtensionRuntime(config)\n}\n"],"mappings":"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","names":[]}
|