@ourlu/assistant-sdk 0.2.3 → 0.2.5

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 (51) hide show
  1. package/dist/iife/audio.v1.028c93fe.js +160 -0
  2. package/dist/iife/audio.v1.051ececf.js +183 -0
  3. package/dist/iife/audio.v1.200db1a6.js +208 -0
  4. package/dist/iife/audio.v1.20858b08.js +191 -0
  5. package/dist/iife/audio.v1.2690e445.js +179 -0
  6. package/dist/iife/audio.v1.2742ff12.js +188 -0
  7. package/dist/iife/audio.v1.70af81b3.js +191 -0
  8. package/dist/iife/audio.v1.95146620.js +1 -1
  9. package/dist/iife/audio.v1.b330baaf.js +166 -0
  10. package/dist/iife/audio.v1.bb9c2d88.js +181 -0
  11. package/dist/iife/audio.v1.ea7571b2.js +182 -0
  12. package/dist/iife/audio.v1.fc0aa8af.js +191 -0
  13. package/dist/iife/audio.v1.js +107 -95
  14. package/dist/iife/engine.v1.2b5bb43b.js +735 -0
  15. package/dist/iife/engine.v1.3b09dc20.js +3 -3
  16. package/dist/iife/engine.v1.56074e5a.js +769 -0
  17. package/dist/iife/engine.v1.61c10e6c.js +770 -0
  18. package/dist/iife/engine.v1.773fc15d.js +2 -2
  19. package/dist/iife/engine.v1.80d2230f.js +770 -0
  20. package/dist/iife/engine.v1.940ba9ea.js +764 -0
  21. package/dist/iife/engine.v1.99a33ee2.js +767 -0
  22. package/dist/iife/engine.v1.9ca6b7ec.js +756 -0
  23. package/dist/iife/engine.v1.a1f7dea2.js +764 -0
  24. package/dist/iife/engine.v1.c0c00bd0.js +721 -0
  25. package/dist/iife/engine.v1.c127656e.js +820 -0
  26. package/dist/iife/engine.v1.c54c9a1a.js +770 -0
  27. package/dist/iife/engine.v1.d1052e81.js +770 -0
  28. package/dist/iife/engine.v1.f6d23a0f.js +770 -0
  29. package/dist/iife/engine.v1.js +135 -36
  30. package/dist/iife/loader.v1.js +5 -1
  31. package/dist/iife/signalement.v1.d321dfde.js +518 -0
  32. package/dist/iife/signalement.v1.js +518 -0
  33. package/dist/iife/ui.v1.00abf020.js +895 -0
  34. package/dist/iife/ui.v1.5d2d4504.js +942 -0
  35. package/dist/iife/ui.v1.6afac75f.js +944 -0
  36. package/dist/iife/ui.v1.6becaa84.js +895 -0
  37. package/dist/iife/ui.v1.6c9e4995.js +895 -0
  38. package/dist/iife/ui.v1.7fb4db0b.js +935 -0
  39. package/dist/iife/ui.v1.88bf5494.js +898 -0
  40. package/dist/iife/ui.v1.923a4e6d.js +937 -0
  41. package/dist/iife/ui.v1.9bfe2815.js +942 -0
  42. package/dist/iife/ui.v1.a8cfe724.js +900 -0
  43. package/dist/iife/ui.v1.c58e1d58.js +959 -0
  44. package/dist/iife/ui.v1.cdfe9a45.js +919 -0
  45. package/dist/iife/ui.v1.e007c7c4.js +926 -0
  46. package/dist/iife/ui.v1.e24ba2bd.js +903 -0
  47. package/dist/iife/ui.v1.f1d8e998.js +903 -0
  48. package/dist/iife/ui.v1.fc52b520.js +895 -0
  49. package/dist/iife/ui.v1.js +154 -147
  50. package/dist/iife/widget-manifest.json +4 -3
  51. package/package.json +2 -1
@@ -0,0 +1,182 @@
1
+ (function() {
2
+ "use strict";
3
+ var runtime = window.__OurluWidgetRuntimeV1 || (window.__OurluWidgetRuntimeV1 = {});
4
+ if (!runtime.utils) {
5
+ throw new Error("Widget runtime utils module must be loaded first.");
6
+ }
7
+
8
+ var resolveRecorderMimeType = runtime.utils.resolveRecorderMimeType;
9
+ var encodeArrayBufferToBase64 = runtime.utils.encodeArrayBufferToBase64;
10
+
11
+ function WidgetAudioManager(state, api, ui) {
12
+ this.state = state;
13
+ this.api = api;
14
+ this.ui = ui;
15
+ this.stream = null;
16
+ this.recorder = null;
17
+ this.chunks = [];
18
+ this._recording = false;
19
+ this._processing = false;
20
+ }
21
+
22
+ WidgetAudioManager.prototype.isSupported = function() {
23
+ return !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia && typeof MediaRecorder !== "undefined");
24
+ };
25
+
26
+ WidgetAudioManager.prototype.toggle = async function() {
27
+ if (this._processing) return;
28
+ if (this._recording) return this.stop();
29
+ return this.start();
30
+ };
31
+
32
+ WidgetAudioManager.prototype.start = async function() {
33
+ if (!this.isSupported() || this.state.sending || this._recording || this._processing) return;
34
+ this.ui.showError("");
35
+ this.ui.setInput("");
36
+ this.ui.setMicListening(true);
37
+ try {
38
+ this.stream = await navigator.mediaDevices.getUserMedia({ audio: true });
39
+ this.chunks = [];
40
+ var mimeType = resolveRecorderMimeType();
41
+ this.recorder = new MediaRecorder(this.stream, mimeType ? { mimeType: mimeType } : {});
42
+ var self = this;
43
+ this.recorder.ondataavailable = function(event) {
44
+ if (event.data && event.data.size > 0) self.chunks.push(event.data);
45
+ };
46
+ this.recorder.start(250);
47
+ this._recording = true;
48
+ this.state.listening = true;
49
+ } catch (error) {
50
+ this.cleanupMedia();
51
+ this._recording = false;
52
+ this.state.listening = false;
53
+ this.ui.setMicListening(false);
54
+ this.ui.showError("Erreur démarrage micro : " + (error.message || error));
55
+ }
56
+ };
57
+
58
+ WidgetAudioManager.prototype.stop = async function() {
59
+ if (!this._recording) return;
60
+ this._recording = false;
61
+ this._processing = true;
62
+ this.state.listening = false;
63
+ this.ui.setMicListening(false);
64
+ this.ui.setComposerDisabled(true);
65
+ try {
66
+ var blob = await this.collectRecordedBlob();
67
+ this.cleanupMedia();
68
+ if (!blob || blob.size === 0) {
69
+ this.ui.showError("Aucun audio enregistré.");
70
+ return;
71
+ }
72
+ this.ui.setInputPlaceholder("Transcription en cours…");
73
+ var base64 = encodeArrayBufferToBase64(await blob.arrayBuffer());
74
+ var sessionId = await this.api.ensureSession();
75
+ var audioSession = await this.api.startAudioSession(sessionId);
76
+ var audioSessionId = audioSession.audio_session_id || "";
77
+ await this.api.sendAudioChunk(sessionId, audioSessionId, base64);
78
+ var transcription = this.openTranscriptionListener(sessionId, audioSessionId);
79
+ await transcription.connected;
80
+ await this.api.closeAudioSession(sessionId, audioSessionId);
81
+ var transcribedText = await transcription.result;
82
+ this.ui.setInputPlaceholder("");
83
+ if (transcribedText) {
84
+ this.ui.setInput(transcribedText);
85
+ }
86
+ } catch (error) {
87
+ this.cleanupMedia();
88
+ this.ui.showError("Erreur transcription audio : " + (error.message || error));
89
+ } finally {
90
+ this._processing = false;
91
+ this.ui.setInputPlaceholder("");
92
+ this.ui.setComposerDisabled(false);
93
+ }
94
+ };
95
+
96
+ WidgetAudioManager.prototype.openTranscriptionListener = function(sessionId, audioSessionId) {
97
+ var resolved = false;
98
+ var stopStream = null;
99
+ var resolveResult, rejectResult, resolveConnected;
100
+ var resultPromise = new Promise(function(res, rej) { resolveResult = res; rejectResult = rej; });
101
+ var connectedPromise = new Promise(function(res) { resolveConnected = res; });
102
+ var timeout = setTimeout(function() {
103
+ if (!resolved) {
104
+ resolved = true;
105
+ if (stopStream) stopStream();
106
+ rejectResult(new Error("Transcription audio : délai dépassé."));
107
+ }
108
+ }, 30000);
109
+ function belongsToCurrentSession(payload) {
110
+ if (!audioSessionId) return true;
111
+ var payloadAudioId = payload && payload.audio_session_id ? payload.audio_session_id : "";
112
+ return !payloadAudioId || payloadAudioId === audioSessionId;
113
+ }
114
+ this.api.streamAudioDraft(sessionId, {
115
+ onDelta: function() {},
116
+ onComplete: function(payload) {
117
+ if (resolved || !belongsToCurrentSession(payload)) return;
118
+ resolved = true;
119
+ clearTimeout(timeout);
120
+ if (stopStream) stopStream();
121
+ resolveResult(payload && payload.text ? String(payload.text).trim() : "");
122
+ },
123
+ onError: function(payload) {
124
+ if (resolved || !belongsToCurrentSession(payload)) return;
125
+ resolved = true;
126
+ clearTimeout(timeout);
127
+ if (stopStream) stopStream();
128
+ rejectResult(new Error((payload && payload.message) || "Erreur transcription audio"));
129
+ },
130
+ onTransportError: function(message) {
131
+ if (resolved) return;
132
+ var normalized = String(message || "").toLowerCase();
133
+ var isAbort = normalized.indexOf("aborted") !== -1 || normalized.indexOf("aborterror") !== -1;
134
+ if (isAbort) return;
135
+ resolved = true;
136
+ clearTimeout(timeout);
137
+ if (stopStream) stopStream();
138
+ rejectResult(new Error(message || "Erreur stream audio"));
139
+ }
140
+ }).then(function(stop) {
141
+ stopStream = stop;
142
+ resolveConnected();
143
+ }).catch(function(err) {
144
+ resolveConnected();
145
+ if (!resolved) {
146
+ resolved = true;
147
+ clearTimeout(timeout);
148
+ rejectResult(err);
149
+ }
150
+ });
151
+ return { connected: connectedPromise, result: resultPromise };
152
+ };
153
+
154
+ WidgetAudioManager.prototype.collectRecordedBlob = function() {
155
+ var self = this;
156
+ if (!this.recorder) return Promise.resolve(new Blob());
157
+ return new Promise(function(resolve) {
158
+ var recorder = self.recorder;
159
+ function done() {
160
+ var blob = new Blob(self.chunks, { type: recorder.mimeType || "audio/webm" });
161
+ resolve(blob);
162
+ }
163
+ if (recorder.state === "inactive") return done();
164
+ recorder.addEventListener("stop", done, { once: true });
165
+ recorder.stop();
166
+ });
167
+ };
168
+
169
+ WidgetAudioManager.prototype.cleanupMedia = function() {
170
+ if (this.recorder) {
171
+ this.recorder.ondataavailable = null;
172
+ this.recorder = null;
173
+ }
174
+ if (this.stream) {
175
+ this.stream.getTracks().forEach(function(track) { track.stop(); });
176
+ this.stream = null;
177
+ }
178
+ this.chunks = [];
179
+ };
180
+
181
+ runtime.WidgetAudioManager = WidgetAudioManager;
182
+ })();
@@ -0,0 +1,191 @@
1
+ (function() {
2
+ "use strict";
3
+ var runtime = window.__OurluWidgetRuntimeV1 || (window.__OurluWidgetRuntimeV1 = {});
4
+ if (!runtime.utils) {
5
+ throw new Error("Widget runtime utils module must be loaded first.");
6
+ }
7
+
8
+ var resolveRecorderMimeType = runtime.utils.resolveRecorderMimeType;
9
+ var encodeArrayBufferToBase64 = runtime.utils.encodeArrayBufferToBase64;
10
+
11
+ function WidgetAudioManager(state, api, ui) {
12
+ this.state = state;
13
+ this.api = api;
14
+ this.ui = ui;
15
+ this.stream = null;
16
+ this.recorder = null;
17
+ this.chunks = [];
18
+ this._recording = false;
19
+ this._processing = false;
20
+ }
21
+
22
+ WidgetAudioManager.prototype.isSupported = function() {
23
+ return !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia && typeof MediaRecorder !== "undefined");
24
+ };
25
+
26
+ WidgetAudioManager.prototype.toggle = async function() {
27
+ if (this._processing) return;
28
+ if (this._recording) return this.stop();
29
+ return this.start();
30
+ };
31
+
32
+ WidgetAudioManager.prototype.start = async function() {
33
+ if (!this.isSupported() || this.state.sending || this._recording || this._processing) return;
34
+ this.ui.showError("");
35
+ this.ui.setInput("");
36
+ this.ui.setMicListening(true);
37
+ try {
38
+ this.stream = await navigator.mediaDevices.getUserMedia({ audio: true });
39
+ this.chunks = [];
40
+ var mimeType = resolveRecorderMimeType();
41
+ this.recorder = new MediaRecorder(this.stream, mimeType ? { mimeType: mimeType } : {});
42
+ var self = this;
43
+ this.recorder.ondataavailable = function(event) {
44
+ if (event.data && event.data.size > 0) self.chunks.push(event.data);
45
+ };
46
+ this.recorder.start(250);
47
+ this._recording = true;
48
+ this.state.listening = true;
49
+ } catch (error) {
50
+ this.cleanupMedia();
51
+ this._recording = false;
52
+ this.state.listening = false;
53
+ this.ui.setMicListening(false);
54
+ this.ui.showError("Erreur démarrage micro : " + (error.message || error));
55
+ }
56
+ };
57
+
58
+ WidgetAudioManager.prototype.stop = async function() {
59
+ if (!this._recording) return;
60
+ this._recording = false;
61
+ this._processing = true;
62
+ this.state.listening = false;
63
+ this.ui.setMicListening(false);
64
+ this.ui.setComposerDisabled(true);
65
+ this.ui.setInputPlaceholder("Transcription en cours…");
66
+ try {
67
+ var blobAndSession = await Promise.all([
68
+ this.collectRecordedBlob(),
69
+ this.api.ensureSession()
70
+ ]);
71
+ var blob = blobAndSession[0];
72
+ var sessionId = blobAndSession[1];
73
+ this.cleanupMedia();
74
+ if (!blob || blob.size === 0) {
75
+ this.ui.showError("Aucun audio enregistré.");
76
+ return;
77
+ }
78
+ var base64 = encodeArrayBufferToBase64(await blob.arrayBuffer());
79
+ var transcription = this.openTranscriptionListener(sessionId);
80
+ await transcription.connected;
81
+ var audioSession = await this.api.startAudioSession(sessionId);
82
+ var audioSessionId = audioSession.audio_session_id || "";
83
+ transcription.setAudioSessionId(audioSessionId);
84
+ await this.api.sendAudioChunk(sessionId, audioSessionId, base64);
85
+ await this.api.closeAudioSession(sessionId, audioSessionId);
86
+ var transcribedText = await transcription.result;
87
+ if (transcribedText) {
88
+ this.ui.setInput(transcribedText);
89
+ }
90
+ } catch (error) {
91
+ this.cleanupMedia();
92
+ this.ui.showError("Erreur transcription audio : " + (error.message || error));
93
+ } finally {
94
+ this._processing = false;
95
+ this.ui.setInputPlaceholder("");
96
+ this.ui.setComposerDisabled(false);
97
+ }
98
+ };
99
+
100
+ WidgetAudioManager.prototype.openTranscriptionListener = function(sessionId) {
101
+ var resolved = false;
102
+ var stopStream = null;
103
+ var expectedAudioSessionId = "";
104
+ var resolveResult, rejectResult, resolveConnected;
105
+ var resultPromise = new Promise(function(res, rej) { resolveResult = res; rejectResult = rej; });
106
+ var connectedPromise = new Promise(function(res) { resolveConnected = res; });
107
+ var timeout = setTimeout(function() {
108
+ if (!resolved) {
109
+ resolved = true;
110
+ if (stopStream) stopStream();
111
+ rejectResult(new Error("Transcription audio : délai dépassé."));
112
+ }
113
+ }, 60000);
114
+ function belongsToCurrentSession(payload) {
115
+ if (!expectedAudioSessionId) return true;
116
+ var payloadAudioId = payload && payload.audio_session_id ? payload.audio_session_id : "";
117
+ return !payloadAudioId || payloadAudioId === expectedAudioSessionId;
118
+ }
119
+ this.api.streamAudioDraft(sessionId, {
120
+ onDelta: function() {},
121
+ onComplete: function(payload) {
122
+ if (resolved || !belongsToCurrentSession(payload)) return;
123
+ resolved = true;
124
+ clearTimeout(timeout);
125
+ if (stopStream) stopStream();
126
+ resolveResult(payload && payload.text ? String(payload.text).trim() : "");
127
+ },
128
+ onError: function(payload) {
129
+ if (resolved || !belongsToCurrentSession(payload)) return;
130
+ resolved = true;
131
+ clearTimeout(timeout);
132
+ if (stopStream) stopStream();
133
+ rejectResult(new Error((payload && payload.message) || "Erreur transcription audio"));
134
+ },
135
+ onTransportError: function(message) {
136
+ if (resolved) return;
137
+ var normalized = String(message || "").toLowerCase();
138
+ var isAbort = normalized.indexOf("aborted") !== -1 || normalized.indexOf("aborterror") !== -1;
139
+ if (isAbort) return;
140
+ resolved = true;
141
+ clearTimeout(timeout);
142
+ if (stopStream) stopStream();
143
+ rejectResult(new Error(message || "Erreur stream audio"));
144
+ }
145
+ }).then(function(stop) {
146
+ stopStream = stop;
147
+ resolveConnected();
148
+ }).catch(function(err) {
149
+ resolveConnected();
150
+ if (!resolved) {
151
+ resolved = true;
152
+ clearTimeout(timeout);
153
+ rejectResult(err);
154
+ }
155
+ });
156
+ return {
157
+ connected: connectedPromise,
158
+ result: resultPromise,
159
+ setAudioSessionId: function(id) { expectedAudioSessionId = id; }
160
+ };
161
+ };
162
+
163
+ WidgetAudioManager.prototype.collectRecordedBlob = function() {
164
+ var self = this;
165
+ if (!this.recorder) return Promise.resolve(new Blob());
166
+ return new Promise(function(resolve) {
167
+ var recorder = self.recorder;
168
+ function done() {
169
+ var blob = new Blob(self.chunks, { type: recorder.mimeType || "audio/webm" });
170
+ resolve(blob);
171
+ }
172
+ if (recorder.state === "inactive") return done();
173
+ recorder.addEventListener("stop", done, { once: true });
174
+ recorder.stop();
175
+ });
176
+ };
177
+
178
+ WidgetAudioManager.prototype.cleanupMedia = function() {
179
+ if (this.recorder) {
180
+ this.recorder.ondataavailable = null;
181
+ this.recorder = null;
182
+ }
183
+ if (this.stream) {
184
+ this.stream.getTracks().forEach(function(track) { track.stop(); });
185
+ this.stream = null;
186
+ }
187
+ this.chunks = [];
188
+ };
189
+
190
+ runtime.WidgetAudioManager = WidgetAudioManager;
191
+ })();
@@ -5,7 +5,6 @@
5
5
  throw new Error("Widget runtime utils module must be loaded first.");
6
6
  }
7
7
 
8
- var mergeTranscript = runtime.utils.mergeTranscript;
9
8
  var resolveRecorderMimeType = runtime.utils.resolveRecorderMimeType;
10
9
  var encodeArrayBufferToBase64 = runtime.utils.encodeArrayBufferToBase64;
11
10
 
@@ -16,11 +15,8 @@
16
15
  this.stream = null;
17
16
  this.recorder = null;
18
17
  this.chunks = [];
19
- this.stopDraftStream = null;
20
- this.activeAudioSessionId = "";
21
- this.cleanupTimer = null;
22
- this._starting = false;
23
- this._stopping = false;
18
+ this._recording = false;
19
+ this._processing = false;
24
20
  }
25
21
 
26
22
  WidgetAudioManager.prototype.isSupported = function() {
@@ -28,34 +24,17 @@
28
24
  };
29
25
 
30
26
  WidgetAudioManager.prototype.toggle = async function() {
31
- if (this._starting || this._stopping) return;
32
- if (this.state.listening) return this.stop();
27
+ if (this._processing) return;
28
+ if (this._recording) return this.stop();
33
29
  return this.start();
34
30
  };
35
31
 
36
32
  WidgetAudioManager.prototype.start = async function() {
37
- if (!this.isSupported() || this.state.sending || this._starting || this._stopping) return;
38
- this._starting = true;
33
+ if (!this.isSupported() || this.state.sending || this._recording || this._processing) return;
34
+ this.ui.showError("");
35
+ this.ui.setInput("");
39
36
  this.ui.setMicListening(true);
40
37
  try {
41
- this.ui.showError("");
42
- this.clearDraftCleanup();
43
- if (this.activeAudioSessionId && this.state.sessionId) {
44
- try {
45
- await this.api.closeAudioSession(this.state.sessionId, this.activeAudioSessionId);
46
- } catch (_) { /* best-effort */ }
47
- this.activeAudioSessionId = "";
48
- }
49
- var sessionId = await this.api.ensureSession();
50
- if (this.stopDraftStream) this.stopDraftStream();
51
- this.stopDraftStream = await this.api.streamAudioDraft(sessionId, {
52
- onDelta: this.handleDraftDelta.bind(this),
53
- onComplete: this.handleDraftComplete.bind(this),
54
- onError: this.handleDraftError.bind(this),
55
- onTransportError: this.handleDraftTransportError.bind(this)
56
- });
57
- var audioSession = await this.api.startAudioSession(sessionId);
58
- this.activeAudioSessionId = audioSession.audio_session_id || "";
59
38
  this.stream = await navigator.mediaDevices.getUserMedia({ audio: true });
60
39
  this.chunks = [];
61
40
  var mimeType = resolveRecorderMimeType();
@@ -65,52 +44,129 @@
65
44
  if (event.data && event.data.size > 0) self.chunks.push(event.data);
66
45
  };
67
46
  this.recorder.start(250);
47
+ this._recording = true;
68
48
  this.state.listening = true;
69
49
  } catch (error) {
70
50
  this.cleanupMedia();
71
- this.activeAudioSessionId = "";
51
+ this._recording = false;
72
52
  this.state.listening = false;
73
53
  this.ui.setMicListening(false);
74
- this.ui.showError("Erreur démarrage audio : " + (error.message || error));
75
- } finally {
76
- this._starting = false;
54
+ this.ui.showError("Erreur démarrage micro : " + (error.message || error));
77
55
  }
78
56
  };
79
57
 
80
58
  WidgetAudioManager.prototype.stop = async function() {
81
- if (!this.state.listening || this._stopping) return;
82
- this._stopping = true;
59
+ if (!this._recording) return;
60
+ this._recording = false;
61
+ this._processing = true;
83
62
  this.state.listening = false;
84
63
  this.ui.setMicListening(false);
85
- var sessionId = this.state.sessionId;
86
- var audioSessionId = this.activeAudioSessionId;
64
+ this.ui.setComposerDisabled(true);
65
+ this.ui.setInputPlaceholder("Transcription en cours…");
87
66
  try {
88
- var blob = await this.stopRecorder();
89
- if (blob && blob.size > 0 && sessionId && audioSessionId) {
90
- var base64 = encodeArrayBufferToBase64(await blob.arrayBuffer());
91
- await this.api.sendAudioChunk(sessionId, audioSessionId, base64);
67
+ var blobAndSession = await Promise.all([
68
+ this.collectRecordedBlob(),
69
+ this.api.ensureSession()
70
+ ]);
71
+ var blob = blobAndSession[0];
72
+ var sessionId = blobAndSession[1];
73
+ this.cleanupMedia();
74
+ if (!blob || blob.size === 0) {
75
+ this.ui.showError("Aucun audio enregistré.");
76
+ return;
77
+ }
78
+ var base64 = encodeArrayBufferToBase64(await blob.arrayBuffer());
79
+ var transcription = this.openTranscriptionListener(sessionId);
80
+ await transcription.connected;
81
+ var audioSession = await this.api.startAudioSession(sessionId);
82
+ var audioSessionId = audioSession.audio_session_id || "";
83
+ transcription.setAudioSessionId(audioSessionId);
84
+ await this.api.sendAudioChunk(sessionId, audioSessionId, base64);
85
+ await this.api.closeAudioSession(sessionId, audioSessionId);
86
+ var transcribedText = await transcription.result;
87
+ if (transcribedText) {
88
+ this.ui.setInput(transcribedText);
92
89
  }
93
- if (sessionId && audioSessionId) await this.api.closeAudioSession(sessionId, audioSessionId);
94
90
  } catch (error) {
95
- this.ui.showError("Erreur fermeture audio : " + (error.message || error));
91
+ this.cleanupMedia();
92
+ this.ui.showError("Erreur transcription audio : " + (error.message || error));
96
93
  } finally {
97
- this.activeAudioSessionId = "";
98
- this._stopping = false;
99
- this.scheduleDraftCleanup();
94
+ this._processing = false;
95
+ this.ui.setInputPlaceholder("");
96
+ this.ui.setComposerDisabled(false);
100
97
  }
101
98
  };
102
99
 
103
- WidgetAudioManager.prototype.stopRecorder = function() {
104
- var self = this;
105
- if (!this.recorder) {
106
- this.cleanupMedia();
107
- return Promise.resolve(new Blob());
100
+ WidgetAudioManager.prototype.openTranscriptionListener = function(sessionId) {
101
+ var resolved = false;
102
+ var stopStream = null;
103
+ var expectedAudioSessionId = "";
104
+ var resolveResult, rejectResult, resolveConnected;
105
+ var resultPromise = new Promise(function(res, rej) { resolveResult = res; rejectResult = rej; });
106
+ var connectedPromise = new Promise(function(res) { resolveConnected = res; });
107
+ var timeout = setTimeout(function() {
108
+ if (!resolved) {
109
+ resolved = true;
110
+ if (stopStream) stopStream();
111
+ rejectResult(new Error("Transcription audio : délai dépassé."));
112
+ }
113
+ }, 60000);
114
+ function belongsToCurrentSession(payload) {
115
+ if (!expectedAudioSessionId) return false;
116
+ var payloadAudioId = payload && payload.audio_session_id ? payload.audio_session_id : "";
117
+ return !payloadAudioId || payloadAudioId === expectedAudioSessionId;
108
118
  }
119
+ this.api.streamAudioDraft(sessionId, {
120
+ onDelta: function() {},
121
+ onComplete: function(payload) {
122
+ if (resolved || !belongsToCurrentSession(payload)) return;
123
+ resolved = true;
124
+ clearTimeout(timeout);
125
+ if (stopStream) stopStream();
126
+ resolveResult(payload && payload.text ? String(payload.text).trim() : "");
127
+ },
128
+ onError: function(payload) {
129
+ if (resolved || !belongsToCurrentSession(payload)) return;
130
+ resolved = true;
131
+ clearTimeout(timeout);
132
+ if (stopStream) stopStream();
133
+ rejectResult(new Error((payload && payload.message) || "Erreur transcription audio"));
134
+ },
135
+ onTransportError: function(message) {
136
+ if (resolved) return;
137
+ var normalized = String(message || "").toLowerCase();
138
+ var isAbort = normalized.indexOf("aborted") !== -1 || normalized.indexOf("aborterror") !== -1;
139
+ if (isAbort) return;
140
+ resolved = true;
141
+ clearTimeout(timeout);
142
+ if (stopStream) stopStream();
143
+ rejectResult(new Error(message || "Erreur stream audio"));
144
+ }
145
+ }).then(function(stop) {
146
+ stopStream = stop;
147
+ resolveConnected();
148
+ }).catch(function(err) {
149
+ resolveConnected();
150
+ if (!resolved) {
151
+ resolved = true;
152
+ clearTimeout(timeout);
153
+ rejectResult(err);
154
+ }
155
+ });
156
+ return {
157
+ connected: connectedPromise,
158
+ result: resultPromise,
159
+ setAudioSessionId: function(id) { expectedAudioSessionId = id; }
160
+ };
161
+ };
162
+
163
+ WidgetAudioManager.prototype.collectRecordedBlob = function() {
164
+ var self = this;
165
+ if (!this.recorder) return Promise.resolve(new Blob());
109
166
  return new Promise(function(resolve) {
110
167
  var recorder = self.recorder;
111
168
  function done() {
112
169
  var blob = new Blob(self.chunks, { type: recorder.mimeType || "audio/webm" });
113
- self.cleanupMedia();
114
170
  resolve(blob);
115
171
  }
116
172
  if (recorder.state === "inactive") return done();
@@ -131,49 +187,5 @@
131
187
  this.chunks = [];
132
188
  };
133
189
 
134
- WidgetAudioManager.prototype.handleDraftDelta = function(payload) {
135
- if (!payload || !payload.delta) return;
136
- this.ui.setInput(mergeTranscript(this.ui.inputValue(), payload.delta));
137
- };
138
-
139
- WidgetAudioManager.prototype.handleDraftComplete = function(payload) {
140
- if (payload && payload.text) this.ui.setInput(String(payload.text).trim());
141
- this.scheduleDraftCleanup();
142
- };
143
-
144
- WidgetAudioManager.prototype.handleDraftError = function(payload) {
145
- this.ui.showError((payload && payload.message) || "Erreur transcription audio");
146
- this.scheduleDraftCleanup();
147
- };
148
-
149
- WidgetAudioManager.prototype.isExpectedStreamAbortMessage = function(message) {
150
- var normalized = String(message || "").toLowerCase();
151
- return normalized.indexOf("aborted") !== -1 ||
152
- normalized.indexOf("aborterror") !== -1 ||
153
- normalized.indexOf("body stream buffer was aborted") !== -1 ||
154
- normalized.indexOf("the operation was aborted") !== -1;
155
- };
156
-
157
- WidgetAudioManager.prototype.handleDraftTransportError = function(message) {
158
- if (this.isExpectedStreamAbortMessage(message)) return;
159
- this.ui.showError(message || "Erreur stream audio");
160
- };
161
-
162
- WidgetAudioManager.prototype.clearDraftCleanup = function() {
163
- if (!this.cleanupTimer) return;
164
- clearTimeout(this.cleanupTimer);
165
- this.cleanupTimer = null;
166
- };
167
-
168
- WidgetAudioManager.prototype.scheduleDraftCleanup = function() {
169
- var self = this;
170
- this.clearDraftCleanup();
171
- this.cleanupTimer = setTimeout(function() {
172
- if (self.stopDraftStream) self.stopDraftStream();
173
- self.stopDraftStream = null;
174
- self.cleanupTimer = null;
175
- }, 3000);
176
- };
177
-
178
190
  runtime.WidgetAudioManager = WidgetAudioManager;
179
191
  })();