wally-ui 1.14.1 → 1.15.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.
- package/package.json +1 -1
- package/playground/showcase/public/sitemap.xml +15 -0
- package/playground/showcase/src/app/app.routes.server.ts +4 -0
- package/playground/showcase/src/app/components/ai/ai-composer/ai-composer.html +11 -2
- package/playground/showcase/src/app/components/ai/ai-composer/ai-composer.ts +13 -3
- package/playground/showcase/src/app/components/audio-waveform/audio-waveform.css +0 -0
- package/playground/showcase/src/app/components/audio-waveform/audio-waveform.html +41 -0
- package/playground/showcase/src/app/components/audio-waveform/audio-waveform.service.spec.ts +16 -0
- package/playground/showcase/src/app/components/audio-waveform/audio-waveform.service.ts +175 -0
- package/playground/showcase/src/app/components/audio-waveform/audio-waveform.spec.ts +23 -0
- package/playground/showcase/src/app/components/audio-waveform/audio-waveform.ts +64 -0
- package/playground/showcase/src/app/pages/documentation/components/audio-waveform-docs/audio-waveform-docs.css +1 -0
- package/playground/showcase/src/app/pages/documentation/components/audio-waveform-docs/audio-waveform-docs.examples.ts +146 -0
- package/playground/showcase/src/app/pages/documentation/components/audio-waveform-docs/audio-waveform-docs.html +576 -0
- package/playground/showcase/src/app/pages/documentation/components/audio-waveform-docs/audio-waveform-docs.ts +124 -0
- package/playground/showcase/src/app/pages/documentation/components/components.html +27 -0
- package/playground/showcase/src/app/pages/documentation/components/components.routes.ts +4 -0
- package/playground/showcase/src/app/pages/home/home.html +1 -1
|
@@ -0,0 +1,576 @@
|
|
|
1
|
+
<main class="min-h-dvh bg-white dark:bg-[#0a0a0a] font-mono" role="main">
|
|
2
|
+
<div class="max-w-4xl mx-auto px-4 py-8 sm:px-6 md:px-8">
|
|
3
|
+
<!-- Breadcrumb Navigation -->
|
|
4
|
+
<nav class="mb-8" aria-label="Breadcrumb navigation">
|
|
5
|
+
<wally-breadcrumb [items]="breadcrumbItems"></wally-breadcrumb>
|
|
6
|
+
</nav>
|
|
7
|
+
|
|
8
|
+
<!-- Header -->
|
|
9
|
+
<header class="mb-12" role="banner">
|
|
10
|
+
<h1 id="page-title" class="text-3xl sm:text-4xl font-bold text-[#0a0a0a] dark:text-white mb-4 uppercase">
|
|
11
|
+
<span aria-hidden="true">>_ </span>Audio Waveform
|
|
12
|
+
</h1>
|
|
13
|
+
<p class="text-sm sm:text-base text-neutral-700 dark:text-neutral-300 border-l-2 border-neutral-300 dark:border-neutral-700 pl-4 leading-relaxed">
|
|
14
|
+
Real-time audio waveform visualizer component with microphone input, FFT frequency analysis, and automatic audio recording. Features responsive bar count, smooth 60fps animations, and built-in recording functionality with download support.
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
<!-- AI Assistant Links -->
|
|
18
|
+
<div class="flex flex-wrap gap-0 mt-6 border-2 border-neutral-300 dark:border-neutral-700" role="list"
|
|
19
|
+
aria-label="Open documentation in AI coding assistants">
|
|
20
|
+
<a [href]="claudeUrl" target="_blank"
|
|
21
|
+
class="group flex-1 min-w-[200px] flex items-center justify-center gap-2 px-4 py-3 bg-white dark:bg-[#0a0a0a] border-b-2 sm:border-b-0 sm:border-r-2 border-neutral-300 dark:border-neutral-700 hover:bg-[#0a0a0a] dark:hover:bg-white transition-all duration-150 cursor-pointer"
|
|
22
|
+
rel="noopener noreferrer" role="listitem"
|
|
23
|
+
aria-label="Open Audio Waveform component documentation in Claude AI assistant (opens in new tab)">
|
|
24
|
+
<svg xmlns="http://www.w3.org/2000/svg"
|
|
25
|
+
class="size-5 text-[#0a0a0a] dark:text-neutral-400 group-hover:text-white dark:group-hover:text-[#0a0a0a] transition-colors duration-150"
|
|
26
|
+
viewBox="0 0 24 24" aria-hidden="true">
|
|
27
|
+
<path fill="currentColor"
|
|
28
|
+
d="m4.714 15.956l4.718-2.648l.079-.23l-.08-.128h-.23l-.79-.048l-2.695-.073l-2.337-.097l-2.265-.122l-.57-.121l-.535-.704l.055-.353l.48-.321l.685.06l1.518.104l2.277.157l1.651.098l2.447.255h.389l.054-.158l-.133-.097l-.103-.098l-2.356-1.596l-2.55-1.688l-1.336-.972l-.722-.491L2 6.223l-.158-1.008l.656-.722l.88.06l.224.061l.893.686l1.906 1.476l2.49 1.833l.364.304l.146-.104l.018-.072l-.164-.274l-1.354-2.446l-1.445-2.49l-.644-1.032l-.17-.619a3 3 0 0 1-.103-.729L6.287.133L6.7 0l.995.134l.42.364l.619 1.415L9.735 4.14l1.555 3.03l.455.898l.243.832l.09.255h.159V9.01l.127-1.706l.237-2.095l.23-2.695l.08-.76l.376-.91l.747-.492l.583.28l.48.685l-.067.444l-.286 1.851l-.558 2.903l-.365 1.942h.213l.243-.242l.983-1.306l1.652-2.064l.728-.82l.85-.904l.547-.431h1.032l.759 1.129l-.34 1.166l-1.063 1.347l-.88 1.142l-1.263 1.7l-.79 1.36l.074.11l.188-.02l2.853-.606l1.542-.28l1.84-.315l.832.388l.09.395l-.327.807l-1.967.486l-2.307.462l-3.436.813l-.043.03l.049.061l1.548.146l.662.036h1.62l3.018.225l.79.522l.473.638l-.08.485l-1.213.62l-1.64-.389l-3.825-.91l-1.31-.329h-.183v.11l1.093 1.068l2.003 1.81l2.508 2.33l.127.578l-.321.455l-.34-.049l-2.204-1.657l-.85-.747l-1.925-1.62h-.127v.17l.443.649l2.343 3.521l.122 1.08l-.17.353l-.607.213l-.668-.122l-1.372-1.924l-1.415-2.168l-1.141-1.943l-.14.08l-.674 7.254l-.316.37l-.728.28l-.607-.461l-.322-.747l.322-1.476l.388-1.924l.316-1.53l.285-1.9l.17-.632l-.012-.042l-.14.018l-1.432 1.967l-2.18 2.945l-1.724 1.845l-.413.164l-.716-.37l.066-.662l.401-.589l2.386-3.036l1.439-1.882l.929-1.086l-.006-.158h-.055L4.138 18.56l-1.13.146l-.485-.456l.06-.746l.231-.243l1.907-1.312Z" />
|
|
29
|
+
</svg>
|
|
30
|
+
<span
|
|
31
|
+
class="text-xs sm:text-sm font-medium text-[#0a0a0a] dark:text-neutral-400 group-hover:text-white dark:group-hover:text-[#0a0a0a] transition-colors duration-150">
|
|
32
|
+
Open in Claude
|
|
33
|
+
</span>
|
|
34
|
+
</a>
|
|
35
|
+
|
|
36
|
+
<a [href]="chatGptUrl" target="_blank"
|
|
37
|
+
class="group flex-1 min-w-[200px] flex items-center justify-center gap-2 px-4 py-3 bg-white dark:bg-[#0a0a0a] hover:bg-[#0a0a0a] dark:hover:bg-white transition-all duration-150 cursor-pointer"
|
|
38
|
+
rel="noopener noreferrer" role="listitem"
|
|
39
|
+
aria-label="Open Audio Waveform component documentation in ChatGPT AI assistant (opens in new tab)">
|
|
40
|
+
<svg xmlns="http://www.w3.org/2000/svg"
|
|
41
|
+
class="size-5 text-[#0a0a0a] dark:text-neutral-400 group-hover:text-white dark:group-hover:text-[#0a0a0a] transition-colors duration-150"
|
|
42
|
+
viewBox="0 0 48 48" aria-hidden="true">
|
|
43
|
+
<path fill="none" stroke="currentColor" stroke-width="3" stroke-linejoin="round"
|
|
44
|
+
d="M18.38 27.94v-14.4l11.19-6.46c6.2-3.58 17.3 5.25 12.64 13.33" />
|
|
45
|
+
<path fill="none" stroke="currentColor" stroke-width="3" stroke-linejoin="round"
|
|
46
|
+
d="m18.38 20.94l12.47-7.2l11.19 6.46c6.2 3.58 4.1 17.61-5.23 17.61" />
|
|
47
|
+
<path fill="none" stroke="currentColor" stroke-width="3" stroke-linejoin="round"
|
|
48
|
+
d="m24.44 17.44l12.47 7.2v12.93c0 7.16-13.2 12.36-17.86 4.28" />
|
|
49
|
+
<path fill="none" stroke="currentColor" stroke-width="3" stroke-linejoin="round"
|
|
50
|
+
d="M30.5 21.2v14.14L19.31 41.8c-6.2 3.58-17.3-5.25-12.64-13.33" />
|
|
51
|
+
<path fill="none" stroke="currentColor" stroke-width="3" stroke-linejoin="round"
|
|
52
|
+
d="m30.5 27.94l-12.47 7.2l-11.19-6.46c-6.21-3.59-4.11-17.61 5.22-17.61" />
|
|
53
|
+
<path fill="none" stroke="currentColor" stroke-width="3" stroke-linejoin="round"
|
|
54
|
+
d="m24.44 31.44l-12.47-7.2V11.31c0-7.16 13.2-12.36 17.86-4.28" />
|
|
55
|
+
</svg>
|
|
56
|
+
<span
|
|
57
|
+
class="text-xs sm:text-sm font-medium text-[#0a0a0a] dark:text-neutral-400 group-hover:text-white dark:group-hover:text-[#0a0a0a] transition-colors duration-150">
|
|
58
|
+
Open in ChatGPT
|
|
59
|
+
</span>
|
|
60
|
+
</a>
|
|
61
|
+
</div>
|
|
62
|
+
</header>
|
|
63
|
+
|
|
64
|
+
<!-- Installation -->
|
|
65
|
+
<section id="installation" class="mb-12" aria-labelledby="installation-heading">
|
|
66
|
+
<h2 id="installation-heading" class="text-[10px] sm:text-xs text-neutral-500 dark:text-neutral-500 uppercase tracking-wider mb-4">
|
|
67
|
+
[ Installation ]
|
|
68
|
+
</h2>
|
|
69
|
+
<div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3">
|
|
70
|
+
<pre><code [innerHTML]="installationCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
|
|
71
|
+
</div>
|
|
72
|
+
</section>
|
|
73
|
+
|
|
74
|
+
<!-- Preview -->
|
|
75
|
+
<section id="preview" class="mb-12" aria-labelledby="preview-heading">
|
|
76
|
+
<h2 id="preview-heading" class="text-[10px] sm:text-xs text-neutral-500 dark:text-neutral-500 uppercase tracking-wider mb-4">
|
|
77
|
+
[ Live Preview ]
|
|
78
|
+
</h2>
|
|
79
|
+
<div class="p-8 border-2 border-neutral-300 dark:border-neutral-700 bg-white dark:bg-[#0a0a0a]" role="img"
|
|
80
|
+
aria-label="Live interactive audio waveform visualizer demo">
|
|
81
|
+
<div class="flex flex-col gap-4">
|
|
82
|
+
<!-- Waveform Component -->
|
|
83
|
+
@if (isRecording()) {
|
|
84
|
+
<wally-audio-waveform
|
|
85
|
+
[isStartRecording]="isRecording()"
|
|
86
|
+
[isStopRecording]="!isRecording()"
|
|
87
|
+
[showTimer]="true">
|
|
88
|
+
</wally-audio-waveform>
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
<!-- Controls -->
|
|
92
|
+
<div class="flex gap-2 justify-center">
|
|
93
|
+
@if (!isRecording()) {
|
|
94
|
+
<wally-button
|
|
95
|
+
variant="destructive"
|
|
96
|
+
(buttonClick)="startRecording()"
|
|
97
|
+
ariaLabel="Start audio recording">
|
|
98
|
+
Start Recording
|
|
99
|
+
</wally-button>
|
|
100
|
+
} @else {
|
|
101
|
+
<wally-button
|
|
102
|
+
variant="secondary"
|
|
103
|
+
(buttonClick)="stopRecording()"
|
|
104
|
+
ariaLabel="Stop audio recording">
|
|
105
|
+
Stop Recording
|
|
106
|
+
</wally-button>
|
|
107
|
+
}
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
<aside class="mt-4 p-4 bg-amber-50 dark:bg-amber-900/10 border-2 border-amber-400 dark:border-amber-700" role="note">
|
|
112
|
+
<p class="text-xs sm:text-sm text-amber-700 dark:text-amber-400 leading-relaxed">
|
|
113
|
+
<strong>Tip:</strong> Click "Start Recording" to see the real-time waveform visualization. Allow microphone access when prompted. The component automatically adapts bar count for mobile (30 bars) and desktop (65 bars).
|
|
114
|
+
</p>
|
|
115
|
+
</aside>
|
|
116
|
+
</section>
|
|
117
|
+
|
|
118
|
+
<!-- Import -->
|
|
119
|
+
<section id="import" class="mb-12" aria-labelledby="import-heading">
|
|
120
|
+
<h2 id="import-heading" class="text-[10px] sm:text-xs text-neutral-500 dark:text-neutral-500 uppercase tracking-wider mb-4">
|
|
121
|
+
[ Import ]
|
|
122
|
+
</h2>
|
|
123
|
+
<div class="space-y-4">
|
|
124
|
+
<div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3">
|
|
125
|
+
<pre><code [innerHTML]="importCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
<div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3">
|
|
129
|
+
<pre><code [innerHTML]="componentImportCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
</section>
|
|
133
|
+
|
|
134
|
+
<!-- Basic Usage -->
|
|
135
|
+
<section id="basic-usage" class="mb-12" aria-labelledby="basic-usage-heading">
|
|
136
|
+
<h2 id="basic-usage-heading" class="text-[10px] sm:text-xs text-neutral-500 dark:text-neutral-500 uppercase tracking-wider mb-4">
|
|
137
|
+
[ Basic Usage ]
|
|
138
|
+
</h2>
|
|
139
|
+
|
|
140
|
+
<!-- Minimal Example -->
|
|
141
|
+
<article class="mb-8" aria-labelledby="minimal-example-heading">
|
|
142
|
+
<h3 id="minimal-example-heading" class="text-sm sm:text-base font-bold text-[#0a0a0a] dark:text-white mb-3 uppercase">
|
|
143
|
+
<span aria-hidden="true">> </span>Minimal Example
|
|
144
|
+
</h3>
|
|
145
|
+
<div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3 mb-4">
|
|
146
|
+
<pre><code [innerHTML]="basicUsageCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
|
|
147
|
+
</div>
|
|
148
|
+
</article>
|
|
149
|
+
|
|
150
|
+
<!-- With Timer -->
|
|
151
|
+
<article class="mb-8" aria-labelledby="with-timer-heading">
|
|
152
|
+
<h3 id="with-timer-heading" class="text-sm sm:text-base font-bold text-[#0a0a0a] dark:text-white mb-3 uppercase">
|
|
153
|
+
<span aria-hidden="true">> </span>With Recording Timer
|
|
154
|
+
</h3>
|
|
155
|
+
<div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3 mb-4">
|
|
156
|
+
<pre><code [innerHTML]="withTimerCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
|
|
157
|
+
</div>
|
|
158
|
+
</article>
|
|
159
|
+
|
|
160
|
+
<!-- Complete Example -->
|
|
161
|
+
<article class="mb-8" aria-labelledby="complete-example-heading">
|
|
162
|
+
<h3 id="complete-example-heading" class="text-sm sm:text-base font-bold text-[#0a0a0a] dark:text-white mb-3 uppercase">
|
|
163
|
+
<span aria-hidden="true">> </span>Complete Example with Controls
|
|
164
|
+
</h3>
|
|
165
|
+
<div class="space-y-4">
|
|
166
|
+
<div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3">
|
|
167
|
+
<pre><code [innerHTML]="completeExampleCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
|
|
168
|
+
</div>
|
|
169
|
+
<div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3">
|
|
170
|
+
<pre><code [innerHTML]="completeExampleTsCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
</article>
|
|
174
|
+
</section>
|
|
175
|
+
|
|
176
|
+
<!-- Visual Examples -->
|
|
177
|
+
<section id="visual-examples" class="mb-12" aria-labelledby="visual-examples-heading">
|
|
178
|
+
<h2 id="visual-examples-heading" class="text-[10px] sm:text-xs text-neutral-500 dark:text-neutral-500 uppercase tracking-wider mb-4">
|
|
179
|
+
[ Visual Examples ]
|
|
180
|
+
</h2>
|
|
181
|
+
|
|
182
|
+
<!-- Without Timer -->
|
|
183
|
+
<article class="mb-8" aria-labelledby="without-timer-example-heading">
|
|
184
|
+
<h3 id="without-timer-example-heading" class="text-sm sm:text-base font-bold text-[#0a0a0a] dark:text-white mb-3 uppercase">
|
|
185
|
+
<span aria-hidden="true">> </span>Without Timer
|
|
186
|
+
</h3>
|
|
187
|
+
<p class="text-xs sm:text-sm text-neutral-600 dark:text-neutral-400 mb-4 leading-relaxed">
|
|
188
|
+
Basic waveform visualization without recording timer display.
|
|
189
|
+
</p>
|
|
190
|
+
<div class="p-8 border-2 border-neutral-300 dark:border-neutral-700 bg-white dark:bg-[#0a0a0a]" role="img"
|
|
191
|
+
aria-label="Audio waveform example without timer">
|
|
192
|
+
<div class="flex flex-col gap-4">
|
|
193
|
+
@if (isRecordingWithoutTimer()) {
|
|
194
|
+
<wally-audio-waveform
|
|
195
|
+
[isStartRecording]="isRecordingWithoutTimer()"
|
|
196
|
+
[isStopRecording]="!isRecordingWithoutTimer()"
|
|
197
|
+
[showTimer]="false">
|
|
198
|
+
</wally-audio-waveform>
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
<div class="flex gap-2 justify-center">
|
|
202
|
+
@if (!isRecordingWithoutTimer()) {
|
|
203
|
+
<wally-button
|
|
204
|
+
variant="destructive"
|
|
205
|
+
(buttonClick)="startRecordingWithoutTimer()"
|
|
206
|
+
ariaLabel="Start recording without timer">
|
|
207
|
+
Start Recording
|
|
208
|
+
</wally-button>
|
|
209
|
+
} @else {
|
|
210
|
+
<wally-button
|
|
211
|
+
variant="secondary"
|
|
212
|
+
(buttonClick)="stopRecordingWithoutTimer()"
|
|
213
|
+
ariaLabel="Stop recording">
|
|
214
|
+
Stop Recording
|
|
215
|
+
</wally-button>
|
|
216
|
+
}
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
219
|
+
</div>
|
|
220
|
+
</article>
|
|
221
|
+
|
|
222
|
+
<!-- With Timer -->
|
|
223
|
+
<article class="mb-8" aria-labelledby="with-timer-example-heading">
|
|
224
|
+
<h3 id="with-timer-example-heading" class="text-sm sm:text-base font-bold text-[#0a0a0a] dark:text-white mb-3 uppercase">
|
|
225
|
+
<span aria-hidden="true">> </span>With Timer
|
|
226
|
+
</h3>
|
|
227
|
+
<p class="text-xs sm:text-sm text-neutral-600 dark:text-neutral-400 mb-4 leading-relaxed">
|
|
228
|
+
Waveform visualization with recording timer in MM:SS format.
|
|
229
|
+
</p>
|
|
230
|
+
<div class="p-8 border-2 border-neutral-300 dark:border-neutral-700 bg-white dark:bg-[#0a0a0a]" role="img"
|
|
231
|
+
aria-label="Audio waveform example with timer">
|
|
232
|
+
<div class="flex flex-col gap-4">
|
|
233
|
+
@if (isRecordingWithTimer()) {
|
|
234
|
+
<wally-audio-waveform
|
|
235
|
+
[isStartRecording]="isRecordingWithTimer()"
|
|
236
|
+
[isStopRecording]="!isRecordingWithTimer()"
|
|
237
|
+
[showTimer]="true">
|
|
238
|
+
</wally-audio-waveform>
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
<div class="flex gap-2 justify-center">
|
|
242
|
+
@if (!isRecordingWithTimer()) {
|
|
243
|
+
<wally-button
|
|
244
|
+
variant="destructive"
|
|
245
|
+
(buttonClick)="startRecordingWithTimer()"
|
|
246
|
+
ariaLabel="Start recording with timer">
|
|
247
|
+
Start Recording
|
|
248
|
+
</wally-button>
|
|
249
|
+
} @else {
|
|
250
|
+
<wally-button
|
|
251
|
+
variant="secondary"
|
|
252
|
+
(buttonClick)="stopRecordingWithTimer()"
|
|
253
|
+
ariaLabel="Stop recording">
|
|
254
|
+
Stop Recording
|
|
255
|
+
</wally-button>
|
|
256
|
+
}
|
|
257
|
+
</div>
|
|
258
|
+
</div>
|
|
259
|
+
</div>
|
|
260
|
+
</article>
|
|
261
|
+
|
|
262
|
+
<!-- Download Example -->
|
|
263
|
+
<article class="mb-8" aria-labelledby="download-example-heading">
|
|
264
|
+
<h3 id="download-example-heading" class="text-sm sm:text-base font-bold text-[#0a0a0a] dark:text-white mb-3 uppercase">
|
|
265
|
+
<span aria-hidden="true">> </span>With Download Functionality
|
|
266
|
+
</h3>
|
|
267
|
+
<p class="text-xs sm:text-sm text-neutral-600 dark:text-neutral-400 mb-4 leading-relaxed">
|
|
268
|
+
Record audio and download the result as a WebM file.
|
|
269
|
+
</p>
|
|
270
|
+
<div class="p-8 border-2 border-neutral-300 dark:border-neutral-700 bg-white dark:bg-[#0a0a0a]" role="img"
|
|
271
|
+
aria-label="Audio waveform with download functionality">
|
|
272
|
+
<div class="flex flex-col gap-4">
|
|
273
|
+
<wally-audio-waveform
|
|
274
|
+
#downloadWaveform
|
|
275
|
+
[isStartRecording]="isRecordingWithDownload()"
|
|
276
|
+
[isStopRecording]="!isRecordingWithDownload()"
|
|
277
|
+
[showTimer]="true">
|
|
278
|
+
</wally-audio-waveform>
|
|
279
|
+
|
|
280
|
+
<div class="flex gap-2 justify-center flex-wrap">
|
|
281
|
+
@if (!isRecordingWithDownload() && !downloadWaveform.audioWaveformService.recordedAudioUrl()) {
|
|
282
|
+
<wally-button
|
|
283
|
+
variant="destructive"
|
|
284
|
+
(buttonClick)="startRecordingWithDownload()"
|
|
285
|
+
ariaLabel="Start recording">
|
|
286
|
+
Start Recording
|
|
287
|
+
</wally-button>
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
@if (isRecordingWithDownload()) {
|
|
291
|
+
<wally-button
|
|
292
|
+
variant="secondary"
|
|
293
|
+
(buttonClick)="stopRecordingWithDownload()"
|
|
294
|
+
ariaLabel="Stop recording">
|
|
295
|
+
Stop Recording
|
|
296
|
+
</wally-button>
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
@if (downloadWaveform.audioWaveformService.recordedAudioUrl()) {
|
|
300
|
+
<wally-button
|
|
301
|
+
variant="primary"
|
|
302
|
+
(buttonClick)="downloadWaveform.audioWaveformService.downloadRecording('my-recording.webm')"
|
|
303
|
+
ariaLabel="Download recording">
|
|
304
|
+
Download Recording
|
|
305
|
+
</wally-button>
|
|
306
|
+
<wally-button
|
|
307
|
+
variant="ghost"
|
|
308
|
+
(buttonClick)="downloadWaveform.audioWaveformService.clearRecording()"
|
|
309
|
+
ariaLabel="Clear recording">
|
|
310
|
+
Clear
|
|
311
|
+
</wally-button>
|
|
312
|
+
}
|
|
313
|
+
</div>
|
|
314
|
+
</div>
|
|
315
|
+
</div>
|
|
316
|
+
</article>
|
|
317
|
+
</section>
|
|
318
|
+
|
|
319
|
+
<!-- Service Integration -->
|
|
320
|
+
<section id="service-integration" class="mb-12" aria-labelledby="service-heading">
|
|
321
|
+
<h2 id="service-heading" class="text-[10px] sm:text-xs text-neutral-500 dark:text-neutral-500 uppercase tracking-wider mb-4">
|
|
322
|
+
[ Service Integration ]
|
|
323
|
+
</h2>
|
|
324
|
+
<p class="text-xs sm:text-sm text-neutral-600 dark:text-neutral-400 mb-6 leading-relaxed">
|
|
325
|
+
Access the AudioWaveformService to download, retrieve, or clear recorded audio.
|
|
326
|
+
</p>
|
|
327
|
+
<div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3">
|
|
328
|
+
<pre><code [innerHTML]="serviceIntegrationCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
|
|
329
|
+
</div>
|
|
330
|
+
</section>
|
|
331
|
+
|
|
332
|
+
<!-- How It Works -->
|
|
333
|
+
<section id="how-it-works" class="mb-12" aria-labelledby="how-it-works-heading">
|
|
334
|
+
<h2 id="how-it-works-heading" class="text-[10px] sm:text-xs text-neutral-500 dark:text-neutral-500 uppercase tracking-wider mb-4">
|
|
335
|
+
[ How It Works ]
|
|
336
|
+
</h2>
|
|
337
|
+
<p class="text-xs sm:text-sm text-neutral-600 dark:text-neutral-400 mb-4 leading-relaxed">
|
|
338
|
+
The component uses the Web Audio API to analyze microphone input in real-time with FFT (Fast Fourier Transform) frequency analysis.
|
|
339
|
+
</p>
|
|
340
|
+
<div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3 mb-6">
|
|
341
|
+
<pre><code [innerHTML]="webAudioConceptsCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
|
|
342
|
+
</div>
|
|
343
|
+
|
|
344
|
+
<article aria-labelledby="technical-flow-heading">
|
|
345
|
+
<h3 id="technical-flow-heading" class="text-sm sm:text-base font-bold text-[#0a0a0a] dark:text-white mb-3 uppercase">
|
|
346
|
+
<span aria-hidden="true">> </span>Technical Flow
|
|
347
|
+
</h3>
|
|
348
|
+
<ol class="space-y-2 text-xs sm:text-sm text-neutral-600 dark:text-neutral-400 leading-relaxed list-decimal list-inside">
|
|
349
|
+
<li><strong class="text-[#0a0a0a] dark:text-white">Microphone Access:</strong> getUserMedia() requests permission</li>
|
|
350
|
+
<li><strong class="text-[#0a0a0a] dark:text-white">Audio Context:</strong> Creates AudioContext for processing</li>
|
|
351
|
+
<li><strong class="text-[#0a0a0a] dark:text-white">Frequency Analysis:</strong> AnalyserNode performs FFT (256 samples → 128 frequencies)</li>
|
|
352
|
+
<li><strong class="text-[#0a0a0a] dark:text-white">Data Extraction:</strong> getByteFrequencyData() returns values 0-255</li>
|
|
353
|
+
<li><strong class="text-[#0a0a0a] dark:text-white">Bar Grouping:</strong> Averages frequencies into 30/65 bars (responsive)</li>
|
|
354
|
+
<li><strong class="text-[#0a0a0a] dark:text-white">Normalization:</strong> Converts to 0-100% for CSS heights</li>
|
|
355
|
+
<li><strong class="text-[#0a0a0a] dark:text-white">Animation Loop:</strong> requestAnimationFrame updates at 60fps</li>
|
|
356
|
+
<li><strong class="text-[#0a0a0a] dark:text-white">Recording:</strong> MediaRecorder saves audio as WebM Blob</li>
|
|
357
|
+
</ol>
|
|
358
|
+
</article>
|
|
359
|
+
</section>
|
|
360
|
+
|
|
361
|
+
<!-- Configuration -->
|
|
362
|
+
<section id="configuration" class="mb-12" aria-labelledby="configuration-heading">
|
|
363
|
+
<h2 id="configuration-heading" class="text-[10px] sm:text-xs text-neutral-500 dark:text-neutral-500 uppercase tracking-wider mb-4">
|
|
364
|
+
[ Configuration ]
|
|
365
|
+
</h2>
|
|
366
|
+
<p class="text-xs sm:text-sm text-neutral-600 dark:text-neutral-400 mb-4 leading-relaxed">
|
|
367
|
+
Service-level configuration (read-only constants for optimal performance).
|
|
368
|
+
</p>
|
|
369
|
+
|
|
370
|
+
<div class="space-y-4">
|
|
371
|
+
<div class="space-y-2">
|
|
372
|
+
<div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3">
|
|
373
|
+
<pre><code [innerHTML]="configFFTSizeCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
|
|
374
|
+
</div>
|
|
375
|
+
<p class="text-xs sm:text-sm text-neutral-600 dark:text-neutral-400 leading-relaxed">FFT size for frequency analysis. Higher values provide more detail but use more CPU.</p>
|
|
376
|
+
</div>
|
|
377
|
+
|
|
378
|
+
<div class="space-y-2">
|
|
379
|
+
<div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3">
|
|
380
|
+
<pre><code [innerHTML]="configBarCountCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
|
|
381
|
+
</div>
|
|
382
|
+
<p class="text-xs sm:text-sm text-neutral-600 dark:text-neutral-400 leading-relaxed">Automatically adjusts based on viewport width for optimal display.</p>
|
|
383
|
+
</div>
|
|
384
|
+
|
|
385
|
+
<div class="space-y-2">
|
|
386
|
+
<div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3">
|
|
387
|
+
<pre><code [innerHTML]="configSmoothingCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
|
|
388
|
+
</div>
|
|
389
|
+
<p class="text-xs sm:text-sm text-neutral-600 dark:text-neutral-400 leading-relaxed">Smoothing constant for animation. 0 = no smoothing (choppy), 1 = max smoothing (laggy).</p>
|
|
390
|
+
</div>
|
|
391
|
+
</div>
|
|
392
|
+
</section>
|
|
393
|
+
|
|
394
|
+
<!-- Properties -->
|
|
395
|
+
<section id="properties" class="mb-12" aria-labelledby="properties-heading">
|
|
396
|
+
<h2 id="properties-heading" class="text-[10px] sm:text-xs text-neutral-500 dark:text-neutral-500 uppercase tracking-wider mb-4">
|
|
397
|
+
[ Properties ]
|
|
398
|
+
</h2>
|
|
399
|
+
|
|
400
|
+
<div class="overflow-x-auto">
|
|
401
|
+
<table class="w-full border-collapse border-2 border-neutral-300 dark:border-neutral-700">
|
|
402
|
+
<thead>
|
|
403
|
+
<tr class="bg-neutral-100 dark:bg-[#1a1a1a]">
|
|
404
|
+
<th class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-left text-xs sm:text-sm font-bold text-[#0a0a0a] dark:text-white">Property</th>
|
|
405
|
+
<th class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-left text-xs sm:text-sm font-bold text-[#0a0a0a] dark:text-white">Type</th>
|
|
406
|
+
<th class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-left text-xs sm:text-sm font-bold text-[#0a0a0a] dark:text-white">Default</th>
|
|
407
|
+
<th class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-left text-xs sm:text-sm font-bold text-[#0a0a0a] dark:text-white">Description</th>
|
|
408
|
+
</tr>
|
|
409
|
+
</thead>
|
|
410
|
+
<tbody>
|
|
411
|
+
<!-- Input Properties -->
|
|
412
|
+
<tr class="bg-blue-50 dark:bg-blue-900/20">
|
|
413
|
+
<td colspan="4" class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-2 text-xs sm:text-sm font-bold text-blue-900 dark:text-blue-100 uppercase">Input Properties</td>
|
|
414
|
+
</tr>
|
|
415
|
+
<tr>
|
|
416
|
+
<td class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-xs sm:text-sm">
|
|
417
|
+
<span class="bg-neutral-100 dark:bg-[#1a1a1a] text-[#0a0a0a] dark:text-white px-2 py-1 text-xs">isStartRecording</span>
|
|
418
|
+
</td>
|
|
419
|
+
<td class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-xs sm:text-sm text-neutral-600 dark:text-neutral-400">
|
|
420
|
+
<span class="text-xs">InputSignal<boolean></span>
|
|
421
|
+
</td>
|
|
422
|
+
<td class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-xs sm:text-sm text-neutral-600 dark:text-neutral-400">
|
|
423
|
+
<span class="text-xs">false</span>
|
|
424
|
+
</td>
|
|
425
|
+
<td class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-xs sm:text-sm text-neutral-700 dark:text-neutral-300">Signal to start recording. Set to true to begin capturing audio</td>
|
|
426
|
+
</tr>
|
|
427
|
+
<tr>
|
|
428
|
+
<td class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-xs sm:text-sm">
|
|
429
|
+
<span class="bg-neutral-100 dark:bg-[#1a1a1a] text-[#0a0a0a] dark:text-white px-2 py-1 text-xs">isStopRecording</span>
|
|
430
|
+
</td>
|
|
431
|
+
<td class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-xs sm:text-sm text-neutral-600 dark:text-neutral-400">
|
|
432
|
+
<span class="text-xs">InputSignal<boolean></span>
|
|
433
|
+
</td>
|
|
434
|
+
<td class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-xs sm:text-sm text-neutral-600 dark:text-neutral-400">
|
|
435
|
+
<span class="text-xs">false</span>
|
|
436
|
+
</td>
|
|
437
|
+
<td class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-xs sm:text-sm text-neutral-700 dark:text-neutral-300">Signal to stop recording. Set to true to stop capturing audio</td>
|
|
438
|
+
</tr>
|
|
439
|
+
<tr>
|
|
440
|
+
<td class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-xs sm:text-sm">
|
|
441
|
+
<span class="bg-neutral-100 dark:bg-[#1a1a1a] text-[#0a0a0a] dark:text-white px-2 py-1 text-xs">showTimer</span>
|
|
442
|
+
</td>
|
|
443
|
+
<td class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-xs sm:text-sm text-neutral-600 dark:text-neutral-400">
|
|
444
|
+
<span class="text-xs">InputSignal<boolean></span>
|
|
445
|
+
</td>
|
|
446
|
+
<td class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-xs sm:text-sm text-neutral-600 dark:text-neutral-400">
|
|
447
|
+
<span class="text-xs">false</span>
|
|
448
|
+
</td>
|
|
449
|
+
<td class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-xs sm:text-sm text-neutral-700 dark:text-neutral-300">Controls visibility of recording timer (MM:SS format)</td>
|
|
450
|
+
</tr>
|
|
451
|
+
|
|
452
|
+
<!-- Service Signals -->
|
|
453
|
+
<tr class="bg-green-50 dark:bg-green-900/20">
|
|
454
|
+
<td colspan="4" class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-2 text-xs sm:text-sm font-bold text-green-900 dark:text-green-100 uppercase">Service Signals (Read-Only)</td>
|
|
455
|
+
</tr>
|
|
456
|
+
<tr>
|
|
457
|
+
<td class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-xs sm:text-sm">
|
|
458
|
+
<span class="bg-neutral-100 dark:bg-[#1a1a1a] text-[#0a0a0a] dark:text-white px-2 py-1 text-xs">isRecording</span>
|
|
459
|
+
</td>
|
|
460
|
+
<td class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-xs sm:text-sm text-neutral-600 dark:text-neutral-400">
|
|
461
|
+
<span class="text-xs">WritableSignal<boolean></span>
|
|
462
|
+
</td>
|
|
463
|
+
<td class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-xs sm:text-sm text-neutral-600 dark:text-neutral-400">-</td>
|
|
464
|
+
<td class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-xs sm:text-sm text-neutral-700 dark:text-neutral-300">Current recording state (true = recording, false = stopped)</td>
|
|
465
|
+
</tr>
|
|
466
|
+
<tr>
|
|
467
|
+
<td class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-xs sm:text-sm">
|
|
468
|
+
<span class="bg-neutral-100 dark:bg-[#1a1a1a] text-[#0a0a0a] dark:text-white px-2 py-1 text-xs">audioData</span>
|
|
469
|
+
</td>
|
|
470
|
+
<td class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-xs sm:text-sm text-neutral-600 dark:text-neutral-400">
|
|
471
|
+
<span class="text-xs">WritableSignal<number[]></span>
|
|
472
|
+
</td>
|
|
473
|
+
<td class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-xs sm:text-sm text-neutral-600 dark:text-neutral-400">-</td>
|
|
474
|
+
<td class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-xs sm:text-sm text-neutral-700 dark:text-neutral-300">Array of normalized bar heights (0-100%) updated at 60fps</td>
|
|
475
|
+
</tr>
|
|
476
|
+
<tr>
|
|
477
|
+
<td class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-xs sm:text-sm">
|
|
478
|
+
<span class="bg-neutral-100 dark:bg-[#1a1a1a] text-[#0a0a0a] dark:text-white px-2 py-1 text-xs">recordedAudioBlob</span>
|
|
479
|
+
</td>
|
|
480
|
+
<td class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-xs sm:text-sm text-neutral-600 dark:text-neutral-400">
|
|
481
|
+
<span class="text-xs">WritableSignal<Blob | null></span>
|
|
482
|
+
</td>
|
|
483
|
+
<td class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-xs sm:text-sm text-neutral-600 dark:text-neutral-400">-</td>
|
|
484
|
+
<td class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-xs sm:text-sm text-neutral-700 dark:text-neutral-300">Recorded audio as a Blob (audio/webm format). Available after stopping recording</td>
|
|
485
|
+
</tr>
|
|
486
|
+
<tr>
|
|
487
|
+
<td class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-xs sm:text-sm">
|
|
488
|
+
<span class="bg-neutral-100 dark:bg-[#1a1a1a] text-[#0a0a0a] dark:text-white px-2 py-1 text-xs">recordedAudioUrl</span>
|
|
489
|
+
</td>
|
|
490
|
+
<td class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-xs sm:text-sm text-neutral-600 dark:text-neutral-400">
|
|
491
|
+
<span class="text-xs">WritableSignal<string | null></span>
|
|
492
|
+
</td>
|
|
493
|
+
<td class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-xs sm:text-sm text-neutral-600 dark:text-neutral-400">-</td>
|
|
494
|
+
<td class="border-2 border-neutral-300 dark:border-neutral-700 px-4 py-3 text-xs sm:text-sm text-neutral-700 dark:text-neutral-300">Temporary URL for recorded audio. Use for playback or download</td>
|
|
495
|
+
</tr>
|
|
496
|
+
</tbody>
|
|
497
|
+
</table>
|
|
498
|
+
</div>
|
|
499
|
+
</section>
|
|
500
|
+
|
|
501
|
+
<!-- Methods -->
|
|
502
|
+
<section id="methods" class="mb-12" aria-labelledby="methods-heading">
|
|
503
|
+
<h2 id="methods-heading" class="text-[10px] sm:text-xs text-neutral-500 dark:text-neutral-500 uppercase tracking-wider mb-4">
|
|
504
|
+
[ Service Methods ]
|
|
505
|
+
</h2>
|
|
506
|
+
<p class="text-xs sm:text-sm text-neutral-600 dark:text-neutral-400 mb-4 leading-relaxed">
|
|
507
|
+
Public methods available on AudioWaveformService for managing recorded audio.
|
|
508
|
+
</p>
|
|
509
|
+
|
|
510
|
+
<div class="space-y-4">
|
|
511
|
+
<div class="space-y-2">
|
|
512
|
+
<div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3">
|
|
513
|
+
<pre><code [innerHTML]="methodDownloadRecordingCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
|
|
514
|
+
</div>
|
|
515
|
+
<p class="text-xs sm:text-sm text-neutral-600 dark:text-neutral-400 leading-relaxed">Downloads the recorded audio file. Optionally specify custom filename.</p>
|
|
516
|
+
</div>
|
|
517
|
+
|
|
518
|
+
<div class="space-y-2">
|
|
519
|
+
<div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3">
|
|
520
|
+
<pre><code [innerHTML]="methodClearRecordingCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
|
|
521
|
+
</div>
|
|
522
|
+
<p class="text-xs sm:text-sm text-neutral-600 dark:text-neutral-400 leading-relaxed">Clears recorded audio and revokes URL to free memory. Call after download or discard.</p>
|
|
523
|
+
</div>
|
|
524
|
+
</div>
|
|
525
|
+
</section>
|
|
526
|
+
|
|
527
|
+
<!-- Future Features -->
|
|
528
|
+
<section id="future-features" class="mb-12" aria-labelledby="future-features-heading">
|
|
529
|
+
<h2 id="future-features-heading" class="text-[10px] sm:text-xs text-neutral-500 dark:text-neutral-500 uppercase tracking-wider mb-4">
|
|
530
|
+
[ Future Features ]
|
|
531
|
+
</h2>
|
|
532
|
+
|
|
533
|
+
<article aria-labelledby="transcription-heading">
|
|
534
|
+
<h3 id="transcription-heading" class="text-sm sm:text-base font-bold text-[#0a0a0a] dark:text-white mb-3 uppercase">
|
|
535
|
+
<span aria-hidden="true">> </span>Audio Transcription (Coming Soon)
|
|
536
|
+
</h3>
|
|
537
|
+
<p class="text-xs sm:text-sm text-neutral-600 dark:text-neutral-400 mb-4 leading-relaxed">
|
|
538
|
+
Automatic speech-to-text transcription will be added in a future release. The API preview below shows the planned implementation.
|
|
539
|
+
</p>
|
|
540
|
+
<div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3">
|
|
541
|
+
<pre><code [innerHTML]="futureTranscriptionCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
|
|
542
|
+
</div>
|
|
543
|
+
</article>
|
|
544
|
+
</section>
|
|
545
|
+
|
|
546
|
+
<!-- Browser Support -->
|
|
547
|
+
<section id="browser-support" class="mb-12" aria-labelledby="browser-support-heading">
|
|
548
|
+
<h2 id="browser-support-heading" class="text-[10px] sm:text-xs text-neutral-500 dark:text-neutral-500 uppercase tracking-wider mb-4">
|
|
549
|
+
[ Browser Support ]
|
|
550
|
+
</h2>
|
|
551
|
+
<div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3">
|
|
552
|
+
<pre><code [innerHTML]="browserSupportCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
|
|
553
|
+
</div>
|
|
554
|
+
</section>
|
|
555
|
+
|
|
556
|
+
<!-- Accessibility -->
|
|
557
|
+
<section id="accessibility" class="mb-12" aria-labelledby="accessibility-heading">
|
|
558
|
+
<h2 id="accessibility-heading" class="text-[10px] sm:text-xs text-neutral-500 dark:text-neutral-500 uppercase tracking-wider mb-4">
|
|
559
|
+
[ Accessibility ]
|
|
560
|
+
</h2>
|
|
561
|
+
<p class="text-xs sm:text-sm text-neutral-600 dark:text-neutral-400 mb-4 leading-relaxed">
|
|
562
|
+
The component follows accessibility best practices for audio recording interfaces.
|
|
563
|
+
</p>
|
|
564
|
+
<div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3 mb-6">
|
|
565
|
+
<pre><code [innerHTML]="accessibilityExampleCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
|
|
566
|
+
</div>
|
|
567
|
+
|
|
568
|
+
<ul class="space-y-2 text-xs sm:text-sm text-neutral-600 dark:text-neutral-400 leading-relaxed list-disc list-inside">
|
|
569
|
+
<li><strong class="text-[#0a0a0a] dark:text-white">Waveform Visualization:</strong> Decorative element (aria-hidden), not essential for understanding recording state</li>
|
|
570
|
+
<li><strong class="text-[#0a0a0a] dark:text-white">Recording Timer:</strong> Announced via ARIA live region when showTimer=true</li>
|
|
571
|
+
<li><strong class="text-[#0a0a0a] dark:text-white">Control Buttons:</strong> Use clear aria-label attributes (e.g., "Start audio recording")</li>
|
|
572
|
+
<li><strong class="text-[#0a0a0a] dark:text-white">Keyboard Support:</strong> All interactions keyboard-accessible via standard button controls</li>
|
|
573
|
+
</ul>
|
|
574
|
+
</section>
|
|
575
|
+
</div>
|
|
576
|
+
</main>
|