thechsenone commited on
Commit
e1ce0b3
·
verified ·
1 Parent(s): f68f346

Upload index.js with huggingface_hub

Browse files
Files changed (1) hide show
  1. index.js +493 -0
index.js ADDED
@@ -0,0 +1,493 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class DeviceControlApp {
2
+ constructor() {
3
+ this.model = null;
4
+ this.isModelLoading = false;
5
+ this.currentPattern = null;
6
+ this.animationFrame = null;
7
+
8
+ this.initializeElements();
9
+ this.attachEventListeners();
10
+ this.initializeModel();
11
+ }
12
+
13
+ initializeElements() {
14
+ // Command processing
15
+ this.commandInput = document.getElementById('commandInput');
16
+ this.processCommandBtn = document.getElementById('processCommand');
17
+ this.commandResult = document.getElementById('commandResult');
18
+ this.patternOutput = document.getElementById('patternOutput');
19
+
20
+ // Pattern generator
21
+ this.patternType = document.getElementById('patternType');
22
+ this.duration = document.getElementById('duration');
23
+ this.durationValue = document.getElementById('durationValue');
24
+ this.intensity = document.getElementById('intensity');
25
+ this.intensityValue = document.getElementById('intensityValue');
26
+ this.generatePatternBtn = document.getElementById('generatePattern');
27
+ this.generatedPattern = document.getElementById('generatedPattern');
28
+ this.patternCanvas = document.getElementById('patternCanvas');
29
+ this.patternStats = document.getElementById('patternStats');
30
+
31
+ // Status
32
+ this.statusIndicator = document.getElementById('statusIndicator');
33
+ this.statusText = document.getElementById('statusText');
34
+ this.modelStatus = document.getElementById('modelStatus');
35
+ this.loadingBar = document.getElementById('loadingBar');
36
+ this.loadingProgress = document.getElementById('loadingProgress');
37
+ this.loadingText = document.getElementById('loadingText');
38
+
39
+ // Safety settings
40
+ this.maxIntensity = document.getElementById('maxIntensity');
41
+ this.autoStop = document.getElementById('autoStop');
42
+ this.gradualChanges = document.getElementById('gradualChanges');
43
+ }
44
+
45
+ attachEventListeners() {
46
+ this.processCommandBtn.addEventListener('click', () => this.processCommand());
47
+ this.generatePatternBtn.addEventListener('click', () => this.generatePattern());
48
+
49
+ this.duration.addEventListener('input', (e) => {
50
+ this.durationValue.textContent = e.target.value;
51
+ });
52
+
53
+ this.intensity.addEventListener('input', (e) => {
54
+ this.intensityValue.textContent = e.target.value;
55
+ });
56
+
57
+ // Safety settings
58
+ this.maxIntensity.addEventListener('change', () => this.updateSafetyLimits());
59
+ this.autoStop.addEventListener('change', () => this.updateSafetyLimits());
60
+ this.gradualChanges.addEventListener('change', () => this.updateSafetyLimits());
61
+
62
+ // Enter key for command input
63
+ this.commandInput.addEventListener('keydown', (e) => {
64
+ if (e.key === 'Enter' && e.ctrlKey) {
65
+ this.processCommand();
66
+ }
67
+ });
68
+ }
69
+
70
+ async initializeModel() {
71
+ if (this.isModelLoading) return;
72
+
73
+ this.isModelLoading = true;
74
+ this.updateStatus('loading', 'Loading AI model...');
75
+ this.showLoadingBar(true);
76
+
77
+ try {
78
+ // Use a text generation model for natural language processing
79
+ const modelName = 'Xenova/distilgpt2'; // Lightweight model for fast loading
80
+
81
+ this.model = await pipeline('text-generation', modelName, {
82
+ progress_callback: (progress) => {
83
+ const percentage = Math.round(progress.progress * 100);
84
+ this.updateLoadingProgress(percentage, `Loading model: ${percentage}%`);
85
+ }
86
+ });
87
+
88
+ this.modelStatus.textContent = `Model: ${modelName}`;
89
+ this.updateStatus('ready', 'AI model loaded successfully');
90
+ this.enableButtons(true);
91
+ } catch (error) {
92
+ console.error('Error loading model:', error);
93
+ this.updateStatus('error', 'Failed to load AI model');
94
+ this.modelStatus.textContent = 'Model: Offline';
95
+
96
+ // Enable basic functionality without AI
97
+ this.enableButtons(true);
98
+ } finally {
99
+ this.isModelLoading = false;
100
+ this.showLoadingBar(false);
101
+ }
102
+ }
103
+
104
+ async processCommand() {
105
+ const command = this.commandInput.value.trim();
106
+ if (!command) {
107
+ this.showNotification('Please enter a command', 'warning');
108
+ return;
109
+ }
110
+
111
+ this.updateStatus('processing', 'Processing command...');
112
+ this.processCommandBtn.disabled = true;
113
+
114
+ try {
115
+ let pattern;
116
+
117
+ if (this.model) {
118
+ // Use AI to interpret natural language
119
+ const prompt = `Convert this command to device pattern parameters: "${command}". Format as JSON with intensity, pattern, duration fields.`;
120
+ const result = await this.model(prompt, {
121
+ max_new_tokens: 100,
122
+ temperature: 0.7,
123
+ do_sample: true,
124
+ });
125
+
126
+ pattern = this.parseAIResponse(result[0].generated_text);
127
+ } else {
128
+ // Fallback to rule-based parsing
129
+ pattern = this.parseCommandRuleBased(command);
130
+ }
131
+
132
+ // Apply safety limits
133
+ pattern = this.applySafetyLimits(pattern);
134
+
135
+ // Display result
136
+ this.displayPattern(pattern);
137
+ this.commandResult.classList.remove('hidden');
138
+
139
+ // Visualize pattern
140
+ this.visualizePattern(pattern);
141
+
142
+ this.updateStatus('success', 'Command processed successfully');
143
+
144
+ } catch (error) {
145
+ console.error('Error processing command:', error);
146
+ this.updateStatus('error', 'Failed to process command');
147
+ this.showNotification('Error processing command', 'error');
148
+ } finally {
149
+ this.processCommandBtn.disabled = false;
150
+ }
151
+ }
152
+
153
+ parseAIResponse(response) {
154
+ try {
155
+ // Extract JSON from the response
156
+ const jsonMatch = response.match(/\{[^}]+\}/);
157
+ if (jsonMatch) {
158
+ return JSON.parse(jsonMatch[0]);
159
+ }
160
+ } catch (e) {
161
+ console.error('Failed to parse AI response:', e);
162
+ }
163
+
164
+ // Fallback to default pattern
165
+ return this.getDefaultPattern();
166
+ }
167
+
168
+ parseCommandRuleBased(command) {
169
+ const lowerCommand = command.toLowerCase();
170
+ const pattern = this.getDefaultPattern();
171
+
172
+ // Parse intensity
173
+ if (lowerCommand.includes('high') || lowerCommand.includes('strong')) {
174
+ pattern.intensity = 80;
175
+ } else if (lowerCommand.includes('low') || lowerCommand.includes('gentle') || lowerCommand.includes('soft')) {
176
+ pattern.intensity = 30;
177
+ } else if (lowerCommand.includes('medium')) {
178
+ pattern.intensity = 50;
179
+ }
180
+
181
+ // Parse pattern type
182
+ if (lowerCommand.includes('wave')) {
183
+ pattern.pattern = 'wave';
184
+ } else if (lowerCommand.includes('pulse')) {
185
+ pattern.pattern = 'pulse';
186
+ } else if (lowerCommand.includes('ramp')) {
187
+ pattern.pattern = 'ramp';
188
+ } else if (lowerCommand.includes('random')) {
189
+ pattern.pattern = 'random';
190
+ }
191
+
192
+ // Parse duration
193
+ const durationMatch = command.match(/(\d+)\s*(second|minute|min|s|m)/i);
194
+ if (durationMatch) {
195
+ let value = parseInt(durationMatch[1]);
196
+ if (durationMatch[2].toLowerCase().startsWith('m')) {
197
+ value *= 60;
198
+ }
199
+ pattern.duration = Math.min(Math.max(value, 5), 60);
200
+ }
201
+
202
+ return pattern;
203
+ }
204
+
205
+ getDefaultPattern() {
206
+ return {
207
+ intensity: parseInt(this.intensity.value),
208
+ pattern: this.patternType.value,
209
+ duration: parseInt(this.duration.value)
210
+ };
211
+ }
212
+
213
+ generatePattern() {
214
+ this.updateStatus('generating', 'Generating pattern...');
215
+
216
+ const pattern = {
217
+ intensity: parseInt(this.intensity.value),
218
+ pattern: this.patternType.value,
219
+ duration: parseInt(this.duration.value)
220
+ };
221
+
222
+ // Apply safety limits
223
+ const safePattern = this.applySafetyLimits(pattern);
224
+
225
+ // Generate pattern data
226
+ const patternData = this.generatePatternData(safePattern);
227
+
228
+ // Visualize
229
+ this.visualizePatternData(patternData, safePattern);
230
+ this.generatedPattern.classList.remove('hidden');
231
+
232
+ this.updateStatus('success', 'Pattern generated successfully');
233
+ }
234
+
235
+ generatePatternData(pattern) {
236
+ const steps = pattern.duration * 10; // 10 steps per second
237
+ const data = [];
238
+
239
+ for (let i = 0; i < steps; i++) {
240
+ let value = 0;
241
+ const t = i / steps;
242
+
243
+ switch (pattern.pattern) {
244
+ case 'wave':
245
+ value = Math.sin(t * Math.PI * 4) * pattern.intensity;
246
+ break;
247
+ case 'pulse':
248
+ value = (Math.sin(t * Math.PI * 8) > 0) ? pattern.intensity : 0;
249
+ break;
250
+ case 'ramp':
251
+ value = t * pattern.intensity;
252
+ break;
253
+ case 'random':
254
+ value = Math.random() * pattern.intensity;
255
+ break;
256
+ case 'custom':
257
+ // Generate interesting custom pattern
258
+ value = (Math.sin(t * Math.PI * 6) * 0.5 + Math.sin(t * Math.PI * 13) * 0.3) * pattern.intensity;
259
+ break;
260
+ }
261
+
262
+ data.push(Math.max(0, Math.min(100, value + pattern.intensity / 2)));
263
+ }
264
+
265
+ return data;
266
+ }
267
+
268
+ visualizePattern(pattern) {
269
+ const patternData = this.generatePatternData(pattern);
270
+ this.visualizePatternData(patternData, pattern);
271
+ this.generatedPattern.classList.remove('hidden');
272
+ }
273
+
274
+ visualizePatternData(data, pattern) {
275
+ const ctx = this.patternCanvas.getContext('2d');
276
+ const width = this.patternCanvas.width;
277
+ const height = this.patternCanvas.height;
278
+
279
+ // Clear canvas
280
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.3)';
281
+ ctx.fillRect(0, 0, width, height);
282
+
283
+ // Draw grid
284
+ ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)';
285
+ ctx.lineWidth = 1;
286
+ for (let i = 0; i <= 4; i++) {
287
+ const y = (height / 4) * i;
288
+ ctx.beginPath();
289
+ ctx.moveTo(0, y);
290
+ ctx.lineTo(width, y);
291
+ ctx.stroke();
292
+ }
293
+
294
+ // Draw pattern
295
+ ctx.strokeStyle = 'rgba(168, 85, 247, 0.8)';
296
+ ctx.lineWidth = 2;
297
+ ctx.beginPath();
298
+
299
+ data.forEach((value, index) => {
300
+ const x = (index / data.length) * width;
301
+ const y = height - (value / 100) * height;
302
+
303
+ if (index === 0) {
304
+ ctx.moveTo(x, y);
305
+ } else {
306
+ ctx.lineTo(x, y);
307
+ }
308
+ });
309
+
310
+ ctx.stroke();
311
+
312
+ // Add glow effect
313
+ ctx.shadowColor = 'rgba(168, 85, 247, 0.5)';
314
+ ctx.shadowBlur = 10;
315
+ ctx.stroke();
316
+
317
+ // Update stats
318
+ const avgIntensity = data.reduce((a, b) => a + b, 0) / data.length;
319
+ const maxIntensity = Math.max(...data);
320
+ const minIntensity = Math.min(...data);
321
+
322
+ this.patternStats.textContent = `Avg: ${avgIntensity.toFixed(1)}% | Max: ${maxIntensity.toFixed(1)}% | Min: ${minIntensity.toFixed(1)}%`;
323
+ }
324
+
325
+ applySafetyLimits(pattern) {
326
+ const safePattern = { ...pattern };
327
+
328
+ // Apply max intensity limit
329
+ if (this.maxIntensity.checked) {
330
+ safePattern.intensity = Math.min(safePattern.intensity, 80);
331
+ }
332
+
333
+ // Apply duration limit
334
+ if (this.autoStop.checked) {
335
+ safePattern.duration = Math.min(safePattern.duration, 30);
336
+ }
337
+
338
+ // Ensure minimum values
339
+ safePattern.intensity = Math.max(safePattern.intensity, 0);
340
+ safePattern.duration = Math.max(safePattern.duration, 5);
341
+
342
+ return safePattern;
343
+ }
344
+
345
+ updateSafetyLimits() {
346
+ // Update intensity display if max limit is enabled
347
+ if (this.maxIntensity.checked) {
348
+ const currentIntensity = parseInt(this.intensity.value);
349
+ if (currentIntensity > 80) {
350
+ this.intensity.value = 80;
351
+ this.intensityValue.textContent = 80;
352
+ }
353
+ this.intensity.max = 80;
354
+ } else {
355
+ this.intensity.max = 100;
356
+ }
357
+
358
+ // Update duration display if auto-stop is enabled
359
+ if (this.autoStop.checked) {
360
+ const currentDuration = parseInt(this.duration.value);
361
+ if (currentDuration > 30) {
362
+ this.duration.value = 30;
363
+ this.durationValue.textContent = 30;
364
+ }
365
+ this.duration.max = 30;
366
+ } else {
367
+ this.duration.max = 60;
368
+ }
369
+ }
370
+
371
+ displayPattern(pattern) {
372
+ this.patternOutput.textContent = JSON.stringify(pattern, null, 2);
373
+ }
374
+
375
+ updateStatus(type, message) {
376
+ this.statusText.textContent = message;
377
+
378
+ // Update indicator color
379
+ this.statusIndicator.className = 'w-3 h-3 rounded-full ';
380
+ switch (type) {
381
+ case 'ready':
382
+ this.statusIndicator.className += 'bg-green-400 animate-pulse';
383
+ break;
384
+ case 'loading':
385
+ case 'processing':
386
+ case 'generating':
387
+ this.statusIndicator.className += 'bg-yellow-400 animate-pulse';
388
+ break;
389
+ case 'error':
390
+ this.statusIndicator.className += 'bg-red-400';
391
+ break;
392
+ case 'success':
393
+ this.statusIndicator.className += 'bg-blue-400 animate-pulse';
394
+ break;
395
+ }
396
+ }
397
+
398
+ showLoadingBar(show) {
399
+ if (show) {
400
+ this.loadingBar.classList.remove('hidden');
401
+ } else {
402
+ this.loadingBar.classList.add('hidden');
403
+ }
404
+ }
405
+
406
+ updateLoadingProgress(percentage, text) {
407
+ this.loadingProgress.style.width = `${percentage}%`;
408
+ this.loadingText.textContent = text;
409
+ }
410
+
411
+ enableButtons(enabled) {
412
+ this.processCommandBtn.disabled = !enabled;
413
+ this.generatePatternBtn.disabled = !enabled;
414
+ }
415
+
416
+ showNotification(message, type = 'info') {
417
+ // Create notification element
418
+ const notification = document.createElement('div');
419
+ notification.className = `fixed top-4 right-4 p-4 rounded-lg shadow-lg transform transition-all duration-300 z-50`;
420
+
421
+ // Style based on type
422
+ switch (type) {
423
+ case 'error':
424
+ notification.className += ' bg-red-500 text-white';
425
+ break;
426
+ case 'warning':
427
+ notification.className += ' bg-yellow-500 text-white';
428
+ break;
429
+ case 'success':
430
+ notification.className += ' bg-green-500 text-white';
431
+ break;
432
+ default:
433
+ notification.className += ' bg-blue-500 text-white';
434
+ }
435
+
436
+ notification.textContent = message;
437
+ document.body.appendChild(notification);
438
+
439
+ // Animate in
440
+ setTimeout(() => {
441
+ notification.style.transform = 'translateX(0)';
442
+ }, 10);
443
+
444
+ // Remove after 3 seconds
445
+ setTimeout(() => {
446
+ notification.style.transform = 'translateX(400px)';
447
+ setTimeout(() => {
448
+ document.body.removeChild(notification);
449
+ }, 300);
450
+ }, 3000);
451
+ }
452
+ }
453
+
454
+ // Toggle checkbox styles
455
+ const style = document.createElement('style');
456
+ style.textContent = `
457
+ .toggle-checkbox {
458
+ appearance: none;
459
+ width: 44px;
460
+ height: 24px;
461
+ background: rgba(255, 255, 255, 0.2);
462
+ border-radius: 12px;
463
+ position: relative;
464
+ cursor: pointer;
465
+ transition: background 0.3s;
466
+ }
467
+
468
+ .toggle-checkbox:checked {
469
+ background: linear-gradient(to right, #9333ea, #ec4899);
470
+ }
471
+
472
+ .toggle-checkbox::after {
473
+ content: '';
474
+ position: absolute;
475
+ width: 20px;
476
+ height: 20px;
477
+ background: white;
478
+ border-radius: 50%;
479
+ top: 2px;
480
+ left: 2px;
481
+ transition: transform 0.3s;
482
+ }
483
+
484
+ .toggle-checkbox:checked::after {
485
+ transform: translateX(20px);
486
+ }
487
+ `;
488
+ document.head.appendChild(style);
489
+
490
+ // Initialize app when DOM is ready
491
+ document.addEventListener('DOMContentLoaded', () => {
492
+ new DeviceControlApp();
493
+ });