agentgui 1.0.507 → 1.0.508
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/lib/tool-manager.js +57 -6
- package/package.json +1 -1
- package/static/index.html +9 -0
- package/static/js/client.js +48 -0
- package/static/js/stt-handler.js +138 -0
- package/static/js/voice.js +17 -110
package/lib/tool-manager.js
CHANGED
|
@@ -254,23 +254,74 @@ export async function checkForUpdates(toolId) {
|
|
|
254
254
|
|
|
255
255
|
const spawnBunxProc = (pkg, onProgress) => new Promise((resolve) => {
|
|
256
256
|
const cmd = isWindows ? 'bunx.cmd' : 'bunx';
|
|
257
|
-
const proc = spawn(cmd, [pkg], { stdio: ['pipe', 'pipe', 'pipe'], timeout: 300000, shell: isWindows });
|
|
258
257
|
let completed = false, stderr = '', stdout = '';
|
|
259
|
-
|
|
260
|
-
proc
|
|
261
|
-
|
|
258
|
+
let lastDataTime = Date.now();
|
|
259
|
+
let proc;
|
|
260
|
+
|
|
261
|
+
try {
|
|
262
|
+
proc = spawn(cmd, [pkg], { stdio: ['pipe', 'pipe', 'pipe'], timeout: 300000, shell: isWindows });
|
|
263
|
+
} catch (err) {
|
|
264
|
+
return resolve({ success: false, error: `Failed to spawn bunx: ${err.message}` });
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (!proc) {
|
|
268
|
+
return resolve({ success: false, error: 'Failed to spawn bunx process' });
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const timer = setTimeout(() => {
|
|
272
|
+
if (!completed) {
|
|
273
|
+
completed = true;
|
|
274
|
+
try { proc.kill('SIGKILL'); } catch (_) {}
|
|
275
|
+
resolve({ success: false, error: 'Timeout (5min)' });
|
|
276
|
+
}
|
|
277
|
+
}, 300000);
|
|
278
|
+
|
|
279
|
+
const heartbeatTimer = setInterval(() => {
|
|
280
|
+
if (completed) { clearInterval(heartbeatTimer); return; }
|
|
281
|
+
const timeSinceLastData = Date.now() - lastDataTime;
|
|
282
|
+
if (timeSinceLastData > 30000) {
|
|
283
|
+
console.warn(`[tool-manager] No output from bunx ${pkg} for ${timeSinceLastData}ms - process may be hung`);
|
|
284
|
+
}
|
|
285
|
+
}, 30000);
|
|
286
|
+
|
|
287
|
+
const onData = (d) => {
|
|
288
|
+
lastDataTime = Date.now();
|
|
289
|
+
if (onProgress) onProgress({ type: 'progress', data: d.toString() });
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
if (proc.stdout) proc.stdout.on('data', (d) => { stdout += d.toString(); onData(d); });
|
|
293
|
+
if (proc.stderr) proc.stderr.on('data', (d) => { stderr += d.toString(); onData(d); });
|
|
294
|
+
|
|
262
295
|
proc.on('close', (code) => {
|
|
263
296
|
clearTimeout(timer);
|
|
297
|
+
clearInterval(heartbeatTimer);
|
|
264
298
|
if (completed) return;
|
|
265
299
|
completed = true;
|
|
266
300
|
const output = stdout + stderr;
|
|
267
|
-
|
|
301
|
+
const successPatterns = [
|
|
302
|
+
code === 0,
|
|
303
|
+
output.includes('upgraded'),
|
|
304
|
+
output.includes('registered'),
|
|
305
|
+
output.includes('Hooks registered'),
|
|
306
|
+
output.includes('successfully'),
|
|
307
|
+
output.includes('Done'),
|
|
308
|
+
code === 0 && !output.includes('error')
|
|
309
|
+
];
|
|
310
|
+
if (successPatterns.some(p => p)) {
|
|
268
311
|
resolve({ success: true, error: null });
|
|
269
312
|
} else {
|
|
270
313
|
resolve({ success: false, error: output.substring(0, 1000) || 'Failed' });
|
|
271
314
|
}
|
|
272
315
|
});
|
|
273
|
-
|
|
316
|
+
|
|
317
|
+
proc.on('error', (err) => {
|
|
318
|
+
clearTimeout(timer);
|
|
319
|
+
clearInterval(heartbeatTimer);
|
|
320
|
+
if (!completed) {
|
|
321
|
+
completed = true;
|
|
322
|
+
resolve({ success: false, error: `Process error: ${err.message}` });
|
|
323
|
+
}
|
|
324
|
+
});
|
|
274
325
|
});
|
|
275
326
|
|
|
276
327
|
export async function install(toolId, onProgress) {
|
package/package.json
CHANGED
package/static/index.html
CHANGED
|
@@ -3231,6 +3231,14 @@
|
|
|
3231
3231
|
aria-label="Message input"
|
|
3232
3232
|
rows="1"
|
|
3233
3233
|
></textarea>
|
|
3234
|
+
<button class="voice-mic-btn" id="chatMicBtn" title="Record voice input" aria-label="Voice input">
|
|
3235
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
3236
|
+
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/>
|
|
3237
|
+
<path d="M19 10v2a7 7 0 0 1-14 0v-2"/>
|
|
3238
|
+
<line x1="12" y1="19" x2="12" y2="23"/>
|
|
3239
|
+
<line x1="8" y1="23" x2="16" y2="23"/>
|
|
3240
|
+
</svg>
|
|
3241
|
+
</button>
|
|
3234
3242
|
<button class="inject-btn" id="injectBtn" title="Inject instructions into running agent" aria-label="Inject instructions">
|
|
3235
3243
|
<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18">
|
|
3236
3244
|
<path d="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z"/>
|
|
@@ -3288,6 +3296,7 @@
|
|
|
3288
3296
|
<script defer src="/gm/js/terminal.js"></script>
|
|
3289
3297
|
<script defer src="/gm/js/script-runner.js"></script>
|
|
3290
3298
|
<script defer src="/gm/js/tools-manager.js"></script>
|
|
3299
|
+
<script defer src="/gm/js/stt-handler.js"></script>
|
|
3291
3300
|
<script defer src="/gm/js/client.js"></script>
|
|
3292
3301
|
<script type="module" src="/gm/js/voice.js"></script>
|
|
3293
3302
|
<script defer src="/gm/js/features.js"></script>
|
package/static/js/client.js
CHANGED
|
@@ -394,6 +394,8 @@ class AgentGUIClient {
|
|
|
394
394
|
this.ui.sendButton.addEventListener('click', () => this.startExecution());
|
|
395
395
|
}
|
|
396
396
|
|
|
397
|
+
this.setupChatMicButton();
|
|
398
|
+
|
|
397
399
|
this.ui.stopButton = document.getElementById('stopBtn');
|
|
398
400
|
this.ui.injectButton = document.getElementById('injectBtn');
|
|
399
401
|
|
|
@@ -486,6 +488,52 @@ class AgentGUIClient {
|
|
|
486
488
|
});
|
|
487
489
|
}
|
|
488
490
|
|
|
491
|
+
setupChatMicButton() {
|
|
492
|
+
const chatMicBtn = document.getElementById('chatMicBtn');
|
|
493
|
+
if (!chatMicBtn) return;
|
|
494
|
+
|
|
495
|
+
let isRecording = false;
|
|
496
|
+
|
|
497
|
+
chatMicBtn.addEventListener('mousedown', async (e) => {
|
|
498
|
+
e.preventDefault();
|
|
499
|
+
if (isRecording) return;
|
|
500
|
+
isRecording = true;
|
|
501
|
+
chatMicBtn.classList.add('recording');
|
|
502
|
+
const result = await window.STTHandler.startRecording();
|
|
503
|
+
if (!result.success) {
|
|
504
|
+
isRecording = false;
|
|
505
|
+
chatMicBtn.classList.remove('recording');
|
|
506
|
+
alert('Microphone access denied: ' + result.error);
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
chatMicBtn.addEventListener('mouseup', async (e) => {
|
|
511
|
+
e.preventDefault();
|
|
512
|
+
if (!isRecording) return;
|
|
513
|
+
isRecording = false;
|
|
514
|
+
chatMicBtn.classList.remove('recording');
|
|
515
|
+
const result = await window.STTHandler.stopRecording();
|
|
516
|
+
if (result.success) {
|
|
517
|
+
if (this.ui.messageInput) {
|
|
518
|
+
this.ui.messageInput.value = result.text;
|
|
519
|
+
}
|
|
520
|
+
} else {
|
|
521
|
+
alert('Transcription failed: ' + result.error);
|
|
522
|
+
}
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
chatMicBtn.addEventListener('mouseleave', async (e) => {
|
|
526
|
+
if (isRecording) {
|
|
527
|
+
isRecording = false;
|
|
528
|
+
chatMicBtn.classList.remove('recording');
|
|
529
|
+
const result = await window.STTHandler.stopRecording();
|
|
530
|
+
if (result.success && this.ui.messageInput) {
|
|
531
|
+
this.ui.messageInput.value = result.text;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
|
|
489
537
|
/**
|
|
490
538
|
* Connect to WebSocket
|
|
491
539
|
*/
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
(function() {
|
|
2
|
+
var BASE = window.__BASE_URL || '';
|
|
3
|
+
var isRecording = false;
|
|
4
|
+
var mediaStream = null;
|
|
5
|
+
var audioContext = null;
|
|
6
|
+
var workletNode = null;
|
|
7
|
+
var recordedChunks = [];
|
|
8
|
+
var TARGET_SAMPLE_RATE = 16000;
|
|
9
|
+
|
|
10
|
+
window.STTHandler = {
|
|
11
|
+
isRecording: function() { return isRecording; },
|
|
12
|
+
|
|
13
|
+
startRecording: async function() {
|
|
14
|
+
if (isRecording) return;
|
|
15
|
+
try {
|
|
16
|
+
mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
17
|
+
audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
|
18
|
+
var source = audioContext.createMediaStreamSource(mediaStream);
|
|
19
|
+
recordedChunks = [];
|
|
20
|
+
await audioContext.audioWorklet.addModule(BASE + '/js/audio-recorder-processor.js');
|
|
21
|
+
workletNode = new AudioWorkletNode(audioContext, 'recorder-processor');
|
|
22
|
+
workletNode.port.onmessage = function(e) {
|
|
23
|
+
recordedChunks.push(e.data);
|
|
24
|
+
};
|
|
25
|
+
source.connect(workletNode);
|
|
26
|
+
isRecording = true;
|
|
27
|
+
return { success: true };
|
|
28
|
+
} catch (e) {
|
|
29
|
+
isRecording = false;
|
|
30
|
+
return { success: false, error: e.message };
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
stopRecording: async function() {
|
|
35
|
+
if (!isRecording) return { success: false, error: 'Not recording' };
|
|
36
|
+
isRecording = false;
|
|
37
|
+
|
|
38
|
+
if (workletNode) {
|
|
39
|
+
workletNode.port.postMessage('stop');
|
|
40
|
+
workletNode.disconnect();
|
|
41
|
+
workletNode = null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (mediaStream) {
|
|
45
|
+
mediaStream.getTracks().forEach(function(t) { t.stop(); });
|
|
46
|
+
mediaStream = null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
var sourceSampleRate = audioContext ? audioContext.sampleRate : 48000;
|
|
50
|
+
if (audioContext) {
|
|
51
|
+
audioContext.close().catch(function() {});
|
|
52
|
+
audioContext = null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (recordedChunks.length === 0) {
|
|
56
|
+
return { success: false, error: 'No audio recorded' };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
var totalLen = 0;
|
|
60
|
+
for (var i = 0; i < recordedChunks.length; i++) totalLen += recordedChunks[i].length;
|
|
61
|
+
var merged = new Float32Array(totalLen);
|
|
62
|
+
var offset = 0;
|
|
63
|
+
for (var j = 0; j < recordedChunks.length; j++) {
|
|
64
|
+
merged.set(recordedChunks[j], offset);
|
|
65
|
+
offset += recordedChunks[j].length;
|
|
66
|
+
}
|
|
67
|
+
recordedChunks = [];
|
|
68
|
+
|
|
69
|
+
var resampled = resampleBuffer(merged, sourceSampleRate, TARGET_SAMPLE_RATE);
|
|
70
|
+
var wavBuffer = encodeWav(resampled, TARGET_SAMPLE_RATE);
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
var resp = await fetch(BASE + '/api/stt', {
|
|
74
|
+
method: 'POST',
|
|
75
|
+
headers: { 'Content-Type': 'audio/wav' },
|
|
76
|
+
body: wavBuffer
|
|
77
|
+
});
|
|
78
|
+
var data = await resp.json();
|
|
79
|
+
if (data.text) {
|
|
80
|
+
return { success: true, text: data.text };
|
|
81
|
+
} else if (data.error) {
|
|
82
|
+
return { success: false, error: data.error };
|
|
83
|
+
} else {
|
|
84
|
+
return { success: false, error: 'No transcription returned' };
|
|
85
|
+
}
|
|
86
|
+
} catch (e) {
|
|
87
|
+
return { success: false, error: 'Transcription failed: ' + e.message };
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
function resampleBuffer(inputBuffer, fromRate, toRate) {
|
|
93
|
+
if (fromRate === toRate) return inputBuffer;
|
|
94
|
+
var ratio = fromRate / toRate;
|
|
95
|
+
var newLen = Math.round(inputBuffer.length / ratio);
|
|
96
|
+
var result = new Float32Array(newLen);
|
|
97
|
+
for (var i = 0; i < newLen; i++) {
|
|
98
|
+
var srcIdx = i * ratio;
|
|
99
|
+
var lo = Math.floor(srcIdx);
|
|
100
|
+
var hi = Math.min(lo + 1, inputBuffer.length - 1);
|
|
101
|
+
var frac = srcIdx - lo;
|
|
102
|
+
result[i] = inputBuffer[lo] * (1 - frac) + inputBuffer[hi] * frac;
|
|
103
|
+
}
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function encodeWav(float32Audio, sampleRate) {
|
|
108
|
+
var numSamples = float32Audio.length;
|
|
109
|
+
var bytesPerSample = 2;
|
|
110
|
+
var dataSize = numSamples * bytesPerSample;
|
|
111
|
+
var buffer = new ArrayBuffer(44 + dataSize);
|
|
112
|
+
var view = new DataView(buffer);
|
|
113
|
+
|
|
114
|
+
function writeStr(off, str) {
|
|
115
|
+
for (var i = 0; i < str.length; i++) view.setUint8(off + i, str.charCodeAt(i));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
writeStr(0, 'RIFF');
|
|
119
|
+
view.setUint32(4, 36 + dataSize, true);
|
|
120
|
+
writeStr(8, 'WAVE');
|
|
121
|
+
writeStr(12, 'fmt ');
|
|
122
|
+
view.setUint32(16, 16, true);
|
|
123
|
+
view.setUint16(20, 1, true);
|
|
124
|
+
view.setUint16(22, 1, true);
|
|
125
|
+
view.setUint32(24, sampleRate, true);
|
|
126
|
+
view.setUint32(28, sampleRate * bytesPerSample, true);
|
|
127
|
+
view.setUint16(32, bytesPerSample, true);
|
|
128
|
+
view.setUint16(34, 16, true);
|
|
129
|
+
writeStr(36, 'data');
|
|
130
|
+
view.setUint32(40, dataSize, true);
|
|
131
|
+
|
|
132
|
+
for (var i = 0; i < numSamples; i++) {
|
|
133
|
+
var s = Math.max(-1, Math.min(1, float32Audio[i]));
|
|
134
|
+
view.setInt16(44 + i * 2, s < 0 ? s * 32768 : s * 32767, true);
|
|
135
|
+
}
|
|
136
|
+
return buffer;
|
|
137
|
+
}
|
|
138
|
+
})();
|
package/static/js/voice.js
CHANGED
|
@@ -271,49 +271,6 @@
|
|
|
271
271
|
}
|
|
272
272
|
}
|
|
273
273
|
|
|
274
|
-
function resampleBuffer(inputBuffer, fromRate, toRate) {
|
|
275
|
-
if (fromRate === toRate) return inputBuffer;
|
|
276
|
-
var ratio = fromRate / toRate;
|
|
277
|
-
var newLen = Math.round(inputBuffer.length / ratio);
|
|
278
|
-
var result = new Float32Array(newLen);
|
|
279
|
-
for (var i = 0; i < newLen; i++) {
|
|
280
|
-
var srcIdx = i * ratio;
|
|
281
|
-
var lo = Math.floor(srcIdx);
|
|
282
|
-
var hi = Math.min(lo + 1, inputBuffer.length - 1);
|
|
283
|
-
var frac = srcIdx - lo;
|
|
284
|
-
result[i] = inputBuffer[lo] * (1 - frac) + inputBuffer[hi] * frac;
|
|
285
|
-
}
|
|
286
|
-
return result;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
function encodeWav(float32Audio, sampleRate) {
|
|
290
|
-
var numSamples = float32Audio.length;
|
|
291
|
-
var bytesPerSample = 2;
|
|
292
|
-
var dataSize = numSamples * bytesPerSample;
|
|
293
|
-
var buffer = new ArrayBuffer(44 + dataSize);
|
|
294
|
-
var view = new DataView(buffer);
|
|
295
|
-
function writeStr(off, str) {
|
|
296
|
-
for (var i = 0; i < str.length; i++) view.setUint8(off + i, str.charCodeAt(i));
|
|
297
|
-
}
|
|
298
|
-
writeStr(0, 'RIFF');
|
|
299
|
-
view.setUint32(4, 36 + dataSize, true);
|
|
300
|
-
writeStr(8, 'WAVE');
|
|
301
|
-
writeStr(12, 'fmt ');
|
|
302
|
-
view.setUint32(16, 16, true);
|
|
303
|
-
view.setUint16(20, 1, true);
|
|
304
|
-
view.setUint16(22, 1, true);
|
|
305
|
-
view.setUint32(24, sampleRate, true);
|
|
306
|
-
view.setUint32(28, sampleRate * bytesPerSample, true);
|
|
307
|
-
view.setUint16(32, bytesPerSample, true);
|
|
308
|
-
view.setUint16(34, 16, true);
|
|
309
|
-
writeStr(36, 'data');
|
|
310
|
-
view.setUint32(40, dataSize, true);
|
|
311
|
-
for (var i = 0; i < numSamples; i++) {
|
|
312
|
-
var s = Math.max(-1, Math.min(1, float32Audio[i]));
|
|
313
|
-
view.setInt16(44 + i * 2, s < 0 ? s * 32768 : s * 32767, true);
|
|
314
|
-
}
|
|
315
|
-
return buffer;
|
|
316
|
-
}
|
|
317
274
|
|
|
318
275
|
async function startRecording() {
|
|
319
276
|
if (isRecording) return;
|
|
@@ -326,23 +283,13 @@
|
|
|
326
283
|
el.setAttribute('data-final', '');
|
|
327
284
|
}
|
|
328
285
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
|
332
|
-
var source = audioContext.createMediaStreamSource(mediaStream);
|
|
333
|
-
recordedChunks = [];
|
|
334
|
-
await audioContext.audioWorklet.addModule(BASE + '/js/audio-recorder-processor.js');
|
|
335
|
-
workletNode = new AudioWorkletNode(audioContext, 'recorder-processor');
|
|
336
|
-
workletNode.port.onmessage = function(e) {
|
|
337
|
-
recordedChunks.push(e.data);
|
|
338
|
-
};
|
|
339
|
-
source.connect(workletNode);
|
|
286
|
+
var result = await window.STTHandler.startRecording();
|
|
287
|
+
if (result.success) {
|
|
340
288
|
isRecording = true;
|
|
341
289
|
var micBtn = document.getElementById('voiceMicBtn');
|
|
342
290
|
if (micBtn) micBtn.classList.add('recording');
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
if (el) el.textContent = 'Mic access denied or unavailable: ' + e.message;
|
|
291
|
+
} else {
|
|
292
|
+
if (el) el.textContent = 'Mic access denied: ' + result.error;
|
|
346
293
|
}
|
|
347
294
|
}
|
|
348
295
|
|
|
@@ -352,24 +299,7 @@
|
|
|
352
299
|
var micBtn = document.getElementById('voiceMicBtn');
|
|
353
300
|
if (micBtn) micBtn.classList.remove('recording');
|
|
354
301
|
var el = document.getElementById('voiceTranscript');
|
|
355
|
-
|
|
356
|
-
if (mediaStream) {
|
|
357
|
-
mediaStream.getTracks().forEach(function(t) { t.stop(); });
|
|
358
|
-
mediaStream = null;
|
|
359
|
-
}
|
|
360
|
-
var sourceSampleRate = audioContext ? audioContext.sampleRate : 48000;
|
|
361
|
-
if (audioContext) { audioContext.close().catch(function() {}); audioContext = null; }
|
|
362
|
-
if (recordedChunks.length === 0) return;
|
|
363
|
-
var totalLen = 0;
|
|
364
|
-
for (var i = 0; i < recordedChunks.length; i++) totalLen += recordedChunks[i].length;
|
|
365
|
-
var merged = new Float32Array(totalLen);
|
|
366
|
-
var offset = 0;
|
|
367
|
-
for (var j = 0; j < recordedChunks.length; j++) {
|
|
368
|
-
merged.set(recordedChunks[j], offset);
|
|
369
|
-
offset += recordedChunks[j].length;
|
|
370
|
-
}
|
|
371
|
-
recordedChunks = [];
|
|
372
|
-
var resampled = resampleBuffer(merged, sourceSampleRate, TARGET_SAMPLE_RATE);
|
|
302
|
+
|
|
373
303
|
if (el) {
|
|
374
304
|
if (el.value !== undefined) {
|
|
375
305
|
el.value = 'Transcribing...';
|
|
@@ -377,46 +307,23 @@
|
|
|
377
307
|
el.textContent = 'Transcribing...';
|
|
378
308
|
}
|
|
379
309
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
if (el) {
|
|
390
|
-
if (el.value !== undefined) {
|
|
391
|
-
el.value = data.text;
|
|
392
|
-
} else {
|
|
393
|
-
el.textContent = data.text;
|
|
394
|
-
el.setAttribute('data-final', data.text);
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
} else if (data.error) {
|
|
398
|
-
if (el) {
|
|
399
|
-
if (el.value !== undefined) {
|
|
400
|
-
el.value = 'Error: ' + data.error;
|
|
401
|
-
} else {
|
|
402
|
-
el.textContent = 'Error: ' + data.error;
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
} else {
|
|
406
|
-
if (el) {
|
|
407
|
-
if (el.value !== undefined) {
|
|
408
|
-
el.value = '';
|
|
409
|
-
} else {
|
|
410
|
-
el.textContent = '';
|
|
411
|
-
}
|
|
310
|
+
|
|
311
|
+
var result = await window.STTHandler.stopRecording();
|
|
312
|
+
if (result.success) {
|
|
313
|
+
if (el) {
|
|
314
|
+
if (el.value !== undefined) {
|
|
315
|
+
el.value = result.text;
|
|
316
|
+
} else {
|
|
317
|
+
el.textContent = result.text;
|
|
318
|
+
el.setAttribute('data-final', result.text);
|
|
412
319
|
}
|
|
413
320
|
}
|
|
414
|
-
}
|
|
321
|
+
} else {
|
|
415
322
|
if (el) {
|
|
416
323
|
if (el.value !== undefined) {
|
|
417
|
-
el.value = '
|
|
324
|
+
el.value = 'Error: ' + result.error;
|
|
418
325
|
} else {
|
|
419
|
-
el.textContent = '
|
|
326
|
+
el.textContent = 'Error: ' + result.error;
|
|
420
327
|
}
|
|
421
328
|
}
|
|
422
329
|
}
|