@hoardodile/sdk-web 0.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/LICENSE +18 -0
- package/README.md +47 -0
- package/dist/index.d.ts +727 -0
- package/dist/index.js +683 -0
- package/dist/index.js.map +1 -0
- package/package.json +50 -0
- package/src/bridge.ts +178 -0
- package/src/codecs.ts +54 -0
- package/src/fixtures.ts +154 -0
- package/src/index.ts +92 -0
- package/src/lifecycle.ts +200 -0
- package/src/protocol.ts +448 -0
- package/src/runtime.test.ts +63 -0
- package/src/runtime.ts +279 -0
- package/src/stores.ts +103 -0
- package/src/types.ts +252 -0
- package/src/urls.ts +66 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,683 @@
|
|
|
1
|
+
import { imageVariantQuery } from '@hoardodile/sdk-types/image-variant';
|
|
2
|
+
|
|
3
|
+
// src/protocol.ts
|
|
4
|
+
var PROTOCOL_VERSION = 1;
|
|
5
|
+
var pluginThemePalettes = [
|
|
6
|
+
"mono",
|
|
7
|
+
"sage",
|
|
8
|
+
"parchment",
|
|
9
|
+
"azure",
|
|
10
|
+
"hoardodile"
|
|
11
|
+
];
|
|
12
|
+
var hostPushKeys = {
|
|
13
|
+
context: "context",
|
|
14
|
+
visibility: "visibility",
|
|
15
|
+
themeChanged: "themeChanged",
|
|
16
|
+
fontsChanged: "fontsChanged",
|
|
17
|
+
languageChanged: "languageChanged",
|
|
18
|
+
prefsChanged: "prefsChanged",
|
|
19
|
+
cacheChanged: "cacheChanged",
|
|
20
|
+
anchorJump: "anchorJump",
|
|
21
|
+
resInvalidate: "res:invalidate",
|
|
22
|
+
resourcesInvalidate: "resources:invalidate",
|
|
23
|
+
messagesInvalidate: "messages:invalidate",
|
|
24
|
+
danmakuInvalidate: "danmaku:invalidate"
|
|
25
|
+
};
|
|
26
|
+
var pluginMethods = {
|
|
27
|
+
// Files
|
|
28
|
+
readFile: "readFile",
|
|
29
|
+
listFiles: "listFiles",
|
|
30
|
+
// Messages
|
|
31
|
+
listMessages: "listMessages",
|
|
32
|
+
createMessage: "createMessage",
|
|
33
|
+
// Danmaku
|
|
34
|
+
listDanmaku: "listDanmaku",
|
|
35
|
+
createDanmaku: "createDanmaku",
|
|
36
|
+
// Preferences / cache
|
|
37
|
+
setPref: "setPref",
|
|
38
|
+
setCache: "setCache",
|
|
39
|
+
// Cache invalidation
|
|
40
|
+
invalidate: "invalidate",
|
|
41
|
+
// Plugin asset vault
|
|
42
|
+
download: "download",
|
|
43
|
+
deleteAsset: "deleteAsset",
|
|
44
|
+
// Logging — must match the PluginRequests keys exactly,
|
|
45
|
+
// otherwise plugin log calls are silently swallowed.
|
|
46
|
+
logInfo: "logInfo",
|
|
47
|
+
logWarn: "logWarn",
|
|
48
|
+
logError: "logError"
|
|
49
|
+
};
|
|
50
|
+
var invalidatePushKeys = {
|
|
51
|
+
resource: hostPushKeys.resInvalidate,
|
|
52
|
+
resources: hostPushKeys.resourcesInvalidate,
|
|
53
|
+
messages: hostPushKeys.messagesInvalidate,
|
|
54
|
+
danmaku: hostPushKeys.danmakuInvalidate
|
|
55
|
+
};
|
|
56
|
+
var pluginRequestTimeouts = {
|
|
57
|
+
readFile: 12e4,
|
|
58
|
+
download: 3e5
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// src/bridge.ts
|
|
62
|
+
var REQUEST_TIMEOUT_MS = 1e4;
|
|
63
|
+
var nextId = 1;
|
|
64
|
+
var hostBridge;
|
|
65
|
+
function isRecord(value) {
|
|
66
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
67
|
+
}
|
|
68
|
+
function isValidHostMessage(msg) {
|
|
69
|
+
return isRecord(msg) && "type" in msg;
|
|
70
|
+
}
|
|
71
|
+
function ensureHostBridge() {
|
|
72
|
+
if (hostBridge !== void 0) return hostBridge;
|
|
73
|
+
const pending = /* @__PURE__ */ new Map();
|
|
74
|
+
const subscribers = /* @__PURE__ */ new Map();
|
|
75
|
+
window.addEventListener(
|
|
76
|
+
"message",
|
|
77
|
+
function handleMessage(event) {
|
|
78
|
+
const selfEmbedded = window.parent === window;
|
|
79
|
+
if (event.source !== window.parent && !(selfEmbedded && event.source === null)) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const msg = event.data;
|
|
83
|
+
if (!isValidHostMessage(msg)) return;
|
|
84
|
+
if (msg.type === "response") {
|
|
85
|
+
const entry = pending.get(msg.id);
|
|
86
|
+
if (entry === void 0) return;
|
|
87
|
+
pending.delete(msg.id);
|
|
88
|
+
clearTimeout(entry.timeoutId);
|
|
89
|
+
if (msg.ok) {
|
|
90
|
+
entry.resolve(msg.data);
|
|
91
|
+
} else {
|
|
92
|
+
const err = new Error(msg.error ?? "Unknown error");
|
|
93
|
+
const code = msg.errorCode ?? msg.errorName;
|
|
94
|
+
if (code !== void 0 && code.length > 0) {
|
|
95
|
+
err.name = code;
|
|
96
|
+
}
|
|
97
|
+
entry.reject(err);
|
|
98
|
+
}
|
|
99
|
+
} else if (msg.type === "push") {
|
|
100
|
+
const handlers = subscribers.get(msg.key);
|
|
101
|
+
if (handlers !== void 0) {
|
|
102
|
+
for (const handler of handlers) {
|
|
103
|
+
handler(msg.data);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
);
|
|
109
|
+
function request(resId, method, ...args) {
|
|
110
|
+
const id = nextId++;
|
|
111
|
+
const input = args[0];
|
|
112
|
+
const timeoutMs = pluginRequestTimeouts[method] ?? REQUEST_TIMEOUT_MS;
|
|
113
|
+
const params = input;
|
|
114
|
+
return new Promise((resolve, reject) => {
|
|
115
|
+
const timeoutId = setTimeout(() => {
|
|
116
|
+
pending.delete(id);
|
|
117
|
+
reject(new Error(`Request timed out: ${String(method)}`));
|
|
118
|
+
}, timeoutMs);
|
|
119
|
+
pending.set(id, {
|
|
120
|
+
resolve,
|
|
121
|
+
reject,
|
|
122
|
+
timeoutId
|
|
123
|
+
});
|
|
124
|
+
const message = {
|
|
125
|
+
type: "request",
|
|
126
|
+
id,
|
|
127
|
+
method,
|
|
128
|
+
params,
|
|
129
|
+
proto: PROTOCOL_VERSION,
|
|
130
|
+
resId
|
|
131
|
+
};
|
|
132
|
+
window.parent.postMessage(message, "*");
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
function subscribe(key, handler) {
|
|
136
|
+
const keyString = key;
|
|
137
|
+
let handlers = subscribers.get(keyString);
|
|
138
|
+
if (handlers === void 0) {
|
|
139
|
+
handlers = /* @__PURE__ */ new Set();
|
|
140
|
+
subscribers.set(keyString, handlers);
|
|
141
|
+
const message = {
|
|
142
|
+
type: "subscribe",
|
|
143
|
+
key: keyString,
|
|
144
|
+
proto: PROTOCOL_VERSION
|
|
145
|
+
};
|
|
146
|
+
window.parent.postMessage(message, "*");
|
|
147
|
+
}
|
|
148
|
+
const wrapped = (data) => handler(data);
|
|
149
|
+
handlers.add(wrapped);
|
|
150
|
+
return function unsubscribe() {
|
|
151
|
+
handlers.delete(wrapped);
|
|
152
|
+
if (handlers.size === 0) {
|
|
153
|
+
subscribers.delete(keyString);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function makeHost(resId) {
|
|
158
|
+
return {
|
|
159
|
+
request(method, ...args) {
|
|
160
|
+
return request(resId, method, ...args);
|
|
161
|
+
},
|
|
162
|
+
subscribe,
|
|
163
|
+
withScope: makeHost
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
hostBridge = makeHost(void 0);
|
|
167
|
+
return hostBridge;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// src/codecs.ts
|
|
171
|
+
function jsonCodec() {
|
|
172
|
+
return {
|
|
173
|
+
encode(value) {
|
|
174
|
+
return JSON.stringify(value);
|
|
175
|
+
},
|
|
176
|
+
decode(raw) {
|
|
177
|
+
try {
|
|
178
|
+
return JSON.parse(raw);
|
|
179
|
+
} catch {
|
|
180
|
+
return void 0;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
function numberCodec(fallback) {
|
|
186
|
+
return {
|
|
187
|
+
encode(value) {
|
|
188
|
+
return String(value);
|
|
189
|
+
},
|
|
190
|
+
decode(raw) {
|
|
191
|
+
const n = Number(raw);
|
|
192
|
+
return Number.isFinite(n) ? n : fallback;
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
function booleanCodec() {
|
|
197
|
+
return {
|
|
198
|
+
encode(value) {
|
|
199
|
+
return value ? "1" : "0";
|
|
200
|
+
},
|
|
201
|
+
decode(raw) {
|
|
202
|
+
if (raw === "1" || raw === "true") return true;
|
|
203
|
+
if (raw === "0" || raw === "false") return false;
|
|
204
|
+
return void 0;
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
function mergeDeep(target, source) {
|
|
209
|
+
const result = { ...target };
|
|
210
|
+
for (const key of Object.keys(source)) {
|
|
211
|
+
const sourceValue = source[key];
|
|
212
|
+
const targetValue = result[key];
|
|
213
|
+
if (sourceValue !== void 0 && typeof sourceValue === "object" && !Array.isArray(sourceValue) && targetValue !== void 0 && typeof targetValue === "object" && !Array.isArray(targetValue)) {
|
|
214
|
+
result[key] = mergeDeep(
|
|
215
|
+
targetValue,
|
|
216
|
+
sourceValue
|
|
217
|
+
);
|
|
218
|
+
} else if (sourceValue !== void 0) {
|
|
219
|
+
result[key] = sourceValue;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return result;
|
|
223
|
+
}
|
|
224
|
+
function createWebPluginAPI(overrides) {
|
|
225
|
+
const base = {
|
|
226
|
+
logInfo: () => {
|
|
227
|
+
},
|
|
228
|
+
logWarn: () => {
|
|
229
|
+
},
|
|
230
|
+
logError: () => {
|
|
231
|
+
},
|
|
232
|
+
resource: {
|
|
233
|
+
id: "r-test",
|
|
234
|
+
name: "test",
|
|
235
|
+
sourceMeta: void 0,
|
|
236
|
+
searchMeta: void 0,
|
|
237
|
+
fileStats: void 0,
|
|
238
|
+
contentPluginId: "p-test"
|
|
239
|
+
},
|
|
240
|
+
listFiles: async () => [],
|
|
241
|
+
readFile: async () => new ArrayBuffer(0),
|
|
242
|
+
resolveFileUrl: (filename, variant) => buildMockFileUrl(filename, variant),
|
|
243
|
+
resolveExtractedUrl: (path) => `/extracted/${path}`,
|
|
244
|
+
extractProgressUrl: () => "/extract-progress/",
|
|
245
|
+
resolveBaseUrl: () => "/files/",
|
|
246
|
+
resolveFrameUrl: (filename, timeMs) => `/frame/${filename}/${timeMs}`,
|
|
247
|
+
download: async () => {
|
|
248
|
+
throw new Error("download stub not overridden");
|
|
249
|
+
},
|
|
250
|
+
resolveAssetUrl: (path) => `/plugin-assets/p-test/./${path}`,
|
|
251
|
+
deleteAsset: async () => ({ existed: false }),
|
|
252
|
+
listMessages: async () => [],
|
|
253
|
+
createMessage: async () => {
|
|
254
|
+
throw new Error("createMessage stub not overridden");
|
|
255
|
+
},
|
|
256
|
+
listDanmaku: async () => [],
|
|
257
|
+
createDanmaku: async () => {
|
|
258
|
+
throw new Error("createDanmaku stub not overridden");
|
|
259
|
+
},
|
|
260
|
+
getPref: () => void 0,
|
|
261
|
+
setPref: () => {
|
|
262
|
+
},
|
|
263
|
+
getCache: () => void 0,
|
|
264
|
+
setCache: () => {
|
|
265
|
+
},
|
|
266
|
+
listCache: () => [],
|
|
267
|
+
invalidate: async () => {
|
|
268
|
+
},
|
|
269
|
+
onAnchorJump: () => () => {
|
|
270
|
+
},
|
|
271
|
+
useFileList: () => ({
|
|
272
|
+
data: [],
|
|
273
|
+
isLoading: false,
|
|
274
|
+
isError: false,
|
|
275
|
+
error: null
|
|
276
|
+
}),
|
|
277
|
+
useMessageList: () => ({
|
|
278
|
+
data: [],
|
|
279
|
+
isLoading: false,
|
|
280
|
+
isError: false,
|
|
281
|
+
error: null
|
|
282
|
+
}),
|
|
283
|
+
useCreateMessage: () => ({
|
|
284
|
+
mutate: async () => {
|
|
285
|
+
throw new Error("useCreateMessage stub not overridden");
|
|
286
|
+
},
|
|
287
|
+
isPending: false
|
|
288
|
+
}),
|
|
289
|
+
useDanmakuList: () => ({
|
|
290
|
+
data: [],
|
|
291
|
+
isLoading: false,
|
|
292
|
+
isError: false,
|
|
293
|
+
error: null
|
|
294
|
+
}),
|
|
295
|
+
useCreateDanmaku: () => ({
|
|
296
|
+
mutate: async () => {
|
|
297
|
+
throw new Error("useCreateDanmaku stub not overridden");
|
|
298
|
+
},
|
|
299
|
+
isPending: false
|
|
300
|
+
}),
|
|
301
|
+
usePref: (_key, defaultValue, _codec) => [defaultValue, (_next) => {
|
|
302
|
+
}],
|
|
303
|
+
useTheme: () => ({
|
|
304
|
+
resolvedTheme: "light",
|
|
305
|
+
palette: "mono",
|
|
306
|
+
iconStyle: "duotone"
|
|
307
|
+
}),
|
|
308
|
+
useFont: () => ({ family: "", cssPaths: [] })
|
|
309
|
+
};
|
|
310
|
+
return overrides === void 0 ? base : mergeDeep(base, overrides);
|
|
311
|
+
}
|
|
312
|
+
function buildMockFileUrl(filename, variant) {
|
|
313
|
+
const url = `/files/${filename}`;
|
|
314
|
+
if (variant === "preview") {
|
|
315
|
+
return `${url}?size=preview`;
|
|
316
|
+
}
|
|
317
|
+
if (variant !== void 0 && variant !== "original") {
|
|
318
|
+
return `${url}?${imageVariantQuery(variant)}`;
|
|
319
|
+
}
|
|
320
|
+
return url;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// src/lifecycle.ts
|
|
324
|
+
var pluginContext;
|
|
325
|
+
function getPluginContext() {
|
|
326
|
+
return pluginContext;
|
|
327
|
+
}
|
|
328
|
+
function setPluginContext(ctx) {
|
|
329
|
+
pluginContext = ctx;
|
|
330
|
+
}
|
|
331
|
+
var currentVisibility = true;
|
|
332
|
+
var visibilityListeners = /* @__PURE__ */ new Set();
|
|
333
|
+
function subscribeToVisibility(cb) {
|
|
334
|
+
visibilityListeners.add(cb);
|
|
335
|
+
return () => {
|
|
336
|
+
visibilityListeners.delete(cb);
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
function getVisibilitySnapshot() {
|
|
340
|
+
return currentVisibility;
|
|
341
|
+
}
|
|
342
|
+
function publishVisibilityChange(visible) {
|
|
343
|
+
if (currentVisibility === visible) return;
|
|
344
|
+
currentVisibility = visible;
|
|
345
|
+
for (const cb of visibilityListeners) {
|
|
346
|
+
cb(visible);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
function afterNextPaintedFrame(cb) {
|
|
350
|
+
requestAnimationFrame(() => {
|
|
351
|
+
requestAnimationFrame(cb);
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
function mountPlugin(mount) {
|
|
355
|
+
function applyContext(ctx) {
|
|
356
|
+
setPluginContext(ctx);
|
|
357
|
+
publishVisibilityChange(true);
|
|
358
|
+
mount(ctx);
|
|
359
|
+
afterNextPaintedFrame(() => {
|
|
360
|
+
window.parent.postMessage(
|
|
361
|
+
{
|
|
362
|
+
type: "contextPainted",
|
|
363
|
+
resId: ctx.resId,
|
|
364
|
+
proto: PROTOCOL_VERSION
|
|
365
|
+
},
|
|
366
|
+
"*"
|
|
367
|
+
);
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
window.addEventListener("message", (event) => {
|
|
371
|
+
if (event.source !== window.parent) return;
|
|
372
|
+
const msg = event.data;
|
|
373
|
+
if (!isRecord2(msg) || msg.type !== "push") return;
|
|
374
|
+
if (msg.key === hostPushKeys.context) {
|
|
375
|
+
applyContext(msg.data);
|
|
376
|
+
} else if (msg.key === hostPushKeys.visibility) {
|
|
377
|
+
publishVisibilityChange(msg.data.visible);
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
window.addEventListener("context-ready", (e) => {
|
|
381
|
+
applyContext(e.detail);
|
|
382
|
+
});
|
|
383
|
+
window.addEventListener("visibility-changed", (e) => {
|
|
384
|
+
publishVisibilityChange(
|
|
385
|
+
e.detail.visible
|
|
386
|
+
);
|
|
387
|
+
});
|
|
388
|
+
const w = window;
|
|
389
|
+
if (w.__pluginContext !== void 0) {
|
|
390
|
+
applyContext(w.__pluginContext);
|
|
391
|
+
}
|
|
392
|
+
if (w.__pluginVisibility !== void 0) {
|
|
393
|
+
publishVisibilityChange(
|
|
394
|
+
w.__pluginVisibility.visible
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
function applyTheme(resolvedTheme, palette, iconStyle) {
|
|
399
|
+
const root = document.documentElement;
|
|
400
|
+
root.classList.remove("light", "dark");
|
|
401
|
+
root.classList.add(resolvedTheme);
|
|
402
|
+
for (const cls of [...root.classList]) {
|
|
403
|
+
if (cls.startsWith("theme-")) root.classList.remove(cls);
|
|
404
|
+
}
|
|
405
|
+
if (palette !== "mono") {
|
|
406
|
+
root.classList.add(`theme-${palette}`);
|
|
407
|
+
}
|
|
408
|
+
root.dataset.iconStyle = iconStyle;
|
|
409
|
+
}
|
|
410
|
+
var FONT_STYLE_ID = "plugin-host-font";
|
|
411
|
+
function applyFonts(family, cssPaths) {
|
|
412
|
+
for (const path of cssPaths) {
|
|
413
|
+
const selector = `link[rel="stylesheet"][href="${path}"]`;
|
|
414
|
+
if (document.head.querySelector(selector) !== null) continue;
|
|
415
|
+
const link = document.createElement("link");
|
|
416
|
+
link.rel = "stylesheet";
|
|
417
|
+
link.href = path;
|
|
418
|
+
document.head.appendChild(link);
|
|
419
|
+
}
|
|
420
|
+
const root = document.documentElement;
|
|
421
|
+
if (family === "") {
|
|
422
|
+
root.style.removeProperty("--font-app");
|
|
423
|
+
} else {
|
|
424
|
+
root.style.setProperty("--font-app", family);
|
|
425
|
+
}
|
|
426
|
+
if (document.getElementById(FONT_STYLE_ID) === null) {
|
|
427
|
+
const style = document.createElement("style");
|
|
428
|
+
style.id = FONT_STYLE_ID;
|
|
429
|
+
style.textContent = "html{font-family:var(--font-app,var(--font-sans))}";
|
|
430
|
+
document.head.appendChild(style);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
function isRecord2(value) {
|
|
434
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// src/stores.ts
|
|
438
|
+
var pluginPrefStore = /* @__PURE__ */ new Map();
|
|
439
|
+
var pluginCacheStore = /* @__PURE__ */ new Map();
|
|
440
|
+
function seedPluginStores(ctx) {
|
|
441
|
+
pluginPrefStore.clear();
|
|
442
|
+
for (const [k, v] of Object.entries(ctx.initialPrefs)) {
|
|
443
|
+
pluginPrefStore.set(k, v);
|
|
444
|
+
}
|
|
445
|
+
pluginCacheStore.clear();
|
|
446
|
+
for (const [k, v] of Object.entries(ctx.initialCache)) {
|
|
447
|
+
pluginCacheStore.set(k, v);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
function getPluginPrefStore() {
|
|
451
|
+
return pluginPrefStore;
|
|
452
|
+
}
|
|
453
|
+
function setPluginPref(key, value) {
|
|
454
|
+
pluginPrefStore.set(key, value);
|
|
455
|
+
}
|
|
456
|
+
function getPluginCacheStore() {
|
|
457
|
+
return pluginCacheStore;
|
|
458
|
+
}
|
|
459
|
+
function setPluginCache(key, value) {
|
|
460
|
+
pluginCacheStore.set(key, value);
|
|
461
|
+
}
|
|
462
|
+
function snapshotCacheEntries() {
|
|
463
|
+
const result = [];
|
|
464
|
+
for (const [key, value] of pluginCacheStore) {
|
|
465
|
+
result.push({ key, value });
|
|
466
|
+
}
|
|
467
|
+
return result;
|
|
468
|
+
}
|
|
469
|
+
var prefChangeListeners = /* @__PURE__ */ new Map();
|
|
470
|
+
function subscribeToPrefChanges(key, cb) {
|
|
471
|
+
let listeners = prefChangeListeners.get(key);
|
|
472
|
+
if (listeners === void 0) {
|
|
473
|
+
listeners = /* @__PURE__ */ new Set();
|
|
474
|
+
prefChangeListeners.set(key, listeners);
|
|
475
|
+
}
|
|
476
|
+
listeners.add(cb);
|
|
477
|
+
return function unsubscribe() {
|
|
478
|
+
listeners.delete(cb);
|
|
479
|
+
if (listeners.size === 0) {
|
|
480
|
+
prefChangeListeners.delete(key);
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
function broadcastPrefChange(key) {
|
|
485
|
+
const listeners = prefChangeListeners.get(key);
|
|
486
|
+
if (listeners === void 0) return;
|
|
487
|
+
for (const cb of listeners) {
|
|
488
|
+
cb();
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
function resolveFilesBaseUrl(resId, token) {
|
|
492
|
+
return `/api/resources/${resId}/files/${encodeURIComponent(token)}/`;
|
|
493
|
+
}
|
|
494
|
+
function buildFileUrl(resId, filename, token, variant) {
|
|
495
|
+
const url = `/api/resources/${resId}/files/${encodeURIComponent(token)}/${encodeURIComponent(filename)}`;
|
|
496
|
+
if (variant === "preview") {
|
|
497
|
+
return `${url}?size=preview`;
|
|
498
|
+
}
|
|
499
|
+
if (variant !== void 0 && variant !== "original") {
|
|
500
|
+
return `${url}?${imageVariantQuery(variant)}`;
|
|
501
|
+
}
|
|
502
|
+
return url;
|
|
503
|
+
}
|
|
504
|
+
function buildFrameUrl(resId, filename, timeMs, token) {
|
|
505
|
+
const time = String(Math.max(0, Math.round(timeMs)));
|
|
506
|
+
return `/api/resources/${resId}/frame/${encodeURIComponent(token)}/${encodeURIComponent(filename)}/${time}`;
|
|
507
|
+
}
|
|
508
|
+
function buildAssetUrl(pluginId, path, token) {
|
|
509
|
+
return `/api/plugin-assets/${encodeURIComponent(pluginId)}/${encodeURIComponent(
|
|
510
|
+
token
|
|
511
|
+
)}/${encodeURIComponent(path)}`;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// src/runtime.ts
|
|
515
|
+
function isRecord3(value) {
|
|
516
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
517
|
+
}
|
|
518
|
+
function extractThemePayload(data) {
|
|
519
|
+
if (!isRecord3(data)) {
|
|
520
|
+
return {
|
|
521
|
+
resolvedTheme: void 0,
|
|
522
|
+
palette: void 0,
|
|
523
|
+
iconStyle: void 0
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
return {
|
|
527
|
+
resolvedTheme: typeof data.resolvedTheme === "string" ? data.resolvedTheme : void 0,
|
|
528
|
+
palette: typeof data.palette === "string" ? data.palette : void 0,
|
|
529
|
+
iconStyle: typeof data.iconStyle === "string" ? data.iconStyle : void 0
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
function extractFontsPayload(data) {
|
|
533
|
+
if (!isRecord3(data) || typeof data.family !== "string") return void 0;
|
|
534
|
+
const cssPaths = Array.isArray(data.cssPaths) ? data.cssPaths.filter((p) => typeof p === "string") : [];
|
|
535
|
+
return { family: data.family, cssPaths };
|
|
536
|
+
}
|
|
537
|
+
function extractPrefPayload(data) {
|
|
538
|
+
if (!isRecord3(data)) return void 0;
|
|
539
|
+
const key = data.key;
|
|
540
|
+
if (typeof key !== "string") return void 0;
|
|
541
|
+
return {
|
|
542
|
+
key,
|
|
543
|
+
value: typeof data.value === "string" ? data.value : void 0
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
function createIframeHostAPI(ctx) {
|
|
547
|
+
const host = ensureHostBridge().withScope(ctx.resId);
|
|
548
|
+
seedPluginStores(ctx);
|
|
549
|
+
function logInfo(message, data) {
|
|
550
|
+
host.request("logInfo", { message, data }).catch(() => {
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
function logWarn(message, data) {
|
|
554
|
+
host.request("logWarn", { message, data }).catch(() => {
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
function logError(message, data) {
|
|
558
|
+
host.request("logError", { message, data }).catch(() => {
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
function listFiles() {
|
|
562
|
+
return host.request("listFiles");
|
|
563
|
+
}
|
|
564
|
+
function readFile(path, range) {
|
|
565
|
+
return host.request("readFile", { path, range });
|
|
566
|
+
}
|
|
567
|
+
function resolveFileUrl(filename, variant) {
|
|
568
|
+
return buildFileUrl(ctx.resId, filename, ctx.fileToken, variant);
|
|
569
|
+
}
|
|
570
|
+
function resolveExtractedUrl(path) {
|
|
571
|
+
return `/api/resources/${ctx.resId}/extracted/${encodeURIComponent(
|
|
572
|
+
ctx.fileToken
|
|
573
|
+
)}/${encodeURIComponent(path)}`;
|
|
574
|
+
}
|
|
575
|
+
function extractProgressUrl() {
|
|
576
|
+
return `/api/resources/${ctx.resId}/extract-progress/${encodeURIComponent(
|
|
577
|
+
ctx.fileToken
|
|
578
|
+
)}/`;
|
|
579
|
+
}
|
|
580
|
+
function resolveBaseUrl() {
|
|
581
|
+
return resolveFilesBaseUrl(ctx.resId, ctx.fileToken);
|
|
582
|
+
}
|
|
583
|
+
function resolveFrameUrl(filename, timeMs) {
|
|
584
|
+
return buildFrameUrl(ctx.resId, filename, timeMs, ctx.fileToken);
|
|
585
|
+
}
|
|
586
|
+
function listMessages() {
|
|
587
|
+
return host.request("listMessages");
|
|
588
|
+
}
|
|
589
|
+
function createMessage(input) {
|
|
590
|
+
return host.request("createMessage", {
|
|
591
|
+
body: input.body,
|
|
592
|
+
anchor: input.anchor === void 0 ? void 0 : { data: input.anchor }
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
function listDanmaku(filter) {
|
|
596
|
+
return host.request("listDanmaku", { filter });
|
|
597
|
+
}
|
|
598
|
+
function createDanmaku(input) {
|
|
599
|
+
return host.request("createDanmaku", {
|
|
600
|
+
text: input.text,
|
|
601
|
+
anchor: { data: input.anchor },
|
|
602
|
+
mode: input.mode
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
function getPref(key) {
|
|
606
|
+
return getPluginPrefStore().get(key) ?? void 0;
|
|
607
|
+
}
|
|
608
|
+
function setPref(key, value) {
|
|
609
|
+
setPluginPref(key, value);
|
|
610
|
+
broadcastPrefChange(key);
|
|
611
|
+
host.request("setPref", { key, value }).catch(() => {
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
function getCache(key) {
|
|
615
|
+
return getPluginCacheStore().get(key) ?? void 0;
|
|
616
|
+
}
|
|
617
|
+
function setCache(key, value) {
|
|
618
|
+
setPluginCache(key, value);
|
|
619
|
+
host.request("setCache", { key, value }).catch(() => {
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
function listCache() {
|
|
623
|
+
return snapshotCacheEntries();
|
|
624
|
+
}
|
|
625
|
+
function invalidate(target) {
|
|
626
|
+
return host.request("invalidate", { target });
|
|
627
|
+
}
|
|
628
|
+
function download(request) {
|
|
629
|
+
return host.request("download", request);
|
|
630
|
+
}
|
|
631
|
+
function resolveAssetUrl(path) {
|
|
632
|
+
if (ctx.assetToken.length === 0) {
|
|
633
|
+
throw new Error(
|
|
634
|
+
'resolveAssetUrl() \u2014 the plugin has no asset token: declare "download": true in the manifest and reload the preview'
|
|
635
|
+
);
|
|
636
|
+
}
|
|
637
|
+
return buildAssetUrl(ctx.pluginId, path, ctx.assetToken);
|
|
638
|
+
}
|
|
639
|
+
function deleteAsset(path) {
|
|
640
|
+
return host.request("deleteAsset", { path });
|
|
641
|
+
}
|
|
642
|
+
function onAnchorJump(cb) {
|
|
643
|
+
return host.subscribe("anchorJump", cb);
|
|
644
|
+
}
|
|
645
|
+
return {
|
|
646
|
+
logInfo,
|
|
647
|
+
logWarn,
|
|
648
|
+
logError,
|
|
649
|
+
resource: {
|
|
650
|
+
id: ctx.resId,
|
|
651
|
+
name: ctx.resName,
|
|
652
|
+
sourceMeta: ctx.sourceMeta,
|
|
653
|
+
searchMeta: ctx.searchMeta,
|
|
654
|
+
fileStats: ctx.fileStats,
|
|
655
|
+
contentPluginId: ctx.contentPluginId
|
|
656
|
+
},
|
|
657
|
+
listFiles,
|
|
658
|
+
readFile,
|
|
659
|
+
resolveFileUrl,
|
|
660
|
+
resolveExtractedUrl,
|
|
661
|
+
extractProgressUrl,
|
|
662
|
+
resolveBaseUrl,
|
|
663
|
+
resolveFrameUrl,
|
|
664
|
+
download,
|
|
665
|
+
resolveAssetUrl,
|
|
666
|
+
deleteAsset,
|
|
667
|
+
listMessages,
|
|
668
|
+
createMessage,
|
|
669
|
+
listDanmaku,
|
|
670
|
+
createDanmaku,
|
|
671
|
+
getPref,
|
|
672
|
+
setPref,
|
|
673
|
+
getCache,
|
|
674
|
+
setCache,
|
|
675
|
+
listCache,
|
|
676
|
+
invalidate,
|
|
677
|
+
onAnchorJump
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
export { PROTOCOL_VERSION, applyFonts, applyTheme, booleanCodec, broadcastPrefChange, createIframeHostAPI, createWebPluginAPI, ensureHostBridge, extractFontsPayload, extractPrefPayload, extractThemePayload, getPluginCacheStore, getPluginContext, getPluginPrefStore, getVisibilitySnapshot, hostPushKeys, invalidatePushKeys, isRecord, jsonCodec, mountPlugin, numberCodec, pluginMethods, pluginThemePalettes, seedPluginStores, setPluginCache, setPluginPref, snapshotCacheEntries, subscribeToPrefChanges, subscribeToVisibility };
|
|
682
|
+
//# sourceMappingURL=index.js.map
|
|
683
|
+
//# sourceMappingURL=index.js.map
|