@kbve/droid 0.0.6 → 0.1.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/droid.mjs +2312 -74
- package/index.d.ts +32 -0
- package/lib/agents/api-types.d.ts +121 -0
- package/lib/agents/api-types.d.ts.map +1 -0
- package/lib/agents/auth/init.d.ts +8 -0
- package/lib/agents/auth/init.d.ts.map +1 -0
- package/lib/agents/cache.d.ts +9 -0
- package/lib/agents/cache.d.ts.map +1 -0
- package/lib/agents/client/callGuildVault.d.ts +3 -0
- package/lib/agents/client/callGuildVault.d.ts.map +1 -0
- package/lib/agents/client/callJson.d.ts +3 -0
- package/lib/agents/client/callJson.d.ts.map +1 -0
- package/lib/agents/config.d.ts +36 -0
- package/lib/agents/config.d.ts.map +1 -0
- package/lib/agents/constants.d.ts +11 -0
- package/lib/agents/constants.d.ts.map +1 -0
- package/lib/agents/createAgents.d.ts +4 -0
- package/lib/agents/createAgents.d.ts.map +1 -0
- package/lib/agents/ctx.d.ts +46 -0
- package/lib/agents/ctx.d.ts.map +1 -0
- package/lib/agents/discord/bot.d.ts +21 -0
- package/lib/agents/discord/bot.d.ts.map +1 -0
- package/lib/agents/discord/guilds.d.ts +8 -0
- package/lib/agents/discord/guilds.d.ts.map +1 -0
- package/lib/agents/formDrafts.d.ts +5 -0
- package/lib/agents/formDrafts.d.ts.map +1 -0
- package/lib/agents/generated/agents-schema.d.ts +73 -0
- package/lib/agents/generated/agents-schema.d.ts.map +1 -0
- package/lib/agents/generated/discordsh-agents-schema.d.ts +71 -0
- package/lib/agents/generated/discordsh-agents-schema.d.ts.map +1 -0
- package/lib/agents/github/backfill.d.ts +23 -0
- package/lib/agents/github/backfill.d.ts.map +1 -0
- package/lib/agents/github/events.d.ts +10 -0
- package/lib/agents/github/events.d.ts.map +1 -0
- package/lib/agents/github/pat.d.ts +13 -0
- package/lib/agents/github/pat.d.ts.map +1 -0
- package/lib/agents/github/repoConfig.d.ts +25 -0
- package/lib/agents/github/repoConfig.d.ts.map +1 -0
- package/lib/agents/github/tokens.d.ts +22 -0
- package/lib/agents/github/tokens.d.ts.map +1 -0
- package/lib/agents/github/webhook.d.ts +37 -0
- package/lib/agents/github/webhook.d.ts.map +1 -0
- package/lib/agents/index.d.ts +8 -0
- package/lib/agents/index.d.ts.map +1 -0
- package/lib/agents/state/atoms.d.ts +122 -0
- package/lib/agents/state/atoms.d.ts.map +1 -0
- package/lib/agents/types.d.ts +65 -0
- package/lib/agents/types.d.ts.map +1 -0
- package/lib/gateway/SupabaseGateway.d.ts +38 -0
- package/lib/gateway/SupabaseGateway.d.ts.map +1 -0
- package/lib/gateway/WorkerCommunication.d.ts +39 -0
- package/lib/gateway/WorkerCommunication.d.ts.map +1 -0
- package/lib/gateway/WorkerPool.d.ts +55 -0
- package/lib/gateway/WorkerPool.d.ts.map +1 -0
- package/lib/gateway/capabilities.d.ts +23 -0
- package/lib/gateway/capabilities.d.ts.map +1 -0
- package/lib/gateway/index.d.ts +9 -0
- package/lib/gateway/index.d.ts.map +1 -0
- package/lib/gateway/strategies/DirectStrategy.d.ts +52 -0
- package/lib/gateway/strategies/DirectStrategy.d.ts.map +1 -0
- package/lib/gateway/strategies/SharedWorkerStrategy.d.ts +46 -0
- package/lib/gateway/strategies/SharedWorkerStrategy.d.ts.map +1 -0
- package/lib/gateway/strategies/WebWorkerStrategy.d.ts +45 -0
- package/lib/gateway/strategies/WebWorkerStrategy.d.ts.map +1 -0
- package/lib/gateway/types.d.ts +81 -0
- package/lib/gateway/types.d.ts.map +1 -0
- package/lib/workers/canvas-ui-renderer.d.ts +25 -0
- package/lib/workers/canvas-ui-renderer.d.ts.map +1 -0
- package/lib/workers/canvas-worker.d.ts +14 -0
- package/lib/workers/canvas-worker.d.ts.map +1 -0
- package/lib/workers/canvas-worker.js +5 -302
- package/lib/workers/data.d.ts +40 -0
- package/lib/workers/data.d.ts.map +1 -0
- package/lib/workers/db-worker.d.ts +69 -0
- package/lib/workers/db-worker.d.ts.map +1 -0
- package/lib/workers/db-worker.js +13 -3636
- package/lib/workers/events.d.ts +11 -0
- package/lib/workers/events.d.ts.map +1 -0
- package/lib/workers/flexbuilder.d.ts +4 -0
- package/lib/workers/flexbuilder.d.ts.map +1 -0
- package/lib/workers/init.d.ts +11 -0
- package/lib/workers/init.d.ts.map +1 -0
- package/lib/workers/main.d.ts +105 -0
- package/lib/workers/main.d.ts.map +1 -0
- package/lib/workers/supabase-db-worker.d.ts +2 -0
- package/lib/workers/supabase-db-worker.d.ts.map +1 -0
- package/lib/workers/supabase-db-worker.js +23 -0
- package/lib/workers/supabase-shared-worker.d.ts +2 -0
- package/lib/workers/supabase-shared-worker.d.ts.map +1 -0
- package/lib/workers/supabase-shared-worker.js +25 -0
- package/lib/workers/tools.d.ts +4 -0
- package/lib/workers/tools.d.ts.map +1 -0
- package/lib/workers/validate.d.ts +15 -0
- package/lib/workers/validate.d.ts.map +1 -0
- package/lib/workers/ws-worker.d.ts +10 -0
- package/lib/workers/ws-worker.d.ts.map +1 -0
- package/lib/workers/ws-worker.js +5 -273
- package/main.js +5711 -0
- package/package.json +18 -6
- package/src/index.ts +41 -13
- package/src/lib/agents/api-types.ts +170 -0
- package/src/lib/agents/auth/init.ts +200 -0
- package/src/lib/agents/cache.ts +127 -0
- package/src/lib/agents/client/callGuildVault.ts +71 -0
- package/src/lib/agents/client/callJson.ts +64 -0
- package/src/lib/agents/config.ts +83 -0
- package/src/lib/agents/constants.ts +11 -0
- package/src/lib/agents/createAgents.ts +62 -0
- package/src/lib/agents/ctx.ts +63 -0
- package/src/lib/agents/discord/bot.ts +169 -0
- package/src/lib/agents/discord/guilds.ts +106 -0
- package/src/lib/agents/formDrafts.ts +55 -0
- package/src/lib/agents/generated/agents-schema.ts +155 -0
- package/src/lib/agents/generated/discordsh-agents-schema.ts +96 -0
- package/src/lib/agents/github/backfill.ts +126 -0
- package/src/lib/agents/github/events.ts +131 -0
- package/src/lib/agents/github/pat.ts +150 -0
- package/src/lib/agents/github/repoConfig.ts +240 -0
- package/src/lib/agents/github/tokens.ts +222 -0
- package/src/lib/agents/github/webhook.ts +349 -0
- package/src/lib/agents/index.ts +35 -0
- package/src/lib/agents/state/atoms.ts +122 -0
- package/src/lib/agents/types.ts +83 -0
- package/src/lib/api.ts +71 -0
- package/src/lib/droid.spec.ts +14 -19
- package/src/lib/droid.ts +48 -4
- package/src/lib/gateway/SupabaseGateway.ts +236 -0
- package/src/lib/gateway/WorkerCommunication.ts +132 -0
- package/src/lib/gateway/WorkerPool.ts +304 -0
- package/src/lib/gateway/capabilities.spec.ts +144 -0
- package/src/lib/gateway/capabilities.ts +80 -0
- package/src/lib/gateway/index.ts +15 -0
- package/src/lib/gateway/strategies/DirectStrategy.ts +443 -0
- package/src/lib/gateway/strategies/SharedWorkerStrategy.ts +278 -0
- package/src/lib/gateway/strategies/WebWorkerStrategy.ts +278 -0
- package/src/lib/gateway/types.ts +139 -0
- package/src/lib/mod/mod-manager.ts +13 -8
- package/src/lib/mod/module/bento/mod-bento.worker.ts +1 -1
- package/src/lib/mod/module/supabase/mod-supabase.spec.ts +21 -25
- package/src/lib/mod/module/supabase/mod-supabase.ts +73 -0
- package/src/lib/mod/module/supabase/mod-supabase.worker.ts +1 -56
- package/src/lib/state/auth.spec.ts +58 -0
- package/src/lib/state/auth.ts +148 -0
- package/src/lib/state/bento.spec.ts +76 -0
- package/src/lib/state/bento.ts +51 -0
- package/src/lib/state/index.ts +98 -0
- package/src/lib/state/overlay-manager.ts +125 -0
- package/src/lib/state/profile-sync.ts +214 -0
- package/src/lib/state/profile.ts +155 -0
- package/src/lib/state/router.ts +24 -0
- package/src/lib/state/staff.ts +128 -0
- package/src/lib/state/sync-bus.ts +109 -0
- package/src/lib/state/theme-sync.ts +75 -0
- package/src/lib/state/toasts.spec.ts +106 -0
- package/src/lib/state/toasts.ts +34 -0
- package/src/lib/state/ui.spec.ts +148 -0
- package/src/lib/state/ui.ts +45 -0
- package/src/lib/state/welcome-toast.spec.ts +101 -0
- package/src/lib/state/welcome-toast.ts +76 -0
- package/src/lib/sw-recovery.spec.ts +165 -0
- package/src/lib/sw-recovery.ts +141 -0
- package/src/lib/types/bento.spec.ts +116 -0
- package/src/lib/types/bento.ts +94 -55
- package/src/lib/types/event-schemas.spec.ts +120 -0
- package/src/lib/types/event-types.spec.ts +96 -0
- package/src/lib/types/event-types.ts +113 -3
- package/src/lib/types/modules.ts +12 -5
- package/src/lib/types/ui-event-types.ts +66 -0
- package/src/lib/workers/canvas-ui-renderer.ts +266 -0
- package/src/lib/workers/canvas-worker.ts +94 -9
- package/src/lib/workers/data.ts +64 -37
- package/src/lib/workers/db-worker.ts +105 -32
- package/src/lib/workers/events.spec.ts +208 -0
- package/src/lib/workers/events.ts +27 -14
- package/src/lib/workers/flexbuilder.ts +2 -2
- package/src/lib/workers/init.ts +25 -17
- package/src/lib/workers/main.ts +506 -327
- package/src/lib/workers/supabase-db-worker.ts +243 -0
- package/src/lib/workers/supabase-shared-worker.ts +607 -0
- package/src/lib/workers/validate.ts +64 -0
- package/src/lib/workers/ws-worker.spec.ts +41 -0
- package/src/lib/workers/ws-worker.ts +255 -31
- package/src/setup-vitest.ts +4 -2
- package/src/types.ts +30 -0
- package/src/vite-env.d.ts +1 -0
- package/workers/main.js +2 -1125
- package/comlink.js +0 -253
- package/index.js +0 -2972
- package/reference.js +0 -259
- package/src/types.d.ts +0 -31
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ToastPayload,
|
|
3
|
+
TooltipPayload,
|
|
4
|
+
ModalPayload,
|
|
5
|
+
} from '../types/ui-event-types';
|
|
6
|
+
|
|
7
|
+
interface ThemeColors {
|
|
8
|
+
accent: string;
|
|
9
|
+
accentLow: string;
|
|
10
|
+
accentHigh: string;
|
|
11
|
+
bg: string;
|
|
12
|
+
bgAccent: string;
|
|
13
|
+
text: string;
|
|
14
|
+
textAccent: string;
|
|
15
|
+
border: string;
|
|
16
|
+
white: string;
|
|
17
|
+
black: string;
|
|
18
|
+
mode: 'light' | 'dark';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const SEVERITY_COLORS: Record<
|
|
22
|
+
string,
|
|
23
|
+
{ bg: string; border: string; text: string }
|
|
24
|
+
> = {
|
|
25
|
+
success: { bg: '#065f46', border: '#10b981', text: '#d1fae5' },
|
|
26
|
+
warning: { bg: '#78350f', border: '#f59e0b', text: '#fef3c7' },
|
|
27
|
+
error: { bg: '#7f1d1d', border: '#ef4444', text: '#fee2e2' },
|
|
28
|
+
info: { bg: '#1e3a5f', border: '#3b82f6', text: '#dbeafe' },
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
interface ActiveToast extends ToastPayload {
|
|
32
|
+
createdAt: number;
|
|
33
|
+
opacity: number;
|
|
34
|
+
y: number;
|
|
35
|
+
targetY: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export class CanvasUIRenderer {
|
|
39
|
+
private ctx: OffscreenCanvasRenderingContext2D | null = null;
|
|
40
|
+
private canvas: OffscreenCanvas | null = null;
|
|
41
|
+
private toasts: ActiveToast[] = [];
|
|
42
|
+
private tooltip: TooltipPayload | null = null;
|
|
43
|
+
private modal: ModalPayload | null = null;
|
|
44
|
+
private theme: ThemeColors = {
|
|
45
|
+
accent: '#8b5cf6',
|
|
46
|
+
accentLow: '#1e1033',
|
|
47
|
+
accentHigh: '#c4b5fd',
|
|
48
|
+
bg: '#0a0a0a',
|
|
49
|
+
bgAccent: '#3b1f6e',
|
|
50
|
+
text: '#e0e0e0',
|
|
51
|
+
textAccent: '#a78bfa',
|
|
52
|
+
border: '#4c1d95',
|
|
53
|
+
white: '#ffffff',
|
|
54
|
+
black: '#000000',
|
|
55
|
+
mode: 'dark',
|
|
56
|
+
};
|
|
57
|
+
private animFrame: number | null = null;
|
|
58
|
+
private dbGet: ((key: string) => Promise<string | null>) | null = null;
|
|
59
|
+
|
|
60
|
+
async bind(
|
|
61
|
+
canvas: OffscreenCanvas,
|
|
62
|
+
dbGet: (key: string) => Promise<string | null>,
|
|
63
|
+
): Promise<void> {
|
|
64
|
+
this.canvas = canvas;
|
|
65
|
+
this.ctx = canvas.getContext('2d');
|
|
66
|
+
this.dbGet = dbGet;
|
|
67
|
+
await this.refreshTheme();
|
|
68
|
+
this.startLoop();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async refreshTheme(): Promise<void> {
|
|
72
|
+
if (!this.dbGet) return;
|
|
73
|
+
this.theme = {
|
|
74
|
+
accent: (await this.dbGet('theme:accent')) ?? '#8b5cf6',
|
|
75
|
+
accentLow: (await this.dbGet('theme:accent-low')) ?? '#1e1033',
|
|
76
|
+
accentHigh: (await this.dbGet('theme:accent-high')) ?? '#c4b5fd',
|
|
77
|
+
bg: (await this.dbGet('theme:bg')) ?? '#0a0a0a',
|
|
78
|
+
bgAccent: (await this.dbGet('theme:bg-accent')) ?? '#3b1f6e',
|
|
79
|
+
text: (await this.dbGet('theme:text')) ?? '#e0e0e0',
|
|
80
|
+
textAccent: (await this.dbGet('theme:text-accent')) ?? '#a78bfa',
|
|
81
|
+
border: (await this.dbGet('theme:border')) ?? '#4c1d95',
|
|
82
|
+
white: (await this.dbGet('theme:white')) ?? '#ffffff',
|
|
83
|
+
black: (await this.dbGet('theme:black')) ?? '#000000',
|
|
84
|
+
mode:
|
|
85
|
+
((await this.dbGet('theme:mode')) as 'light' | 'dark') ??
|
|
86
|
+
'dark',
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
addToast(payload: ToastPayload): void {
|
|
91
|
+
const yOffset = 20 + this.toasts.length * 80;
|
|
92
|
+
this.toasts.push({
|
|
93
|
+
...payload,
|
|
94
|
+
createdAt: Date.now(),
|
|
95
|
+
opacity: 0,
|
|
96
|
+
y: yOffset - 20,
|
|
97
|
+
targetY: yOffset,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
removeToast(id: string): void {
|
|
102
|
+
const idx = this.toasts.findIndex((t) => t.id === id);
|
|
103
|
+
if (idx !== -1) this.toasts.splice(idx, 1);
|
|
104
|
+
this.recalcPositions();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
showTooltip(payload: TooltipPayload | null): void {
|
|
108
|
+
this.tooltip = payload;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
showModal(payload: ModalPayload | null): void {
|
|
112
|
+
this.modal = payload;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
private recalcPositions(): void {
|
|
116
|
+
this.toasts.forEach((t, i) => {
|
|
117
|
+
t.targetY = 20 + i * 80;
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private startLoop(): void {
|
|
122
|
+
const draw = () => {
|
|
123
|
+
this.render();
|
|
124
|
+
this.animFrame = requestAnimationFrame(draw);
|
|
125
|
+
};
|
|
126
|
+
draw();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private render(): void {
|
|
130
|
+
if (!this.ctx || !this.canvas) return;
|
|
131
|
+
const { width, height } = this.canvas;
|
|
132
|
+
this.ctx.clearRect(0, 0, width, height);
|
|
133
|
+
|
|
134
|
+
// Auto-dismiss expired toasts
|
|
135
|
+
const now = Date.now();
|
|
136
|
+
this.toasts = this.toasts.filter((t) => {
|
|
137
|
+
if (
|
|
138
|
+
t.duration &&
|
|
139
|
+
t.duration > 0 &&
|
|
140
|
+
now - t.createdAt > t.duration
|
|
141
|
+
) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
return true;
|
|
145
|
+
});
|
|
146
|
+
this.recalcPositions();
|
|
147
|
+
|
|
148
|
+
// Animate toasts
|
|
149
|
+
for (const toast of this.toasts) {
|
|
150
|
+
if (toast.opacity < 1)
|
|
151
|
+
toast.opacity = Math.min(1, toast.opacity + 0.05);
|
|
152
|
+
toast.y += (toast.targetY - toast.y) * 0.15;
|
|
153
|
+
this.drawToast(toast, width);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Draw modal backdrop + content
|
|
157
|
+
if (this.modal) {
|
|
158
|
+
this.drawModal(width, height);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private drawToast(toast: ActiveToast, canvasWidth: number): void {
|
|
163
|
+
if (!this.ctx) return;
|
|
164
|
+
const ctx = this.ctx;
|
|
165
|
+
const w = 320;
|
|
166
|
+
const h = 64;
|
|
167
|
+
const x = canvasWidth - w - 20;
|
|
168
|
+
const y = toast.y;
|
|
169
|
+
const colors =
|
|
170
|
+
SEVERITY_COLORS[toast.severity] ?? SEVERITY_COLORS['info'];
|
|
171
|
+
|
|
172
|
+
ctx.globalAlpha = toast.opacity;
|
|
173
|
+
|
|
174
|
+
// Background
|
|
175
|
+
ctx.fillStyle = colors.bg;
|
|
176
|
+
this.roundRect(ctx, x, y, w, h, 8);
|
|
177
|
+
ctx.fill();
|
|
178
|
+
|
|
179
|
+
// Border
|
|
180
|
+
ctx.strokeStyle = colors.border;
|
|
181
|
+
ctx.lineWidth = 1.5;
|
|
182
|
+
this.roundRect(ctx, x, y, w, h, 8);
|
|
183
|
+
ctx.stroke();
|
|
184
|
+
|
|
185
|
+
// Text
|
|
186
|
+
ctx.fillStyle = colors.text;
|
|
187
|
+
ctx.font = '14px system-ui, -apple-system, sans-serif';
|
|
188
|
+
ctx.fillText(toast.message, x + 12, y + 24, w - 24);
|
|
189
|
+
|
|
190
|
+
// Severity label
|
|
191
|
+
ctx.font = 'bold 10px system-ui, sans-serif';
|
|
192
|
+
ctx.fillStyle = colors.border;
|
|
193
|
+
ctx.fillText(toast.severity.toUpperCase(), x + 12, y + 48);
|
|
194
|
+
|
|
195
|
+
ctx.globalAlpha = 1;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private drawModal(canvasWidth: number, canvasHeight: number): void {
|
|
199
|
+
if (!this.ctx || !this.modal) return;
|
|
200
|
+
const ctx = this.ctx;
|
|
201
|
+
|
|
202
|
+
// Backdrop
|
|
203
|
+
ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
|
|
204
|
+
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
|
|
205
|
+
|
|
206
|
+
// Modal box
|
|
207
|
+
const w = Math.min(500, canvasWidth - 40);
|
|
208
|
+
const h = 300;
|
|
209
|
+
const x = (canvasWidth - w) / 2;
|
|
210
|
+
const y = (canvasHeight - h) / 2;
|
|
211
|
+
|
|
212
|
+
ctx.fillStyle = this.theme.bg;
|
|
213
|
+
this.roundRect(ctx, x, y, w, h, 12);
|
|
214
|
+
ctx.fill();
|
|
215
|
+
|
|
216
|
+
ctx.strokeStyle = this.theme.border;
|
|
217
|
+
ctx.lineWidth = 1;
|
|
218
|
+
this.roundRect(ctx, x, y, w, h, 12);
|
|
219
|
+
ctx.stroke();
|
|
220
|
+
|
|
221
|
+
// Title
|
|
222
|
+
if (this.modal.title) {
|
|
223
|
+
ctx.fillStyle = this.theme.text;
|
|
224
|
+
ctx.font = 'bold 18px system-ui, sans-serif';
|
|
225
|
+
ctx.fillText(this.modal.title, x + 20, y + 36, w - 40);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// ID label
|
|
229
|
+
ctx.fillStyle = this.theme.textAccent;
|
|
230
|
+
ctx.font = '12px system-ui, sans-serif';
|
|
231
|
+
ctx.fillText(this.modal.id, x + 20, y + 60, w - 40);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
private roundRect(
|
|
235
|
+
ctx: OffscreenCanvasRenderingContext2D,
|
|
236
|
+
x: number,
|
|
237
|
+
y: number,
|
|
238
|
+
w: number,
|
|
239
|
+
h: number,
|
|
240
|
+
r: number,
|
|
241
|
+
): void {
|
|
242
|
+
ctx.beginPath();
|
|
243
|
+
ctx.moveTo(x + r, y);
|
|
244
|
+
ctx.lineTo(x + w - r, y);
|
|
245
|
+
ctx.quadraticCurveTo(x + w, y, x + w, y + r);
|
|
246
|
+
ctx.lineTo(x + w, y + h - r);
|
|
247
|
+
ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
|
|
248
|
+
ctx.lineTo(x + r, y + h);
|
|
249
|
+
ctx.quadraticCurveTo(x, y + h, x, y + h - r);
|
|
250
|
+
ctx.lineTo(x, y + r);
|
|
251
|
+
ctx.quadraticCurveTo(x, y, x + r, y);
|
|
252
|
+
ctx.closePath();
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
unbind(): void {
|
|
256
|
+
if (this.animFrame !== null) {
|
|
257
|
+
cancelAnimationFrame(this.animFrame);
|
|
258
|
+
this.animFrame = null;
|
|
259
|
+
}
|
|
260
|
+
this.ctx = null;
|
|
261
|
+
this.canvas = null;
|
|
262
|
+
this.toasts = [];
|
|
263
|
+
this.tooltip = null;
|
|
264
|
+
this.modal = null;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
@@ -1,8 +1,28 @@
|
|
|
1
1
|
import { expose } from 'comlink';
|
|
2
|
+
import { CanvasUIRenderer } from './canvas-ui-renderer';
|
|
3
|
+
import type {
|
|
4
|
+
ToastPayload,
|
|
5
|
+
TooltipPayload,
|
|
6
|
+
ModalPayload,
|
|
7
|
+
} from '../types/ui-event-types';
|
|
2
8
|
|
|
3
9
|
export interface CanvasWorkerAPI {
|
|
4
|
-
bindCanvas(
|
|
10
|
+
bindCanvas(
|
|
11
|
+
panelId: string,
|
|
12
|
+
canvas: OffscreenCanvas,
|
|
13
|
+
mode?: CanvasDrawMode,
|
|
14
|
+
): Promise<void>;
|
|
5
15
|
unbindCanvas(panelId: string): Promise<void>;
|
|
16
|
+
bindUICanvas(
|
|
17
|
+
canvas: OffscreenCanvas,
|
|
18
|
+
dbGet: (key: string) => Promise<string | null>,
|
|
19
|
+
): Promise<void>;
|
|
20
|
+
unbindUICanvas(): void;
|
|
21
|
+
addCanvasToast(payload: ToastPayload): void;
|
|
22
|
+
removeCanvasToast(id: string): void;
|
|
23
|
+
showCanvasTooltip(payload: TooltipPayload | null): void;
|
|
24
|
+
showCanvasModal(payload: ModalPayload | null): void;
|
|
25
|
+
refreshUITheme(): Promise<void>;
|
|
6
26
|
}
|
|
7
27
|
|
|
8
28
|
interface CanvasBinding {
|
|
@@ -15,18 +35,38 @@ interface CanvasBinding {
|
|
|
15
35
|
|
|
16
36
|
export type CanvasDrawMode = 'static' | 'animated' | 'dynamic';
|
|
17
37
|
|
|
38
|
+
const uiRenderer = new CanvasUIRenderer();
|
|
39
|
+
|
|
40
|
+
// Listen for theme changes from main thread
|
|
41
|
+
try {
|
|
42
|
+
const bc = new BroadcastChannel('kbve_theme');
|
|
43
|
+
bc.onmessage = () => {
|
|
44
|
+
void uiRenderer.refreshTheme();
|
|
45
|
+
};
|
|
46
|
+
} catch {
|
|
47
|
+
// BroadcastChannel unavailable in this context
|
|
48
|
+
}
|
|
49
|
+
|
|
18
50
|
const CanvasManager = {
|
|
19
51
|
bindings: new Map<string, CanvasBinding>(),
|
|
20
52
|
|
|
21
|
-
async bindCanvas(
|
|
53
|
+
async bindCanvas(
|
|
54
|
+
panelId: string,
|
|
55
|
+
canvas: OffscreenCanvas,
|
|
56
|
+
mode: CanvasDrawMode = 'animated',
|
|
57
|
+
) {
|
|
22
58
|
const ctx = canvas.getContext('2d');
|
|
23
59
|
|
|
24
60
|
if (!ctx) {
|
|
25
|
-
console.error(
|
|
61
|
+
console.error(
|
|
62
|
+
`[CanvasWorker] Failed to get 2D context for panel ${panelId}`,
|
|
63
|
+
);
|
|
26
64
|
return;
|
|
27
65
|
}
|
|
28
66
|
|
|
29
|
-
console.log(
|
|
67
|
+
console.log(
|
|
68
|
+
`[CanvasWorker] Successfully bound canvas for panel ${panelId} with mode ${mode}`,
|
|
69
|
+
);
|
|
30
70
|
|
|
31
71
|
this.bindings.set(panelId, { ctx, canvas, panelId, mode });
|
|
32
72
|
|
|
@@ -48,7 +88,9 @@ const CanvasManager = {
|
|
|
48
88
|
this.drawDynamic(binding);
|
|
49
89
|
break;
|
|
50
90
|
default:
|
|
51
|
-
console.warn(
|
|
91
|
+
console.warn(
|
|
92
|
+
`[CanvasWorker] Unknown draw mode for panel ${panelId}`,
|
|
93
|
+
);
|
|
52
94
|
}
|
|
53
95
|
},
|
|
54
96
|
|
|
@@ -63,7 +105,12 @@ const CanvasManager = {
|
|
|
63
105
|
const drawFrame = () => {
|
|
64
106
|
hue = (hue + 1) % 360;
|
|
65
107
|
binding.ctx.fillStyle = `hsl(${hue}, 100%, 50%)`;
|
|
66
|
-
binding.ctx.fillRect(
|
|
108
|
+
binding.ctx.fillRect(
|
|
109
|
+
0,
|
|
110
|
+
0,
|
|
111
|
+
binding.canvas.width,
|
|
112
|
+
binding.canvas.height,
|
|
113
|
+
);
|
|
67
114
|
|
|
68
115
|
binding.animationFrame = requestAnimationFrame(drawFrame);
|
|
69
116
|
};
|
|
@@ -76,18 +123,23 @@ const CanvasManager = {
|
|
|
76
123
|
|
|
77
124
|
const drawFrame = () => {
|
|
78
125
|
time += 0.05;
|
|
79
|
-
binding.ctx.clearRect(
|
|
126
|
+
binding.ctx.clearRect(
|
|
127
|
+
0,
|
|
128
|
+
0,
|
|
129
|
+
binding.canvas.width,
|
|
130
|
+
binding.canvas.height,
|
|
131
|
+
);
|
|
80
132
|
binding.ctx.beginPath();
|
|
81
133
|
binding.ctx.arc(
|
|
82
134
|
binding.canvas.width / 2 + Math.sin(time) * 50,
|
|
83
135
|
binding.canvas.height / 2 + Math.cos(time) * 50,
|
|
84
136
|
30,
|
|
85
137
|
0,
|
|
86
|
-
Math.PI * 2
|
|
138
|
+
Math.PI * 2,
|
|
87
139
|
);
|
|
88
140
|
binding.ctx.fillStyle = 'orange';
|
|
89
141
|
binding.ctx.fill();
|
|
90
|
-
|
|
142
|
+
|
|
91
143
|
binding.animationFrame = requestAnimationFrame(drawFrame);
|
|
92
144
|
};
|
|
93
145
|
|
|
@@ -102,6 +154,39 @@ const CanvasManager = {
|
|
|
102
154
|
this.bindings.delete(panelId);
|
|
103
155
|
console.log(`[CanvasWorker] Unbound canvas for panel ${panelId}`);
|
|
104
156
|
},
|
|
157
|
+
|
|
158
|
+
// ── UI Overlay Canvas ──
|
|
159
|
+
|
|
160
|
+
async bindUICanvas(
|
|
161
|
+
canvas: OffscreenCanvas,
|
|
162
|
+
dbGet: (key: string) => Promise<string | null>,
|
|
163
|
+
) {
|
|
164
|
+
await uiRenderer.bind(canvas, dbGet);
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
unbindUICanvas() {
|
|
168
|
+
uiRenderer.unbind();
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
addCanvasToast(payload: ToastPayload) {
|
|
172
|
+
uiRenderer.addToast(payload);
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
removeCanvasToast(id: string) {
|
|
176
|
+
uiRenderer.removeToast(id);
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
showCanvasTooltip(payload: TooltipPayload | null) {
|
|
180
|
+
uiRenderer.showTooltip(payload);
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
showCanvasModal(payload: ModalPayload | null) {
|
|
184
|
+
uiRenderer.showModal(payload);
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
refreshUITheme() {
|
|
188
|
+
return uiRenderer.refreshTheme();
|
|
189
|
+
},
|
|
105
190
|
};
|
|
106
191
|
|
|
107
192
|
expose(CanvasManager);
|
package/src/lib/workers/data.ts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
PayloadFormat,
|
|
3
|
+
type JediEnvelopeFlex,
|
|
4
|
+
MessageKind,
|
|
5
|
+
} from '../types/jedi';
|
|
2
6
|
import { builder, toReference } from './flexbuilder';
|
|
3
7
|
|
|
4
|
-
|
|
5
|
-
|
|
8
|
+
function buildFlexPayload<T extends Record<string, unknown>>(
|
|
9
|
+
payload: T,
|
|
10
|
+
): Uint8Array {
|
|
6
11
|
const b = builder();
|
|
7
12
|
b.startMap();
|
|
8
13
|
|
|
@@ -15,7 +20,7 @@ function buildFlexPayload<T extends Record<string, any>>(payload: T): Uint8Array
|
|
|
15
20
|
return b.finish();
|
|
16
21
|
}
|
|
17
22
|
|
|
18
|
-
export function wrapEnvelope<T extends Record<string,
|
|
23
|
+
export function wrapEnvelope<T extends Record<string, unknown>>(
|
|
19
24
|
payload: T,
|
|
20
25
|
kind: number,
|
|
21
26
|
format: PayloadFormat,
|
|
@@ -34,22 +39,30 @@ export function wrapEnvelope<T extends Record<string, any>>(
|
|
|
34
39
|
|
|
35
40
|
const b = builder();
|
|
36
41
|
b.startMap();
|
|
37
|
-
b.addKey('version');
|
|
38
|
-
b.
|
|
39
|
-
b.addKey('
|
|
40
|
-
b.
|
|
41
|
-
b.addKey('
|
|
42
|
+
b.addKey('version');
|
|
43
|
+
b.add(version);
|
|
44
|
+
b.addKey('kind');
|
|
45
|
+
b.add(kind);
|
|
46
|
+
b.addKey('format');
|
|
47
|
+
b.add(format);
|
|
48
|
+
b.addKey('payload');
|
|
49
|
+
b.add(serializedPayload);
|
|
50
|
+
b.addKey('metadata');
|
|
51
|
+
b.add(metadata ?? new Uint8Array());
|
|
42
52
|
b.end();
|
|
43
53
|
|
|
44
54
|
return b.finish();
|
|
45
55
|
}
|
|
46
56
|
|
|
47
57
|
export function unwrapEnvelope<T = unknown>(
|
|
48
|
-
buffer: Uint8Array | ArrayBuffer
|
|
58
|
+
buffer: Uint8Array | ArrayBuffer,
|
|
49
59
|
): { envelope: JediEnvelopeFlex; payload: T } {
|
|
50
60
|
const view = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
|
|
51
61
|
|
|
52
|
-
const root = toReference(view.buffer as ArrayBuffer).toObject() as Record<
|
|
62
|
+
const root = toReference(view.buffer as ArrayBuffer).toObject() as Record<
|
|
63
|
+
string,
|
|
64
|
+
unknown
|
|
65
|
+
>;
|
|
53
66
|
|
|
54
67
|
if (
|
|
55
68
|
typeof root['version'] !== 'number' ||
|
|
@@ -65,8 +78,10 @@ export function unwrapEnvelope<T = unknown>(
|
|
|
65
78
|
version: root['version'],
|
|
66
79
|
kind: root['kind'],
|
|
67
80
|
format: root['format'],
|
|
68
|
-
payload: new Uint8Array(root['payload']),
|
|
69
|
-
metadata: root['metadata']
|
|
81
|
+
payload: new Uint8Array(root['payload'] as ArrayLike<number>),
|
|
82
|
+
metadata: root['metadata']
|
|
83
|
+
? new Uint8Array(root['metadata'] as ArrayLike<number>)
|
|
84
|
+
: undefined,
|
|
70
85
|
};
|
|
71
86
|
|
|
72
87
|
let parsedPayload: T;
|
|
@@ -75,9 +90,13 @@ export function unwrapEnvelope<T = unknown>(
|
|
|
75
90
|
const ref = toReference(envelope.payload.buffer as ArrayBuffer);
|
|
76
91
|
parsedPayload = ref.toObject() as T;
|
|
77
92
|
} else if (envelope.format === PayloadFormat.JSON) {
|
|
78
|
-
parsedPayload = JSON.parse(
|
|
93
|
+
parsedPayload = JSON.parse(
|
|
94
|
+
new TextDecoder().decode(envelope.payload),
|
|
95
|
+
) as T;
|
|
79
96
|
} else {
|
|
80
|
-
throw new Error(
|
|
97
|
+
throw new Error(
|
|
98
|
+
`[unwrapEnvelope] Unsupported format: ${envelope.format}`,
|
|
99
|
+
);
|
|
81
100
|
}
|
|
82
101
|
|
|
83
102
|
return { envelope, payload: parsedPayload };
|
|
@@ -89,7 +108,8 @@ export function unwrapFlexToJson(buffer: Uint8Array) {
|
|
|
89
108
|
|
|
90
109
|
export function inspectFlex(buffer: Uint8Array | ArrayBuffer): void {
|
|
91
110
|
try {
|
|
92
|
-
const view =
|
|
111
|
+
const view =
|
|
112
|
+
buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
|
|
93
113
|
const obj = unwrapFlexToJson(view);
|
|
94
114
|
console.log('[FlexObject]', JSON.stringify(obj, null, 2));
|
|
95
115
|
} catch (err) {
|
|
@@ -101,14 +121,13 @@ export function hasKind(kind: number, flag: number): boolean {
|
|
|
101
121
|
return (kind & flag) === flag;
|
|
102
122
|
}
|
|
103
123
|
|
|
104
|
-
|
|
105
124
|
// Redis Helpers
|
|
106
125
|
|
|
107
126
|
export function wrapRedisSet(key: string, value: string): Uint8Array {
|
|
108
127
|
return wrapEnvelope(
|
|
109
128
|
{ key, value },
|
|
110
129
|
MessageKind.SET | MessageKind.REDIS,
|
|
111
|
-
PayloadFormat.FLEX
|
|
130
|
+
PayloadFormat.FLEX,
|
|
112
131
|
);
|
|
113
132
|
}
|
|
114
133
|
|
|
@@ -116,7 +135,7 @@ export function wrapRedisGet(key: string): Uint8Array {
|
|
|
116
135
|
return wrapEnvelope(
|
|
117
136
|
{ key },
|
|
118
137
|
MessageKind.GET | MessageKind.REDIS,
|
|
119
|
-
PayloadFormat.FLEX
|
|
138
|
+
PayloadFormat.FLEX,
|
|
120
139
|
);
|
|
121
140
|
}
|
|
122
141
|
|
|
@@ -124,11 +143,13 @@ export function wrapRedisDel(key: string): Uint8Array {
|
|
|
124
143
|
return wrapEnvelope(
|
|
125
144
|
{ key },
|
|
126
145
|
MessageKind.DEL | MessageKind.REDIS,
|
|
127
|
-
PayloadFormat.FLEX
|
|
146
|
+
PayloadFormat.FLEX,
|
|
128
147
|
);
|
|
129
148
|
}
|
|
130
149
|
|
|
131
|
-
export function parseRedisPayload<T = unknown>(
|
|
150
|
+
export function parseRedisPayload<T = unknown>(
|
|
151
|
+
envelopeBytes: Uint8Array,
|
|
152
|
+
): {
|
|
132
153
|
envelope: JediEnvelopeFlex;
|
|
133
154
|
payload: T;
|
|
134
155
|
} {
|
|
@@ -156,25 +177,28 @@ export function wrapRedisXRead(
|
|
|
156
177
|
count?: number,
|
|
157
178
|
block?: number,
|
|
158
179
|
): Uint8Array {
|
|
159
|
-
|
|
160
180
|
const inner = builder();
|
|
161
181
|
inner.startMap();
|
|
162
182
|
|
|
163
183
|
inner.addKey('streams');
|
|
164
|
-
inner.startVector();
|
|
184
|
+
inner.startVector();
|
|
165
185
|
for (const { stream, id } of streams) {
|
|
166
186
|
inner.startMap();
|
|
167
|
-
inner.addKey('stream');
|
|
168
|
-
inner.
|
|
187
|
+
inner.addKey('stream');
|
|
188
|
+
inner.add(stream);
|
|
189
|
+
inner.addKey('id');
|
|
190
|
+
inner.add(id);
|
|
169
191
|
inner.end();
|
|
170
|
-
|
|
192
|
+
}
|
|
171
193
|
inner.end();
|
|
172
194
|
|
|
173
195
|
if (count !== undefined) {
|
|
174
|
-
inner.addKey('count');
|
|
196
|
+
inner.addKey('count');
|
|
197
|
+
inner.add(count);
|
|
175
198
|
}
|
|
176
199
|
if (block !== undefined) {
|
|
177
|
-
inner.addKey('block');
|
|
200
|
+
inner.addKey('block');
|
|
201
|
+
inner.add(block);
|
|
178
202
|
}
|
|
179
203
|
|
|
180
204
|
inner.end();
|
|
@@ -182,18 +206,21 @@ export function wrapRedisXRead(
|
|
|
182
206
|
|
|
183
207
|
const b = builder();
|
|
184
208
|
b.startMap();
|
|
185
|
-
b.addKey('version');
|
|
186
|
-
b.
|
|
187
|
-
b.addKey('
|
|
188
|
-
b.
|
|
189
|
-
b.addKey('
|
|
209
|
+
b.addKey('version');
|
|
210
|
+
b.add(1);
|
|
211
|
+
b.addKey('kind');
|
|
212
|
+
b.add(MessageKind.READ | MessageKind.STREAM | MessageKind.REDIS);
|
|
213
|
+
b.addKey('format');
|
|
214
|
+
b.add(PayloadFormat.FLEX);
|
|
215
|
+
b.addKey('payload');
|
|
216
|
+
b.add(serializedPayload);
|
|
217
|
+
b.addKey('metadata');
|
|
218
|
+
b.add(new Uint8Array());
|
|
190
219
|
b.end();
|
|
191
220
|
|
|
192
221
|
return b.finish();
|
|
193
222
|
}
|
|
194
223
|
|
|
195
|
-
|
|
196
|
-
|
|
197
224
|
export const scopeData = {
|
|
198
225
|
wrapEnvelope,
|
|
199
226
|
unwrapEnvelope,
|
|
@@ -202,14 +229,14 @@ export const scopeData = {
|
|
|
202
229
|
unwrapFlexToJson,
|
|
203
230
|
inspectFlex,
|
|
204
231
|
hasKind,
|
|
205
|
-
|
|
232
|
+
redis: {
|
|
206
233
|
wrapRedisSet,
|
|
207
234
|
wrapRedisGet,
|
|
208
235
|
wrapRedisDel,
|
|
209
236
|
wrapRedisXAdd,
|
|
210
237
|
wrapRedisXRead,
|
|
211
238
|
parseRedisPayload,
|
|
212
|
-
}
|
|
239
|
+
},
|
|
213
240
|
};
|
|
214
241
|
|
|
215
242
|
export type FlexDataAPI = typeof scopeData;
|