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