@testgorilla/tgo-ui 11.22.3 → 12.0.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.
- package/fesm2022/testgorilla-tgo-ui-components-accordion.mjs +335 -229
- package/fesm2022/testgorilla-tgo-ui-components-accordion.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-ai-audio-circle.mjs +547 -465
- package/fesm2022/testgorilla-tgo-ui-components-ai-audio-circle.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-ai-caveat.mjs +160 -75
- package/fesm2022/testgorilla-tgo-ui-components-ai-caveat.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-ai-feedback.mjs +678 -278
- package/fesm2022/testgorilla-tgo-ui-components-ai-feedback.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-alert-banner.mjs +337 -274
- package/fesm2022/testgorilla-tgo-ui-components-alert-banner.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-audio-waveform.mjs +351 -192
- package/fesm2022/testgorilla-tgo-ui-components-audio-waveform.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-autocomplete-v2.mjs +1950 -960
- package/fesm2022/testgorilla-tgo-ui-components-autocomplete-v2.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-autocomplete.mjs +1206 -1044
- package/fesm2022/testgorilla-tgo-ui-components-autocomplete.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-avatar.mjs +328 -182
- package/fesm2022/testgorilla-tgo-ui-components-avatar.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-badge.mjs +383 -203
- package/fesm2022/testgorilla-tgo-ui-components-badge.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-breadcrumb.mjs +553 -261
- package/fesm2022/testgorilla-tgo-ui-components-breadcrumb.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-button.mjs +709 -739
- package/fesm2022/testgorilla-tgo-ui-components-button.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-card.mjs +219 -184
- package/fesm2022/testgorilla-tgo-ui-components-card.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-checkbox.mjs +515 -458
- package/fesm2022/testgorilla-tgo-ui-components-checkbox.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-checklist.mjs +423 -162
- package/fesm2022/testgorilla-tgo-ui-components-checklist.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-core.mjs +10784 -10996
- package/fesm2022/testgorilla-tgo-ui-components-core.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-datepicker.mjs +810 -533
- package/fesm2022/testgorilla-tgo-ui-components-datepicker.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-device-control.mjs +473 -116
- package/fesm2022/testgorilla-tgo-ui-components-device-control.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-dialog.mjs +1149 -540
- package/fesm2022/testgorilla-tgo-ui-components-dialog.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-divider.mjs +148 -84
- package/fesm2022/testgorilla-tgo-ui-components-divider.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-donut-chart.mjs +239 -142
- package/fesm2022/testgorilla-tgo-ui-components-donut-chart.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-dropdown.mjs +599 -467
- package/fesm2022/testgorilla-tgo-ui-components-dropdown.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-elevation-shadow.mjs +126 -70
- package/fesm2022/testgorilla-tgo-ui-components-elevation-shadow.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-empty-state.mjs +324 -329
- package/fesm2022/testgorilla-tgo-ui-components-empty-state.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-field.mjs +969 -648
- package/fesm2022/testgorilla-tgo-ui-components-field.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-file-upload.mjs +553 -419
- package/fesm2022/testgorilla-tgo-ui-components-file-upload.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-filter-button.mjs +847 -452
- package/fesm2022/testgorilla-tgo-ui-components-filter-button.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-gaussian-chart.mjs +398 -302
- package/fesm2022/testgorilla-tgo-ui-components-gaussian-chart.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-icon-label.mjs +162 -95
- package/fesm2022/testgorilla-tgo-ui-components-icon-label.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-icon.mjs +1825 -1795
- package/fesm2022/testgorilla-tgo-ui-components-icon.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-infinite-scroll-table.mjs +1018 -672
- package/fesm2022/testgorilla-tgo-ui-components-infinite-scroll-table.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-inline-field.mjs +758 -368
- package/fesm2022/testgorilla-tgo-ui-components-inline-field.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-logo.mjs +204 -212
- package/fesm2022/testgorilla-tgo-ui-components-logo.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-media-card.mjs +183 -48
- package/fesm2022/testgorilla-tgo-ui-components-media-card.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-media-dialog.mjs +243 -79
- package/fesm2022/testgorilla-tgo-ui-components-media-dialog.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-multi-input.mjs +751 -361
- package/fesm2022/testgorilla-tgo-ui-components-multi-input.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-navbar.mjs +1122 -385
- package/fesm2022/testgorilla-tgo-ui-components-navbar.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-overflow-menu.mjs +689 -413
- package/fesm2022/testgorilla-tgo-ui-components-overflow-menu.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-page-header.mjs +227 -92
- package/fesm2022/testgorilla-tgo-ui-components-page-header.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-paginator.mjs +174 -113
- package/fesm2022/testgorilla-tgo-ui-components-paginator.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-password-criteria.mjs +367 -165
- package/fesm2022/testgorilla-tgo-ui-components-password-criteria.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-password-strength.mjs +253 -118
- package/fesm2022/testgorilla-tgo-ui-components-password-strength.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-phone-input.mjs +730 -444
- package/fesm2022/testgorilla-tgo-ui-components-phone-input.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-popover.mjs +533 -221
- package/fesm2022/testgorilla-tgo-ui-components-popover.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-popup-tip.mjs +274 -76
- package/fesm2022/testgorilla-tgo-ui-components-popup-tip.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-progress-bar.mjs +184 -124
- package/fesm2022/testgorilla-tgo-ui-components-progress-bar.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-prompt.mjs +1223 -574
- package/fesm2022/testgorilla-tgo-ui-components-prompt.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-radial-progress.mjs +249 -160
- package/fesm2022/testgorilla-tgo-ui-components-radial-progress.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-radio-button.mjs +447 -439
- package/fesm2022/testgorilla-tgo-ui-components-radio-button.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-rating.mjs +226 -150
- package/fesm2022/testgorilla-tgo-ui-components-rating.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-scale-table.mjs +255 -67
- package/fesm2022/testgorilla-tgo-ui-components-scale-table.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-scale.mjs +205 -162
- package/fesm2022/testgorilla-tgo-ui-components-scale.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-segmented-bar.mjs +245 -153
- package/fesm2022/testgorilla-tgo-ui-components-segmented-bar.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-segmented-button.mjs +274 -140
- package/fesm2022/testgorilla-tgo-ui-components-segmented-button.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-segmented-toggle.mjs +319 -147
- package/fesm2022/testgorilla-tgo-ui-components-segmented-toggle.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-selectable-card.mjs +286 -84
- package/fesm2022/testgorilla-tgo-ui-components-selectable-card.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-side-navigation.mjs +660 -208
- package/fesm2022/testgorilla-tgo-ui-components-side-navigation.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-side-panel.mjs +579 -320
- package/fesm2022/testgorilla-tgo-ui-components-side-panel.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-side-sheet.mjs +419 -200
- package/fesm2022/testgorilla-tgo-ui-components-side-sheet.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-skeleton.mjs +132 -80
- package/fesm2022/testgorilla-tgo-ui-components-skeleton.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-slider.mjs +741 -390
- package/fesm2022/testgorilla-tgo-ui-components-slider.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-snackbar.mjs +455 -329
- package/fesm2022/testgorilla-tgo-ui-components-snackbar.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-spider-chart.mjs +543 -514
- package/fesm2022/testgorilla-tgo-ui-components-spider-chart.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-spinner.mjs +193 -104
- package/fesm2022/testgorilla-tgo-ui-components-spinner.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-stepper.mjs +760 -524
- package/fesm2022/testgorilla-tgo-ui-components-stepper.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-table.mjs +714 -415
- package/fesm2022/testgorilla-tgo-ui-components-table.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-tabs.mjs +847 -438
- package/fesm2022/testgorilla-tgo-ui-components-tabs.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-tag.mjs +535 -275
- package/fesm2022/testgorilla-tgo-ui-components-tag.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-toggle.mjs +316 -246
- package/fesm2022/testgorilla-tgo-ui-components-toggle.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-tooltip.mjs +404 -284
- package/fesm2022/testgorilla-tgo-ui-components-tooltip.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-universal-skills.mjs +664 -383
- package/fesm2022/testgorilla-tgo-ui-components-universal-skills.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-validation-error.mjs +201 -112
- package/fesm2022/testgorilla-tgo-ui-components-validation-error.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui-components-write-with-ai.mjs +1014 -458
- package/fesm2022/testgorilla-tgo-ui-components-write-with-ai.mjs.map +1 -1
- package/fesm2022/testgorilla-tgo-ui.mjs +147 -155
- package/fesm2022/testgorilla-tgo-ui.mjs.map +1 -1
- package/mcp/catalog.json +1 -1
- package/package.json +16 -17
- package/projects/tgo-canopy-ui/theme/_palette.scss +23 -18
- package/types/testgorilla-tgo-ui-components-accordion.d.ts +214 -158
- package/types/testgorilla-tgo-ui-components-ai-audio-circle.d.ts +93 -69
- package/types/testgorilla-tgo-ui-components-ai-caveat.d.ts +37 -34
- package/types/testgorilla-tgo-ui-components-ai-feedback.d.ts +126 -86
- package/types/testgorilla-tgo-ui-components-alert-banner.d.ts +260 -185
- package/types/testgorilla-tgo-ui-components-audio-waveform.d.ts +96 -67
- package/types/testgorilla-tgo-ui-components-autocomplete-v2.d.ts +455 -256
- package/types/testgorilla-tgo-ui-components-autocomplete.d.ts +545 -390
- package/types/testgorilla-tgo-ui-components-avatar.d.ts +128 -102
- package/types/testgorilla-tgo-ui-components-badge.d.ts +191 -139
- package/types/testgorilla-tgo-ui-components-breadcrumb.d.ts +100 -62
- package/types/testgorilla-tgo-ui-components-button.d.ts +479 -340
- package/types/testgorilla-tgo-ui-components-card.d.ts +153 -114
- package/types/testgorilla-tgo-ui-components-checkbox.d.ts +299 -223
- package/types/testgorilla-tgo-ui-components-checklist.d.ts +149 -97
- package/types/testgorilla-tgo-ui-components-core.d.ts +307 -295
- package/types/testgorilla-tgo-ui-components-datepicker.d.ts +404 -299
- package/types/testgorilla-tgo-ui-components-device-control.d.ts +151 -81
- package/types/testgorilla-tgo-ui-components-dialog.d.ts +442 -329
- package/types/testgorilla-tgo-ui-components-divider.d.ts +64 -51
- package/types/testgorilla-tgo-ui-components-donut-chart.d.ts +41 -37
- package/types/testgorilla-tgo-ui-components-dropdown.d.ts +335 -241
- package/types/testgorilla-tgo-ui-components-elevation-shadow.d.ts +48 -43
- package/types/testgorilla-tgo-ui-components-empty-state.d.ts +317 -215
- package/types/testgorilla-tgo-ui-components-field.d.ts +559 -388
- package/types/testgorilla-tgo-ui-components-file-upload.d.ts +230 -168
- package/types/testgorilla-tgo-ui-components-filter-button.d.ts +263 -206
- package/types/testgorilla-tgo-ui-components-gaussian-chart.d.ts +107 -94
- package/types/testgorilla-tgo-ui-components-icon-label.d.ts +84 -61
- package/types/testgorilla-tgo-ui-components-icon.d.ts +137 -110
- package/types/testgorilla-tgo-ui-components-infinite-scroll-table.d.ts +429 -303
- package/types/testgorilla-tgo-ui-components-inline-field.d.ts +338 -254
- package/types/testgorilla-tgo-ui-components-logo.d.ts +119 -101
- package/types/testgorilla-tgo-ui-components-media-card.d.ts +51 -35
- package/types/testgorilla-tgo-ui-components-media-dialog.d.ts +40 -45
- package/types/testgorilla-tgo-ui-components-multi-input.d.ts +283 -194
- package/types/testgorilla-tgo-ui-components-navbar.d.ts +326 -208
- package/types/testgorilla-tgo-ui-components-overflow-menu.d.ts +276 -204
- package/types/testgorilla-tgo-ui-components-page-header.d.ts +67 -50
- package/types/testgorilla-tgo-ui-components-paginator.d.ts +103 -70
- package/types/testgorilla-tgo-ui-components-password-criteria.d.ts +67 -58
- package/types/testgorilla-tgo-ui-components-password-strength.d.ts +66 -57
- package/types/testgorilla-tgo-ui-components-phone-input.d.ts +217 -175
- package/types/testgorilla-tgo-ui-components-popover.d.ts +94 -53
- package/types/testgorilla-tgo-ui-components-popup-tip.d.ts +81 -44
- package/types/testgorilla-tgo-ui-components-progress-bar.d.ts +115 -84
- package/types/testgorilla-tgo-ui-components-prompt.d.ts +315 -219
- package/types/testgorilla-tgo-ui-components-radial-progress.d.ts +146 -107
- package/types/testgorilla-tgo-ui-components-radio-button.d.ts +296 -219
- package/types/testgorilla-tgo-ui-components-rating.d.ts +134 -95
- package/types/testgorilla-tgo-ui-components-scale-table.d.ts +67 -47
- package/types/testgorilla-tgo-ui-components-scale.d.ts +91 -66
- package/types/testgorilla-tgo-ui-components-segmented-bar.d.ts +118 -89
- package/types/testgorilla-tgo-ui-components-segmented-button.d.ts +107 -80
- package/types/testgorilla-tgo-ui-components-segmented-toggle.d.ts +138 -105
- package/types/testgorilla-tgo-ui-components-selectable-card.d.ts +90 -56
- package/types/testgorilla-tgo-ui-components-side-navigation.d.ts +183 -107
- package/types/testgorilla-tgo-ui-components-side-panel.d.ts +156 -145
- package/types/testgorilla-tgo-ui-components-side-sheet.d.ts +98 -89
- package/types/testgorilla-tgo-ui-components-skeleton.d.ts +50 -33
- package/types/testgorilla-tgo-ui-components-slider.d.ts +278 -198
- package/types/testgorilla-tgo-ui-components-snackbar.d.ts +211 -159
- package/types/testgorilla-tgo-ui-components-spider-chart.d.ts +140 -121
- package/types/testgorilla-tgo-ui-components-spinner.d.ts +93 -72
- package/types/testgorilla-tgo-ui-components-stepper.d.ts +480 -338
- package/types/testgorilla-tgo-ui-components-table.d.ts +280 -233
- package/types/testgorilla-tgo-ui-components-tabs.d.ts +282 -183
- package/types/testgorilla-tgo-ui-components-tag.d.ts +266 -193
- package/types/testgorilla-tgo-ui-components-toggle.d.ts +180 -132
- package/types/testgorilla-tgo-ui-components-tooltip.d.ts +142 -105
- package/types/testgorilla-tgo-ui-components-universal-skills.d.ts +165 -144
- package/types/testgorilla-tgo-ui-components-validation-error.d.ts +70 -51
- package/types/testgorilla-tgo-ui-components-write-with-ai.d.ts +210 -133
- package/types/testgorilla-tgo-ui.d.ts +52 -45
|
@@ -1,195 +1,354 @@
|
|
|
1
|
-
import * as i0 from
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
import { ChangeDetectionStrategy, Component, Injectable, NgModule, computed, effect, inject, input, signal } from "@angular/core";
|
|
4
3
|
const DEFAULT_AUDIO_WAVEFORM_CONFIG = {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
barCount: 5,
|
|
5
|
+
smoothing: .55,
|
|
6
|
+
fftSize: 256
|
|
8
7
|
};
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
8
|
+
const AUDIO_WAVEFORM_BAR_COLORS = [
|
|
9
|
+
"#C065D0",
|
|
10
|
+
"#A86FDA",
|
|
11
|
+
"#9078E3",
|
|
12
|
+
"#7882EC",
|
|
13
|
+
"#6889F3"
|
|
14
|
+
];
|
|
15
|
+
var AudioWaveformService = class AudioWaveformService {
|
|
16
|
+
constructor() {
|
|
17
|
+
this.ctx = null;
|
|
18
|
+
this.analyser = null;
|
|
19
|
+
this.source = null;
|
|
20
|
+
this.stream = null;
|
|
21
|
+
this.raf = null;
|
|
22
|
+
this.bars = signal([], ...ngDevMode ? [{ debugName: "bars" }] : /* istanbul ignore next */ []);
|
|
23
|
+
this.active = signal(false, ...ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []);
|
|
24
|
+
}
|
|
25
|
+
async start(barCount = DEFAULT_AUDIO_WAVEFORM_CONFIG.barCount, smoothing = DEFAULT_AUDIO_WAVEFORM_CONFIG.smoothing) {
|
|
26
|
+
if (this.active()) return;
|
|
27
|
+
try {
|
|
28
|
+
this.stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
29
|
+
this.ctx = new AudioContext();
|
|
30
|
+
this.analyser = this.ctx.createAnalyser();
|
|
31
|
+
this.analyser.fftSize = DEFAULT_AUDIO_WAVEFORM_CONFIG.fftSize;
|
|
32
|
+
this.analyser.smoothingTimeConstant = smoothing;
|
|
33
|
+
this.source = this.ctx.createMediaStreamSource(this.stream);
|
|
34
|
+
this.source.connect(this.analyser);
|
|
35
|
+
this.active.set(true);
|
|
36
|
+
const data = new Uint8Array(this.analyser.frequencyBinCount);
|
|
37
|
+
const speechBinCount = Math.max(1, Math.floor(data.length * .6));
|
|
38
|
+
const analyserRef = this.analyser;
|
|
39
|
+
const tick = () => {
|
|
40
|
+
analyserRef.getByteFrequencyData(data);
|
|
41
|
+
let sumSq = 0;
|
|
42
|
+
for (let i = 0; i < speechBinCount; i++) {
|
|
43
|
+
const v = data[i] / 255;
|
|
44
|
+
sumSq += v * v;
|
|
45
|
+
}
|
|
46
|
+
const rms = Math.sqrt(sumSq / speechBinCount);
|
|
47
|
+
const amplitude = Math.min(1, Math.pow(rms, .55) * 1.5);
|
|
48
|
+
const values = this.createSymmetricBars(barCount, amplitude);
|
|
49
|
+
this.bars.set(values);
|
|
50
|
+
this.raf = requestAnimationFrame(tick);
|
|
51
|
+
};
|
|
52
|
+
tick();
|
|
53
|
+
} catch (e) {
|
|
54
|
+
console.error("[AudioWaveform] Mic access denied:", e);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
stop() {
|
|
58
|
+
if (this.raf !== null) cancelAnimationFrame(this.raf);
|
|
59
|
+
this.source?.disconnect();
|
|
60
|
+
this.analyser?.disconnect();
|
|
61
|
+
this.stream?.getTracks().forEach((t) => t.stop());
|
|
62
|
+
if (this.ctx && this.ctx.state !== "closed") this.ctx.close();
|
|
63
|
+
this.ctx = null;
|
|
64
|
+
this.analyser = null;
|
|
65
|
+
this.source = null;
|
|
66
|
+
this.stream = null;
|
|
67
|
+
this.raf = null;
|
|
68
|
+
this.active.set(false);
|
|
69
|
+
this.bars.set([]);
|
|
70
|
+
}
|
|
71
|
+
ngOnDestroy() {
|
|
72
|
+
this.stop();
|
|
73
|
+
}
|
|
74
|
+
createSymmetricBars(barCount, amplitude) {
|
|
75
|
+
const center = (barCount - 1) / 2;
|
|
76
|
+
const maxDistance = center;
|
|
77
|
+
return Array.from({ length: barCount }, (_, i) => {
|
|
78
|
+
return amplitude * (1 - Math.abs(i - center) / maxDistance * .65);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
static {
|
|
82
|
+
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
83
|
+
minVersion: "12.0.0",
|
|
84
|
+
version: "22.2.4",
|
|
85
|
+
ngImport: i0,
|
|
86
|
+
type: AudioWaveformService,
|
|
87
|
+
deps: [],
|
|
88
|
+
target: i0.ɵɵFactoryTarget.Injectable
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
static {
|
|
92
|
+
this.ɵprov = i0.ɵɵngDeclareInjectable({
|
|
93
|
+
minVersion: "12.0.0",
|
|
94
|
+
version: "22.2.4",
|
|
95
|
+
ngImport: i0,
|
|
96
|
+
type: AudioWaveformService,
|
|
97
|
+
providedIn: "root"
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
102
|
+
minVersion: "12.0.0",
|
|
103
|
+
version: "22.2.4",
|
|
104
|
+
ngImport: i0,
|
|
105
|
+
type: AudioWaveformService,
|
|
106
|
+
decorators: [{
|
|
107
|
+
type: Injectable,
|
|
108
|
+
args: [{ providedIn: "root" }]
|
|
109
|
+
}]
|
|
110
|
+
});
|
|
111
|
+
var AudioWaveformComponent = class AudioWaveformComponent {
|
|
112
|
+
constructor() {
|
|
113
|
+
this.waveformService = inject(AudioWaveformService);
|
|
114
|
+
this.barCount = DEFAULT_AUDIO_WAVEFORM_CONFIG.barCount;
|
|
115
|
+
this.width = input(40, ...ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []);
|
|
116
|
+
this.height = input(22, ...ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []);
|
|
117
|
+
this.gap = input(3, ...ngDevMode ? [{ debugName: "gap" }] : /* istanbul ignore next */ []);
|
|
118
|
+
this.minScale = input(.08, ...ngDevMode ? [{ debugName: "minScale" }] : /* istanbul ignore next */ []);
|
|
119
|
+
this.smoothing = input(DEFAULT_AUDIO_WAVEFORM_CONFIG.smoothing, ...ngDevMode ? [{ debugName: "smoothing" }] : /* istanbul ignore next */ []);
|
|
120
|
+
this.bars = input(null, ...ngDevMode ? [{ debugName: "bars" }] : /* istanbul ignore next */ []);
|
|
121
|
+
this.active = input(null, ...ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []);
|
|
122
|
+
this.controlled = computed(() => this.active() !== null, ...ngDevMode ? [{ debugName: "controlled" }] : /* istanbul ignore next */ []);
|
|
123
|
+
this.effectiveBars = computed(() => {
|
|
124
|
+
if (this.controlled()) return this.bars() ?? [];
|
|
125
|
+
return this.waveformService.bars();
|
|
126
|
+
}, ...ngDevMode ? [{ debugName: "effectiveBars" }] : /* istanbul ignore next */ []);
|
|
127
|
+
this.effectiveActive = computed(() => {
|
|
128
|
+
if (this.controlled()) return this.active() ?? false;
|
|
129
|
+
return this.waveformService.active();
|
|
130
|
+
}, ...ngDevMode ? [{ debugName: "effectiveActive" }] : /* istanbul ignore next */ []);
|
|
131
|
+
this.barWidth = computed(() => {
|
|
132
|
+
const count = this.barCount;
|
|
133
|
+
return (this.width() - this.gap() * (count - 1)) / count;
|
|
134
|
+
}, ...ngDevMode ? [{ debugName: "barWidth" }] : /* istanbul ignore next */ []);
|
|
135
|
+
effect(() => {
|
|
136
|
+
if (!this.controlled()) this.waveformService.start(this.barCount, this.smoothing());
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
barHeight(value) {
|
|
140
|
+
const h = this.height();
|
|
141
|
+
const min = this.minScale();
|
|
142
|
+
return h * (min + value * (1 - min));
|
|
143
|
+
}
|
|
144
|
+
barRadius() {
|
|
145
|
+
return this.barWidth() / 2;
|
|
146
|
+
}
|
|
147
|
+
barValue(index) {
|
|
148
|
+
return this.effectiveBars()[index] ?? 0;
|
|
149
|
+
}
|
|
150
|
+
barColor(index) {
|
|
151
|
+
return AUDIO_WAVEFORM_BAR_COLORS[index] ?? AUDIO_WAVEFORM_BAR_COLORS[0];
|
|
152
|
+
}
|
|
153
|
+
barOffset(index) {
|
|
154
|
+
return (this.barWidth() + this.gap()) * index;
|
|
155
|
+
}
|
|
156
|
+
ngOnDestroy() {
|
|
157
|
+
if (!this.controlled()) this.waveformService.stop();
|
|
158
|
+
}
|
|
159
|
+
static {
|
|
160
|
+
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
161
|
+
minVersion: "12.0.0",
|
|
162
|
+
version: "22.2.4",
|
|
163
|
+
ngImport: i0,
|
|
164
|
+
type: AudioWaveformComponent,
|
|
165
|
+
deps: [],
|
|
166
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
static {
|
|
170
|
+
this.ɵcmp = i0.ɵɵngDeclareComponent({
|
|
171
|
+
minVersion: "17.0.0",
|
|
172
|
+
version: "22.2.4",
|
|
173
|
+
type: AudioWaveformComponent,
|
|
174
|
+
isStandalone: true,
|
|
175
|
+
selector: "ui-audio-waveform",
|
|
176
|
+
inputs: {
|
|
177
|
+
width: {
|
|
178
|
+
classPropertyName: "width",
|
|
179
|
+
publicName: "width",
|
|
180
|
+
isSignal: true,
|
|
181
|
+
isRequired: false,
|
|
182
|
+
transformFunction: null
|
|
183
|
+
},
|
|
184
|
+
height: {
|
|
185
|
+
classPropertyName: "height",
|
|
186
|
+
publicName: "height",
|
|
187
|
+
isSignal: true,
|
|
188
|
+
isRequired: false,
|
|
189
|
+
transformFunction: null
|
|
190
|
+
},
|
|
191
|
+
gap: {
|
|
192
|
+
classPropertyName: "gap",
|
|
193
|
+
publicName: "gap",
|
|
194
|
+
isSignal: true,
|
|
195
|
+
isRequired: false,
|
|
196
|
+
transformFunction: null
|
|
197
|
+
},
|
|
198
|
+
minScale: {
|
|
199
|
+
classPropertyName: "minScale",
|
|
200
|
+
publicName: "minScale",
|
|
201
|
+
isSignal: true,
|
|
202
|
+
isRequired: false,
|
|
203
|
+
transformFunction: null
|
|
204
|
+
},
|
|
205
|
+
smoothing: {
|
|
206
|
+
classPropertyName: "smoothing",
|
|
207
|
+
publicName: "smoothing",
|
|
208
|
+
isSignal: true,
|
|
209
|
+
isRequired: false,
|
|
210
|
+
transformFunction: null
|
|
211
|
+
},
|
|
212
|
+
bars: {
|
|
213
|
+
classPropertyName: "bars",
|
|
214
|
+
publicName: "bars",
|
|
215
|
+
isSignal: true,
|
|
216
|
+
isRequired: false,
|
|
217
|
+
transformFunction: null
|
|
218
|
+
},
|
|
219
|
+
active: {
|
|
220
|
+
classPropertyName: "active",
|
|
221
|
+
publicName: "active",
|
|
222
|
+
isSignal: true,
|
|
223
|
+
isRequired: false,
|
|
224
|
+
transformFunction: null
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
ngImport: i0,
|
|
228
|
+
template: "<div\n class=\"waveform\"\n [style.width.px]=\"width()\"\n [style.height.px]=\"height()\"\n [style.gap.px]=\"gap()\"\n role=\"img\"\n [attr.aria-label]=\"effectiveActive() ? 'Audio waveform active' : 'Audio waveform inactive'\"\n>\n @for (item of effectiveBars(); track $index) {\n <div\n class=\"waveform-bar\"\n [style.width.px]=\"barWidth()\"\n [style.height.px]=\"barHeight(barValue($index))\"\n [style.border-radius.px]=\"barRadius()\"\n [style.background-color]=\"barColor($index)\"\n ></div>\n }\n</div>\n",
|
|
229
|
+
styles: [".waveform{display:inline-flex;align-items:center}.waveform-bar{flex-shrink:0;transition:height 80ms ease-out}\n"],
|
|
230
|
+
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
235
|
+
minVersion: "12.0.0",
|
|
236
|
+
version: "22.2.4",
|
|
237
|
+
ngImport: i0,
|
|
238
|
+
type: AudioWaveformComponent,
|
|
239
|
+
decorators: [{
|
|
240
|
+
type: Component,
|
|
241
|
+
args: [{
|
|
242
|
+
selector: "ui-audio-waveform",
|
|
243
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
244
|
+
imports: [],
|
|
245
|
+
template: "<div\n class=\"waveform\"\n [style.width.px]=\"width()\"\n [style.height.px]=\"height()\"\n [style.gap.px]=\"gap()\"\n role=\"img\"\n [attr.aria-label]=\"effectiveActive() ? 'Audio waveform active' : 'Audio waveform inactive'\"\n>\n @for (item of effectiveBars(); track $index) {\n <div\n class=\"waveform-bar\"\n [style.width.px]=\"barWidth()\"\n [style.height.px]=\"barHeight(barValue($index))\"\n [style.border-radius.px]=\"barRadius()\"\n [style.background-color]=\"barColor($index)\"\n ></div>\n }\n</div>\n",
|
|
246
|
+
styles: [".waveform{display:inline-flex;align-items:center}.waveform-bar{flex-shrink:0;transition:height 80ms ease-out}\n"]
|
|
247
|
+
}]
|
|
248
|
+
}],
|
|
249
|
+
ctorParameters: () => [],
|
|
250
|
+
propDecorators: {
|
|
251
|
+
width: [{
|
|
252
|
+
type: i0.Input,
|
|
253
|
+
args: [{
|
|
254
|
+
isSignal: true,
|
|
255
|
+
alias: "width",
|
|
256
|
+
required: false
|
|
257
|
+
}]
|
|
258
|
+
}],
|
|
259
|
+
height: [{
|
|
260
|
+
type: i0.Input,
|
|
261
|
+
args: [{
|
|
262
|
+
isSignal: true,
|
|
263
|
+
alias: "height",
|
|
264
|
+
required: false
|
|
265
|
+
}]
|
|
266
|
+
}],
|
|
267
|
+
gap: [{
|
|
268
|
+
type: i0.Input,
|
|
269
|
+
args: [{
|
|
270
|
+
isSignal: true,
|
|
271
|
+
alias: "gap",
|
|
272
|
+
required: false
|
|
273
|
+
}]
|
|
274
|
+
}],
|
|
275
|
+
minScale: [{
|
|
276
|
+
type: i0.Input,
|
|
277
|
+
args: [{
|
|
278
|
+
isSignal: true,
|
|
279
|
+
alias: "minScale",
|
|
280
|
+
required: false
|
|
281
|
+
}]
|
|
282
|
+
}],
|
|
283
|
+
smoothing: [{
|
|
284
|
+
type: i0.Input,
|
|
285
|
+
args: [{
|
|
286
|
+
isSignal: true,
|
|
287
|
+
alias: "smoothing",
|
|
288
|
+
required: false
|
|
289
|
+
}]
|
|
290
|
+
}],
|
|
291
|
+
bars: [{
|
|
292
|
+
type: i0.Input,
|
|
293
|
+
args: [{
|
|
294
|
+
isSignal: true,
|
|
295
|
+
alias: "bars",
|
|
296
|
+
required: false
|
|
297
|
+
}]
|
|
298
|
+
}],
|
|
299
|
+
active: [{
|
|
300
|
+
type: i0.Input,
|
|
301
|
+
args: [{
|
|
302
|
+
isSignal: true,
|
|
303
|
+
alias: "active",
|
|
304
|
+
required: false
|
|
305
|
+
}]
|
|
306
|
+
}]
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
var AudioWaveformModule = class AudioWaveformModule {
|
|
310
|
+
static {
|
|
311
|
+
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
312
|
+
minVersion: "12.0.0",
|
|
313
|
+
version: "22.2.4",
|
|
314
|
+
ngImport: i0,
|
|
315
|
+
type: AudioWaveformModule,
|
|
316
|
+
deps: [],
|
|
317
|
+
target: i0.ɵɵFactoryTarget.NgModule
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
static {
|
|
321
|
+
this.ɵmod = i0.ɵɵngDeclareNgModule({
|
|
322
|
+
minVersion: "14.0.0",
|
|
323
|
+
version: "22.2.4",
|
|
324
|
+
ngImport: i0,
|
|
325
|
+
type: AudioWaveformModule,
|
|
326
|
+
imports: [AudioWaveformComponent],
|
|
327
|
+
exports: [AudioWaveformComponent]
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
static {
|
|
331
|
+
this.ɵinj = i0.ɵɵngDeclareInjector({
|
|
332
|
+
minVersion: "12.0.0",
|
|
333
|
+
version: "22.2.4",
|
|
334
|
+
ngImport: i0,
|
|
335
|
+
type: AudioWaveformModule
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
340
|
+
minVersion: "12.0.0",
|
|
341
|
+
version: "22.2.4",
|
|
342
|
+
ngImport: i0,
|
|
343
|
+
type: AudioWaveformModule,
|
|
344
|
+
decorators: [{
|
|
345
|
+
type: NgModule,
|
|
346
|
+
args: [{
|
|
347
|
+
imports: [AudioWaveformComponent],
|
|
348
|
+
exports: [AudioWaveformComponent]
|
|
349
|
+
}]
|
|
350
|
+
}]
|
|
351
|
+
});
|
|
194
352
|
export { AUDIO_WAVEFORM_BAR_COLORS, AudioWaveformComponent, AudioWaveformModule, AudioWaveformService, DEFAULT_AUDIO_WAVEFORM_CONFIG };
|
|
195
|
-
|
|
353
|
+
|
|
354
|
+
//# sourceMappingURL=testgorilla-tgo-ui-components-audio-waveform.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testgorilla-tgo-ui-components-audio-waveform.mjs","sources":["../../../components/audio-waveform/audio-waveform.model.ts","../../../components/audio-waveform/audio-waveform.service.ts","../../../components/audio-waveform/audio-waveform.component.ts","../../../components/audio-waveform/audio-waveform.component.html","../../../components/audio-waveform/audio-waveform.module.ts","../../../components/audio-waveform/testgorilla-tgo-ui-components-audio-waveform.ts"],"sourcesContent":["export interface AudioWaveformConfig {\n barCount: number;\n smoothing: number;\n fftSize: number;\n}\n\nexport const DEFAULT_AUDIO_WAVEFORM_CONFIG: AudioWaveformConfig = {\n barCount: 5,\n smoothing: 0.55,\n fftSize: 256,\n};\n\n/** Per-bar colors for the 5-bar waveform (left to right) */\nexport const AUDIO_WAVEFORM_BAR_COLORS: string[] = ['#C065D0', '#A86FDA', '#9078E3', '#7882EC', '#6889F3'];\n","import { Injectable, OnDestroy, signal } from '@angular/core';\n\nimport { DEFAULT_AUDIO_WAVEFORM_CONFIG } from './audio-waveform.model';\n\n@Injectable({ providedIn: 'root' })\nexport class AudioWaveformService implements OnDestroy {\n private ctx: AudioContext | null = null;\n private analyser: AnalyserNode | null = null;\n private source: MediaStreamAudioSourceNode | null = null;\n private stream: MediaStream | null = null;\n private raf: number | null = null;\n\n readonly bars = signal<number[]>([]);\n readonly active = signal<boolean>(false);\n\n async start(\n barCount = DEFAULT_AUDIO_WAVEFORM_CONFIG.barCount,\n smoothing = DEFAULT_AUDIO_WAVEFORM_CONFIG.smoothing\n ): Promise<void> {\n if (this.active()) {\n return;\n }\n\n try {\n this.stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n this.ctx = new AudioContext();\n this.analyser = this.ctx.createAnalyser();\n this.analyser.fftSize = DEFAULT_AUDIO_WAVEFORM_CONFIG.fftSize;\n this.analyser.smoothingTimeConstant = smoothing;\n this.source = this.ctx.createMediaStreamSource(this.stream);\n this.source.connect(this.analyser);\n this.active.set(true);\n\n const data = new Uint8Array(this.analyser.frequencyBinCount);\n // Focus on the lower ~60% of bins where speech energy concentrates\n const speechBinCount = Math.max(1, Math.floor(data.length * 0.6));\n\n const analyserRef = this.analyser;\n const tick = (): void => {\n analyserRef.getByteFrequencyData(data);\n\n // RMS (root mean square) of speech-range bins — represents perceived loudness,\n // unlike peak which saturates on any single spike\n let sumSq = 0;\n for (let i = 0; i < speechBinCount; i++) {\n const v = data[i] / 255;\n sumSq += v * v;\n }\n const rms = Math.sqrt(sumSq / speechBinCount);\n\n // Power curve keeps quiet→loud variation visible. Gain multiplier ensures\n // loud speech reaches full bar height. Clamped to [0, 1].\n const amplitude = Math.min(1, Math.pow(rms, 0.55) * 1.5);\n\n const values = this.createSymmetricBars(barCount, amplitude);\n this.bars.set(values);\n this.raf = requestAnimationFrame(tick);\n };\n tick();\n } catch (e) {\n console.error('[AudioWaveform] Mic access denied:', e);\n }\n }\n\n stop(): void {\n if (this.raf !== null) {\n cancelAnimationFrame(this.raf);\n }\n this.source?.disconnect();\n this.analyser?.disconnect();\n this.stream?.getTracks().forEach(t => t.stop());\n if (this.ctx && this.ctx.state !== 'closed') {\n void this.ctx.close();\n }\n this.ctx = null;\n this.analyser = null;\n this.source = null;\n this.stream = null;\n this.raf = null;\n this.active.set(false);\n this.bars.set([]);\n }\n\n ngOnDestroy(): void {\n this.stop();\n }\n\n /**\n * Creates a symmetric bar distribution where middle bars are highest.\n * Uses a bell-curve-like pattern centered at the middle bar.\n */\n private createSymmetricBars(barCount: number, amplitude: number): number[] {\n const center = (barCount - 1) / 2;\n const maxDistance = center;\n\n return Array.from({ length: barCount }, (_, i) => {\n const distance = Math.abs(i - center);\n // Weight decreases as distance from center increases\n // Using a quadratic falloff for smoother appearance\n const weight = 1 - (distance / maxDistance) * 0.65;\n return amplitude * weight;\n });\n }\n}\n","import { Component, ChangeDetectionStrategy, OnDestroy, computed, effect, inject, input } from '@angular/core';\n\nimport { DEFAULT_AUDIO_WAVEFORM_CONFIG, AUDIO_WAVEFORM_BAR_COLORS } from './audio-waveform.model';\nimport { AudioWaveformService } from './audio-waveform.service';\n\n@Component({\n selector: 'ui-audio-waveform',\n templateUrl: './audio-waveform.component.html',\n styleUrls: ['./audio-waveform.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [],\n})\nexport class AudioWaveformComponent implements OnDestroy {\n private readonly waveformService = inject(AudioWaveformService);\n\n readonly barCount = DEFAULT_AUDIO_WAVEFORM_CONFIG.barCount;\n readonly width = input<number>(40);\n readonly height = input<number>(22);\n readonly gap = input<number>(3);\n readonly minScale = input<number>(0.08);\n readonly smoothing = input<number>(DEFAULT_AUDIO_WAVEFORM_CONFIG.smoothing);\n\n /**\n * When provided, the component operates in controlled mode —\n * it renders externally-supplied bar values instead of managing the mic itself.\n */\n readonly bars = input<number[] | null>(null);\n\n /** Mirror of `bars` for controlled mode; required when `bars` is set. */\n readonly active = input<boolean | null>(null);\n\n readonly controlled = computed(() => this.active() !== null);\n\n readonly effectiveBars = computed(() => {\n if (this.controlled()) {\n return this.bars() ?? [];\n }\n return this.waveformService.bars();\n });\n\n readonly effectiveActive = computed(() => {\n if (this.controlled()) {\n return this.active() ?? false;\n }\n return this.waveformService.active();\n });\n\n readonly barWidth = computed(() => {\n const count = this.barCount;\n return (this.width() - this.gap() * (count - 1)) / count;\n });\n\n constructor() {\n effect(() => {\n if (!this.controlled()) {\n void this.waveformService.start(this.barCount, this.smoothing());\n }\n });\n }\n\n barHeight(value: number): number {\n const h = this.height();\n const min = this.minScale();\n return h * (min + value * (1 - min));\n }\n\n barRadius(): number {\n return this.barWidth() / 2;\n }\n\n barValue(index: number): number {\n const bars = this.effectiveBars();\n return bars[index] ?? 0;\n }\n\n barColor(index: number): string {\n return AUDIO_WAVEFORM_BAR_COLORS[index] ?? AUDIO_WAVEFORM_BAR_COLORS[0];\n }\n\n /** Returns the x-offset for a bar so the shared CSS gradient aligns correctly. */\n barOffset(index: number): number {\n return (this.barWidth() + this.gap()) * index;\n }\n\n ngOnDestroy(): void {\n if (!this.controlled()) {\n this.waveformService.stop();\n }\n }\n}\n","<div\n class=\"waveform\"\n [style.width.px]=\"width()\"\n [style.height.px]=\"height()\"\n [style.gap.px]=\"gap()\"\n role=\"img\"\n [attr.aria-label]=\"effectiveActive() ? 'Audio waveform active' : 'Audio waveform inactive'\"\n>\n @for (item of effectiveBars(); track $index) {\n <div\n class=\"waveform-bar\"\n [style.width.px]=\"barWidth()\"\n [style.height.px]=\"barHeight(barValue($index))\"\n [style.border-radius.px]=\"barRadius()\"\n [style.background-color]=\"barColor($index)\"\n ></div>\n }\n</div>\n","import { NgModule } from '@angular/core';\n\nimport { AudioWaveformComponent } from './audio-waveform.component';\n\n@NgModule({\n imports: [AudioWaveformComponent],\n exports: [AudioWaveformComponent],\n})\nexport class AudioWaveformModule {}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"testgorilla-tgo-ui-components-audio-waveform.mjs","names":[],"sources":["../../../components/audio-waveform/audio-waveform.model.ts","../../../components/audio-waveform/audio-waveform.service.ts","../../../components/audio-waveform/audio-waveform.component.ts","../../../components/audio-waveform/audio-waveform.component.html","../../../components/audio-waveform/audio-waveform.module.ts"],"sourcesContent":["export interface AudioWaveformConfig {\n barCount: number;\n smoothing: number;\n fftSize: number;\n}\n\nexport const DEFAULT_AUDIO_WAVEFORM_CONFIG: AudioWaveformConfig = {\n barCount: 5,\n smoothing: 0.55,\n fftSize: 256,\n};\n\n/** Per-bar colors for the 5-bar waveform (left to right) */\nexport const AUDIO_WAVEFORM_BAR_COLORS: string[] = ['#C065D0', '#A86FDA', '#9078E3', '#7882EC', '#6889F3'];\n","import { Injectable, OnDestroy, signal } from '@angular/core';\n\nimport { DEFAULT_AUDIO_WAVEFORM_CONFIG } from './audio-waveform.model';\n\n@Injectable({ providedIn: 'root' })\nexport class AudioWaveformService implements OnDestroy {\n private ctx: AudioContext | null = null;\n private analyser: AnalyserNode | null = null;\n private source: MediaStreamAudioSourceNode | null = null;\n private stream: MediaStream | null = null;\n private raf: number | null = null;\n\n readonly bars = signal<number[]>([]);\n readonly active = signal<boolean>(false);\n\n async start(\n barCount = DEFAULT_AUDIO_WAVEFORM_CONFIG.barCount,\n smoothing = DEFAULT_AUDIO_WAVEFORM_CONFIG.smoothing\n ): Promise<void> {\n if (this.active()) {\n return;\n }\n\n try {\n this.stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n this.ctx = new AudioContext();\n this.analyser = this.ctx.createAnalyser();\n this.analyser.fftSize = DEFAULT_AUDIO_WAVEFORM_CONFIG.fftSize;\n this.analyser.smoothingTimeConstant = smoothing;\n this.source = this.ctx.createMediaStreamSource(this.stream);\n this.source.connect(this.analyser);\n this.active.set(true);\n\n const data = new Uint8Array(this.analyser.frequencyBinCount);\n // Focus on the lower ~60% of bins where speech energy concentrates\n const speechBinCount = Math.max(1, Math.floor(data.length * 0.6));\n\n const analyserRef = this.analyser;\n const tick = (): void => {\n analyserRef.getByteFrequencyData(data);\n\n // RMS (root mean square) of speech-range bins — represents perceived loudness,\n // unlike peak which saturates on any single spike\n let sumSq = 0;\n for (let i = 0; i < speechBinCount; i++) {\n const v = data[i] / 255;\n sumSq += v * v;\n }\n const rms = Math.sqrt(sumSq / speechBinCount);\n\n // Power curve keeps quiet→loud variation visible. Gain multiplier ensures\n // loud speech reaches full bar height. Clamped to [0, 1].\n const amplitude = Math.min(1, Math.pow(rms, 0.55) * 1.5);\n\n const values = this.createSymmetricBars(barCount, amplitude);\n this.bars.set(values);\n this.raf = requestAnimationFrame(tick);\n };\n tick();\n } catch (e) {\n console.error('[AudioWaveform] Mic access denied:', e);\n }\n }\n\n stop(): void {\n if (this.raf !== null) {\n cancelAnimationFrame(this.raf);\n }\n this.source?.disconnect();\n this.analyser?.disconnect();\n this.stream?.getTracks().forEach(t => t.stop());\n if (this.ctx && this.ctx.state !== 'closed') {\n void this.ctx.close();\n }\n this.ctx = null;\n this.analyser = null;\n this.source = null;\n this.stream = null;\n this.raf = null;\n this.active.set(false);\n this.bars.set([]);\n }\n\n ngOnDestroy(): void {\n this.stop();\n }\n\n /**\n * Creates a symmetric bar distribution where middle bars are highest.\n * Uses a bell-curve-like pattern centered at the middle bar.\n */\n private createSymmetricBars(barCount: number, amplitude: number): number[] {\n const center = (barCount - 1) / 2;\n const maxDistance = center;\n\n return Array.from({ length: barCount }, (_, i) => {\n const distance = Math.abs(i - center);\n // Weight decreases as distance from center increases\n // Using a quadratic falloff for smoother appearance\n const weight = 1 - (distance / maxDistance) * 0.65;\n return amplitude * weight;\n });\n }\n}\n","import { Component, ChangeDetectionStrategy, OnDestroy, computed, effect, inject, input } from '@angular/core';\n\nimport { DEFAULT_AUDIO_WAVEFORM_CONFIG, AUDIO_WAVEFORM_BAR_COLORS } from './audio-waveform.model';\nimport { AudioWaveformService } from './audio-waveform.service';\n\n@Component({\n selector: 'ui-audio-waveform',\n templateUrl: './audio-waveform.component.html',\n styleUrls: ['./audio-waveform.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [],\n})\nexport class AudioWaveformComponent implements OnDestroy {\n private readonly waveformService = inject(AudioWaveformService);\n\n readonly barCount = DEFAULT_AUDIO_WAVEFORM_CONFIG.barCount;\n readonly width = input<number>(40);\n readonly height = input<number>(22);\n readonly gap = input<number>(3);\n readonly minScale = input<number>(0.08);\n readonly smoothing = input<number>(DEFAULT_AUDIO_WAVEFORM_CONFIG.smoothing);\n\n /**\n * When provided, the component operates in controlled mode —\n * it renders externally-supplied bar values instead of managing the mic itself.\n */\n readonly bars = input<number[] | null>(null);\n\n /** Mirror of `bars` for controlled mode; required when `bars` is set. */\n readonly active = input<boolean | null>(null);\n\n readonly controlled = computed(() => this.active() !== null);\n\n readonly effectiveBars = computed(() => {\n if (this.controlled()) {\n return this.bars() ?? [];\n }\n return this.waveformService.bars();\n });\n\n readonly effectiveActive = computed(() => {\n if (this.controlled()) {\n return this.active() ?? false;\n }\n return this.waveformService.active();\n });\n\n readonly barWidth = computed(() => {\n const count = this.barCount;\n return (this.width() - this.gap() * (count - 1)) / count;\n });\n\n constructor() {\n effect(() => {\n if (!this.controlled()) {\n void this.waveformService.start(this.barCount, this.smoothing());\n }\n });\n }\n\n barHeight(value: number): number {\n const h = this.height();\n const min = this.minScale();\n return h * (min + value * (1 - min));\n }\n\n barRadius(): number {\n return this.barWidth() / 2;\n }\n\n barValue(index: number): number {\n const bars = this.effectiveBars();\n return bars[index] ?? 0;\n }\n\n barColor(index: number): string {\n return AUDIO_WAVEFORM_BAR_COLORS[index] ?? AUDIO_WAVEFORM_BAR_COLORS[0];\n }\n\n /** Returns the x-offset for a bar so the shared CSS gradient aligns correctly. */\n barOffset(index: number): number {\n return (this.barWidth() + this.gap()) * index;\n }\n\n ngOnDestroy(): void {\n if (!this.controlled()) {\n this.waveformService.stop();\n }\n }\n}\n","<div\n class=\"waveform\"\n [style.width.px]=\"width()\"\n [style.height.px]=\"height()\"\n [style.gap.px]=\"gap()\"\n role=\"img\"\n [attr.aria-label]=\"effectiveActive() ? 'Audio waveform active' : 'Audio waveform inactive'\"\n>\n @for (item of effectiveBars(); track $index) {\n <div\n class=\"waveform-bar\"\n [style.width.px]=\"barWidth()\"\n [style.height.px]=\"barHeight(barValue($index))\"\n [style.border-radius.px]=\"barRadius()\"\n [style.background-color]=\"barColor($index)\"\n ></div>\n }\n</div>\n","import { NgModule } from '@angular/core';\n\nimport { AudioWaveformComponent } from './audio-waveform.component';\n\n@NgModule({\n imports: [AudioWaveformComponent],\n exports: [AudioWaveformComponent],\n})\nexport class AudioWaveformModule {}\n"],"mappings":"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"}
|