@viji-dev/sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/.gitignore +29 -0
  2. package/LICENSE +13 -0
  3. package/README.md +103 -0
  4. package/bin/viji.js +75 -0
  5. package/eslint.config.js +37 -0
  6. package/index.html +20 -0
  7. package/package.json +82 -0
  8. package/postcss.config.js +6 -0
  9. package/public/favicon.png +0 -0
  10. package/scenes/audio-visualizer/main.js +287 -0
  11. package/scenes/core-demo/main.js +532 -0
  12. package/scenes/demo-scene/main.js +619 -0
  13. package/scenes/global.d.ts +15 -0
  14. package/scenes/particle-system/main.js +349 -0
  15. package/scenes/tsconfig.json +12 -0
  16. package/scenes/video-mirror/main.ts +436 -0
  17. package/src/App.css +42 -0
  18. package/src/App.tsx +279 -0
  19. package/src/cli/commands/build.js +147 -0
  20. package/src/cli/commands/create.js +71 -0
  21. package/src/cli/commands/dev.js +108 -0
  22. package/src/cli/commands/init.js +262 -0
  23. package/src/cli/utils/cli-utils.js +208 -0
  24. package/src/cli/utils/scene-compiler.js +432 -0
  25. package/src/components/SDKPage.tsx +337 -0
  26. package/src/components/core/CoreContainer.tsx +126 -0
  27. package/src/components/ui/DeviceSelectionList.tsx +137 -0
  28. package/src/components/ui/FPSCounter.tsx +78 -0
  29. package/src/components/ui/FileDropzonePanel.tsx +120 -0
  30. package/src/components/ui/FileListPanel.tsx +285 -0
  31. package/src/components/ui/InputExpansionPanel.tsx +31 -0
  32. package/src/components/ui/MediaPlayerControls.tsx +191 -0
  33. package/src/components/ui/MenuContainer.tsx +71 -0
  34. package/src/components/ui/ParametersMenu.tsx +797 -0
  35. package/src/components/ui/ProjectSwitcherMenu.tsx +192 -0
  36. package/src/components/ui/QuickInputControls.tsx +542 -0
  37. package/src/components/ui/SDKMenuSystem.tsx +96 -0
  38. package/src/components/ui/SettingsMenu.tsx +346 -0
  39. package/src/components/ui/SimpleInputControls.tsx +137 -0
  40. package/src/index.css +68 -0
  41. package/src/main.tsx +10 -0
  42. package/src/scenes-hmr.ts +158 -0
  43. package/src/services/project-filesystem.ts +436 -0
  44. package/src/stores/scene-player/index.ts +3 -0
  45. package/src/stores/scene-player/input-manager.store.ts +1045 -0
  46. package/src/stores/scene-player/scene-session.store.ts +659 -0
  47. package/src/styles/globals.css +111 -0
  48. package/src/templates/minimal-template.js +11 -0
  49. package/src/utils/debounce.js +34 -0
  50. package/src/vite-env.d.ts +1 -0
  51. package/tailwind.config.js +18 -0
  52. package/tsconfig.app.json +27 -0
  53. package/tsconfig.json +27 -0
  54. package/tsconfig.node.json +27 -0
  55. package/vite.config.ts +54 -0
@@ -0,0 +1,349 @@
1
+ // Interactive Particle System Scene - Parameter Object API
2
+ console.log('⚡ PARTICLE SYSTEM SCENE LOADED');
3
+
4
+ // Define parameters using helper functions (returns parameter objects)
5
+ const particleCount = viji.slider(150, {
6
+ min: 10,
7
+ max: 500,
8
+ step: 10,
9
+ label: 'Particle Count',
10
+ description: 'Number of particles in the system',
11
+ group: 'particles'
12
+ });
13
+
14
+ const particleSize = viji.slider(3, {
15
+ min: 1,
16
+ max: 10,
17
+ step: 0.5,
18
+ label: 'Particle Size',
19
+ description: 'Base size of particles',
20
+ group: 'particles'
21
+ });
22
+
23
+ const particleLifetime = viji.slider(5.0, {
24
+ min: 1.0,
25
+ max: 20.0,
26
+ step: 0.5,
27
+ label: 'Particle Lifetime',
28
+ description: 'How long particles live (seconds)',
29
+ group: 'particles'
30
+ });
31
+
32
+ const gravity = viji.slider(0.1, {
33
+ min: -1.0,
34
+ max: 2.0,
35
+ step: 0.05,
36
+ label: 'Gravity',
37
+ description: 'Downward force on particles',
38
+ group: 'physics'
39
+ });
40
+
41
+ const airResistance = viji.slider(0.98, {
42
+ min: 0.9,
43
+ max: 1.0,
44
+ step: 0.005,
45
+ label: 'Air Resistance',
46
+ description: 'Velocity dampening factor',
47
+ group: 'physics'
48
+ });
49
+
50
+ const bounce = viji.slider(0.7, {
51
+ min: 0.0,
52
+ max: 1.0,
53
+ step: 0.05,
54
+ label: 'Bounce',
55
+ description: 'Energy retained on collision',
56
+ group: 'physics'
57
+ });
58
+
59
+ const friction = viji.slider(0.9, {
60
+ min: 0.5,
61
+ max: 1.0,
62
+ step: 0.05,
63
+ label: 'Friction',
64
+ description: 'Friction when particles hit ground',
65
+ group: 'physics'
66
+ });
67
+
68
+ const mouseInteraction = viji.toggle(true, {
69
+ label: 'Mouse Interaction',
70
+ description: 'Particles react to mouse movement',
71
+ group: 'interaction',
72
+ category: 'interaction'
73
+ });
74
+
75
+ const mouseForce = viji.slider(5.0, {
76
+ min: 0.1,
77
+ max: 20.0,
78
+ step: 0.5,
79
+ label: 'Mouse Force',
80
+ description: 'Strength of mouse attraction/repulsion',
81
+ group: 'interaction',
82
+ category: 'interaction'
83
+ });
84
+
85
+ const mouseRadius = viji.slider(100, {
86
+ min: 20,
87
+ max: 300,
88
+ step: 10,
89
+ label: 'Mouse Radius',
90
+ description: 'Range of mouse influence',
91
+ group: 'interaction',
92
+ category: 'interaction'
93
+ });
94
+
95
+ const attractMode = viji.toggle(false, {
96
+ label: 'Attract Mode',
97
+ description: 'Attract particles to mouse (vs repel)',
98
+ group: 'interaction',
99
+ category: 'interaction'
100
+ });
101
+
102
+ const colorMode = viji.select('velocity', {
103
+ options: ['velocity', 'age', 'position', 'static'],
104
+ label: 'Color Mode',
105
+ description: 'How particles are colored',
106
+ group: 'colors'
107
+ });
108
+
109
+ const staticColor = viji.color('#ff6b6b', {
110
+ label: 'Static Color',
111
+ description: 'Color when using static color mode',
112
+ group: 'colors'
113
+ });
114
+
115
+ const backgroundColor = viji.color('#0a0a0a', {
116
+ label: 'Background Color',
117
+ description: 'Scene background color',
118
+ group: 'colors'
119
+ });
120
+
121
+ // Scene state
122
+ let particles = [];
123
+ let frameTime = 0;
124
+
125
+ class Particle {
126
+ constructor(x, y) {
127
+ this.x = x;
128
+ this.y = y;
129
+ this.vx = (Math.random() - 0.5) * 4;
130
+ this.vy = (Math.random() - 0.5) * 4;
131
+ this.age = 0;
132
+ this.maxAge = particleLifetime.value + (Math.random() - 0.5) * 2;
133
+ this.size = particleSize.value + (Math.random() - 0.5) * 2;
134
+ this.id = Math.random();
135
+ }
136
+
137
+ update(deltaTime, mouse) {
138
+ const dt = deltaTime / 16.67; // Normalize to ~60fps
139
+
140
+ // Age the particle
141
+ this.age += dt;
142
+
143
+ // Apply physics
144
+ this.vy += gravity.value * dt;
145
+ this.vx *= Math.pow(airResistance.value, dt);
146
+ this.vy *= Math.pow(airResistance.value, dt);
147
+
148
+ // Mouse interaction
149
+ if (mouseInteraction.value && mouse && mouse.isInCanvas) {
150
+ const dx = mouse.x - this.x;
151
+ const dy = mouse.y - this.y;
152
+ const dist = Math.sqrt(dx * dx + dy * dy);
153
+
154
+ if (dist < mouseRadius.value && dist > 0) {
155
+ const force = mouseForce.value * (1 - dist / mouseRadius.value);
156
+ const angle = Math.atan2(dy, dx);
157
+
158
+ if (attractMode.value) {
159
+ // Attract to mouse
160
+ this.vx += Math.cos(angle) * force * dt;
161
+ this.vy += Math.sin(angle) * force * dt;
162
+ } else {
163
+ // Repel from mouse
164
+ this.vx -= Math.cos(angle) * force * dt;
165
+ this.vy -= Math.sin(angle) * force * dt;
166
+ }
167
+ }
168
+ }
169
+
170
+ // Update position
171
+ this.x += this.vx * dt;
172
+ this.y += this.vy * dt;
173
+ }
174
+
175
+ checkCollisions(width, height) {
176
+ // Floor collision
177
+ if (this.y + this.size > height) {
178
+ this.y = height - this.size;
179
+ this.vy *= -bounce.value;
180
+ this.vx *= friction.value;
181
+ }
182
+
183
+ // Ceiling collision
184
+ if (this.y - this.size < 0) {
185
+ this.y = this.size;
186
+ this.vy *= -bounce.value;
187
+ }
188
+
189
+ // Wall collisions
190
+ if (this.x + this.size > width) {
191
+ this.x = width - this.size;
192
+ this.vx *= -bounce.value;
193
+ }
194
+ if (this.x - this.size < 0) {
195
+ this.x = this.size;
196
+ this.vx *= -bounce.value;
197
+ }
198
+ }
199
+
200
+ isDead() {
201
+ return this.age >= this.maxAge;
202
+ }
203
+
204
+ getColor() {
205
+ switch (colorMode.value) {
206
+ case 'velocity':
207
+ const speed = Math.sqrt(this.vx * this.vx + this.vy * this.vy);
208
+ const hue = Math.min(speed * 20, 360);
209
+ return `hsl(${hue}, 80%, 60%)`;
210
+
211
+ case 'age':
212
+ const ageRatio = this.age / this.maxAge;
213
+ const ageBrightness = Math.max(0, 1 - ageRatio);
214
+ return `hsl(30, 80%, ${ageBrightness * 60 + 20}%)`;
215
+
216
+ case 'position':
217
+ const positionHue = (this.x / 800) * 360; // Assumes ~800px width
218
+ return `hsl(${positionHue}, 70%, 50%)`;
219
+
220
+ case 'static':
221
+ default:
222
+ return staticColor.value;
223
+ }
224
+ }
225
+ }
226
+
227
+ function spawnParticle(x, y) {
228
+ if (particles.length < particleCount.value) {
229
+ particles.push(new Particle(x, y));
230
+ }
231
+ }
232
+
233
+ function spawnParticles(count, width, height) {
234
+ for (let i = 0; i < count; i++) {
235
+ if (particles.length < particleCount.value) {
236
+ const x = Math.random() * width;
237
+ const y = Math.random() * height * 0.5; // Spawn in upper half
238
+ spawnParticle(x, y);
239
+ }
240
+ }
241
+ }
242
+
243
+ // Render function using parameter object API
244
+ function render(viji) {
245
+ const ctx = viji.useContext('2d');
246
+ const mouse = viji.mouse;
247
+
248
+ frameTime += viji.deltaTime;
249
+
250
+ // Clear background
251
+ ctx.fillStyle = backgroundColor.value;
252
+ ctx.fillRect(0, 0, viji.width, viji.height);
253
+
254
+ // Maintain particle count
255
+ const targetCount = particleCount.value;
256
+ if (particles.length < targetCount) {
257
+ const needed = Math.min(5, targetCount - particles.length); // Spawn gradually
258
+ spawnParticles(needed, viji.width, viji.height);
259
+ }
260
+
261
+ // Update particles
262
+ for (let i = particles.length - 1; i >= 0; i--) {
263
+ const particle = particles[i];
264
+
265
+ particle.update(viji.deltaTime, mouse);
266
+ particle.checkCollisions(viji.width, viji.height);
267
+
268
+ // Remove dead particles
269
+ if (particle.isDead() || particles.length > targetCount) {
270
+ particles.splice(i, 1);
271
+ continue;
272
+ }
273
+ }
274
+
275
+ // Draw particles
276
+ particles.forEach(particle => {
277
+ ctx.fillStyle = particle.getColor();
278
+ ctx.beginPath();
279
+ ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2);
280
+ ctx.fill();
281
+
282
+ // Add slight glow effect
283
+ const glowSize = particle.size * 2;
284
+ const gradient = ctx.createRadialGradient(
285
+ particle.x, particle.y, particle.size,
286
+ particle.x, particle.y, glowSize
287
+ );
288
+ // Convert color to rgba format with alpha transparency
289
+ const baseColor = particle.getColor();
290
+ let colorWithAlpha;
291
+ if (baseColor.startsWith('hsl(')) {
292
+ // Convert hsl(h, s, l) to hsla(h, s, l, a)
293
+ colorWithAlpha = baseColor.replace('hsl(', 'hsla(').replace(')', ', 0.25)');
294
+ } else if (baseColor.startsWith('#')) {
295
+ // Convert hex to rgba
296
+ const hex = baseColor.slice(1);
297
+ const r = parseInt(hex.slice(0, 2), 16);
298
+ const g = parseInt(hex.slice(2, 4), 16);
299
+ const b = parseInt(hex.slice(4, 6), 16);
300
+ colorWithAlpha = `rgba(${r}, ${g}, ${b}, 0.25)`;
301
+ } else {
302
+ // Fallback for other color formats
303
+ colorWithAlpha = baseColor;
304
+ }
305
+ gradient.addColorStop(0, colorWithAlpha);
306
+ gradient.addColorStop(1, 'transparent');
307
+ ctx.fillStyle = gradient;
308
+ ctx.beginPath();
309
+ ctx.arc(particle.x, particle.y, glowSize, 0, Math.PI * 2);
310
+ ctx.fill();
311
+ });
312
+
313
+ // Draw mouse interaction indicator
314
+ if (mouseInteraction.value && mouse && mouse.isInCanvas) {
315
+ ctx.strokeStyle = attractMode.value ? '#00ff00' : '#ff0000';
316
+ ctx.lineWidth = 2;
317
+ ctx.setLineDash([5, 5]);
318
+ ctx.beginPath();
319
+ ctx.arc(mouse.x, mouse.y, mouseRadius.value, 0, Math.PI * 2);
320
+ ctx.stroke();
321
+ ctx.setLineDash([]);
322
+
323
+ // Draw force indicator
324
+ ctx.fillStyle = attractMode.value ? 'rgba(0, 255, 0, 0.1)' : 'rgba(255, 0, 0, 0.1)';
325
+ ctx.beginPath();
326
+ ctx.arc(mouse.x, mouse.y, mouseRadius.value, 0, Math.PI * 2);
327
+ ctx.fill();
328
+ }
329
+
330
+ // Draw info
331
+ ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
332
+ ctx.font = '16px monospace';
333
+ ctx.textAlign = 'left';
334
+ ctx.fillText(`🎯 PARTICLE SYSTEM`, 20, 30);
335
+
336
+ ctx.font = '12px monospace';
337
+ ctx.fillText(`Particles: ${particles.length} / ${particleCount.value}`, 20, 55);
338
+ ctx.fillText(`Physics: G=${gravity.value} AR=${airResistance.value}`, 20, 75);
339
+ ctx.fillText(`Color Mode: ${colorMode.value}`, 20, 95);
340
+
341
+ if (mouseInteraction.value && mouse && mouse.isInCanvas) {
342
+ ctx.fillText(`Mouse: ${attractMode.value ? 'ATTRACT' : 'REPEL'} (Force: ${mouseForce.value})`, 20, 115);
343
+ }
344
+
345
+ // Draw frame info
346
+ ctx.textAlign = 'right';
347
+ ctx.fillText(`Frame: ${viji.frameCount}`, viji.width - 20, viji.height - 25);
348
+ ctx.fillText(`Time: ${viji.time.toFixed(1)}s`, viji.width - 20, viji.height - 10);
349
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "extends": "../tsconfig.json",
3
+ "compilerOptions": {
4
+ "noUnusedLocals": false,
5
+ "noUnusedParameters": false,
6
+ "moduleDetection": "force"
7
+ },
8
+ "include": [
9
+ "./**/*"
10
+ ]
11
+ }
12
+