@kbve/droid 0.0.1 → 0.0.2
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 +1 -0
- package/README.md +5 -1
- package/assets/canvas-worker-DbsoY2Xv.js +5 -0
- package/assets/db-worker-CjXS52DQ.js +14 -0
- package/assets/ws-worker-FssAQgaD.js +5 -0
- package/droid.cjs.js +1 -1
- package/droid.es.js +1 -1
- package/index-DSyyyiLP.js +3034 -0
- package/main-BYzQVPCs.js +1455 -0
- package/package.json +8 -2
- package/src/index.ts +3 -0
- package/src/lib/droid.spec.ts +26 -0
- package/src/lib/droid.ts +5 -0
- package/src/lib/mod/mod-manager.ts +72 -0
- package/src/lib/mod/mod-urls.ts +3 -0
- package/src/lib/mod/module/bento/mod-bento.worker.ts +128 -0
- package/src/lib/mod/module/phaser/mod-phaser.ts +0 -0
- package/src/lib/mod/module/supabase/mod-supabase.spec.ts +30 -0
- package/src/lib/mod/module/supabase/mod-supabase.worker.ts +59 -0
- package/src/lib/types/bento.ts +80 -0
- package/src/lib/types/discord.ts +40 -0
- package/src/lib/types/event-types.ts +35 -0
- package/src/lib/types/jedi.ts +66 -0
- package/src/lib/types/modules.ts +46 -0
- package/src/lib/types/panel-types.ts +18 -0
- package/src/lib/types/supabase-esm.d.ts +4 -0
- package/src/lib/workers/canvas-worker.ts +107 -0
- package/src/lib/workers/data.ts +215 -0
- package/src/lib/workers/db-worker.ts +203 -0
- package/src/lib/workers/events.ts +52 -0
- package/src/lib/workers/flexbuilder.ts +6 -0
- package/src/lib/workers/init.ts +40 -0
- package/src/lib/workers/main.ts +356 -0
- package/src/lib/workers/tools.ts +60 -0
- package/src/lib/workers/ws-worker.ts +73 -0
- package/src/setup-vitest.ts +2 -0
- package/src/types.d.ts +31 -0
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import { wrap, transfer, proxy } from 'comlink';
|
|
2
|
+
import type { Remote } from 'comlink';
|
|
3
|
+
import { persistentMap } from '@nanostores/persistent';
|
|
4
|
+
import type { LocalStorageAPI } from './db-worker';
|
|
5
|
+
import type { WSInstance } from './ws-worker';
|
|
6
|
+
import { initializeWorkerDatabase, type InitWorkerOptions } from './init';
|
|
7
|
+
import type { CanvasWorkerAPI } from './canvas-worker';
|
|
8
|
+
import { getModManager } from '../mod/mod-manager';
|
|
9
|
+
import { scopeData } from './data';
|
|
10
|
+
import { dispatchAsync, renderVNode } from './tools';
|
|
11
|
+
import type { PanelPayload, PanelId } from '../types/panel-types';
|
|
12
|
+
import { DroidEvents } from './events';
|
|
13
|
+
|
|
14
|
+
const EXPECTED_DB_VERSION = '1.0.3';
|
|
15
|
+
|
|
16
|
+
// * DeepProxy
|
|
17
|
+
|
|
18
|
+
function deepProxy<T>(obj: T): T {
|
|
19
|
+
if (typeof obj === 'function') return proxy(obj) as T;
|
|
20
|
+
|
|
21
|
+
if (obj && typeof obj === 'object') {
|
|
22
|
+
const result: any = Array.isArray(obj) ? [] : {};
|
|
23
|
+
for (const key in obj) {
|
|
24
|
+
result[key] = deepProxy(obj[key]);
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return obj;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// * WebSocket
|
|
33
|
+
async function initWsComlink(): Promise<Remote<WSInstance>> {
|
|
34
|
+
const worker = new SharedWorker(new URL('./ws-worker', import.meta.url), {
|
|
35
|
+
type: 'module',
|
|
36
|
+
});
|
|
37
|
+
worker.port.start();
|
|
38
|
+
return wrap<WSInstance>(worker.port);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// * Interface -> Moved to the panels.ts
|
|
42
|
+
|
|
43
|
+
// * UIUX
|
|
44
|
+
|
|
45
|
+
const uiuxState = persistentMap<{
|
|
46
|
+
panelManager: Record<PanelId,
|
|
47
|
+
{
|
|
48
|
+
open: boolean;
|
|
49
|
+
payload?: PanelPayload;
|
|
50
|
+
}
|
|
51
|
+
>;
|
|
52
|
+
themeManager: { theme: 'light' | 'dark' | 'auto' };
|
|
53
|
+
toastManager: Record<string, any>;
|
|
54
|
+
scrollY: number;
|
|
55
|
+
}>(
|
|
56
|
+
'uiux-state',
|
|
57
|
+
{
|
|
58
|
+
panelManager: {
|
|
59
|
+
top: { open: false },
|
|
60
|
+
right: { open: false },
|
|
61
|
+
bottom: { open: false },
|
|
62
|
+
left: { open: false },
|
|
63
|
+
},
|
|
64
|
+
themeManager: { theme: 'auto' },
|
|
65
|
+
toastManager: {},
|
|
66
|
+
scrollY: 0,
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
encode: JSON.stringify,
|
|
70
|
+
decode: JSON.parse,
|
|
71
|
+
},
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const canvasWorker = wrap<CanvasWorkerAPI>(
|
|
75
|
+
new Worker(new URL('./canvas-worker', import.meta.url), { type: 'module' }),
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
export const uiux = {
|
|
79
|
+
state: uiuxState,
|
|
80
|
+
worker: canvasWorker,
|
|
81
|
+
openPanel(id: PanelId, payload?: PanelPayload) {
|
|
82
|
+
const panels = { ...uiuxState.get().panelManager };
|
|
83
|
+
panels[id] = { open: true, payload };
|
|
84
|
+
uiuxState.setKey('panelManager', panels);
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
closePanel(id: PanelId) {
|
|
88
|
+
const panels = { ...uiuxState.get().panelManager };
|
|
89
|
+
panels[id] = { open: false, payload: undefined };
|
|
90
|
+
uiuxState.setKey('panelManager', panels);
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
togglePanel(
|
|
94
|
+
id: PanelId,
|
|
95
|
+
payload?: PanelPayload,
|
|
96
|
+
) {
|
|
97
|
+
const panels = { ...uiuxState.get().panelManager };
|
|
98
|
+
const isOpen = panels[id]?.open ?? false;
|
|
99
|
+
panels[id] = { open: !isOpen, payload: !isOpen ? payload : undefined };
|
|
100
|
+
uiuxState.setKey('panelManager', panels);
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
setTheme(theme: 'light' | 'dark' | 'auto') {
|
|
104
|
+
uiuxState.setKey('themeManager', { theme });
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
addToast(id: string, data: any) {
|
|
108
|
+
const toasts = { ...uiuxState.get().toastManager, [id]: data };
|
|
109
|
+
uiuxState.setKey('toastManager', toasts);
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
removeToast(id: string) {
|
|
113
|
+
const toasts = { ...uiuxState.get().toastManager };
|
|
114
|
+
delete toasts[id];
|
|
115
|
+
uiuxState.setKey('toastManager', toasts);
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
async dispatchCanvasRequest(
|
|
119
|
+
panelId: PanelId,
|
|
120
|
+
canvasEl: HTMLCanvasElement,
|
|
121
|
+
mode: 'static' | 'animated' | 'dynamic' = 'animated',
|
|
122
|
+
) {
|
|
123
|
+
const offscreen = canvasEl.transferControlToOffscreen();
|
|
124
|
+
await this.worker.bindCanvas(panelId, offscreen, mode);
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
closeAllPanels() {
|
|
128
|
+
const panels = { ...uiuxState.get().panelManager };
|
|
129
|
+
console.log('error panel is closing');
|
|
130
|
+
|
|
131
|
+
for (const id of Object.keys(panels) as Array<
|
|
132
|
+
PanelId
|
|
133
|
+
>) {
|
|
134
|
+
panels[id] = { open: false, payload: undefined };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
uiuxState.setKey('panelManager', panels);
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
emitFromWorker(msg: any) {
|
|
141
|
+
if (msg.type === 'injectVNode' && msg.vnode) {
|
|
142
|
+
dispatchAsync(() => {
|
|
143
|
+
const target = document.getElementById('bento-grid-inject');
|
|
144
|
+
if (!target) {
|
|
145
|
+
console.warn(
|
|
146
|
+
'[KBVE] No injection target found: #bento-grid-inject',
|
|
147
|
+
);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const el = renderVNode(msg.vnode);
|
|
152
|
+
el.classList.add('animate-fade-in');
|
|
153
|
+
if (msg.vnode.id) {
|
|
154
|
+
const existing = document.getElementById(msg.vnode.id);
|
|
155
|
+
if (existing) existing.remove();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
target.appendChild(el);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// * i18n
|
|
165
|
+
|
|
166
|
+
const i18nStore = persistentMap<Record<string, string>>(
|
|
167
|
+
'i18n-cache',
|
|
168
|
+
{},
|
|
169
|
+
{
|
|
170
|
+
encode: JSON.stringify,
|
|
171
|
+
decode: JSON.parse,
|
|
172
|
+
},
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
export const i18n = {
|
|
176
|
+
store: i18nStore,
|
|
177
|
+
api: null as Remote<LocalStorageAPI> | null,
|
|
178
|
+
ready: Promise.resolve(),
|
|
179
|
+
|
|
180
|
+
get(key: string): string {
|
|
181
|
+
return i18nStore.get()[key] ?? `[${key}]`;
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
async getAsync(key: string): Promise<string> {
|
|
185
|
+
const cached = i18nStore.get()[key];
|
|
186
|
+
if (cached !== undefined) return cached;
|
|
187
|
+
|
|
188
|
+
if (!this.api) return `[${key}]`;
|
|
189
|
+
|
|
190
|
+
const value = await this.api.getTranslation(key);
|
|
191
|
+
if (value !== null) {
|
|
192
|
+
i18nStore.setKey(key, value);
|
|
193
|
+
return value;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return `[${key}]`;
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
set(key: string, value: string) {
|
|
200
|
+
i18nStore.setKey(key, value);
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
async hydrate(api: Remote<LocalStorageAPI>, keys: string[]) {
|
|
204
|
+
this.api = api;
|
|
205
|
+
for (const key of keys) {
|
|
206
|
+
const value = await api.getTranslation(key);
|
|
207
|
+
if (value !== null) {
|
|
208
|
+
i18nStore.setKey(key, value);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
async hydrateLocale(locale = 'en') {
|
|
214
|
+
if (!this.api) return;
|
|
215
|
+
|
|
216
|
+
const allKeys = await this.api.getAllI18nKeys();
|
|
217
|
+
const localeKeys = allKeys.filter((key) =>
|
|
218
|
+
key.startsWith(`${locale}:`),
|
|
219
|
+
);
|
|
220
|
+
const translations = await this.api.getTranslations(localeKeys);
|
|
221
|
+
|
|
222
|
+
for (const [key, value] of Object.entries(translations)) {
|
|
223
|
+
console.log(`[i18n.setKey] ${key} = ${value}`);
|
|
224
|
+
this.store.setKey(key, value);
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
function initSWComlink() {
|
|
230
|
+
if (!navigator.serviceWorker?.controller) return;
|
|
231
|
+
const channel = new MessageChannel();
|
|
232
|
+
navigator.serviceWorker.controller.postMessage(channel.port2, [
|
|
233
|
+
channel.port2,
|
|
234
|
+
]);
|
|
235
|
+
channel.port1.start();
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
async function initStorageComlink(): Promise<Remote<LocalStorageAPI>> {
|
|
239
|
+
const worker = new SharedWorker(new URL('./db-worker', import.meta.url), {
|
|
240
|
+
type: 'module',
|
|
241
|
+
});
|
|
242
|
+
worker.port.start();
|
|
243
|
+
const api = wrap<LocalStorageAPI>(worker.port);
|
|
244
|
+
|
|
245
|
+
const version = await api.getVersion();
|
|
246
|
+
if (version !== EXPECTED_DB_VERSION) {
|
|
247
|
+
await initializeWorkerDatabase(api, {
|
|
248
|
+
version: EXPECTED_DB_VERSION,
|
|
249
|
+
i18nPath: 'https://discord.sh/i18n/db.json',
|
|
250
|
+
locale: 'en',
|
|
251
|
+
defaults: { welcome: 'Welcome!', theme: 'dark' },
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return api;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
let initialized = false;
|
|
259
|
+
|
|
260
|
+
// * Bridge
|
|
261
|
+
export function bridgeWsToDb(
|
|
262
|
+
ws: Remote<WSInstance>,
|
|
263
|
+
db: Remote<LocalStorageAPI>,
|
|
264
|
+
) {
|
|
265
|
+
const handler = proxy(async (buf: ArrayBuffer) => {
|
|
266
|
+
dispatchAsync(() => {
|
|
267
|
+
const key = `ws:${Date.now()}`;
|
|
268
|
+
void db.storeWsMessage(key, buf);
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
ws.onMessage(transfer(handler, [0]));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// * MAIN
|
|
276
|
+
export async function main() {
|
|
277
|
+
if (!initialized) {
|
|
278
|
+
initialized = true;
|
|
279
|
+
|
|
280
|
+
// Attach to existing service worker (or wait for one to take control)
|
|
281
|
+
if (navigator.serviceWorker?.controller) {
|
|
282
|
+
initSWComlink();
|
|
283
|
+
} else {
|
|
284
|
+
navigator.serviceWorker?.addEventListener(
|
|
285
|
+
'controllerchange',
|
|
286
|
+
initSWComlink,
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const needsInit =
|
|
292
|
+
!window.kbve?.api || !window.kbve?.i18n || !window.kbve?.uiux;
|
|
293
|
+
|
|
294
|
+
if (needsInit) {
|
|
295
|
+
const api = await initStorageComlink();
|
|
296
|
+
const ws = await initWsComlink();
|
|
297
|
+
const mod = await getModManager();
|
|
298
|
+
const events = DroidEvents;
|
|
299
|
+
|
|
300
|
+
for (const handle of Object.values(mod.registry)) {
|
|
301
|
+
if (typeof handle.instance.init === 'function') {
|
|
302
|
+
await handle.instance.init({
|
|
303
|
+
emitFromWorker: uiux.emitFromWorker,
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
console.log('[Event] -> Fire Mod Ready');
|
|
307
|
+
events.emit('droid-mod-ready', {
|
|
308
|
+
meta: handle.meta,
|
|
309
|
+
timestamp: Date.now(),
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
bridgeWsToDb(ws, api);
|
|
314
|
+
|
|
315
|
+
const data = scopeData;
|
|
316
|
+
i18n.api = api;
|
|
317
|
+
i18n.ready = i18n.hydrateLocale('en');
|
|
318
|
+
|
|
319
|
+
window.kbve = {
|
|
320
|
+
...(window.kbve || {}),
|
|
321
|
+
api,
|
|
322
|
+
i18n,
|
|
323
|
+
uiux,
|
|
324
|
+
ws,
|
|
325
|
+
data,
|
|
326
|
+
mod,
|
|
327
|
+
events,
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
//window.kbve = deepProxy(window.kbve);
|
|
331
|
+
|
|
332
|
+
await i18n.ready;
|
|
333
|
+
|
|
334
|
+
window.kbve.events.emit('droid-ready', {
|
|
335
|
+
timestamp: Date.now(),
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
document.addEventListener('astro:page-load', () => {
|
|
339
|
+
console.debug('[KBVE] Re-dispatched droid-ready after astro:page-load');
|
|
340
|
+
window.kbve?.events.emit('droid-ready', {
|
|
341
|
+
timestamp: Date.now(),
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// document.addEventListener('astro:page-load', () => {
|
|
346
|
+
// console.debug('[KBVE] Re-dispatched droid-ready after DomContentLoaded');
|
|
347
|
+
// window.kbve?.events.emit('droid-ready', {
|
|
348
|
+
// timestamp: Date.now(),
|
|
349
|
+
// });
|
|
350
|
+
// });
|
|
351
|
+
|
|
352
|
+
console.log('[KBVE] Global API ready');
|
|
353
|
+
} else {
|
|
354
|
+
console.log('[KBVE] Already initialized');
|
|
355
|
+
}
|
|
356
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { VirtualNode } from '../types/modules';
|
|
2
|
+
|
|
3
|
+
export function dispatchAsync(fn: () => void) {
|
|
4
|
+
if (typeof queueMicrotask === 'function') {
|
|
5
|
+
queueMicrotask(fn);
|
|
6
|
+
} else {
|
|
7
|
+
setTimeout(fn, 0);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function renderVNode(vnode: VirtualNode): HTMLElement {
|
|
12
|
+
const el = document.createElement(vnode.tag);
|
|
13
|
+
|
|
14
|
+
if (vnode.class) el.className = vnode.class;
|
|
15
|
+
|
|
16
|
+
if (vnode.attrs) {
|
|
17
|
+
for (const [key, value] of Object.entries(vnode.attrs)) {
|
|
18
|
+
if (typeof value === 'function' && key.startsWith('on')) {
|
|
19
|
+
el.addEventListener(key.slice(2).toLowerCase(), value);
|
|
20
|
+
} else if (key === 'style' && typeof value === 'object') {
|
|
21
|
+
Object.assign(el.style, value);
|
|
22
|
+
} else if (key === 'dataset' && typeof value === 'object') {
|
|
23
|
+
for (const [data_key, data_value] of Object.entries(value)) {
|
|
24
|
+
el.dataset[data_key] = String(data_value);
|
|
25
|
+
}
|
|
26
|
+
} else {
|
|
27
|
+
try {
|
|
28
|
+
el.setAttribute(key, String(value));
|
|
29
|
+
} catch {
|
|
30
|
+
//
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (vnode.style) {
|
|
37
|
+
Object.assign(el.style, vnode.style);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// if (vnode.children) {
|
|
41
|
+
// for (const child of vnode.children) {
|
|
42
|
+
// el.appendChild(
|
|
43
|
+
// typeof child === 'string'
|
|
44
|
+
// ? document.createTextNode(child)
|
|
45
|
+
// : renderVNode(child),
|
|
46
|
+
// );
|
|
47
|
+
// }
|
|
48
|
+
// }
|
|
49
|
+
|
|
50
|
+
if (vnode.children) {
|
|
51
|
+
for (const child of vnode.children) {
|
|
52
|
+
const node = typeof child === 'string'
|
|
53
|
+
? document.createTextNode(child)
|
|
54
|
+
: renderVNode(child);
|
|
55
|
+
el.appendChild(node);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return el;
|
|
60
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { wrap, expose, type Remote } from 'comlink';
|
|
2
|
+
import type { LocalStorageAPI } from './db-worker';
|
|
3
|
+
import { toReference, builder } from './flexbuilder';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
interface SharedWorkerGlobalScope extends Worker {
|
|
7
|
+
onconnect: (event: MessageEvent) => void;
|
|
8
|
+
}
|
|
9
|
+
declare const self: SharedWorkerGlobalScope;
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
let ws: WebSocket | null = null;
|
|
13
|
+
let onMessageCallback: ((data: any) => void) | null = null;
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
// --- WebSocket API ---
|
|
17
|
+
const wsInstanceAPI = {
|
|
18
|
+
async connect(url: string) {
|
|
19
|
+
if (ws) return;
|
|
20
|
+
|
|
21
|
+
ws = new WebSocket(url);
|
|
22
|
+
ws.binaryType = 'arraybuffer';
|
|
23
|
+
|
|
24
|
+
ws.onopen = () => console.log('[WS] Connected:', url);
|
|
25
|
+
|
|
26
|
+
ws.onmessage = (e) => {
|
|
27
|
+
try {
|
|
28
|
+
console.log('[WS] Received binary message');
|
|
29
|
+
|
|
30
|
+
if (e.data instanceof ArrayBuffer) {
|
|
31
|
+
onMessageCallback?.(e.data);
|
|
32
|
+
}
|
|
33
|
+
} catch (err) {
|
|
34
|
+
console.error('[WS] Failed to forward message', err);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
ws.onerror = (e) => console.error('[WS] Error:', e);
|
|
40
|
+
ws.onclose = () => {
|
|
41
|
+
console.log('[WS] Disconnected');
|
|
42
|
+
ws = null;
|
|
43
|
+
};
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
async send(payload: Uint8Array) {
|
|
47
|
+
if (ws?.readyState === WebSocket.OPEN) {
|
|
48
|
+
ws.send(payload);
|
|
49
|
+
} else {
|
|
50
|
+
console.warn('[WS] Tried to send while disconnected');
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
async close() {
|
|
55
|
+
ws?.close();
|
|
56
|
+
ws = null;
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
onMessage(callback: (data: any) => void) {
|
|
60
|
+
onMessageCallback = callback;
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export type WSInstance = typeof wsInstanceAPI;
|
|
67
|
+
|
|
68
|
+
self.onconnect = (event: MessageEvent) => {
|
|
69
|
+
const port = event.ports[0];
|
|
70
|
+
port.start();
|
|
71
|
+
expose(wsInstanceAPI, port);
|
|
72
|
+
};
|
|
73
|
+
|
package/src/types.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/// <reference lib="webworker" />
|
|
2
|
+
|
|
3
|
+
import type { Remote } from 'comlink';
|
|
4
|
+
import type { LocalStorageAPI } from './lib/workers/db-worker';
|
|
5
|
+
import type { WSInstance } from './lib/workers/ws-worker';
|
|
6
|
+
import { FlexDataAPI } from './lib/workers/data';
|
|
7
|
+
import type { ModManager } from './types/modules';
|
|
8
|
+
import type { DroidEventBus } from './lib/workers/events';
|
|
9
|
+
|
|
10
|
+
export interface KBVEGlobal {
|
|
11
|
+
api: Remote<LocalStorageAPI>;
|
|
12
|
+
i18n: typeof I18nInstance;
|
|
13
|
+
uiux: typeof UiUxInstance;
|
|
14
|
+
ws: Remote<WSInstance>;
|
|
15
|
+
data: FlexDataAPI;
|
|
16
|
+
mod: ModManager;
|
|
17
|
+
events: DroidEventBus;
|
|
18
|
+
|
|
19
|
+
// Helper Flags @GPT
|
|
20
|
+
// droidReady?: boolean;
|
|
21
|
+
// waitForDroidReady?: () => Promise<void>;
|
|
22
|
+
|
|
23
|
+
// <T>
|
|
24
|
+
[key: string]: unknown;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
declare global {
|
|
28
|
+
interface Window {
|
|
29
|
+
kbve?: KBVEGlobal;
|
|
30
|
+
}
|
|
31
|
+
}
|