@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.
Files changed (190) hide show
  1. package/droid.mjs +2312 -74
  2. package/index.d.ts +32 -0
  3. package/lib/agents/api-types.d.ts +121 -0
  4. package/lib/agents/api-types.d.ts.map +1 -0
  5. package/lib/agents/auth/init.d.ts +8 -0
  6. package/lib/agents/auth/init.d.ts.map +1 -0
  7. package/lib/agents/cache.d.ts +9 -0
  8. package/lib/agents/cache.d.ts.map +1 -0
  9. package/lib/agents/client/callGuildVault.d.ts +3 -0
  10. package/lib/agents/client/callGuildVault.d.ts.map +1 -0
  11. package/lib/agents/client/callJson.d.ts +3 -0
  12. package/lib/agents/client/callJson.d.ts.map +1 -0
  13. package/lib/agents/config.d.ts +36 -0
  14. package/lib/agents/config.d.ts.map +1 -0
  15. package/lib/agents/constants.d.ts +11 -0
  16. package/lib/agents/constants.d.ts.map +1 -0
  17. package/lib/agents/createAgents.d.ts +4 -0
  18. package/lib/agents/createAgents.d.ts.map +1 -0
  19. package/lib/agents/ctx.d.ts +46 -0
  20. package/lib/agents/ctx.d.ts.map +1 -0
  21. package/lib/agents/discord/bot.d.ts +21 -0
  22. package/lib/agents/discord/bot.d.ts.map +1 -0
  23. package/lib/agents/discord/guilds.d.ts +8 -0
  24. package/lib/agents/discord/guilds.d.ts.map +1 -0
  25. package/lib/agents/formDrafts.d.ts +5 -0
  26. package/lib/agents/formDrafts.d.ts.map +1 -0
  27. package/lib/agents/generated/agents-schema.d.ts +73 -0
  28. package/lib/agents/generated/agents-schema.d.ts.map +1 -0
  29. package/lib/agents/generated/discordsh-agents-schema.d.ts +71 -0
  30. package/lib/agents/generated/discordsh-agents-schema.d.ts.map +1 -0
  31. package/lib/agents/github/backfill.d.ts +23 -0
  32. package/lib/agents/github/backfill.d.ts.map +1 -0
  33. package/lib/agents/github/events.d.ts +10 -0
  34. package/lib/agents/github/events.d.ts.map +1 -0
  35. package/lib/agents/github/pat.d.ts +13 -0
  36. package/lib/agents/github/pat.d.ts.map +1 -0
  37. package/lib/agents/github/repoConfig.d.ts +25 -0
  38. package/lib/agents/github/repoConfig.d.ts.map +1 -0
  39. package/lib/agents/github/tokens.d.ts +22 -0
  40. package/lib/agents/github/tokens.d.ts.map +1 -0
  41. package/lib/agents/github/webhook.d.ts +37 -0
  42. package/lib/agents/github/webhook.d.ts.map +1 -0
  43. package/lib/agents/index.d.ts +8 -0
  44. package/lib/agents/index.d.ts.map +1 -0
  45. package/lib/agents/state/atoms.d.ts +122 -0
  46. package/lib/agents/state/atoms.d.ts.map +1 -0
  47. package/lib/agents/types.d.ts +65 -0
  48. package/lib/agents/types.d.ts.map +1 -0
  49. package/lib/gateway/SupabaseGateway.d.ts +38 -0
  50. package/lib/gateway/SupabaseGateway.d.ts.map +1 -0
  51. package/lib/gateway/WorkerCommunication.d.ts +39 -0
  52. package/lib/gateway/WorkerCommunication.d.ts.map +1 -0
  53. package/lib/gateway/WorkerPool.d.ts +55 -0
  54. package/lib/gateway/WorkerPool.d.ts.map +1 -0
  55. package/lib/gateway/capabilities.d.ts +23 -0
  56. package/lib/gateway/capabilities.d.ts.map +1 -0
  57. package/lib/gateway/index.d.ts +9 -0
  58. package/lib/gateway/index.d.ts.map +1 -0
  59. package/lib/gateway/strategies/DirectStrategy.d.ts +52 -0
  60. package/lib/gateway/strategies/DirectStrategy.d.ts.map +1 -0
  61. package/lib/gateway/strategies/SharedWorkerStrategy.d.ts +46 -0
  62. package/lib/gateway/strategies/SharedWorkerStrategy.d.ts.map +1 -0
  63. package/lib/gateway/strategies/WebWorkerStrategy.d.ts +45 -0
  64. package/lib/gateway/strategies/WebWorkerStrategy.d.ts.map +1 -0
  65. package/lib/gateway/types.d.ts +81 -0
  66. package/lib/gateway/types.d.ts.map +1 -0
  67. package/lib/workers/canvas-ui-renderer.d.ts +25 -0
  68. package/lib/workers/canvas-ui-renderer.d.ts.map +1 -0
  69. package/lib/workers/canvas-worker.d.ts +14 -0
  70. package/lib/workers/canvas-worker.d.ts.map +1 -0
  71. package/lib/workers/canvas-worker.js +5 -302
  72. package/lib/workers/data.d.ts +40 -0
  73. package/lib/workers/data.d.ts.map +1 -0
  74. package/lib/workers/db-worker.d.ts +69 -0
  75. package/lib/workers/db-worker.d.ts.map +1 -0
  76. package/lib/workers/db-worker.js +13 -3636
  77. package/lib/workers/events.d.ts +11 -0
  78. package/lib/workers/events.d.ts.map +1 -0
  79. package/lib/workers/flexbuilder.d.ts +4 -0
  80. package/lib/workers/flexbuilder.d.ts.map +1 -0
  81. package/lib/workers/init.d.ts +11 -0
  82. package/lib/workers/init.d.ts.map +1 -0
  83. package/lib/workers/main.d.ts +105 -0
  84. package/lib/workers/main.d.ts.map +1 -0
  85. package/lib/workers/supabase-db-worker.d.ts +2 -0
  86. package/lib/workers/supabase-db-worker.d.ts.map +1 -0
  87. package/lib/workers/supabase-db-worker.js +23 -0
  88. package/lib/workers/supabase-shared-worker.d.ts +2 -0
  89. package/lib/workers/supabase-shared-worker.d.ts.map +1 -0
  90. package/lib/workers/supabase-shared-worker.js +25 -0
  91. package/lib/workers/tools.d.ts +4 -0
  92. package/lib/workers/tools.d.ts.map +1 -0
  93. package/lib/workers/validate.d.ts +15 -0
  94. package/lib/workers/validate.d.ts.map +1 -0
  95. package/lib/workers/ws-worker.d.ts +10 -0
  96. package/lib/workers/ws-worker.d.ts.map +1 -0
  97. package/lib/workers/ws-worker.js +5 -273
  98. package/main.js +5711 -0
  99. package/package.json +18 -6
  100. package/src/index.ts +41 -13
  101. package/src/lib/agents/api-types.ts +170 -0
  102. package/src/lib/agents/auth/init.ts +200 -0
  103. package/src/lib/agents/cache.ts +127 -0
  104. package/src/lib/agents/client/callGuildVault.ts +71 -0
  105. package/src/lib/agents/client/callJson.ts +64 -0
  106. package/src/lib/agents/config.ts +83 -0
  107. package/src/lib/agents/constants.ts +11 -0
  108. package/src/lib/agents/createAgents.ts +62 -0
  109. package/src/lib/agents/ctx.ts +63 -0
  110. package/src/lib/agents/discord/bot.ts +169 -0
  111. package/src/lib/agents/discord/guilds.ts +106 -0
  112. package/src/lib/agents/formDrafts.ts +55 -0
  113. package/src/lib/agents/generated/agents-schema.ts +155 -0
  114. package/src/lib/agents/generated/discordsh-agents-schema.ts +96 -0
  115. package/src/lib/agents/github/backfill.ts +126 -0
  116. package/src/lib/agents/github/events.ts +131 -0
  117. package/src/lib/agents/github/pat.ts +150 -0
  118. package/src/lib/agents/github/repoConfig.ts +240 -0
  119. package/src/lib/agents/github/tokens.ts +222 -0
  120. package/src/lib/agents/github/webhook.ts +349 -0
  121. package/src/lib/agents/index.ts +35 -0
  122. package/src/lib/agents/state/atoms.ts +122 -0
  123. package/src/lib/agents/types.ts +83 -0
  124. package/src/lib/api.ts +71 -0
  125. package/src/lib/droid.spec.ts +14 -19
  126. package/src/lib/droid.ts +48 -4
  127. package/src/lib/gateway/SupabaseGateway.ts +236 -0
  128. package/src/lib/gateway/WorkerCommunication.ts +132 -0
  129. package/src/lib/gateway/WorkerPool.ts +304 -0
  130. package/src/lib/gateway/capabilities.spec.ts +144 -0
  131. package/src/lib/gateway/capabilities.ts +80 -0
  132. package/src/lib/gateway/index.ts +15 -0
  133. package/src/lib/gateway/strategies/DirectStrategy.ts +443 -0
  134. package/src/lib/gateway/strategies/SharedWorkerStrategy.ts +278 -0
  135. package/src/lib/gateway/strategies/WebWorkerStrategy.ts +278 -0
  136. package/src/lib/gateway/types.ts +139 -0
  137. package/src/lib/mod/mod-manager.ts +13 -8
  138. package/src/lib/mod/module/bento/mod-bento.worker.ts +1 -1
  139. package/src/lib/mod/module/supabase/mod-supabase.spec.ts +21 -25
  140. package/src/lib/mod/module/supabase/mod-supabase.ts +73 -0
  141. package/src/lib/mod/module/supabase/mod-supabase.worker.ts +1 -56
  142. package/src/lib/state/auth.spec.ts +58 -0
  143. package/src/lib/state/auth.ts +148 -0
  144. package/src/lib/state/bento.spec.ts +76 -0
  145. package/src/lib/state/bento.ts +51 -0
  146. package/src/lib/state/index.ts +98 -0
  147. package/src/lib/state/overlay-manager.ts +125 -0
  148. package/src/lib/state/profile-sync.ts +214 -0
  149. package/src/lib/state/profile.ts +155 -0
  150. package/src/lib/state/router.ts +24 -0
  151. package/src/lib/state/staff.ts +128 -0
  152. package/src/lib/state/sync-bus.ts +109 -0
  153. package/src/lib/state/theme-sync.ts +75 -0
  154. package/src/lib/state/toasts.spec.ts +106 -0
  155. package/src/lib/state/toasts.ts +34 -0
  156. package/src/lib/state/ui.spec.ts +148 -0
  157. package/src/lib/state/ui.ts +45 -0
  158. package/src/lib/state/welcome-toast.spec.ts +101 -0
  159. package/src/lib/state/welcome-toast.ts +76 -0
  160. package/src/lib/sw-recovery.spec.ts +165 -0
  161. package/src/lib/sw-recovery.ts +141 -0
  162. package/src/lib/types/bento.spec.ts +116 -0
  163. package/src/lib/types/bento.ts +94 -55
  164. package/src/lib/types/event-schemas.spec.ts +120 -0
  165. package/src/lib/types/event-types.spec.ts +96 -0
  166. package/src/lib/types/event-types.ts +113 -3
  167. package/src/lib/types/modules.ts +12 -5
  168. package/src/lib/types/ui-event-types.ts +66 -0
  169. package/src/lib/workers/canvas-ui-renderer.ts +266 -0
  170. package/src/lib/workers/canvas-worker.ts +94 -9
  171. package/src/lib/workers/data.ts +64 -37
  172. package/src/lib/workers/db-worker.ts +105 -32
  173. package/src/lib/workers/events.spec.ts +208 -0
  174. package/src/lib/workers/events.ts +27 -14
  175. package/src/lib/workers/flexbuilder.ts +2 -2
  176. package/src/lib/workers/init.ts +25 -17
  177. package/src/lib/workers/main.ts +506 -327
  178. package/src/lib/workers/supabase-db-worker.ts +243 -0
  179. package/src/lib/workers/supabase-shared-worker.ts +607 -0
  180. package/src/lib/workers/validate.ts +64 -0
  181. package/src/lib/workers/ws-worker.spec.ts +41 -0
  182. package/src/lib/workers/ws-worker.ts +255 -31
  183. package/src/setup-vitest.ts +4 -2
  184. package/src/types.ts +30 -0
  185. package/src/vite-env.d.ts +1 -0
  186. package/workers/main.js +2 -1125
  187. package/comlink.js +0 -253
  188. package/index.js +0 -2972
  189. package/reference.js +0 -259
  190. 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(panelId: string, canvas: OffscreenCanvas, mode?: CanvasDrawMode): Promise<void>;
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(panelId: string, canvas: OffscreenCanvas, mode: CanvasDrawMode = 'animated') {
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(`[CanvasWorker] Failed to get 2D context for panel ${panelId}`);
61
+ console.error(
62
+ `[CanvasWorker] Failed to get 2D context for panel ${panelId}`,
63
+ );
26
64
  return;
27
65
  }
28
66
 
29
- console.log(`[CanvasWorker] Successfully bound canvas for panel ${panelId} with mode ${mode}`);
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(`[CanvasWorker] Unknown draw mode for panel ${panelId}`);
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(0, 0, binding.canvas.width, binding.canvas.height);
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(0, 0, binding.canvas.width, binding.canvas.height);
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);
@@ -1,8 +1,13 @@
1
- import { PayloadFormat, JediEnvelopeFlex, MessageKind } from '../types/jedi';
1
+ import {
2
+ PayloadFormat,
3
+ type JediEnvelopeFlex,
4
+ MessageKind,
5
+ } from '../types/jedi';
2
6
  import { builder, toReference } from './flexbuilder';
3
7
 
4
-
5
- function buildFlexPayload<T extends Record<string, any>>(payload: T): Uint8Array {
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, any>>(
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'); b.add(version);
38
- b.addKey('kind'); b.add(kind);
39
- b.addKey('format'); b.add(format);
40
- b.addKey('payload'); b.add(serializedPayload);
41
- b.addKey('metadata'); b.add(metadata ?? new Uint8Array());
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<string, any>;
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'] ? new Uint8Array(root['metadata']) : undefined,
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(new TextDecoder().decode(envelope.payload)) as T;
93
+ parsedPayload = JSON.parse(
94
+ new TextDecoder().decode(envelope.payload),
95
+ ) as T;
79
96
  } else {
80
- throw new Error(`[unwrapEnvelope] Unsupported format: ${envelope.format}`);
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 = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
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>(envelopeBytes: Uint8Array): {
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'); inner.add(stream);
168
- inner.addKey('id'); inner.add(id);
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'); inner.add(count);
196
+ inner.addKey('count');
197
+ inner.add(count);
175
198
  }
176
199
  if (block !== undefined) {
177
- inner.addKey('block'); inner.add(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'); b.add(1);
186
- b.addKey('kind'); b.add(MessageKind.READ | MessageKind.STREAM | MessageKind.REDIS);
187
- b.addKey('format'); b.add(PayloadFormat.FLEX);
188
- b.addKey('payload'); b.add(serializedPayload);
189
- b.addKey('metadata'); b.add(new Uint8Array());
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
- redis: {
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;