create-nativecore 0.1.1 → 0.2.1

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 (175) hide show
  1. package/README.md +6 -14
  2. package/bin/index.mjs +403 -431
  3. package/package.json +3 -2
  4. package/template/.env.example +28 -0
  5. package/template/.htmlhintrc +14 -0
  6. package/template/api/data/dashboard.json +11 -0
  7. package/template/api/data/users.json +18 -0
  8. package/template/api/mockApi.js +161 -0
  9. package/template/assets/icon.svg +13 -0
  10. package/template/assets/logo.svg +25 -0
  11. package/template/eslint.config.js +94 -0
  12. package/template/index.html +137 -0
  13. package/template/manifest.json +19 -0
  14. package/template/public/.well-known/security.txt +9 -0
  15. package/template/public/_headers +24 -0
  16. package/template/public/_redirects +14 -0
  17. package/template/public/assets/icon.svg +13 -0
  18. package/template/public/assets/logo.svg +25 -0
  19. package/template/public/manifest.json +19 -0
  20. package/template/public/robots.txt +13 -0
  21. package/template/public/sitemap.xml +27 -0
  22. package/template/scripts/build-for-bots.mjs +121 -0
  23. package/template/scripts/convert-to-ts.mjs +106 -0
  24. package/template/scripts/fix-encoding.mjs +38 -0
  25. package/template/scripts/fix-svg-paths.mjs +32 -0
  26. package/template/scripts/generate-cf-router.mjs +52 -0
  27. package/template/scripts/inject-dev-tools.mjs +41 -0
  28. package/template/scripts/inject-version.mjs +65 -0
  29. package/template/scripts/make-component.mjs +445 -0
  30. package/template/scripts/make-component.mjs.backup +432 -0
  31. package/template/scripts/make-controller.mjs +119 -0
  32. package/template/scripts/make-core-component.mjs +303 -0
  33. package/template/scripts/make-view.mjs +346 -0
  34. package/template/scripts/minify.mjs +71 -0
  35. package/template/scripts/prepare-static-assets.mjs +141 -0
  36. package/template/scripts/prompt-bot-build.mjs +223 -0
  37. package/template/scripts/remove-component.mjs +170 -0
  38. package/template/scripts/remove-core-component.mjs +156 -0
  39. package/template/scripts/remove-dev.mjs +13 -0
  40. package/template/scripts/remove-view.mjs +200 -0
  41. package/template/scripts/strip-dev-blocks.mjs +30 -0
  42. package/template/scripts/watch-compile.mjs +69 -0
  43. package/template/server.js +1066 -0
  44. package/template/src/app.ts +115 -0
  45. package/template/src/components/appRegistry.ts +8 -0
  46. package/template/src/components/core/app-footer.ts +27 -0
  47. package/template/src/components/core/app-header.ts +175 -0
  48. package/template/src/components/core/app-sidebar.ts +238 -0
  49. package/template/src/components/core/loading-spinner.ts +25 -0
  50. package/template/src/components/core/nc-a.ts +313 -0
  51. package/template/src/components/core/nc-accordion.ts +186 -0
  52. package/template/src/components/core/nc-alert.ts +153 -0
  53. package/template/src/components/core/nc-animation.ts +1150 -0
  54. package/template/src/components/core/nc-autocomplete.ts +271 -0
  55. package/template/src/components/core/nc-avatar-group.ts +113 -0
  56. package/template/src/components/core/nc-avatar.ts +148 -0
  57. package/template/src/components/core/nc-badge.ts +86 -0
  58. package/template/src/components/core/nc-bottom-nav.ts +214 -0
  59. package/template/src/components/core/nc-breadcrumb.ts +96 -0
  60. package/template/src/components/core/nc-button.ts +307 -0
  61. package/template/src/components/core/nc-card.ts +160 -0
  62. package/template/src/components/core/nc-checkbox.ts +282 -0
  63. package/template/src/components/core/nc-chip.ts +115 -0
  64. package/template/src/components/core/nc-code.ts +314 -0
  65. package/template/src/components/core/nc-collapsible.ts +154 -0
  66. package/template/src/components/core/nc-color-picker.ts +268 -0
  67. package/template/src/components/core/nc-copy-button.ts +119 -0
  68. package/template/src/components/core/nc-date-picker.ts +443 -0
  69. package/template/src/components/core/nc-div.ts +280 -0
  70. package/template/src/components/core/nc-divider.ts +81 -0
  71. package/template/src/components/core/nc-drawer.ts +230 -0
  72. package/template/src/components/core/nc-dropdown.ts +178 -0
  73. package/template/src/components/core/nc-empty-state.ts +134 -0
  74. package/template/src/components/core/nc-file-upload.ts +354 -0
  75. package/template/src/components/core/nc-form.ts +312 -0
  76. package/template/src/components/core/nc-image.ts +184 -0
  77. package/template/src/components/core/nc-input.ts +383 -0
  78. package/template/src/components/core/nc-kbd.ts +48 -0
  79. package/template/src/components/core/nc-menu-item.ts +193 -0
  80. package/template/src/components/core/nc-menu.ts +376 -0
  81. package/template/src/components/core/nc-modal.ts +238 -0
  82. package/template/src/components/core/nc-nav-item.ts +151 -0
  83. package/template/src/components/core/nc-number-input.ts +350 -0
  84. package/template/src/components/core/nc-otp-input.ts +235 -0
  85. package/template/src/components/core/nc-pagination.ts +178 -0
  86. package/template/src/components/core/nc-popover.ts +260 -0
  87. package/template/src/components/core/nc-progress-circular.ts +119 -0
  88. package/template/src/components/core/nc-progress.ts +134 -0
  89. package/template/src/components/core/nc-radio.ts +235 -0
  90. package/template/src/components/core/nc-rating.ts +266 -0
  91. package/template/src/components/core/nc-rich-text.ts +283 -0
  92. package/template/src/components/core/nc-scroll-top.ts +116 -0
  93. package/template/src/components/core/nc-select.ts +452 -0
  94. package/template/src/components/core/nc-skeleton.ts +107 -0
  95. package/template/src/components/core/nc-slider.ts +285 -0
  96. package/template/src/components/core/nc-snackbar.ts +230 -0
  97. package/template/src/components/core/nc-splash.ts +343 -0
  98. package/template/src/components/core/nc-stepper.ts +247 -0
  99. package/template/src/components/core/nc-switch.ts +281 -0
  100. package/template/src/components/core/nc-tab-item.ts +138 -0
  101. package/template/src/components/core/nc-table.ts +279 -0
  102. package/template/src/components/core/nc-tabs.ts +554 -0
  103. package/template/src/components/core/nc-tag-input.ts +279 -0
  104. package/template/src/components/core/nc-textarea.ts +216 -0
  105. package/template/src/components/core/nc-time-picker.ts +438 -0
  106. package/template/src/components/core/nc-timeline.ts +186 -0
  107. package/template/src/components/core/nc-tooltip.ts +143 -0
  108. package/template/src/components/frameworkRegistry.ts +68 -0
  109. package/template/src/components/preloadRegistry.ts +28 -0
  110. package/template/src/components/registry.ts +8 -0
  111. package/template/src/components/ui/dashboard-signal-lab.ts +284 -0
  112. package/template/src/constants/apiEndpoints.ts +27 -0
  113. package/template/src/constants/errorMessages.ts +23 -0
  114. package/template/src/constants/index.ts +8 -0
  115. package/template/src/constants/routePaths.ts +15 -0
  116. package/template/src/constants/storageKeys.ts +18 -0
  117. package/template/src/controllers/dashboard.controller.ts +200 -0
  118. package/template/src/controllers/home.controller.ts +21 -0
  119. package/template/src/controllers/index.ts +11 -0
  120. package/template/src/controllers/login.controller.ts +131 -0
  121. package/template/src/core/component.ts +354 -0
  122. package/template/src/core/errorHandler.ts +85 -0
  123. package/template/src/core/gpu-animation.ts +604 -0
  124. package/template/src/core/http.ts +173 -0
  125. package/template/src/core/lazyComponents.ts +90 -0
  126. package/template/src/core/router.ts +653 -0
  127. package/template/src/core/signals.ts +146 -0
  128. package/template/src/core/state.ts +248 -0
  129. package/template/src/dev/component-editor.ts +1363 -0
  130. package/template/src/dev/component-overlay.ts +278 -0
  131. package/template/src/dev/context-menu.ts +223 -0
  132. package/template/src/dev/denc-tools.ts +250 -0
  133. package/template/src/dev/hmr.ts +189 -0
  134. package/template/src/dev/nfbs.code-workspace +27 -0
  135. package/template/src/dev/outline-panel.ts +1247 -0
  136. package/template/src/middleware/auth.middleware.ts +23 -0
  137. package/template/src/routes/routes.ts +38 -0
  138. package/template/src/services/api.service.ts +394 -0
  139. package/template/src/services/auth.service.ts +176 -0
  140. package/template/src/services/index.ts +8 -0
  141. package/template/src/services/logger.service.ts +74 -0
  142. package/template/src/services/storage.service.ts +88 -0
  143. package/template/src/stores/appStore.ts +57 -0
  144. package/template/src/stores/uiStore.ts +36 -0
  145. package/template/src/styles/core-variables.css +219 -0
  146. package/template/src/styles/core.css +710 -0
  147. package/template/src/styles/main.css +3164 -0
  148. package/template/src/styles/variables.css +152 -0
  149. package/template/src/types/global.d.ts +47 -0
  150. package/template/src/utils/cacheBuster.ts +20 -0
  151. package/template/src/utils/dom.ts +149 -0
  152. package/template/src/utils/events.ts +203 -0
  153. package/template/src/utils/form.ts +176 -0
  154. package/template/src/utils/formatters.ts +169 -0
  155. package/template/src/utils/helpers.ts +195 -0
  156. package/template/src/utils/markdown.ts +307 -0
  157. package/template/src/utils/sidebar.ts +96 -0
  158. package/template/src/utils/smoothScroll.ts +85 -0
  159. package/template/src/utils/templates.ts +23 -0
  160. package/template/src/utils/validation.ts +73 -0
  161. package/template/src/views/protected/dashboard.html +293 -0
  162. package/template/src/views/public/home.html +150 -0
  163. package/template/src/views/public/login.html +102 -0
  164. package/template/tests/unit/component.test.ts +87 -0
  165. package/template/tests/unit/computed.test.ts +79 -0
  166. package/template/tests/unit/form.test.ts +68 -0
  167. package/template/tests/unit/formatters.test.ts +49 -0
  168. package/template/tests/unit/lazy-components.test.ts +59 -0
  169. package/template/tests/unit/markdown.test.ts +62 -0
  170. package/template/tests/unit/router.test.ts +112 -0
  171. package/template/tests/unit/signals.test.ts +54 -0
  172. package/template/tests/unit/validation.test.ts +50 -0
  173. package/template/tsconfig.build.json +21 -0
  174. package/template/tsconfig.json +51 -0
  175. package/template/vitest.config.ts +36 -0
@@ -0,0 +1,604 @@
1
+ /**
2
+ * GPU Animation Utilities
3
+ *
4
+ * High-performance animation primitives that leverage:
5
+ * - CSS transforms (GPU composited)
6
+ * - CSS custom properties for dynamic values
7
+ * - Web Animations API for smooth interpolation
8
+ * - RequestAnimationFrame with delta time
9
+ * - Passive event listeners
10
+ * - will-change optimization
11
+ * - contain property for layout isolation
12
+ *
13
+ * These utilities make NativeCore animations outperform other frameworks
14
+ * by maximizing GPU utilization and minimizing main thread work.
15
+ */
16
+
17
+ // Types
18
+ export interface AnimationOptions {
19
+ duration?: number;
20
+ easing?: string;
21
+ delay?: number;
22
+ fill?: FillMode;
23
+ iterations?: number;
24
+ }
25
+
26
+ export interface ParticleConfig {
27
+ count: number;
28
+ colors?: string[];
29
+ size?: { min: number; max: number };
30
+ speed?: { min: number; max: number };
31
+ type?: 'shower' | 'burst' | 'float' | 'spiral' | 'explode' | 'converge' | 'electricity' | 'fire' | 'ripple' | 'firework' | 'explosion';
32
+ }
33
+
34
+ export interface Particle {
35
+ x: number;
36
+ y: number;
37
+ vx: number;
38
+ vy: number;
39
+ size: number;
40
+ color: string;
41
+ alpha: number;
42
+ angle: number;
43
+ life: number;
44
+ maxLife: number;
45
+ }
46
+
47
+ // ============================================
48
+ // GPU Transform Utilities
49
+ // ============================================
50
+
51
+ /**
52
+ * Apply GPU-accelerated transform using translate3d
53
+ * translate3d forces GPU layer creation even for 2D transforms
54
+ */
55
+ export function setGPUTransform(element: HTMLElement, x: number, y: number, z = 0, scale = 1, rotate = 0): void {
56
+ element.style.transform = `translate3d(${x}px, ${y}px, ${z}px) scale(${scale}) rotate(${rotate}deg)`;
57
+ }
58
+
59
+ /**
60
+ * Apply transform using CSS custom properties
61
+ * This allows CSS transitions to handle the animation on GPU
62
+ */
63
+ export function setTransformVars(element: HTMLElement, vars: Record<string, string | number>): void {
64
+ for (const [key, value] of Object.entries(vars)) {
65
+ element.style.setProperty(`--${key}`, typeof value === 'number' ? `${value}px` : value);
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Prepare element for GPU animation with proper hints
71
+ */
72
+ export function prepareForAnimation(element: HTMLElement, properties: string[] = ['transform', 'opacity']): void {
73
+ element.style.willChange = properties.join(', ');
74
+ element.style.contain = 'layout style paint';
75
+ element.style.backfaceVisibility = 'hidden';
76
+ // Force GPU layer
77
+ element.style.transform = element.style.transform || 'translateZ(0)';
78
+ }
79
+
80
+ /**
81
+ * Clean up animation hints to free GPU memory
82
+ */
83
+ export function cleanupAnimation(element: HTMLElement): void {
84
+ element.style.willChange = 'auto';
85
+ element.style.contain = '';
86
+ }
87
+
88
+ // ============================================
89
+ // Web Animations API Wrappers
90
+ // ============================================
91
+
92
+ /**
93
+ * Animate using Web Animations API (GPU accelerated)
94
+ * Returns a Promise that resolves when animation completes
95
+ */
96
+ export function animate(
97
+ element: HTMLElement,
98
+ keyframes: Keyframe[],
99
+ options: AnimationOptions = {}
100
+ ): Promise<void> {
101
+ const {
102
+ duration = 300,
103
+ easing = 'cubic-bezier(0.4, 0, 0.2, 1)',
104
+ delay = 0,
105
+ fill = 'forwards',
106
+ iterations = 1
107
+ } = options;
108
+
109
+ return new Promise((resolve) => {
110
+ const animation = element.animate(keyframes, {
111
+ duration,
112
+ easing,
113
+ delay,
114
+ fill,
115
+ iterations
116
+ });
117
+
118
+ animation.onfinish = () => resolve();
119
+ animation.oncancel = () => resolve();
120
+ });
121
+ }
122
+
123
+ /**
124
+ * GPU-accelerated fade animation
125
+ */
126
+ export function fadeIn(element: HTMLElement, duration = 300): Promise<void> {
127
+ prepareForAnimation(element, ['opacity']);
128
+ return animate(element, [
129
+ { opacity: 0 },
130
+ { opacity: 1 }
131
+ ], { duration });
132
+ }
133
+
134
+ export function fadeOut(element: HTMLElement, duration = 300): Promise<void> {
135
+ prepareForAnimation(element, ['opacity']);
136
+ return animate(element, [
137
+ { opacity: 1 },
138
+ { opacity: 0 }
139
+ ], { duration });
140
+ }
141
+
142
+ /**
143
+ * GPU-accelerated slide animation
144
+ */
145
+ export function slideIn(element: HTMLElement, direction: 'up' | 'down' | 'left' | 'right' = 'up', distance = 40, duration = 400): Promise<void> {
146
+ prepareForAnimation(element);
147
+
148
+ const translations: Record<string, [string, string]> = {
149
+ up: [`translate3d(0, ${distance}px, 0)`, 'translate3d(0, 0, 0)'],
150
+ down: [`translate3d(0, -${distance}px, 0)`, 'translate3d(0, 0, 0)'],
151
+ left: [`translate3d(${distance}px, 0, 0)`, 'translate3d(0, 0, 0)'],
152
+ right: [`translate3d(-${distance}px, 0, 0)`, 'translate3d(0, 0, 0)']
153
+ };
154
+
155
+ const [from, to] = translations[direction];
156
+
157
+ return animate(element, [
158
+ { transform: from, opacity: 0 },
159
+ { transform: to, opacity: 1 }
160
+ ], { duration, easing: 'cubic-bezier(0.4, 0, 0.2, 1)' });
161
+ }
162
+
163
+ /**
164
+ * GPU-accelerated scale animation
165
+ */
166
+ export function scaleIn(element: HTMLElement, duration = 300): Promise<void> {
167
+ prepareForAnimation(element);
168
+ return animate(element, [
169
+ { transform: 'scale3d(0.8, 0.8, 1)', opacity: 0 },
170
+ { transform: 'scale3d(1, 1, 1)', opacity: 1 }
171
+ ], { duration, easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)' });
172
+ }
173
+
174
+ // ============================================
175
+ // High-Performance Animation Loop
176
+ // ============================================
177
+
178
+ export interface AnimationLoop {
179
+ start: () => void;
180
+ stop: () => void;
181
+ isRunning: () => boolean;
182
+ }
183
+
184
+ /**
185
+ * Create a high-performance animation loop with delta time
186
+ * Automatically handles frame timing and cleanup
187
+ */
188
+ export function createAnimationLoop(
189
+ callback: (deltaTime: number, elapsed: number) => boolean | void
190
+ ): AnimationLoop {
191
+ let rafId: number | null = null;
192
+ let lastTime = 0;
193
+ let startTime = 0;
194
+ let running = false;
195
+
196
+ const tick = (currentTime: number) => {
197
+ if (!running) return;
198
+
199
+ if (lastTime === 0) {
200
+ lastTime = currentTime;
201
+ startTime = currentTime;
202
+ }
203
+
204
+ // Delta time in seconds, capped to prevent huge jumps
205
+ const deltaTime = Math.min((currentTime - lastTime) / 1000, 0.1);
206
+ const elapsed = (currentTime - startTime) / 1000;
207
+ lastTime = currentTime;
208
+
209
+ // Callback returns false to stop the loop
210
+ const shouldContinue = callback(deltaTime, elapsed);
211
+
212
+ if (shouldContinue !== false && running) {
213
+ rafId = requestAnimationFrame(tick);
214
+ } else {
215
+ running = false;
216
+ }
217
+ };
218
+
219
+ return {
220
+ start() {
221
+ if (running) return;
222
+ running = true;
223
+ lastTime = 0;
224
+ rafId = requestAnimationFrame(tick);
225
+ },
226
+ stop() {
227
+ running = false;
228
+ if (rafId !== null) {
229
+ cancelAnimationFrame(rafId);
230
+ rafId = null;
231
+ }
232
+ },
233
+ isRunning() {
234
+ return running;
235
+ }
236
+ };
237
+ }
238
+
239
+ // ============================================
240
+ // WebGL Particle System for High Counts
241
+ // ============================================
242
+
243
+ /**
244
+ * Create a WebGL-based particle system for thousands of particles
245
+ * Uses vertex shaders for GPU-computed positions
246
+ */
247
+ export function createWebGLParticleSystem(
248
+ canvas: HTMLCanvasElement,
249
+ config: ParticleConfig
250
+ ): { start: () => void; stop: () => void; destroy: () => void } | null {
251
+ const gl = canvas.getContext('webgl2') || canvas.getContext('webgl');
252
+ if (!gl) {
253
+ console.warn('[GPU Animation] WebGL not available, falling back to canvas');
254
+ return null;
255
+ }
256
+
257
+ const { count, colors = ['#667eea', '#764ba2', '#f093fb'], size = { min: 2, max: 6 } } = config;
258
+
259
+ // Vertex shader - positions computed on GPU
260
+ const vertexShaderSource = `
261
+ attribute vec2 a_position;
262
+ attribute vec2 a_velocity;
263
+ attribute float a_size;
264
+ attribute vec4 a_color;
265
+ attribute float a_life;
266
+
267
+ uniform vec2 u_resolution;
268
+ uniform float u_time;
269
+ uniform float u_deltaTime;
270
+
271
+ varying vec4 v_color;
272
+ varying float v_life;
273
+
274
+ void main() {
275
+ vec2 pos = a_position + a_velocity * u_time;
276
+
277
+ // Normalize to clip space
278
+ vec2 clipSpace = (pos / u_resolution) * 2.0 - 1.0;
279
+ gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
280
+ gl_PointSize = a_size;
281
+
282
+ v_color = a_color;
283
+ v_life = a_life;
284
+ }
285
+ `;
286
+
287
+ // Fragment shader - draws circular particles with alpha
288
+ const fragmentShaderSource = `
289
+ precision mediump float;
290
+
291
+ varying vec4 v_color;
292
+ varying float v_life;
293
+
294
+ void main() {
295
+ vec2 coord = gl_PointCoord - vec2(0.5);
296
+ float dist = length(coord);
297
+
298
+ if (dist > 0.5) discard;
299
+
300
+ float alpha = smoothstep(0.5, 0.2, dist) * v_color.a * v_life;
301
+ gl_FragColor = vec4(v_color.rgb, alpha);
302
+ }
303
+ `;
304
+
305
+ // Compile shaders
306
+ function createShader(type: number, source: string): WebGLShader | null {
307
+ const shader = gl!.createShader(type);
308
+ if (!shader) return null;
309
+ gl!.shaderSource(shader, source);
310
+ gl!.compileShader(shader);
311
+ if (!gl!.getShaderParameter(shader, gl!.COMPILE_STATUS)) {
312
+ console.error(gl!.getShaderInfoLog(shader));
313
+ gl!.deleteShader(shader);
314
+ return null;
315
+ }
316
+ return shader;
317
+ }
318
+
319
+ const vertexShader = createShader(gl.VERTEX_SHADER, vertexShaderSource);
320
+ const fragmentShader = createShader(gl.FRAGMENT_SHADER, fragmentShaderSource);
321
+ if (!vertexShader || !fragmentShader) return null;
322
+
323
+ const program = gl.createProgram()!;
324
+ gl.attachShader(program, vertexShader);
325
+ gl.attachShader(program, fragmentShader);
326
+ gl.linkProgram(program);
327
+
328
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
329
+ console.error(gl.getProgramInfoLog(program));
330
+ return null;
331
+ }
332
+
333
+ // Get locations
334
+ const positionLoc = gl.getAttribLocation(program, 'a_position');
335
+ const velocityLoc = gl.getAttribLocation(program, 'a_velocity');
336
+ const sizeLoc = gl.getAttribLocation(program, 'a_size');
337
+ const colorLoc = gl.getAttribLocation(program, 'a_color');
338
+ const lifeLoc = gl.getAttribLocation(program, 'a_life');
339
+ const resolutionLoc = gl.getUniformLocation(program, 'u_resolution');
340
+ const timeLoc = gl.getUniformLocation(program, 'u_time');
341
+
342
+ // Create buffers
343
+ const positions = new Float32Array(count * 2);
344
+ const velocities = new Float32Array(count * 2);
345
+ const sizes = new Float32Array(count);
346
+ const particleColors = new Float32Array(count * 4);
347
+ const lives = new Float32Array(count);
348
+
349
+ // Initialize particles
350
+ function hexToRgb(hex: string): [number, number, number] {
351
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
352
+ return result ? [
353
+ parseInt(result[1], 16) / 255,
354
+ parseInt(result[2], 16) / 255,
355
+ parseInt(result[3], 16) / 255
356
+ ] : [1, 1, 1];
357
+ }
358
+
359
+ for (let i = 0; i < count; i++) {
360
+ positions[i * 2] = Math.random() * canvas.width;
361
+ positions[i * 2 + 1] = Math.random() * -canvas.height;
362
+ velocities[i * 2] = (Math.random() - 0.5) * 50;
363
+ velocities[i * 2 + 1] = 50 + Math.random() * 100;
364
+ sizes[i] = size.min + Math.random() * (size.max - size.min);
365
+
366
+ const color = hexToRgb(colors[Math.floor(Math.random() * colors.length)]);
367
+ particleColors[i * 4] = color[0];
368
+ particleColors[i * 4 + 1] = color[1];
369
+ particleColors[i * 4 + 2] = color[2];
370
+ particleColors[i * 4 + 3] = 0.8;
371
+
372
+ lives[i] = 1.0;
373
+ }
374
+
375
+ // Create and fill buffers
376
+ const posBuffer = gl.createBuffer();
377
+ gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer);
378
+ gl.bufferData(gl.ARRAY_BUFFER, positions, gl.DYNAMIC_DRAW);
379
+
380
+ const velBuffer = gl.createBuffer();
381
+ gl.bindBuffer(gl.ARRAY_BUFFER, velBuffer);
382
+ gl.bufferData(gl.ARRAY_BUFFER, velocities, gl.STATIC_DRAW);
383
+
384
+ const sizeBuffer = gl.createBuffer();
385
+ gl.bindBuffer(gl.ARRAY_BUFFER, sizeBuffer);
386
+ gl.bufferData(gl.ARRAY_BUFFER, sizes, gl.STATIC_DRAW);
387
+
388
+ const colorBuffer = gl.createBuffer();
389
+ gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
390
+ gl.bufferData(gl.ARRAY_BUFFER, particleColors, gl.STATIC_DRAW);
391
+
392
+ const lifeBuffer = gl.createBuffer();
393
+ gl.bindBuffer(gl.ARRAY_BUFFER, lifeBuffer);
394
+ gl.bufferData(gl.ARRAY_BUFFER, lives, gl.STATIC_DRAW);
395
+
396
+ let animationLoop: AnimationLoop | null = null;
397
+
398
+ return {
399
+ start() {
400
+ gl!.enable(gl!.BLEND);
401
+ gl!.blendFunc(gl!.SRC_ALPHA, gl!.ONE_MINUS_SRC_ALPHA);
402
+
403
+ animationLoop = createAnimationLoop((dt, elapsed) => {
404
+ gl!.viewport(0, 0, canvas.width, canvas.height);
405
+ gl!.clearColor(0, 0, 0, 0);
406
+ gl!.clear(gl!.COLOR_BUFFER_BIT);
407
+
408
+ gl!.useProgram(program);
409
+ gl!.uniform2f(resolutionLoc, canvas.width, canvas.height);
410
+ gl!.uniform1f(timeLoc, elapsed);
411
+
412
+ // Update positions (wrap around)
413
+ for (let i = 0; i < count; i++) {
414
+ positions[i * 2 + 1] += velocities[i * 2 + 1] * dt;
415
+ positions[i * 2] += velocities[i * 2] * dt;
416
+
417
+ if (positions[i * 2 + 1] > canvas.height) {
418
+ positions[i * 2 + 1] = -sizes[i];
419
+ positions[i * 2] = Math.random() * canvas.width;
420
+ }
421
+ }
422
+
423
+ gl!.bindBuffer(gl!.ARRAY_BUFFER, posBuffer);
424
+ gl!.bufferSubData(gl!.ARRAY_BUFFER, 0, positions);
425
+
426
+ // Bind attributes
427
+ gl!.bindBuffer(gl!.ARRAY_BUFFER, posBuffer);
428
+ gl!.enableVertexAttribArray(positionLoc);
429
+ gl!.vertexAttribPointer(positionLoc, 2, gl!.FLOAT, false, 0, 0);
430
+
431
+ gl!.bindBuffer(gl!.ARRAY_BUFFER, velBuffer);
432
+ gl!.enableVertexAttribArray(velocityLoc);
433
+ gl!.vertexAttribPointer(velocityLoc, 2, gl!.FLOAT, false, 0, 0);
434
+
435
+ gl!.bindBuffer(gl!.ARRAY_BUFFER, sizeBuffer);
436
+ gl!.enableVertexAttribArray(sizeLoc);
437
+ gl!.vertexAttribPointer(sizeLoc, 1, gl!.FLOAT, false, 0, 0);
438
+
439
+ gl!.bindBuffer(gl!.ARRAY_BUFFER, colorBuffer);
440
+ gl!.enableVertexAttribArray(colorLoc);
441
+ gl!.vertexAttribPointer(colorLoc, 4, gl!.FLOAT, false, 0, 0);
442
+
443
+ gl!.bindBuffer(gl!.ARRAY_BUFFER, lifeBuffer);
444
+ gl!.enableVertexAttribArray(lifeLoc);
445
+ gl!.vertexAttribPointer(lifeLoc, 1, gl!.FLOAT, false, 0, 0);
446
+
447
+ gl!.drawArrays(gl!.POINTS, 0, count);
448
+ });
449
+
450
+ animationLoop.start();
451
+ },
452
+ stop() {
453
+ animationLoop?.stop();
454
+ },
455
+ destroy() {
456
+ animationLoop?.stop();
457
+ gl!.deleteProgram(program);
458
+ gl!.deleteShader(vertexShader);
459
+ gl!.deleteShader(fragmentShader);
460
+ gl!.deleteBuffer(posBuffer);
461
+ gl!.deleteBuffer(velBuffer);
462
+ gl!.deleteBuffer(sizeBuffer);
463
+ gl!.deleteBuffer(colorBuffer);
464
+ gl!.deleteBuffer(lifeBuffer);
465
+ }
466
+ };
467
+ }
468
+
469
+ // ============================================
470
+ // CSS Animation Injection
471
+ // ============================================
472
+
473
+ const injectedAnimations = new Set<string>();
474
+
475
+ /**
476
+ * Inject keyframe animation into document (once)
477
+ * These run entirely on GPU via compositor
478
+ */
479
+ export function injectKeyframes(name: string, keyframes: string): void {
480
+ if (injectedAnimations.has(name)) return;
481
+
482
+ const style = document.createElement('style');
483
+ style.textContent = `@keyframes ${name} { ${keyframes} }`;
484
+ document.head.appendChild(style);
485
+ injectedAnimations.add(name);
486
+ }
487
+
488
+ /**
489
+ * Common GPU-accelerated keyframe animations
490
+ */
491
+ export function injectCommonAnimations(): void {
492
+ injectKeyframes('nc-fade-in', `
493
+ from { opacity: 0; }
494
+ to { opacity: 1; }
495
+ `);
496
+
497
+ injectKeyframes('nc-fade-out', `
498
+ from { opacity: 1; }
499
+ to { opacity: 0; }
500
+ `);
501
+
502
+ injectKeyframes('nc-slide-up', `
503
+ from { transform: translate3d(0, 40px, 0); opacity: 0; }
504
+ to { transform: translate3d(0, 0, 0); opacity: 1; }
505
+ `);
506
+
507
+ injectKeyframes('nc-slide-down', `
508
+ from { transform: translate3d(0, -40px, 0); opacity: 0; }
509
+ to { transform: translate3d(0, 0, 0); opacity: 1; }
510
+ `);
511
+
512
+ injectKeyframes('nc-scale-in', `
513
+ from { transform: scale3d(0.8, 0.8, 1); opacity: 0; }
514
+ to { transform: scale3d(1, 1, 1); opacity: 1; }
515
+ `);
516
+
517
+ injectKeyframes('nc-spin', `
518
+ from { transform: rotate(0deg); }
519
+ to { transform: rotate(360deg); }
520
+ `);
521
+
522
+ injectKeyframes('nc-pulse', `
523
+ 0%, 100% { transform: scale3d(1, 1, 1); }
524
+ 50% { transform: scale3d(1.05, 1.05, 1); }
525
+ `);
526
+
527
+ injectKeyframes('nc-shake', `
528
+ 0%, 100% { transform: translate3d(0, 0, 0); }
529
+ 25% { transform: translate3d(-5px, 0, 0); }
530
+ 75% { transform: translate3d(5px, 0, 0); }
531
+ `);
532
+
533
+ injectKeyframes('nc-bounce', `
534
+ 0%, 100% { transform: translate3d(0, 0, 0); animation-timing-function: cubic-bezier(0.8, 0, 1, 1); }
535
+ 50% { transform: translate3d(0, -25px, 0); animation-timing-function: cubic-bezier(0, 0, 0.2, 1); }
536
+ `);
537
+ }
538
+
539
+ // ============================================
540
+ // Passive Event Listener Utility
541
+ // ============================================
542
+
543
+ /**
544
+ * Add passive event listener for scroll/touch performance
545
+ */
546
+ export function addPassiveListener(
547
+ element: HTMLElement | Window,
548
+ event: string,
549
+ handler: EventListener
550
+ ): () => void {
551
+ element.addEventListener(event, handler, { passive: true });
552
+ return () => element.removeEventListener(event, handler);
553
+ }
554
+
555
+ /**
556
+ * Throttle function for scroll/resize handlers
557
+ */
558
+ export function throttle<T extends (...args: any[]) => void>(
559
+ fn: T,
560
+ limit: number
561
+ ): T {
562
+ let inThrottle = false;
563
+ return ((...args: any[]) => {
564
+ if (!inThrottle) {
565
+ fn(...args);
566
+ inThrottle = true;
567
+ setTimeout(() => inThrottle = false, limit);
568
+ }
569
+ }) as T;
570
+ }
571
+
572
+ /**
573
+ * RAF-based throttle for smooth animations
574
+ */
575
+ export function rafThrottle<T extends (...args: any[]) => void>(fn: T): T {
576
+ let rafId: number | null = null;
577
+ return ((...args: any[]) => {
578
+ if (rafId !== null) return;
579
+ rafId = requestAnimationFrame(() => {
580
+ fn(...args);
581
+ rafId = null;
582
+ });
583
+ }) as T;
584
+ }
585
+
586
+ // Export all utilities
587
+ export const GPUAnimation = {
588
+ setGPUTransform,
589
+ setTransformVars,
590
+ prepareForAnimation,
591
+ cleanupAnimation,
592
+ animate,
593
+ fadeIn,
594
+ fadeOut,
595
+ slideIn,
596
+ scaleIn,
597
+ createAnimationLoop,
598
+ createWebGLParticleSystem,
599
+ injectKeyframes,
600
+ injectCommonAnimations,
601
+ addPassiveListener,
602
+ throttle,
603
+ rafThrottle
604
+ };