@longline/aqua-ui 1.0.331 → 1.0.335
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/hooks/useSpeechAIRecorder/index.d.ts +1 -0
- package/hooks/useSpeechAIRecorder/index.js +1 -0
- package/hooks/useSpeechAIRecorder/stories/SpeechAIAudioInput.d.ts +6 -0
- package/hooks/{useAssemblyAIRecorder/stories/AssemblyAudioInput.js → useSpeechAIRecorder/stories/SpeechAIAudioInput.js} +4 -4
- package/hooks/useSpeechAIRecorder/useSpeechAIRecorder.d.ts +29 -0
- package/hooks/{useAssemblyAIRecorder/useAssemblyAIRecorder.js → useSpeechAIRecorder/useSpeechAIRecorder.js} +13 -11
- package/hooks/useTextAIStream/index.d.ts +1 -0
- package/hooks/useTextAIStream/index.js +1 -0
- package/hooks/{useOpenAIStream/stories/OpenAIStreamInput.d.ts → useTextAIStream/stories/TextAIStreamInput.d.ts} +3 -3
- package/hooks/{useOpenAIStream/stories/OpenAIStreamInput.js → useTextAIStream/stories/TextAIStreamInput.js} +6 -6
- package/hooks/useTextAIStream/useTextAIStream.d.ts +26 -0
- package/hooks/{useOpenAIStream/useOpenAIStream.js → useTextAIStream/useTextAIStream.js} +16 -4
- package/inputs/DateInput/Selector.js +17 -10
- package/inputs/Editor/Editor.d.ts +7 -6
- package/inputs/Editor/Editor.js +1 -1
- package/inputs/Editor/buttons/{SpeechButton.d.ts → SpeechAIButton.d.ts} +2 -2
- package/inputs/Editor/buttons/{SpeechButton.js → SpeechAIButton.js} +5 -5
- package/inputs/Editor/buttons/TextAIButton.d.ts +28 -0
- package/inputs/Editor/buttons/{OpenAIButton.js → TextAIButton.js} +13 -14
- package/inputs/Editor/buttons/{OpenAIMenu.d.ts → TextAIMenu.d.ts} +2 -2
- package/inputs/Editor/buttons/{OpenAIMenu.js → TextAIMenu.js} +3 -3
- package/inputs/Editor/menu/MenuBar.d.ts +6 -6
- package/inputs/Editor/menu/MenuBar.js +7 -7
- package/package.json +4 -5
- package/hooks/useAssemblyAIRecorder/index.d.ts +0 -1
- package/hooks/useAssemblyAIRecorder/index.js +0 -1
- package/hooks/useAssemblyAIRecorder/stories/AssemblyAudioInput.d.ts +0 -6
- package/hooks/useAssemblyAIRecorder/useAssemblyAIRecorder.d.ts +0 -27
- package/hooks/useOpenAIRecorder/index.d.ts +0 -1
- package/hooks/useOpenAIRecorder/index.js +0 -1
- package/hooks/useOpenAIRecorder/old/AudioInputNoHook.d.ts +0 -8
- package/hooks/useOpenAIRecorder/old/AudioInputNoHook.js +0 -192
- package/hooks/useOpenAIRecorder/old/AudioInputStandardMedia.d.ts +0 -5
- package/hooks/useOpenAIRecorder/old/AudioInputStandardMedia.js +0 -170
- package/hooks/useOpenAIRecorder/stories/OpenAIAudioInput.d.ts +0 -8
- package/hooks/useOpenAIRecorder/stories/OpenAIAudioInput.js +0 -17
- package/hooks/useOpenAIRecorder/useOpenAIRecorder.d.ts +0 -22
- package/hooks/useOpenAIRecorder/useOpenAIRecorder.js +0 -223
- package/hooks/useOpenAIStream/index.d.ts +0 -1
- package/hooks/useOpenAIStream/index.js +0 -1
- package/hooks/useOpenAIStream/useOpenAIStream.d.ts +0 -14
- package/inputs/Editor/buttons/OpenAIButton.d.ts +0 -29
- /package/hooks/{useAssemblyAIRecorder → useSpeechAIRecorder}/SpeechManager.d.ts +0 -0
- /package/hooks/{useAssemblyAIRecorder → useSpeechAIRecorder}/SpeechManager.js +0 -0
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
11
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
12
|
-
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
13
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
14
|
-
function step(op) {
|
|
15
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
16
|
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
17
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
18
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
19
|
-
switch (op[0]) {
|
|
20
|
-
case 0: case 1: t = op; break;
|
|
21
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
22
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
23
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
24
|
-
default:
|
|
25
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
26
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
27
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
28
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
29
|
-
if (t[2]) _.ops.pop();
|
|
30
|
-
_.trys.pop(); continue;
|
|
31
|
-
}
|
|
32
|
-
op = body.call(thisArg, _);
|
|
33
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
34
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
import * as React from 'react';
|
|
38
|
-
import { MediaRecorder, register } from 'extendable-media-recorder';
|
|
39
|
-
import { connect } from 'extendable-media-recorder-wav-encoder';
|
|
40
|
-
import { Fab } from '../../../controls/Fab';
|
|
41
|
-
import { SVG } from '../../../svg';
|
|
42
|
-
/**
|
|
43
|
-
* The extended media recorder allows converting to .wav format.
|
|
44
|
-
*/
|
|
45
|
-
var AudioInput = function (props) {
|
|
46
|
-
var _a = React.useState(false), isRecording = _a[0], setIsRecording = _a[1];
|
|
47
|
-
var _b = React.useState(''), transcript = _b[0], setTranscript = _b[1];
|
|
48
|
-
var mediaRecorderRef = React.useRef(null);
|
|
49
|
-
var streamRef = React.useRef(null);
|
|
50
|
-
var streamIdRef = React.useRef(Date.now().toString()); // Unique per session
|
|
51
|
-
var sourceRef = React.useRef(null);
|
|
52
|
-
var header;
|
|
53
|
-
var startRecording = function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
54
|
-
var _a, _b, mediaRecorder, err_1;
|
|
55
|
-
return __generator(this, function (_c) {
|
|
56
|
-
switch (_c.label) {
|
|
57
|
-
case 0:
|
|
58
|
-
_c.trys.push([0, 4, , 5]);
|
|
59
|
-
_a = register;
|
|
60
|
-
return [4 /*yield*/, connect()];
|
|
61
|
-
case 1: return [4 /*yield*/, _a.apply(void 0, [_c.sent()])];
|
|
62
|
-
case 2:
|
|
63
|
-
_c.sent();
|
|
64
|
-
_b = streamRef;
|
|
65
|
-
return [4 /*yield*/, navigator.mediaDevices.getUserMedia({ audio: true })];
|
|
66
|
-
case 3:
|
|
67
|
-
_b.current = _c.sent();
|
|
68
|
-
mediaRecorder = new MediaRecorder(streamRef.current, { mimeType: 'audio/wav' });
|
|
69
|
-
mediaRecorder.ondataavailable = function (event) { return __awaiter(void 0, void 0, void 0, function () {
|
|
70
|
-
var blob, formData, content, error_1;
|
|
71
|
-
return __generator(this, function (_a) {
|
|
72
|
-
switch (_a.label) {
|
|
73
|
-
case 0:
|
|
74
|
-
if (!(event.data.size > 0)) return [3 /*break*/, 8];
|
|
75
|
-
blob = event.data;
|
|
76
|
-
formData = new FormData();
|
|
77
|
-
if (!(header === undefined)) return [3 /*break*/, 2];
|
|
78
|
-
return [4 /*yield*/, blob.arrayBuffer()];
|
|
79
|
-
case 1:
|
|
80
|
-
header = (_a.sent()).slice(0, 44);
|
|
81
|
-
return [3 /*break*/, 4];
|
|
82
|
-
case 2: return [4 /*yield*/, blob.arrayBuffer()];
|
|
83
|
-
case 3:
|
|
84
|
-
content = _a.sent();
|
|
85
|
-
blob = new Blob([header, content], { type: blob.type });
|
|
86
|
-
_a.label = 4;
|
|
87
|
-
case 4:
|
|
88
|
-
formData.append('audio', blob);
|
|
89
|
-
formData.append('stream_id', streamIdRef.current);
|
|
90
|
-
_a.label = 5;
|
|
91
|
-
case 5:
|
|
92
|
-
_a.trys.push([5, 7, , 8]);
|
|
93
|
-
return [4 /*yield*/, fetch('http://api.flow/api/ai/upload-audio-chunk', {
|
|
94
|
-
method: 'POST',
|
|
95
|
-
body: formData,
|
|
96
|
-
})];
|
|
97
|
-
case 6:
|
|
98
|
-
_a.sent();
|
|
99
|
-
return [3 /*break*/, 8];
|
|
100
|
-
case 7:
|
|
101
|
-
error_1 = _a.sent();
|
|
102
|
-
console.error('Upload failed:', error_1);
|
|
103
|
-
return [3 /*break*/, 8];
|
|
104
|
-
case 8: return [2 /*return*/];
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
}); };
|
|
108
|
-
mediaRecorderRef.current = mediaRecorder;
|
|
109
|
-
mediaRecorder.start(20000); // 4-second chunks
|
|
110
|
-
return [3 /*break*/, 5];
|
|
111
|
-
case 4:
|
|
112
|
-
err_1 = _c.sent();
|
|
113
|
-
console.error('Microphone access error:', err_1);
|
|
114
|
-
setIsRecording(false);
|
|
115
|
-
return [3 /*break*/, 5];
|
|
116
|
-
case 5: return [2 /*return*/];
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
}); };
|
|
120
|
-
React.useEffect(function () {
|
|
121
|
-
var _a, _b, _c;
|
|
122
|
-
if (isRecording) {
|
|
123
|
-
startRecording();
|
|
124
|
-
}
|
|
125
|
-
else {
|
|
126
|
-
if (((_a = mediaRecorderRef.current) === null || _a === void 0 ? void 0 : _a.state) !== 'inactive') {
|
|
127
|
-
(_b = mediaRecorderRef.current) === null || _b === void 0 ? void 0 : _b.stop();
|
|
128
|
-
}
|
|
129
|
-
(_c = streamRef.current) === null || _c === void 0 ? void 0 : _c.getTracks().forEach(function (track) { return track.stop(); });
|
|
130
|
-
}
|
|
131
|
-
;
|
|
132
|
-
}, [isRecording]);
|
|
133
|
-
var readyStateToString = function (state) {
|
|
134
|
-
switch (state) {
|
|
135
|
-
case EventSource.CLOSED: return "CLOSED";
|
|
136
|
-
case EventSource.OPEN: return "OPEN";
|
|
137
|
-
case EventSource.CONNECTING: return "CONNECTING";
|
|
138
|
-
default:
|
|
139
|
-
return "UNKNOWN";
|
|
140
|
-
}
|
|
141
|
-
};
|
|
142
|
-
var eventPhaseToString = function (phase) {
|
|
143
|
-
switch (phase) {
|
|
144
|
-
case Event.NONE: return "NONE";
|
|
145
|
-
case Event.CAPTURING_PHASE: return "CAPTURING_PHASE";
|
|
146
|
-
case Event.AT_TARGET: return "AT_TARGET";
|
|
147
|
-
case Event.BUBBLING_PHASE: return "BUBBLING_PHASE";
|
|
148
|
-
default:
|
|
149
|
-
return "UNKNOWN";
|
|
150
|
-
}
|
|
151
|
-
};
|
|
152
|
-
React.useEffect(function () {
|
|
153
|
-
if (isRecording) {
|
|
154
|
-
sourceRef.current = new EventSource("http://api.flow/api/ai/transcribe-stream/".concat(streamIdRef.current));
|
|
155
|
-
sourceRef.current.onmessage = function (event) {
|
|
156
|
-
console.log("Message:", event.data);
|
|
157
|
-
console.log("ReadyState:", readyStateToString(sourceRef.current.readyState));
|
|
158
|
-
setTranscript(function (prev) { return prev + event.data + '\n'; });
|
|
159
|
-
};
|
|
160
|
-
sourceRef.current.onerror = function (err) {
|
|
161
|
-
console.error('SSE error:', err);
|
|
162
|
-
console.log('EventPhase:', eventPhaseToString(err.eventPhase));
|
|
163
|
-
console.log('ReadyState:', readyStateToString(sourceRef.current.readyState));
|
|
164
|
-
sourceRef.current.close();
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
return function () { var _a; return (_a = sourceRef.current) === null || _a === void 0 ? void 0 : _a.close(); };
|
|
168
|
-
}, [isRecording]);
|
|
169
|
-
var start = function () {
|
|
170
|
-
setTranscript('');
|
|
171
|
-
streamIdRef.current = Date.now().toString(); // Reset for new session
|
|
172
|
-
setIsRecording(true);
|
|
173
|
-
};
|
|
174
|
-
var stop = function () {
|
|
175
|
-
var _a;
|
|
176
|
-
setIsRecording(false);
|
|
177
|
-
if (((_a = mediaRecorderRef.current) === null || _a === void 0 ? void 0 : _a.state) !== 'inactive') {
|
|
178
|
-
mediaRecorderRef.current.stop();
|
|
179
|
-
}
|
|
180
|
-
};
|
|
181
|
-
var handleToggle = function () {
|
|
182
|
-
if (isRecording) {
|
|
183
|
-
stop();
|
|
184
|
-
}
|
|
185
|
-
else {
|
|
186
|
-
start();
|
|
187
|
-
}
|
|
188
|
-
};
|
|
189
|
-
return (React.createElement("div", null,
|
|
190
|
-
React.createElement(Fab, { title: "Record", active: isRecording, icon: SVG.Icons.Microphone, onClick: handleToggle })));
|
|
191
|
-
};
|
|
192
|
-
export { AudioInput };
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
11
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
12
|
-
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
13
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
14
|
-
function step(op) {
|
|
15
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
16
|
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
17
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
18
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
19
|
-
switch (op[0]) {
|
|
20
|
-
case 0: case 1: t = op; break;
|
|
21
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
22
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
23
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
24
|
-
default:
|
|
25
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
26
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
27
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
28
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
29
|
-
if (t[2]) _.ops.pop();
|
|
30
|
-
_.trys.pop(); continue;
|
|
31
|
-
}
|
|
32
|
-
op = body.call(thisArg, _);
|
|
33
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
34
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
import * as React from 'react';
|
|
38
|
-
import { Fab } from '../../../controls/Fab';
|
|
39
|
-
import { SVG } from '../../../svg';
|
|
40
|
-
var AudioInput = function (props) {
|
|
41
|
-
var _a = React.useState(false), isRecording = _a[0], setIsRecording = _a[1];
|
|
42
|
-
var _b = React.useState(''), transcript = _b[0], setTranscript = _b[1];
|
|
43
|
-
var mediaRecorderRef = React.useRef(null);
|
|
44
|
-
var streamRef = React.useRef(null);
|
|
45
|
-
var streamIdRef = React.useRef(Date.now().toString()); // Unique per session
|
|
46
|
-
var sourceRef = React.useRef(null);
|
|
47
|
-
var startRecording = function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
48
|
-
var _a, mediaRecorder, err_1;
|
|
49
|
-
return __generator(this, function (_b) {
|
|
50
|
-
switch (_b.label) {
|
|
51
|
-
case 0:
|
|
52
|
-
_b.trys.push([0, 2, , 3]);
|
|
53
|
-
_a = streamRef;
|
|
54
|
-
return [4 /*yield*/, navigator.mediaDevices.getUserMedia({ audio: true })];
|
|
55
|
-
case 1:
|
|
56
|
-
_a.current = _b.sent();
|
|
57
|
-
mediaRecorder = new MediaRecorder(streamRef.current, { mimeType: 'audio/webm' });
|
|
58
|
-
mediaRecorder.ondataavailable = function (event) { return __awaiter(void 0, void 0, void 0, function () {
|
|
59
|
-
var blob, formData, error_1;
|
|
60
|
-
return __generator(this, function (_a) {
|
|
61
|
-
switch (_a.label) {
|
|
62
|
-
case 0:
|
|
63
|
-
if (!(event.data.size > 0)) return [3 /*break*/, 4];
|
|
64
|
-
blob = event.data;
|
|
65
|
-
formData = new FormData();
|
|
66
|
-
formData.append('audio', blob);
|
|
67
|
-
formData.append('stream_id', streamIdRef.current);
|
|
68
|
-
_a.label = 1;
|
|
69
|
-
case 1:
|
|
70
|
-
_a.trys.push([1, 3, , 4]);
|
|
71
|
-
return [4 /*yield*/, fetch('http://api.flow/api/ai/upload-audio-chunk', {
|
|
72
|
-
method: 'POST',
|
|
73
|
-
body: formData,
|
|
74
|
-
})];
|
|
75
|
-
case 2:
|
|
76
|
-
_a.sent();
|
|
77
|
-
return [3 /*break*/, 4];
|
|
78
|
-
case 3:
|
|
79
|
-
error_1 = _a.sent();
|
|
80
|
-
console.error('Upload failed:', error_1);
|
|
81
|
-
return [3 /*break*/, 4];
|
|
82
|
-
case 4: return [2 /*return*/];
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
}); };
|
|
86
|
-
mediaRecorderRef.current = mediaRecorder;
|
|
87
|
-
mediaRecorder.start(1000); // 1-second chunks
|
|
88
|
-
return [3 /*break*/, 3];
|
|
89
|
-
case 2:
|
|
90
|
-
err_1 = _b.sent();
|
|
91
|
-
console.error('Microphone access error:', err_1);
|
|
92
|
-
setIsRecording(false);
|
|
93
|
-
return [3 /*break*/, 3];
|
|
94
|
-
case 3: return [2 /*return*/];
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
}); };
|
|
98
|
-
React.useEffect(function () {
|
|
99
|
-
var _a, _b, _c;
|
|
100
|
-
if (isRecording) {
|
|
101
|
-
startRecording();
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
if (((_a = mediaRecorderRef.current) === null || _a === void 0 ? void 0 : _a.state) !== 'inactive') {
|
|
105
|
-
(_b = mediaRecorderRef.current) === null || _b === void 0 ? void 0 : _b.stop();
|
|
106
|
-
}
|
|
107
|
-
(_c = streamRef.current) === null || _c === void 0 ? void 0 : _c.getTracks().forEach(function (track) { return track.stop(); });
|
|
108
|
-
}
|
|
109
|
-
;
|
|
110
|
-
}, [isRecording]);
|
|
111
|
-
var readyStateToString = function (state) {
|
|
112
|
-
switch (state) {
|
|
113
|
-
case EventSource.CLOSED: return "CLOSED";
|
|
114
|
-
case EventSource.OPEN: return "OPEN";
|
|
115
|
-
case EventSource.CONNECTING: return "CONNECTING";
|
|
116
|
-
default:
|
|
117
|
-
return "UNKNOWN";
|
|
118
|
-
}
|
|
119
|
-
};
|
|
120
|
-
var eventPhaseToString = function (phase) {
|
|
121
|
-
switch (phase) {
|
|
122
|
-
case Event.NONE: return "NONE";
|
|
123
|
-
case Event.CAPTURING_PHASE: return "CAPTURING_PHASE";
|
|
124
|
-
case Event.AT_TARGET: return "AT_TARGET";
|
|
125
|
-
case Event.BUBBLING_PHASE: return "BUBBLING_PHASE";
|
|
126
|
-
default:
|
|
127
|
-
return "UNKNOWN";
|
|
128
|
-
}
|
|
129
|
-
};
|
|
130
|
-
React.useEffect(function () {
|
|
131
|
-
if (isRecording) {
|
|
132
|
-
sourceRef.current = new EventSource("http://api.flow/api/ai/transcribe-stream/".concat(streamIdRef.current));
|
|
133
|
-
sourceRef.current.onmessage = function (event) {
|
|
134
|
-
console.log("Message:", event.data);
|
|
135
|
-
console.log("ReadyState:", readyStateToString(sourceRef.current.readyState));
|
|
136
|
-
setTranscript(function (prev) { return prev + event.data + '\n'; });
|
|
137
|
-
};
|
|
138
|
-
sourceRef.current.onerror = function (err) {
|
|
139
|
-
console.error('SSE error:', err);
|
|
140
|
-
console.log('EventPhase:', eventPhaseToString(err.eventPhase));
|
|
141
|
-
console.log('ReadyState:', readyStateToString(sourceRef.current.readyState));
|
|
142
|
-
sourceRef.current.close();
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
return function () { var _a; return (_a = sourceRef.current) === null || _a === void 0 ? void 0 : _a.close(); };
|
|
146
|
-
}, [isRecording]);
|
|
147
|
-
var start = function () {
|
|
148
|
-
setTranscript('');
|
|
149
|
-
streamIdRef.current = Date.now().toString(); // Reset for new session
|
|
150
|
-
setIsRecording(true);
|
|
151
|
-
};
|
|
152
|
-
var stop = function () {
|
|
153
|
-
var _a;
|
|
154
|
-
setIsRecording(false);
|
|
155
|
-
if (((_a = mediaRecorderRef.current) === null || _a === void 0 ? void 0 : _a.state) !== 'inactive') {
|
|
156
|
-
mediaRecorderRef.current.stop();
|
|
157
|
-
}
|
|
158
|
-
};
|
|
159
|
-
var handleToggle = function () {
|
|
160
|
-
if (isRecording) {
|
|
161
|
-
stop();
|
|
162
|
-
}
|
|
163
|
-
else {
|
|
164
|
-
start();
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
return (React.createElement("div", null,
|
|
168
|
-
React.createElement(Fab, { title: "Record", active: isRecording, icon: SVG.Icons.Microphone, onClick: handleToggle })));
|
|
169
|
-
};
|
|
170
|
-
export { AudioInput };
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
2
|
-
import { useOpenAIRecorder } from '../useOpenAIRecorder';
|
|
3
|
-
var OpenAIAudioInput = function (props) {
|
|
4
|
-
var _a = useState('initial'), transcript = _a[0], setTranscript = _a[1];
|
|
5
|
-
var _b = useOpenAIRecorder(props.audio_url, props.transcribe_url, props.interval, setTranscript), recordingStatus = _b.recordingStatus, toggleRecording = _b.toggleRecording;
|
|
6
|
-
var getLabel = function (recordingStatus) {
|
|
7
|
-
switch (recordingStatus) {
|
|
8
|
-
case 'connecting': return "Connecting";
|
|
9
|
-
case 'recording': return "Recording";
|
|
10
|
-
default: return "Idle";
|
|
11
|
-
}
|
|
12
|
-
};
|
|
13
|
-
return (React.createElement("div", null,
|
|
14
|
-
React.createElement("button", { onClick: toggleRecording }, getLabel(recordingStatus)),
|
|
15
|
-
React.createElement("p", { style: { border: 'solid 1px blue', minHeight: '200px', color: 'white' } }, transcript)));
|
|
16
|
-
};
|
|
17
|
-
export { OpenAIAudioInput };
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
type TranscriptCallback = (text: string) => void;
|
|
2
|
-
type TRecordingStatus = 'idle' | 'connecting' | 'recording';
|
|
3
|
-
/**
|
|
4
|
-
* useOpenAIRecorder
|
|
5
|
-
*
|
|
6
|
-
* Custom React hook for audio recording and transcription using:
|
|
7
|
-
* - extendable-media-recorder with WAV encoder
|
|
8
|
-
* - Server-Sent Events (SSE) for real-time transcript updates
|
|
9
|
-
*
|
|
10
|
-
* @param `audio_url` - Backend URL for audio chunks
|
|
11
|
-
* @param `transcribe_url` - Backend URL for stream transcription
|
|
12
|
-
* @param interval - Chunk length (in milliseconds)
|
|
13
|
-
* @param onTranscript - Callback function called with the full running transcript
|
|
14
|
-
* @returns An object containing:
|
|
15
|
-
* - recordingStatus: current status of the recording lifecycle
|
|
16
|
-
* - toggleRecording: function to start or stop recording
|
|
17
|
-
*/
|
|
18
|
-
declare const useOpenAIRecorder: (audio_url: string, transcribe_url: string, interval: number, onTranscript: TranscriptCallback) => {
|
|
19
|
-
recordingStatus: TRecordingStatus;
|
|
20
|
-
toggleRecording: () => void;
|
|
21
|
-
};
|
|
22
|
-
export { useOpenAIRecorder, TRecordingStatus };
|
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
11
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
12
|
-
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
13
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
14
|
-
function step(op) {
|
|
15
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
16
|
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
17
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
18
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
19
|
-
switch (op[0]) {
|
|
20
|
-
case 0: case 1: t = op; break;
|
|
21
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
22
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
23
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
24
|
-
default:
|
|
25
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
26
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
27
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
28
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
29
|
-
if (t[2]) _.ops.pop();
|
|
30
|
-
_.trys.pop(); continue;
|
|
31
|
-
}
|
|
32
|
-
op = body.call(thisArg, _);
|
|
33
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
34
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
import { useEffect, useRef, useState, useCallback } from 'react';
|
|
38
|
-
import { MediaRecorder, register } from 'extendable-media-recorder';
|
|
39
|
-
import { connect } from 'extendable-media-recorder-wav-encoder';
|
|
40
|
-
/**
|
|
41
|
-
* useOpenAIRecorder
|
|
42
|
-
*
|
|
43
|
-
* Custom React hook for audio recording and transcription using:
|
|
44
|
-
* - extendable-media-recorder with WAV encoder
|
|
45
|
-
* - Server-Sent Events (SSE) for real-time transcript updates
|
|
46
|
-
*
|
|
47
|
-
* @param `audio_url` - Backend URL for audio chunks
|
|
48
|
-
* @param `transcribe_url` - Backend URL for stream transcription
|
|
49
|
-
* @param interval - Chunk length (in milliseconds)
|
|
50
|
-
* @param onTranscript - Callback function called with the full running transcript
|
|
51
|
-
* @returns An object containing:
|
|
52
|
-
* - recordingStatus: current status of the recording lifecycle
|
|
53
|
-
* - toggleRecording: function to start or stop recording
|
|
54
|
-
*/
|
|
55
|
-
var useOpenAIRecorder = function (audio_url, transcribe_url, interval, onTranscript) {
|
|
56
|
-
// React state to track whether we are idle, connecting, or recording
|
|
57
|
-
var _a = useState('idle'), recordingStatus = _a[0], setRecordingStatus = _a[1];
|
|
58
|
-
// Stores the MediaRecorder instance (from extendable-media-recorder)
|
|
59
|
-
var mediaRecorderRef = useRef(null);
|
|
60
|
-
// Flag to ensure we register the WAV encoder only once per page lifetime
|
|
61
|
-
var hasRegisteredEncoder = useRef(false);
|
|
62
|
-
// The MediaStream from the user's microphone
|
|
63
|
-
var streamRef = useRef(null);
|
|
64
|
-
// A unique ID to identify this recording session (used in SSE + POST)
|
|
65
|
-
var streamIdRef = useRef(Date.now().toString());
|
|
66
|
-
// SSE (Server-Sent Events) source to receive live transcript messages
|
|
67
|
-
var sourceRef = useRef(null);
|
|
68
|
-
// Stores the first audio header (first 44 bytes of .wav file)
|
|
69
|
-
var headerRef = useRef(null);
|
|
70
|
-
// Stores the full accumulated transcript text
|
|
71
|
-
var transcriptRef = useRef('');
|
|
72
|
-
/**
|
|
73
|
-
* Initializes audio capture and begins uploading chunks to the server
|
|
74
|
-
*/
|
|
75
|
-
var startRecording = function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
76
|
-
var _a, _b, mediaRecorder, err_1;
|
|
77
|
-
return __generator(this, function (_c) {
|
|
78
|
-
switch (_c.label) {
|
|
79
|
-
case 0:
|
|
80
|
-
setRecordingStatus('connecting');
|
|
81
|
-
_c.label = 1;
|
|
82
|
-
case 1:
|
|
83
|
-
_c.trys.push([1, 6, , 7]);
|
|
84
|
-
if (!!hasRegisteredEncoder.current) return [3 /*break*/, 4];
|
|
85
|
-
_a = register;
|
|
86
|
-
return [4 /*yield*/, connect()];
|
|
87
|
-
case 2: return [4 /*yield*/, _a.apply(void 0, [_c.sent()])];
|
|
88
|
-
case 3:
|
|
89
|
-
_c.sent();
|
|
90
|
-
hasRegisteredEncoder.current = true;
|
|
91
|
-
_c.label = 4;
|
|
92
|
-
case 4:
|
|
93
|
-
// Request microphone access
|
|
94
|
-
_b = streamRef;
|
|
95
|
-
return [4 /*yield*/, navigator.mediaDevices.getUserMedia({ audio: true })];
|
|
96
|
-
case 5:
|
|
97
|
-
// Request microphone access
|
|
98
|
-
_b.current = _c.sent();
|
|
99
|
-
mediaRecorder = new MediaRecorder(streamRef.current, {
|
|
100
|
-
mimeType: 'audio/wav',
|
|
101
|
-
});
|
|
102
|
-
// Triggered every time a chunk of audio is available
|
|
103
|
-
mediaRecorder.ondataavailable = function (event) { return __awaiter(void 0, void 0, void 0, function () {
|
|
104
|
-
var blob, formData, _a, content, err_2;
|
|
105
|
-
return __generator(this, function (_b) {
|
|
106
|
-
switch (_b.label) {
|
|
107
|
-
case 0:
|
|
108
|
-
if (!(event.data.size > 0)) return [3 /*break*/, 8];
|
|
109
|
-
blob = event.data;
|
|
110
|
-
formData = new FormData();
|
|
111
|
-
if (!!headerRef.current) return [3 /*break*/, 2];
|
|
112
|
-
_a = headerRef;
|
|
113
|
-
return [4 /*yield*/, blob.arrayBuffer()];
|
|
114
|
-
case 1:
|
|
115
|
-
_a.current = (_b.sent()).slice(0, 44);
|
|
116
|
-
return [3 /*break*/, 4];
|
|
117
|
-
case 2: return [4 /*yield*/, blob.arrayBuffer()];
|
|
118
|
-
case 3:
|
|
119
|
-
content = _b.sent();
|
|
120
|
-
blob = new Blob([headerRef.current, content], { type: blob.type });
|
|
121
|
-
_b.label = 4;
|
|
122
|
-
case 4:
|
|
123
|
-
// Append audio chunk and session ID to the form data
|
|
124
|
-
formData.append('audio', blob);
|
|
125
|
-
formData.append('stream_id', streamIdRef.current);
|
|
126
|
-
_b.label = 5;
|
|
127
|
-
case 5:
|
|
128
|
-
_b.trys.push([5, 7, , 8]);
|
|
129
|
-
return [4 /*yield*/, fetch(audio_url, {
|
|
130
|
-
method: 'POST',
|
|
131
|
-
body: formData,
|
|
132
|
-
})];
|
|
133
|
-
case 6:
|
|
134
|
-
_b.sent();
|
|
135
|
-
return [3 /*break*/, 8];
|
|
136
|
-
case 7:
|
|
137
|
-
err_2 = _b.sent();
|
|
138
|
-
console.error('Upload failed:', err_2);
|
|
139
|
-
return [3 /*break*/, 8];
|
|
140
|
-
case 8: return [2 /*return*/];
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
}); };
|
|
144
|
-
mediaRecorderRef.current = mediaRecorder;
|
|
145
|
-
// Start recording with intervals
|
|
146
|
-
mediaRecorder.start(interval);
|
|
147
|
-
// Recording setup is complete
|
|
148
|
-
setRecordingStatus('recording');
|
|
149
|
-
return [3 /*break*/, 7];
|
|
150
|
-
case 6:
|
|
151
|
-
err_1 = _c.sent();
|
|
152
|
-
console.error('Microphone access error:', err_1);
|
|
153
|
-
setRecordingStatus('idle');
|
|
154
|
-
return [3 /*break*/, 7];
|
|
155
|
-
case 7: return [2 /*return*/];
|
|
156
|
-
}
|
|
157
|
-
});
|
|
158
|
-
}); };
|
|
159
|
-
/**
|
|
160
|
-
* Starts listening for transcription updates via SSE
|
|
161
|
-
*/
|
|
162
|
-
var startTranscriptStream = function () {
|
|
163
|
-
// Open connection to transcript stream endpoint
|
|
164
|
-
sourceRef.current = new EventSource("".concat(transcribe_url, "/").concat(streamIdRef.current));
|
|
165
|
-
// Handle each incoming transcript message
|
|
166
|
-
sourceRef.current.onmessage = function (event) {
|
|
167
|
-
// Accumulate transcript and pass it to caller
|
|
168
|
-
transcriptRef.current += event.data + '\n';
|
|
169
|
-
onTranscript(transcriptRef.current);
|
|
170
|
-
};
|
|
171
|
-
// Handle SSE connection error
|
|
172
|
-
sourceRef.current.onerror = function (err) {
|
|
173
|
-
var _a;
|
|
174
|
-
console.error('SSE error:', err);
|
|
175
|
-
(_a = sourceRef.current) === null || _a === void 0 ? void 0 : _a.close();
|
|
176
|
-
};
|
|
177
|
-
};
|
|
178
|
-
/**
|
|
179
|
-
* Stops all active recording and streaming resources
|
|
180
|
-
*/
|
|
181
|
-
var stopRecording = function () {
|
|
182
|
-
var _a, _b, _c, _d;
|
|
183
|
-
// Stop media recorder if it's active
|
|
184
|
-
if (((_a = mediaRecorderRef.current) === null || _a === void 0 ? void 0 : _a.state) !== 'inactive') {
|
|
185
|
-
(_b = mediaRecorderRef.current) === null || _b === void 0 ? void 0 : _b.stop();
|
|
186
|
-
}
|
|
187
|
-
// Stop and release mic stream
|
|
188
|
-
(_c = streamRef.current) === null || _c === void 0 ? void 0 : _c.getTracks().forEach(function (track) { return track.stop(); });
|
|
189
|
-
// Close SSE stream
|
|
190
|
-
(_d = sourceRef.current) === null || _d === void 0 ? void 0 : _d.close();
|
|
191
|
-
// Clear all refs
|
|
192
|
-
mediaRecorderRef.current = null;
|
|
193
|
-
streamRef.current = null;
|
|
194
|
-
sourceRef.current = null;
|
|
195
|
-
setRecordingStatus('idle');
|
|
196
|
-
};
|
|
197
|
-
/**
|
|
198
|
-
* Public API to toggle between recording and stopped states
|
|
199
|
-
*/
|
|
200
|
-
var toggleRecording = useCallback(function () {
|
|
201
|
-
if (recordingStatus === 'recording' || recordingStatus === 'connecting') {
|
|
202
|
-
stopRecording();
|
|
203
|
-
}
|
|
204
|
-
else {
|
|
205
|
-
// Start a new session
|
|
206
|
-
transcriptRef.current = '';
|
|
207
|
-
streamIdRef.current = Date.now().toString(); // Unique session ID
|
|
208
|
-
startRecording();
|
|
209
|
-
startTranscriptStream();
|
|
210
|
-
}
|
|
211
|
-
}, [recordingStatus]);
|
|
212
|
-
/**
|
|
213
|
-
* Clean up everything when the component using this hook is unmounted
|
|
214
|
-
*/
|
|
215
|
-
useEffect(function () {
|
|
216
|
-
return function () { return stopRecording(); };
|
|
217
|
-
}, []);
|
|
218
|
-
return {
|
|
219
|
-
recordingStatus: recordingStatus, // current status: 'idle' | 'connecting' | 'recording'
|
|
220
|
-
toggleRecording: toggleRecording,
|
|
221
|
-
};
|
|
222
|
-
};
|
|
223
|
-
export { useOpenAIRecorder };
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { useOpenAIStream } from './useOpenAIStream';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { useOpenAIStream } from './useOpenAIStream';
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
interface IOpenAIStreamOptions {
|
|
2
|
-
temperature: number;
|
|
3
|
-
top_p: number;
|
|
4
|
-
}
|
|
5
|
-
/**
|
|
6
|
-
* Hook to stream OpenAI responses via a backend endpoint that acts as a proxy.
|
|
7
|
-
*
|
|
8
|
-
* @returns `stream`: a function that takes a prompt and streaming callback.
|
|
9
|
-
*/
|
|
10
|
-
declare const useOpenAIStream: (url: string, authToken?: string) => {
|
|
11
|
-
stream: (prompt: string, onText: (text: string) => Promise<void>, options?: IOpenAIStreamOptions) => Promise<void>;
|
|
12
|
-
cancel: () => void;
|
|
13
|
-
};
|
|
14
|
-
export { useOpenAIStream, IOpenAIStreamOptions };
|