@useupup/preact 3.3.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 (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +80 -0
  3. package/dist/AudioUploader-2WITC5YR-4WGBUCEB.js +298 -0
  4. package/dist/AudioUploader-2WITC5YR-4WGBUCEB.js.map +1 -0
  5. package/dist/AudioUploader-2WITC5YR-GCKVRJAT.cjs +298 -0
  6. package/dist/AudioUploader-2WITC5YR-GCKVRJAT.cjs.map +1 -0
  7. package/dist/BoxUploader-VE5LRJ7M-CWGNYYJV.js +170 -0
  8. package/dist/BoxUploader-VE5LRJ7M-CWGNYYJV.js.map +1 -0
  9. package/dist/BoxUploader-VE5LRJ7M-IE7LYW5P.cjs +170 -0
  10. package/dist/BoxUploader-VE5LRJ7M-IE7LYW5P.cjs.map +1 -0
  11. package/dist/CameraUploader-WF5A6UIS-3KBW647M.cjs +628 -0
  12. package/dist/CameraUploader-WF5A6UIS-3KBW647M.cjs.map +1 -0
  13. package/dist/CameraUploader-WF5A6UIS-DONVXSEZ.js +628 -0
  14. package/dist/CameraUploader-WF5A6UIS-DONVXSEZ.js.map +1 -0
  15. package/dist/DropboxUploader-VMUYG3OP-7FKTLIIL.cjs +172 -0
  16. package/dist/DropboxUploader-VMUYG3OP-7FKTLIIL.cjs.map +1 -0
  17. package/dist/DropboxUploader-VMUYG3OP-DBUPHIG7.js +172 -0
  18. package/dist/DropboxUploader-VMUYG3OP-DBUPHIG7.js.map +1 -0
  19. package/dist/GoogleDriveUploader-YA56X3QR-FMS6MTYU.js +175 -0
  20. package/dist/GoogleDriveUploader-YA56X3QR-FMS6MTYU.js.map +1 -0
  21. package/dist/GoogleDriveUploader-YA56X3QR-OR3CQLX2.cjs +175 -0
  22. package/dist/GoogleDriveUploader-YA56X3QR-OR3CQLX2.cjs.map +1 -0
  23. package/dist/ImageEditorInline-N2NSAODR-QWXPP3ON.js +184 -0
  24. package/dist/ImageEditorInline-N2NSAODR-QWXPP3ON.js.map +1 -0
  25. package/dist/ImageEditorInline-N2NSAODR-ZVAM6EOA.cjs +184 -0
  26. package/dist/ImageEditorInline-N2NSAODR-ZVAM6EOA.cjs.map +1 -0
  27. package/dist/ImageEditorModal-CWKMDVPY-PRD43JPH.cjs +236 -0
  28. package/dist/ImageEditorModal-CWKMDVPY-PRD43JPH.cjs.map +1 -0
  29. package/dist/ImageEditorModal-CWKMDVPY-ZRPTYHYN.js +236 -0
  30. package/dist/ImageEditorModal-CWKMDVPY-ZRPTYHYN.js.map +1 -0
  31. package/dist/OneDriveUploader-I25Y6AJP-FVXK6RGT.js +173 -0
  32. package/dist/OneDriveUploader-I25Y6AJP-FVXK6RGT.js.map +1 -0
  33. package/dist/OneDriveUploader-I25Y6AJP-GBOBEJQR.cjs +173 -0
  34. package/dist/OneDriveUploader-I25Y6AJP-GBOBEJQR.cjs.map +1 -0
  35. package/dist/ScreenCaptureUploader-WKDO4S75-3TT6GNEN.js +286 -0
  36. package/dist/ScreenCaptureUploader-WKDO4S75-3TT6GNEN.js.map +1 -0
  37. package/dist/ScreenCaptureUploader-WKDO4S75-G7ADH264.cjs +286 -0
  38. package/dist/ScreenCaptureUploader-WKDO4S75-G7ADH264.cjs.map +1 -0
  39. package/dist/chunk-2XFVSPFN.cjs +293 -0
  40. package/dist/chunk-2XFVSPFN.cjs.map +1 -0
  41. package/dist/chunk-7JLN2HMZ.cjs +4296 -0
  42. package/dist/chunk-7JLN2HMZ.cjs.map +1 -0
  43. package/dist/chunk-BVMKSTAE.js +4296 -0
  44. package/dist/chunk-BVMKSTAE.js.map +1 -0
  45. package/dist/chunk-C7QBCXAH.js +39 -0
  46. package/dist/chunk-C7QBCXAH.js.map +1 -0
  47. package/dist/chunk-CR6CXFRP.js +195 -0
  48. package/dist/chunk-CR6CXFRP.js.map +1 -0
  49. package/dist/chunk-ETIJATY7.cjs +39 -0
  50. package/dist/chunk-ETIJATY7.cjs.map +1 -0
  51. package/dist/chunk-FH3LYESR.js +411 -0
  52. package/dist/chunk-FH3LYESR.js.map +1 -0
  53. package/dist/chunk-GKMTOOTQ.cjs +411 -0
  54. package/dist/chunk-GKMTOOTQ.cjs.map +1 -0
  55. package/dist/chunk-KHH3WHWU.js +74 -0
  56. package/dist/chunk-KHH3WHWU.js.map +1 -0
  57. package/dist/chunk-V2O5HRJV.cjs +74 -0
  58. package/dist/chunk-V2O5HRJV.cjs.map +1 -0
  59. package/dist/chunk-WCI3AGJV.js +831 -0
  60. package/dist/chunk-WCI3AGJV.js.map +1 -0
  61. package/dist/chunk-YWJHE7U4.cjs +195 -0
  62. package/dist/chunk-YWJHE7U4.cjs.map +1 -0
  63. package/dist/chunk-ZGWETRKI.js +293 -0
  64. package/dist/chunk-ZGWETRKI.js.map +1 -0
  65. package/dist/chunk-ZKZJ5PPP.cjs +831 -0
  66. package/dist/chunk-ZKZJ5PPP.cjs.map +1 -0
  67. package/dist/filerobot-bridge-52YH7W2M.cjs +92 -0
  68. package/dist/filerobot-bridge-52YH7W2M.cjs.map +1 -0
  69. package/dist/filerobot-bridge-ED4XMHX4.js +92 -0
  70. package/dist/filerobot-bridge-ED4XMHX4.js.map +1 -0
  71. package/dist/filerobot-island.css +97 -0
  72. package/dist/filerobot-island.css.map +1 -0
  73. package/dist/filerobot-island.js +77951 -0
  74. package/dist/filerobot-island.js.map +1 -0
  75. package/dist/index.cjs +5600 -0
  76. package/dist/index.cjs.map +1 -0
  77. package/dist/index.d.cts +1 -0
  78. package/dist/index.d.ts +1 -0
  79. package/dist/index.js +5600 -0
  80. package/dist/index.js.map +1 -0
  81. package/dist/styles.d.ts +4 -0
  82. package/dist/tailwind-prefixed.css +2529 -0
  83. package/package.json +130 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Devino
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,80 @@
1
+ # @useupup/preact
2
+
3
+ Preact file uploader with cloud-drive sources (Google Drive, OneDrive, Dropbox,
4
+ Box), resumable uploads, theming, and ICU i18n. It is a **`preact/compat`
5
+ re-export of [`@useupup/react`](https://www.npmjs.com/package/@useupup/react)** — the
6
+ same UI and API, resolved against Preact.
7
+
8
+ Requires Preact 10.13+ (`preact` is a peer dependency; the image editor has
9
+ additional optional peers — see below).
10
+
11
+ ## Install
12
+
13
+ ```sh
14
+ npm i @useupup/preact
15
+ ```
16
+
17
+ ## Usage (Client Mode)
18
+
19
+ Client Mode uploads directly from the browser to your storage; your server only
20
+ issues short-lived upload credentials at `uploadEndpoint`.
21
+
22
+ ```tsx
23
+ import { UpupUploader } from '@useupup/preact'
24
+ import '@useupup/preact/styles'
25
+
26
+ export function App() {
27
+ return (
28
+ <UpupUploader
29
+ provider="aws"
30
+ uploadEndpoint="/api/upload-token"
31
+ onFileUploadComplete={(file, key) =>
32
+ console.log('Uploaded', file.name, 'to', key)
33
+ }
34
+ />
35
+ )
36
+ }
37
+ ```
38
+
39
+ `uploadEndpoint` is your own route returning a presigned upload URL — see the
40
+ quickstart for a ready-made handler.
41
+
42
+ Use it in a Preact project that has the standard `preact/compat` aliases
43
+ (`react` / `react-dom` → `preact/compat`) configured in its bundler, as with any
44
+ React-compatible library.
45
+
46
+ ## Server Mode
47
+
48
+ For credential isolation and server-proxied cloud drives, add
49
+ [`@useupup/server`](https://www.npmjs.com/package/@useupup/server) and point the
50
+ uploader at it — server mode works identically through the `preact/compat`
51
+ re-export:
52
+
53
+ ```tsx
54
+ <UpupUploader mode="server" serverUrl="/api/upup" provider="aws" />
55
+ ```
56
+
57
+ ## Image editor
58
+
59
+ The optional image editor runs as an **isolated real-React island**: it lazily
60
+ loads actual `react` / `react-dom` on demand to render Filerobot, so React never
61
+ enters your main Preact bundle. If you enable the editor, install its peers
62
+ (`react`, `react-dom`, `react-filerobot-image-editor`, and the `konva` /
63
+ `react-konva` / `styled-components` dependencies it needs — see this package's
64
+ `peerDependencies`).
65
+
66
+ ## Also exported
67
+
68
+ Because it re-exports `@useupup/react`, the public surface matches `@useupup/react`
69
+ exactly (`UpupUploader`, `UpupThemeProvider`, the brand icons, and the `use*`
70
+ hooks).
71
+
72
+ ## Links
73
+
74
+ - [Preact quickstart](https://useupup.com/docs/quickstarts/preact)
75
+ - [Documentation](https://useupup.com/docs/)
76
+ - [Source & monorepo](https://github.com/DevinoSolutions/upup)
77
+
78
+ ## License
79
+
80
+ MIT
@@ -0,0 +1,298 @@
1
+ import {
2
+ SourceViewContainer
3
+ } from "./chunk-C7QBCXAH.js";
4
+ import {
5
+ Fragment,
6
+ u,
7
+ useUploaderFiles,
8
+ useUploaderSource,
9
+ useUploaderTheme
10
+ } from "./chunk-ZGWETRKI.js";
11
+ import {
12
+ compat_module_exports,
13
+ init_compat_module
14
+ } from "./chunk-FH3LYESR.js";
15
+
16
+ // ../react/dist/AudioUploader-2WITC5YR.js
17
+ init_compat_module();
18
+ init_compat_module();
19
+ import { cn } from "@useupup/core/internal";
20
+ var BAR_COUNT = 28;
21
+ function AudioWaveform({
22
+ stream
23
+ }) {
24
+ const barsRef = (0, compat_module_exports.useRef)([]);
25
+ (0, compat_module_exports.useEffect)(() => {
26
+ var _a;
27
+ const AudioCtor = (_a = window.AudioContext) != null ? _a : window.webkitAudioContext;
28
+ if (!AudioCtor) return;
29
+ const ctx = new AudioCtor();
30
+ void ctx.resume();
31
+ const source = ctx.createMediaStreamSource(stream);
32
+ const analyser = ctx.createAnalyser();
33
+ analyser.fftSize = 64;
34
+ source.connect(analyser);
35
+ const data = new Uint8Array(analyser.frequencyBinCount);
36
+ let raf = 0;
37
+ const tick = () => {
38
+ var _a2;
39
+ analyser.getByteFrequencyData(data);
40
+ const bars = barsRef.current;
41
+ for (let i = 0; i < bars.length; i++) {
42
+ const v = (_a2 = data[i % data.length]) != null ? _a2 : 0;
43
+ const el = bars[i];
44
+ if (el) el.style.height = `${6 + v / 255 * 34}px`;
45
+ }
46
+ raf = requestAnimationFrame(tick);
47
+ };
48
+ raf = requestAnimationFrame(tick);
49
+ return () => {
50
+ cancelAnimationFrame(raf);
51
+ source.disconnect();
52
+ void ctx.close();
53
+ };
54
+ }, [stream]);
55
+ return /* @__PURE__ */ u(
56
+ "div",
57
+ {
58
+ "data-upup-slot": "audio-waveform",
59
+ "aria-hidden": "true",
60
+ className: "upup-flex upup-h-11 upup-items-center upup-justify-center upup-gap-[3px]",
61
+ children: Array.from({ length: BAR_COUNT }).map((_, i) => /* @__PURE__ */ u(
62
+ "span",
63
+ {
64
+ ref: (el) => {
65
+ barsRef.current[i] = el;
66
+ },
67
+ className: "upup-w-[3px] upup-flex-none upup-rounded-full upup-bg-[#38bdf8]",
68
+ style: { height: "6px" }
69
+ },
70
+ i
71
+ ))
72
+ }
73
+ );
74
+ }
75
+ function AudioUploader() {
76
+ const { setFiles } = useUploaderFiles();
77
+ const { setActiveSource } = useUploaderSource();
78
+ const { isDark: dark } = useUploaderTheme();
79
+ const [state, setState] = (0, compat_module_exports.useState)("idle");
80
+ const [duration, setDuration] = (0, compat_module_exports.useState)(0);
81
+ const [audioUrl, setAudioUrl] = (0, compat_module_exports.useState)(null);
82
+ const [error, setError] = (0, compat_module_exports.useState)(null);
83
+ const mediaRecorder = (0, compat_module_exports.useRef)(null);
84
+ const chunks = (0, compat_module_exports.useRef)([]);
85
+ const timerRef = (0, compat_module_exports.useRef)(null);
86
+ const streamRef = (0, compat_module_exports.useRef)(null);
87
+ (0, compat_module_exports.useEffect)(() => {
88
+ return () => {
89
+ var _a;
90
+ if (timerRef.current) clearInterval(timerRef.current);
91
+ if (audioUrl) URL.revokeObjectURL(audioUrl);
92
+ (_a = streamRef.current) == null ? void 0 : _a.getTracks().forEach((t) => {
93
+ t.stop();
94
+ });
95
+ };
96
+ }, [audioUrl]);
97
+ const startRecording = (0, compat_module_exports.useCallback)(async () => {
98
+ try {
99
+ const stream = await navigator.mediaDevices.getUserMedia({
100
+ audio: true
101
+ });
102
+ streamRef.current = stream;
103
+ chunks.current = [];
104
+ const recorder = new MediaRecorder(stream);
105
+ mediaRecorder.current = recorder;
106
+ recorder.ondataavailable = (e) => {
107
+ if (e.data.size > 0) chunks.current.push(e.data);
108
+ };
109
+ recorder.onstop = () => {
110
+ const blob = new Blob(chunks.current, {
111
+ type: recorder.mimeType || "audio/webm"
112
+ });
113
+ setAudioUrl(URL.createObjectURL(blob));
114
+ stream.getTracks().forEach((t) => {
115
+ t.stop();
116
+ });
117
+ };
118
+ recorder.start();
119
+ setState("recording");
120
+ setDuration(0);
121
+ timerRef.current = setInterval(() => {
122
+ setDuration((d) => d + 1);
123
+ }, 1e3);
124
+ } catch {
125
+ setError(
126
+ "Microphone access denied. Please allow microphone access and try again."
127
+ );
128
+ }
129
+ }, []);
130
+ const stopRecording = (0, compat_module_exports.useCallback)(() => {
131
+ var _a;
132
+ (_a = mediaRecorder.current) == null ? void 0 : _a.stop();
133
+ if (timerRef.current) clearInterval(timerRef.current);
134
+ setState("recorded");
135
+ }, []);
136
+ const discardRecording = (0, compat_module_exports.useCallback)(() => {
137
+ if (audioUrl) URL.revokeObjectURL(audioUrl);
138
+ setAudioUrl(null);
139
+ setDuration(0);
140
+ setState("idle");
141
+ }, [audioUrl]);
142
+ const addRecording = (0, compat_module_exports.useCallback)(() => {
143
+ var _a, _b;
144
+ if (!audioUrl) return;
145
+ const ext = ((_b = (_a = mediaRecorder.current) == null ? void 0 : _a.mimeType) == null ? void 0 : _b.includes("webm")) ? "webm" : "ogg";
146
+ void fetch(audioUrl).then((r) => r.blob()).then((blob) => {
147
+ const file = new File(
148
+ [blob],
149
+ `recording-${Date.now()}.${ext}`,
150
+ { type: blob.type }
151
+ );
152
+ setFiles([file]);
153
+ setActiveSource(void 0);
154
+ }).catch(() => {
155
+ });
156
+ }, [audioUrl, setFiles, setActiveSource]);
157
+ const formatTime = (s) => {
158
+ const m = Math.floor(s / 60);
159
+ const sec = s % 60;
160
+ return `${m}:${sec.toString().padStart(2, "0")}`;
161
+ };
162
+ if (error) {
163
+ return /* @__PURE__ */ u(SourceViewContainer, { "data-upup-slot": "audio-uploader", children: /* @__PURE__ */ u("div", { className: "upup-flex upup-flex-col upup-items-center upup-gap-3 upup-p-6 upup-text-center", children: /* @__PURE__ */ u(
164
+ "p",
165
+ {
166
+ className: cn("upup-text-sm upup-text-red-500", {
167
+ "upup-text-red-400": dark
168
+ }),
169
+ children: error
170
+ }
171
+ ) }) });
172
+ }
173
+ return /* @__PURE__ */ u(SourceViewContainer, { "data-upup-slot": "audio-uploader", children: /* @__PURE__ */ u("div", { className: "upup-flex upup-h-full upup-w-full upup-flex-col upup-items-center upup-justify-center upup-gap-6 upup-p-6", children: [
174
+ /* @__PURE__ */ u(
175
+ "div",
176
+ {
177
+ className: cn(
178
+ "upup-flex upup-h-24 upup-w-24 upup-items-center upup-justify-center upup-rounded-full",
179
+ {
180
+ "upup-bg-red-500/20": state === "recording",
181
+ "upup-bg-[#0ea5e9]/20": state === "idle" || state === "recorded"
182
+ }
183
+ ),
184
+ children: /* @__PURE__ */ u(
185
+ "div",
186
+ {
187
+ className: cn(
188
+ "upup-flex upup-h-16 upup-w-16 upup-items-center upup-justify-center upup-rounded-full upup-transition-all",
189
+ {
190
+ "upup-animate-pulse upup-bg-red-500": state === "recording",
191
+ "upup-bg-[#0ea5e9]": state === "idle" || state === "recorded"
192
+ }
193
+ ),
194
+ children: /* @__PURE__ */ u(
195
+ "svg",
196
+ {
197
+ xmlns: "http://www.w3.org/2000/svg",
198
+ width: "28",
199
+ height: "28",
200
+ viewBox: "0 0 24 24",
201
+ fill: "none",
202
+ stroke: "white",
203
+ strokeWidth: "2",
204
+ strokeLinecap: "round",
205
+ strokeLinejoin: "round",
206
+ children: [
207
+ /* @__PURE__ */ u("path", { d: "M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" }),
208
+ /* @__PURE__ */ u("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
209
+ /* @__PURE__ */ u("line", { x1: "12", x2: "12", y1: "19", y2: "22" })
210
+ ]
211
+ }
212
+ )
213
+ }
214
+ )
215
+ }
216
+ ),
217
+ state === "recording" && streamRef.current && /* @__PURE__ */ u(AudioWaveform, { stream: streamRef.current }),
218
+ /* @__PURE__ */ u(
219
+ "span",
220
+ {
221
+ className: cn(
222
+ "upup-text-2xl upup-font-mono upup-tabular-nums",
223
+ {
224
+ "upup-text-[#1b1b1b]": !dark,
225
+ "upup-text-white": dark
226
+ }
227
+ ),
228
+ children: formatTime(duration)
229
+ }
230
+ ),
231
+ state === "recorded" && audioUrl && /* @__PURE__ */ u(
232
+ "audio",
233
+ {
234
+ controls: true,
235
+ src: audioUrl,
236
+ className: "upup-w-full upup-max-w-xs"
237
+ }
238
+ ),
239
+ /* @__PURE__ */ u("div", { className: "upup-flex upup-gap-3", children: [
240
+ state === "idle" && /* @__PURE__ */ u(
241
+ "button",
242
+ {
243
+ type: "button",
244
+ className: cn(
245
+ "upup-rounded-lg upup-bg-[#0ea5e9] upup-px-6 upup-py-2.5 upup-text-sm upup-font-medium upup-text-white upup-transition-colors hover:upup-bg-[#0284c7]",
246
+ {
247
+ "upup-bg-[#38bdf8] hover:upup-bg-[#0ea5e9] dark:upup-bg-[#38bdf8]": dark
248
+ }
249
+ ),
250
+ onClick: () => {
251
+ void startRecording();
252
+ },
253
+ children: "Start Recording"
254
+ }
255
+ ),
256
+ state === "recording" && /* @__PURE__ */ u(
257
+ "button",
258
+ {
259
+ type: "button",
260
+ className: "upup-rounded-lg upup-bg-red-500 upup-px-6 upup-py-2.5 upup-text-sm upup-font-medium upup-text-white upup-transition-colors hover:upup-bg-red-600",
261
+ onClick: stopRecording,
262
+ children: "Stop Recording"
263
+ }
264
+ ),
265
+ state === "recorded" && /* @__PURE__ */ u(Fragment, { children: [
266
+ /* @__PURE__ */ u(
267
+ "button",
268
+ {
269
+ type: "button",
270
+ className: cn(
271
+ "upup-rounded-lg upup-bg-gray-500 upup-px-4 upup-py-2.5 upup-text-sm upup-font-medium upup-text-white upup-transition-colors hover:upup-bg-gray-600"
272
+ ),
273
+ onClick: discardRecording,
274
+ children: "Discard"
275
+ }
276
+ ),
277
+ /* @__PURE__ */ u(
278
+ "button",
279
+ {
280
+ type: "button",
281
+ className: cn(
282
+ "upup-rounded-lg upup-bg-[#0ea5e9] upup-px-4 upup-py-2.5 upup-text-sm upup-font-medium upup-text-white upup-transition-colors hover:upup-bg-[#0284c7]",
283
+ {
284
+ "upup-bg-[#38bdf8] hover:upup-bg-[#0ea5e9] dark:upup-bg-[#38bdf8]": dark
285
+ }
286
+ ),
287
+ onClick: addRecording,
288
+ children: "Add Recording"
289
+ }
290
+ )
291
+ ] })
292
+ ] })
293
+ ] }) });
294
+ }
295
+ export {
296
+ AudioUploader as default
297
+ };
298
+ //# sourceMappingURL=AudioUploader-2WITC5YR-4WGBUCEB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../react/src/components/AudioUploader.tsx","../../react/src/components/AudioWaveform.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useRef, useState } from 'react'\r\nimport {\r\n useUploaderFiles,\r\n useUploaderSource,\r\n useUploaderTheme,\r\n} from '../context/UploaderContext'\r\nimport { cn } from '@useupup/core/internal'\r\nimport SourceViewContainer from './shared/SourceViewContainer'\r\nimport AudioWaveform from './AudioWaveform'\r\n\r\ntype RecordingState = 'idle' | 'recording' | 'recorded'\r\n\r\nexport default function AudioUploader(): React.ReactElement | null {\r\n const { setFiles } = useUploaderFiles()\r\n const { setActiveSource } = useUploaderSource()\r\n const { isDark: dark } = useUploaderTheme()\r\n\r\n const [state, setState] = useState<RecordingState>('idle')\r\n const [duration, setDuration] = useState(0)\r\n const [audioUrl, setAudioUrl] = useState<string | null>(null)\r\n const [error, setError] = useState<string | null>(null)\r\n\r\n const mediaRecorder = useRef<MediaRecorder | null>(null)\r\n const chunks = useRef<Blob[]>([])\r\n const timerRef = useRef<ReturnType<typeof setInterval> | null>(null)\r\n const streamRef = useRef<MediaStream | null>(null)\r\n\r\n useEffect(() => {\r\n return () => {\r\n if (timerRef.current) clearInterval(timerRef.current)\r\n if (audioUrl) URL.revokeObjectURL(audioUrl)\r\n streamRef.current?.getTracks().forEach(t => {\r\n t.stop()\r\n })\r\n }\r\n }, [audioUrl])\r\n\r\n const startRecording = useCallback(async () => {\r\n try {\r\n const stream = await navigator.mediaDevices.getUserMedia({\r\n audio: true,\r\n })\r\n streamRef.current = stream\r\n chunks.current = []\r\n const recorder = new MediaRecorder(stream)\r\n mediaRecorder.current = recorder\r\n\r\n recorder.ondataavailable = e => {\r\n if (e.data.size > 0) chunks.current.push(e.data)\r\n }\r\n\r\n recorder.onstop = () => {\r\n const blob = new Blob(chunks.current, {\r\n type: recorder.mimeType || 'audio/webm',\r\n })\r\n setAudioUrl(URL.createObjectURL(blob))\r\n stream.getTracks().forEach(t => {\r\n t.stop()\r\n })\r\n }\r\n\r\n recorder.start()\r\n setState('recording')\r\n setDuration(0)\r\n timerRef.current = setInterval(() => {\r\n setDuration(d => d + 1)\r\n }, 1000)\r\n } catch {\r\n // upup-catch: getUserMedia rejection (permission denied / no device) is surfaced to the user via setError, not a system fault\r\n setError(\r\n 'Microphone access denied. Please allow microphone access and try again.',\r\n )\r\n }\r\n }, [])\r\n\r\n const stopRecording = useCallback(() => {\r\n mediaRecorder.current?.stop()\r\n if (timerRef.current) clearInterval(timerRef.current)\r\n setState('recorded')\r\n }, [])\r\n\r\n const discardRecording = useCallback(() => {\r\n if (audioUrl) URL.revokeObjectURL(audioUrl)\r\n setAudioUrl(null)\r\n setDuration(0)\r\n setState('idle')\r\n }, [audioUrl])\r\n\r\n const addRecording = useCallback(() => {\r\n if (!audioUrl) return\r\n const ext = mediaRecorder.current?.mimeType?.includes('webm')\r\n ? 'webm'\r\n : 'ogg'\r\n void fetch(audioUrl)\r\n .then(r => r.blob())\r\n .then(blob => {\r\n const file = new File(\r\n [blob],\r\n `recording-${Date.now()}.${ext}`,\r\n { type: blob.type },\r\n )\r\n setFiles([file])\r\n setActiveSource(undefined)\r\n })\r\n .catch(() => {\r\n // upup-catch: replaying an in-memory object URL cannot fail in practice; ignore\r\n })\r\n }, [audioUrl, setFiles, setActiveSource])\r\n\r\n const formatTime = (s: number) => {\r\n const m = Math.floor(s / 60)\r\n const sec = s % 60\r\n return `${m}:${sec.toString().padStart(2, '0')}`\r\n }\r\n\r\n if (error) {\r\n return (\r\n <SourceViewContainer data-upup-slot=\"audio-uploader\">\r\n <div className=\"upup-flex upup-flex-col upup-items-center upup-gap-3 upup-p-6 upup-text-center\">\r\n <p\r\n className={cn('upup-text-sm upup-text-red-500', {\r\n 'upup-text-red-400': dark,\r\n })}\r\n >\r\n {error}\r\n </p>\r\n </div>\r\n </SourceViewContainer>\r\n )\r\n }\r\n\r\n return (\r\n <SourceViewContainer data-upup-slot=\"audio-uploader\">\r\n <div className=\"upup-flex upup-h-full upup-w-full upup-flex-col upup-items-center upup-justify-center upup-gap-6 upup-p-6\">\r\n <div\r\n className={cn(\r\n 'upup-flex upup-h-24 upup-w-24 upup-items-center upup-justify-center upup-rounded-full',\r\n {\r\n 'upup-bg-red-500/20': state === 'recording',\r\n 'upup-bg-[#0ea5e9]/20':\r\n state === 'idle' || state === 'recorded',\r\n },\r\n )}\r\n >\r\n <div\r\n className={cn(\r\n 'upup-flex upup-h-16 upup-w-16 upup-items-center upup-justify-center upup-rounded-full upup-transition-all',\r\n {\r\n 'upup-animate-pulse upup-bg-red-500':\r\n state === 'recording',\r\n 'upup-bg-[#0ea5e9]':\r\n state === 'idle' || state === 'recorded',\r\n },\r\n )}\r\n >\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"28\"\r\n height=\"28\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"white\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n >\r\n <path d=\"M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z\" />\r\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\" />\r\n <line x1=\"12\" x2=\"12\" y1=\"19\" y2=\"22\" />\r\n </svg>\r\n </div>\r\n </div>\r\n\r\n {state === 'recording' && streamRef.current && (\r\n <AudioWaveform stream={streamRef.current} />\r\n )}\r\n\r\n <span\r\n className={cn(\r\n 'upup-text-2xl upup-font-mono upup-tabular-nums',\r\n {\r\n 'upup-text-[#1b1b1b]': !dark,\r\n 'upup-text-white': dark,\r\n },\r\n )}\r\n >\r\n {formatTime(duration)}\r\n </span>\r\n\r\n {state === 'recorded' && audioUrl && (\r\n <audio\r\n controls\r\n src={audioUrl}\r\n className=\"upup-w-full upup-max-w-xs\"\r\n />\r\n )}\r\n\r\n <div className=\"upup-flex upup-gap-3\">\r\n {state === 'idle' && (\r\n <button\r\n type=\"button\"\r\n className={cn(\r\n 'upup-rounded-lg upup-bg-[#0ea5e9] upup-px-6 upup-py-2.5 upup-text-sm upup-font-medium upup-text-white upup-transition-colors hover:upup-bg-[#0284c7]',\r\n {\r\n 'upup-bg-[#38bdf8] hover:upup-bg-[#0ea5e9] dark:upup-bg-[#38bdf8]':\r\n dark,\r\n },\r\n )}\r\n onClick={() => {\r\n void startRecording()\r\n }}\r\n >\r\n Start Recording\r\n </button>\r\n )}\r\n {state === 'recording' && (\r\n <button\r\n type=\"button\"\r\n className=\"upup-rounded-lg upup-bg-red-500 upup-px-6 upup-py-2.5 upup-text-sm upup-font-medium upup-text-white upup-transition-colors hover:upup-bg-red-600\"\r\n onClick={stopRecording}\r\n >\r\n Stop Recording\r\n </button>\r\n )}\r\n {state === 'recorded' && (\r\n <>\r\n <button\r\n type=\"button\"\r\n className={cn(\r\n 'upup-rounded-lg upup-bg-gray-500 upup-px-4 upup-py-2.5 upup-text-sm upup-font-medium upup-text-white upup-transition-colors hover:upup-bg-gray-600',\r\n )}\r\n onClick={discardRecording}\r\n >\r\n Discard\r\n </button>\r\n <button\r\n type=\"button\"\r\n className={cn(\r\n 'upup-rounded-lg upup-bg-[#0ea5e9] upup-px-4 upup-py-2.5 upup-text-sm upup-font-medium upup-text-white upup-transition-colors hover:upup-bg-[#0284c7]',\r\n {\r\n 'upup-bg-[#38bdf8] hover:upup-bg-[#0ea5e9] dark:upup-bg-[#38bdf8]':\r\n dark,\r\n },\r\n )}\r\n onClick={addRecording}\r\n >\r\n Add Recording\r\n </button>\r\n </>\r\n )}\r\n </div>\r\n </div>\r\n </SourceViewContainer>\r\n )\r\n}\r\n","import React, { useEffect, useRef } from 'react'\r\n\r\nconst BAR_COUNT = 28\r\n\r\n/**\r\n * Live recording waveform (states-tour-3 state D): sky bars whose heights track\r\n * the microphone's live frequency data via a Web Audio AnalyserNode.\r\n *\r\n * This is the ONE place JS-driven visuals are sanctioned — the bars render REAL\r\n * audio data, not fx decoration, so (a) they are NOT classed `upup-fx-*` (the\r\n * motion gate must not freeze live data) and (b) the rAF read loop is\r\n * legitimate. Heights are written straight to the DOM (no per-frame React\r\n * render). The AnalyserNode + AudioContext are torn down on unmount/stop.\r\n */\r\nexport default function AudioWaveform({\r\n stream,\r\n}: Readonly<{ stream: MediaStream }>): React.ReactElement {\r\n const barsRef = useRef<Array<HTMLSpanElement | null>>([])\r\n\r\n useEffect(() => {\r\n const AudioCtor =\r\n window.AudioContext ??\r\n (window as unknown as { webkitAudioContext?: typeof AudioContext })\r\n .webkitAudioContext\r\n if (!AudioCtor) return\r\n const ctx = new AudioCtor()\r\n // Autoplay policy may create the context suspended; without resuming,\r\n // getByteFrequencyData returns all-zeros and the bars silently flatline.\r\n void ctx.resume()\r\n const source = ctx.createMediaStreamSource(stream)\r\n const analyser = ctx.createAnalyser()\r\n analyser.fftSize = 64\r\n source.connect(analyser)\r\n const data = new Uint8Array(analyser.frequencyBinCount)\r\n let raf = 0\r\n const tick = () => {\r\n analyser.getByteFrequencyData(data)\r\n const bars = barsRef.current\r\n for (let i = 0; i < bars.length; i++) {\r\n const v = data[i % data.length] ?? 0\r\n const el = bars[i]\r\n if (el) el.style.height = `${6 + (v / 255) * 34}px`\r\n }\r\n raf = requestAnimationFrame(tick)\r\n }\r\n raf = requestAnimationFrame(tick)\r\n return () => {\r\n cancelAnimationFrame(raf)\r\n source.disconnect()\r\n void ctx.close()\r\n }\r\n }, [stream])\r\n\r\n return (\r\n <div\r\n data-upup-slot=\"audio-waveform\"\r\n aria-hidden=\"true\"\r\n className=\"upup-flex upup-h-11 upup-items-center upup-justify-center upup-gap-[3px]\"\r\n >\r\n {Array.from({ length: BAR_COUNT }).map((_, i) => (\r\n <span\r\n key={i}\r\n ref={el => {\r\n barsRef.current[i] = el\r\n }}\r\n className=\"upup-w-[3px] upup-flex-none upup-rounded-full upup-bg-[#38bdf8]\"\r\n style={{ height: '6px' }}\r\n />\r\n ))}\r\n </div>\r\n )\r\n}\r\n"],"mappings":"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","names":["_a","useRef","useEffect","jsx"]}