ai-progress-controls 0.1.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 (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +823 -0
  3. package/dist/ai-progress-controls.es.js +7191 -0
  4. package/dist/ai-progress-controls.es.js.map +1 -0
  5. package/dist/ai-progress-controls.umd.js +2 -0
  6. package/dist/ai-progress-controls.umd.js.map +1 -0
  7. package/dist/index.d.ts +2212 -0
  8. package/package.json +105 -0
  9. package/src/__tests__/setup.ts +93 -0
  10. package/src/core/base/AIControl.ts +230 -0
  11. package/src/core/base/index.ts +3 -0
  12. package/src/core/base/types.ts +77 -0
  13. package/src/core/base/utils.ts +168 -0
  14. package/src/core/batch-progress/BatchProgress.test.ts +458 -0
  15. package/src/core/batch-progress/BatchProgress.ts +760 -0
  16. package/src/core/batch-progress/index.ts +14 -0
  17. package/src/core/batch-progress/styles.ts +480 -0
  18. package/src/core/batch-progress/types.ts +169 -0
  19. package/src/core/model-loader/ModelLoader.test.ts +311 -0
  20. package/src/core/model-loader/ModelLoader.ts +673 -0
  21. package/src/core/model-loader/index.ts +2 -0
  22. package/src/core/model-loader/styles.ts +496 -0
  23. package/src/core/model-loader/types.ts +127 -0
  24. package/src/core/parameter-panel/ParameterPanel.test.ts +856 -0
  25. package/src/core/parameter-panel/ParameterPanel.ts +877 -0
  26. package/src/core/parameter-panel/index.ts +14 -0
  27. package/src/core/parameter-panel/styles.ts +323 -0
  28. package/src/core/parameter-panel/types.ts +278 -0
  29. package/src/core/parameter-slider/ParameterSlider.test.ts +299 -0
  30. package/src/core/parameter-slider/ParameterSlider.ts +653 -0
  31. package/src/core/parameter-slider/index.ts +8 -0
  32. package/src/core/parameter-slider/styles.ts +493 -0
  33. package/src/core/parameter-slider/types.ts +107 -0
  34. package/src/core/queue-progress/QueueProgress.test.ts +344 -0
  35. package/src/core/queue-progress/QueueProgress.ts +563 -0
  36. package/src/core/queue-progress/index.ts +5 -0
  37. package/src/core/queue-progress/styles.ts +469 -0
  38. package/src/core/queue-progress/types.ts +130 -0
  39. package/src/core/retry-progress/RetryProgress.test.ts +397 -0
  40. package/src/core/retry-progress/RetryProgress.ts +957 -0
  41. package/src/core/retry-progress/index.ts +6 -0
  42. package/src/core/retry-progress/styles.ts +530 -0
  43. package/src/core/retry-progress/types.ts +176 -0
  44. package/src/core/stream-progress/StreamProgress.test.ts +531 -0
  45. package/src/core/stream-progress/StreamProgress.ts +517 -0
  46. package/src/core/stream-progress/index.ts +2 -0
  47. package/src/core/stream-progress/styles.ts +349 -0
  48. package/src/core/stream-progress/types.ts +82 -0
  49. package/src/index.ts +19 -0
@@ -0,0 +1,653 @@
1
+ import { AIControl } from '../base/AIControl';
2
+ import { clamp } from '../base/utils';
3
+ import type {
4
+ ParameterSliderConfig,
5
+ ParameterSliderState,
6
+ PresetValue,
7
+ ValueChangeEvent,
8
+ PresetSelectEvent,
9
+ } from './types';
10
+ import { styles } from './styles';
11
+
12
+ /**
13
+ * ParameterSlider Component
14
+ *
15
+ * Interactive slider for AI parameter configuration (temperature, top-p, penalties, etc.).
16
+ * Supports presets, manual input, keyboard navigation, and accessibility.
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * // Create temperature slider
21
+ * const slider = new ParameterSlider({
22
+ * label: 'Temperature',
23
+ * min: 0,
24
+ * max: 2,
25
+ * value: 0.7,
26
+ * step: 0.1,
27
+ * description: 'Controls randomness in responses',
28
+ * presets: [
29
+ * { value: 0, label: 'Focused' },
30
+ * { value: 0.7, label: 'Balanced' },
31
+ * { value: 1.5, label: 'Creative' }
32
+ * ]
33
+ * });
34
+ *
35
+ * document.body.appendChild(slider);
36
+ *
37
+ * // Listen to value changes
38
+ * slider.addEventListener('valuechange', (e) => {
39
+ * console.log('New value:', e.detail.value);
40
+ * });
41
+ *
42
+ * // Get current value
43
+ * const value = slider.getValue();
44
+ *
45
+ * // Set value programmatically
46
+ * slider.setValue(1.2);
47
+ * ```
48
+ *
49
+ * @fires valuechange - Fired when value changes
50
+ * @fires presetselect - Fired when preset is selected
51
+ */
52
+ export class ParameterSlider extends AIControl {
53
+ protected override config: Required<ParameterSliderConfig>;
54
+ private readonly state: ParameterSliderState;
55
+ private sliderTrack: HTMLElement | null = null;
56
+ private sliderThumb: HTMLElement | null = null;
57
+
58
+ static get observedAttributes() {
59
+ return ['label', 'min', 'max', 'value', 'disabled', 'size', 'variant', 'animation'];
60
+ }
61
+
62
+ constructor(config: ParameterSliderConfig = {}) {
63
+ super({
64
+ debug: config.debug ?? false,
65
+ className: config.className,
66
+ ariaLabel: config.ariaLabel ?? `${config.label ?? 'Parameter'} Slider`,
67
+ });
68
+
69
+ // Set default configuration
70
+ this.config = {
71
+ label: config.label ?? 'Parameter',
72
+ min: config.min ?? 0,
73
+ max: config.max ?? 1,
74
+ value: config.value ?? 0.5,
75
+ defaultValue: config.defaultValue ?? config.value ?? 0.5,
76
+ step: config.step ?? 0.01,
77
+ decimals: config.decimals ?? 2,
78
+ description: config.description ?? '',
79
+ showPresets: config.showPresets ?? true,
80
+ presets: config.presets ?? [],
81
+ showInput: config.showInput ?? true,
82
+ showReset: config.showReset ?? true,
83
+ showRangeLabels: config.showRangeLabels ?? true,
84
+ unit: config.unit ?? '',
85
+ cursorFeedback: config.cursorFeedback ?? true,
86
+ disabled: config.disabled ?? false,
87
+ debug: config.debug ?? false,
88
+ className: config.className ?? '',
89
+ ariaLabel: config.ariaLabel ?? `${config.label ?? 'Parameter'} Slider`,
90
+ size: config.size ?? 'default',
91
+ variant: config.variant ?? 'default',
92
+ animation: config.animation ?? 'none',
93
+ };
94
+
95
+ // Initialize state
96
+ this.state = {
97
+ currentValue: clamp(this.config.value, this.config.min, this.config.max),
98
+ isDragging: false,
99
+ isFocused: false,
100
+ };
101
+
102
+ // Attach shadow DOM
103
+ this.attachShadow({ mode: 'open' });
104
+ }
105
+
106
+ override connectedCallback(): void {
107
+ super.connectedCallback();
108
+ this.render();
109
+ this.attachEventListeners();
110
+ this.log('ParameterSlider mounted');
111
+ }
112
+
113
+ override disconnectedCallback(): void {
114
+ this.removeEventListeners();
115
+ super.disconnectedCallback();
116
+ }
117
+
118
+ protected override getDefaultRole(): string {
119
+ return 'slider';
120
+ }
121
+
122
+ protected override handleAttributeChange(
123
+ name: string,
124
+ _oldValue: string,
125
+ newValue: string
126
+ ): void {
127
+ switch (name) {
128
+ case 'label':
129
+ this.config.label = newValue || 'Parameter';
130
+ this.render();
131
+ break;
132
+ case 'min':
133
+ this.config.min = Number.parseFloat(newValue) || 0;
134
+ this.render();
135
+ break;
136
+ case 'max':
137
+ this.config.max = Number.parseFloat(newValue) || 1;
138
+ this.render();
139
+ break;
140
+ case 'value':
141
+ this.setValue(Number.parseFloat(newValue) || 0.5);
142
+ break;
143
+ case 'disabled':
144
+ this._disabled = newValue !== null;
145
+ this.render();
146
+ break;
147
+ case 'size':
148
+ this.config.size = newValue as any;
149
+ this.render();
150
+ break;
151
+ case 'variant':
152
+ this.config.variant = newValue as any;
153
+ this.render();
154
+ break;
155
+ case 'animation':
156
+ this.config.animation = newValue as any;
157
+ this.render();
158
+ break;
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Get current value
164
+ */
165
+ public getValue(): number {
166
+ return this.state.currentValue;
167
+ }
168
+
169
+ /**
170
+ * Set value programmatically
171
+ */
172
+ public setValue(value: number, source: 'slider' | 'input' | 'preset' | 'reset' = 'slider'): void {
173
+ const previousValue = this.state.currentValue;
174
+ const clampedValue = clamp(value, this.config.min, this.config.max);
175
+
176
+ // Round to step
177
+ const steppedValue = Math.round(clampedValue / this.config.step) * this.config.step;
178
+ const finalValue = Number.parseFloat(steppedValue.toFixed(this.config.decimals));
179
+
180
+ if (finalValue === this.state.currentValue) return;
181
+
182
+ this.state.currentValue = finalValue;
183
+ this.updateSliderPosition();
184
+
185
+ const event: ValueChangeEvent = {
186
+ value: finalValue,
187
+ previousValue,
188
+ source,
189
+ timestamp: Date.now(),
190
+ };
191
+
192
+ this.emit('valuechange', event);
193
+ this.log('Value changed', event);
194
+ }
195
+
196
+ /**
197
+ * Reset to default value
198
+ */
199
+ public reset(): void {
200
+ this.setValue(this.config.defaultValue, 'reset');
201
+ }
202
+
203
+ /**
204
+ * Select a preset value
205
+ */
206
+ public selectPreset(preset: PresetValue): void {
207
+ const previousValue = this.state.currentValue;
208
+ this.setValue(preset.value, 'preset');
209
+
210
+ const event: PresetSelectEvent = {
211
+ preset,
212
+ previousValue,
213
+ timestamp: Date.now(),
214
+ };
215
+
216
+ this.emit('presetselect', event);
217
+ this.log('Preset selected', event);
218
+ }
219
+
220
+ /**
221
+ * Attach event listeners to slider elements
222
+ */
223
+ private attachEventListeners(): void {
224
+ if (!this.shadowRoot) return;
225
+
226
+ // Slider track and thumb
227
+ this.sliderTrack = this.shadowRoot.querySelector('.slider-track');
228
+ this.sliderThumb = this.shadowRoot.querySelector('.slider-thumb');
229
+
230
+ if (this.sliderTrack) {
231
+ this.sliderTrack.addEventListener('click', this.handleTrackClick.bind(this));
232
+ }
233
+
234
+ if (this.sliderThumb) {
235
+ this.sliderThumb.addEventListener('mousedown', this.handleThumbMouseDown.bind(this));
236
+ this.sliderThumb.addEventListener('touchstart', this.handleThumbTouchStart.bind(this), {
237
+ passive: false,
238
+ });
239
+ this.sliderThumb.addEventListener('keydown', this.handleThumbKeyDown.bind(this));
240
+ }
241
+
242
+ // Value input
243
+ const input = this.shadowRoot.querySelector('.value-input') as HTMLInputElement;
244
+ if (input) {
245
+ input.addEventListener('change', this.handleInputChange.bind(this));
246
+ input.addEventListener('blur', this.handleInputBlur.bind(this));
247
+ }
248
+
249
+ // Reset button
250
+ const resetBtn = this.shadowRoot.querySelector('.reset-button');
251
+ if (resetBtn) {
252
+ resetBtn.addEventListener('click', () => this.reset());
253
+ }
254
+
255
+ // Preset buttons
256
+ const presetButtons = this.shadowRoot.querySelectorAll('.preset-button');
257
+ presetButtons.forEach((btn, index) => {
258
+ btn.addEventListener('click', () => {
259
+ const preset = this.config.presets[index];
260
+ if (preset) this.selectPreset(preset);
261
+ });
262
+ });
263
+ }
264
+
265
+ /**
266
+ * Remove event listeners
267
+ */
268
+ private removeEventListeners(): void {
269
+ document.removeEventListener('mousemove', this.handleThumbMouseMove);
270
+ document.removeEventListener('mouseup', this.handleThumbMouseUp);
271
+ document.removeEventListener('touchmove', this.handleThumbTouchMove);
272
+ document.removeEventListener('touchend', this.handleThumbTouchEnd);
273
+ }
274
+
275
+ /**
276
+ * Handle track click
277
+ */
278
+ private handleTrackClick(e: MouseEvent): void {
279
+ if (!this.sliderTrack || this.config.disabled) return;
280
+
281
+ const rect = this.sliderTrack.getBoundingClientRect();
282
+ const percent = (e.clientX - rect.left) / rect.width;
283
+ const value = this.config.min + percent * (this.config.max - this.config.min);
284
+
285
+ this.setValue(value, 'slider');
286
+ }
287
+
288
+ /**
289
+ * Handle thumb mouse down
290
+ */
291
+ private handleThumbMouseDown(e: MouseEvent): void {
292
+ if (this.config.disabled) return;
293
+
294
+ e.preventDefault();
295
+ this.state.isDragging = true;
296
+ this.sliderThumb?.classList.add('dragging');
297
+
298
+ document.addEventListener('mousemove', this.handleThumbMouseMove);
299
+ document.addEventListener('mouseup', this.handleThumbMouseUp);
300
+ }
301
+
302
+ /**
303
+ * Handle thumb mouse move
304
+ */
305
+ private readonly handleThumbMouseMove = (e: MouseEvent): void => {
306
+ if (!this.state.isDragging || !this.sliderTrack) return;
307
+
308
+ const rect = this.sliderTrack.getBoundingClientRect();
309
+ const percent = clamp((e.clientX - rect.left) / rect.width, 0, 1);
310
+ const value = this.config.min + percent * (this.config.max - this.config.min);
311
+
312
+ this.setValue(value, 'slider');
313
+ };
314
+
315
+ /**
316
+ * Handle thumb mouse up
317
+ */
318
+ private readonly handleThumbMouseUp = (): void => {
319
+ this.state.isDragging = false;
320
+ this.sliderThumb?.classList.remove('dragging');
321
+
322
+ document.removeEventListener('mousemove', this.handleThumbMouseMove);
323
+ document.removeEventListener('mouseup', this.handleThumbMouseUp);
324
+ };
325
+
326
+ /**
327
+ * Handle thumb touch start
328
+ */
329
+ private handleThumbTouchStart(e: TouchEvent): void {
330
+ if (this.config.disabled) return;
331
+
332
+ e.preventDefault();
333
+ this.state.isDragging = true;
334
+ this.sliderThumb?.classList.add('dragging');
335
+
336
+ document.addEventListener('touchmove', this.handleThumbTouchMove, { passive: false });
337
+ document.addEventListener('touchend', this.handleThumbTouchEnd);
338
+ }
339
+
340
+ /**
341
+ * Handle thumb touch move
342
+ */
343
+ private readonly handleThumbTouchMove = (e: TouchEvent): void => {
344
+ if (!this.state.isDragging || !this.sliderTrack) return;
345
+
346
+ e.preventDefault();
347
+ const touch = e.touches[0];
348
+ if (!touch) return;
349
+
350
+ const rect = this.sliderTrack.getBoundingClientRect();
351
+ const percent = clamp((touch.clientX - rect.left) / rect.width, 0, 1);
352
+ const value = this.config.min + percent * (this.config.max - this.config.min);
353
+
354
+ this.setValue(value, 'slider');
355
+ };
356
+
357
+ /**
358
+ * Handle thumb touch end
359
+ */
360
+ private readonly handleThumbTouchEnd = (): void => {
361
+ this.state.isDragging = false;
362
+ this.sliderThumb?.classList.remove('dragging');
363
+
364
+ document.removeEventListener('touchmove', this.handleThumbTouchMove);
365
+ document.removeEventListener('touchend', this.handleThumbTouchEnd);
366
+ };
367
+
368
+ /**
369
+ * Handle keyboard navigation
370
+ */
371
+ private handleThumbKeyDown(e: KeyboardEvent): void {
372
+ if (this.config.disabled) return;
373
+
374
+ let delta = 0;
375
+ const largeStep = this.config.step * 10;
376
+
377
+ switch (e.key) {
378
+ case 'ArrowRight':
379
+ case 'ArrowUp':
380
+ delta = this.config.step;
381
+ break;
382
+ case 'ArrowLeft':
383
+ case 'ArrowDown':
384
+ delta = -this.config.step;
385
+ break;
386
+ case 'PageUp':
387
+ delta = largeStep;
388
+ break;
389
+ case 'PageDown':
390
+ delta = -largeStep;
391
+ break;
392
+ case 'Home':
393
+ this.setValue(this.config.min, 'slider');
394
+ e.preventDefault();
395
+ return;
396
+ case 'End':
397
+ this.setValue(this.config.max, 'slider');
398
+ e.preventDefault();
399
+ return;
400
+ default:
401
+ return;
402
+ }
403
+
404
+ e.preventDefault();
405
+ this.setValue(this.state.currentValue + delta, 'slider');
406
+ }
407
+
408
+ /**
409
+ * Handle input field change
410
+ */
411
+ private handleInputChange(e: Event): void {
412
+ const input = e.target as HTMLInputElement;
413
+ const value = Number.parseFloat(input.value);
414
+
415
+ if (!Number.isNaN(value)) {
416
+ this.setValue(value, 'input');
417
+ }
418
+ }
419
+
420
+ /**
421
+ * Handle input field blur (validate)
422
+ */
423
+ private handleInputBlur(e: Event): void {
424
+ const input = e.target as HTMLInputElement;
425
+ input.value = this.state.currentValue.toFixed(this.config.decimals);
426
+ }
427
+
428
+ /**
429
+ * Update slider thumb position based on current value
430
+ */
431
+ private updateSliderPosition(): void {
432
+ if (!this.sliderThumb || !this.shadowRoot) return;
433
+
434
+ const percent =
435
+ ((this.state.currentValue - this.config.min) / (this.config.max - this.config.min)) * 100;
436
+ this.sliderThumb.style.left = `${percent}%`;
437
+
438
+ const fill = this.shadowRoot.querySelector('.slider-fill') as HTMLElement;
439
+ if (fill) {
440
+ fill.style.width = `${percent}%`;
441
+ }
442
+
443
+ // Update value display
444
+ const valueDisplay = this.shadowRoot.querySelector('.current-value');
445
+ if (valueDisplay) {
446
+ valueDisplay.textContent = this.state.currentValue.toFixed(this.config.decimals);
447
+ }
448
+
449
+ // Update input field
450
+ const input = this.shadowRoot.querySelector('.value-input') as HTMLInputElement;
451
+ if (input && document.activeElement !== input) {
452
+ input.value = this.state.currentValue.toFixed(this.config.decimals);
453
+ }
454
+
455
+ // Update active preset
456
+ this.updateActivePreset();
457
+ this.updateCursor();
458
+ }
459
+
460
+ /**
461
+ * Update cursor based on slider state
462
+ */
463
+ private updateCursor(): void {
464
+ if (!this.config.cursorFeedback || !this.sliderThumb) return;
465
+
466
+ if (this.config.disabled) {
467
+ this.sliderThumb.style.cursor = 'not-allowed';
468
+ } else if (this.state.isDragging) {
469
+ this.sliderThumb.style.cursor = 'grabbing';
470
+ } else {
471
+ this.sliderThumb.style.cursor = 'grab';
472
+ }
473
+ }
474
+
475
+ /**
476
+ * Update active preset button
477
+ */
478
+ private updateActivePreset(): void {
479
+ if (!this.shadowRoot) return;
480
+
481
+ const presetButtons = this.shadowRoot.querySelectorAll('.preset-button');
482
+ presetButtons.forEach((btn, index) => {
483
+ const preset = this.config.presets[index];
484
+ if (preset && Math.abs(preset.value - this.state.currentValue) < this.config.step / 2) {
485
+ btn.classList.add('active');
486
+ } else {
487
+ btn.classList.remove('active');
488
+ }
489
+ });
490
+ }
491
+
492
+ /**
493
+ * Render input controls section
494
+ */
495
+ private renderInputControls(): string {
496
+ if (!this.config.showInput && !this.config.showReset) return '';
497
+
498
+ const disabledAttr = this.config.disabled ? 'disabled' : '';
499
+ let controlsHtml = '';
500
+
501
+ if (this.config.showInput) {
502
+ controlsHtml += `
503
+ <span class="input-label">Value:</span>
504
+ <input
505
+ type="number"
506
+ class="value-input"
507
+ value="${this.state.currentValue.toFixed(this.config.decimals)}"
508
+ min="${this.config.min}"
509
+ max="${this.config.max}"
510
+ step="${this.config.step}"
511
+ ${disabledAttr}
512
+ />
513
+ `;
514
+ }
515
+
516
+ if (this.config.showReset) {
517
+ controlsHtml += `
518
+ <button class="reset-button" type="button" ${disabledAttr}>
519
+ Reset
520
+ </button>
521
+ `;
522
+ }
523
+
524
+ return `
525
+ <div class="input-container">
526
+ ${controlsHtml}
527
+ </div>
528
+ `;
529
+ }
530
+
531
+ /**
532
+ * Render the component
533
+ */
534
+ protected render(): void {
535
+ if (!this.shadowRoot) return;
536
+
537
+ // Sync attributes to host element for CSS selectors
538
+ if (this.config.size && this.getAttribute('size') !== this.config.size) {
539
+ this.setAttribute('size', this.config.size);
540
+ }
541
+ if (this.config.variant && this.getAttribute('variant') !== this.config.variant) {
542
+ this.setAttribute('variant', this.config.variant);
543
+ }
544
+ if (this.config.animation && this.getAttribute('animation') !== this.config.animation) {
545
+ this.setAttribute('animation', this.config.animation);
546
+ }
547
+
548
+ const disabledClass = this.config.disabled ? 'disabled' : '';
549
+
550
+ // Header with label and current value
551
+ const headerHtml = `
552
+ <div class="header">
553
+ <div class="label-section">
554
+ <label class="label">${this.config.label}</label>
555
+ ${this.config.description ? `<div class="description">${this.config.description}</div>` : ''}
556
+ </div>
557
+ <div class="value-display">
558
+ <span class="current-value">${this.state.currentValue.toFixed(this.config.decimals)}</span>
559
+ ${this.config.unit ? `<span class="unit">${this.config.unit}</span>` : ''}
560
+ </div>
561
+ </div>
562
+ `;
563
+
564
+ // Slider
565
+ const percent =
566
+ ((this.state.currentValue - this.config.min) / (this.config.max - this.config.min)) * 100;
567
+ const sliderHtml = `
568
+ <div class="slider-container">
569
+ <div class="slider-track" role="presentation">
570
+ <div class="slider-fill" style="width: ${percent}%"></div>
571
+ <div
572
+ class="slider-thumb"
573
+ style="left: ${percent}%"
574
+ role="slider"
575
+ tabindex="${this.config.disabled ? -1 : 0}"
576
+ aria-valuemin="${this.config.min}"
577
+ aria-valuemax="${this.config.max}"
578
+ aria-valuenow="${this.state.currentValue}"
579
+ aria-label="${this.config.ariaLabel}"
580
+ ></div>
581
+ </div>
582
+ ${
583
+ this.config.showRangeLabels
584
+ ? `
585
+ <div class="range-labels">
586
+ <span class="range-label">${this.config.min.toFixed(this.config.decimals)}</span>
587
+ <span class="range-label">${this.config.max.toFixed(this.config.decimals)}</span>
588
+ </div>
589
+ `
590
+ : ''
591
+ }
592
+ </div>
593
+ `;
594
+
595
+ // Presets
596
+ const presetsHtml =
597
+ this.config.showPresets && this.config.presets.length > 0
598
+ ? `
599
+ <div class="presets">
600
+ ${this.config.presets
601
+ .map(
602
+ (preset) => `
603
+ <button class="preset-button" type="button">
604
+ <span class="preset-value">${preset.value}</span>
605
+ <span class="preset-label">${preset.label}</span>
606
+ </button>
607
+ `
608
+ )
609
+ .join('')}
610
+ </div>
611
+ `
612
+ : '';
613
+
614
+ // Input and reset
615
+ const inputHtml = this.renderInputControls();
616
+
617
+ this.shadowRoot.innerHTML = `
618
+ ${styles}
619
+ <div class="parameter-slider ${disabledClass} ${this.config.className}">
620
+ ${headerHtml}
621
+ ${sliderHtml}
622
+ ${presetsHtml}
623
+ ${inputHtml}
624
+ </div>
625
+ `;
626
+
627
+ // Store references after render
628
+ this.sliderTrack = this.shadowRoot.querySelector('.slider-track');
629
+ this.sliderThumb = this.shadowRoot.querySelector('.slider-thumb');
630
+
631
+ // Reattach event listeners
632
+ this.attachEventListeners();
633
+ }
634
+
635
+ /**
636
+ * Get current state (for debugging)
637
+ */
638
+ public getState(): Readonly<ParameterSliderState> {
639
+ return { ...this.state };
640
+ }
641
+
642
+ /**
643
+ * Get current configuration
644
+ */
645
+ public getConfig(): Readonly<Required<ParameterSliderConfig>> {
646
+ return { ...this.config };
647
+ }
648
+ }
649
+
650
+ // Register the custom element
651
+ if (!customElements.get('parameter-slider')) {
652
+ customElements.define('parameter-slider', ParameterSlider);
653
+ }
@@ -0,0 +1,8 @@
1
+ export { ParameterSlider } from './ParameterSlider';
2
+ export type {
3
+ ParameterSliderConfig,
4
+ ParameterSliderState,
5
+ PresetValue,
6
+ ValueChangeEvent,
7
+ PresetSelectEvent,
8
+ } from './types';