@ourlu/assistant-sdk 0.2.4 → 0.2.6

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 (68) 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.19c589a2.js +861 -0
  15. package/dist/iife/engine.v1.2b5bb43b.js +3 -3
  16. package/dist/iife/engine.v1.3b09dc20.js +3 -3
  17. package/dist/iife/engine.v1.56074e5a.js +769 -0
  18. package/dist/iife/engine.v1.61c10e6c.js +770 -0
  19. package/dist/iife/engine.v1.773fc15d.js +2 -2
  20. package/dist/iife/engine.v1.80d2230f.js +3 -3
  21. package/dist/iife/engine.v1.940ba9ea.js +764 -0
  22. package/dist/iife/engine.v1.99a33ee2.js +767 -0
  23. package/dist/iife/engine.v1.9ca6b7ec.js +3 -3
  24. package/dist/iife/engine.v1.a1f7dea2.js +764 -0
  25. package/dist/iife/engine.v1.bc9c0b5e.js +838 -0
  26. package/dist/iife/engine.v1.c0c00bd0.js +3 -3
  27. package/dist/iife/engine.v1.c127656e.js +820 -0
  28. package/dist/iife/engine.v1.c54c9a1a.js +3 -3
  29. package/dist/iife/engine.v1.d1052e81.js +3 -3
  30. package/dist/iife/engine.v1.ec035c58.js +782 -0
  31. package/dist/iife/engine.v1.f6d23a0f.js +770 -0
  32. package/dist/iife/engine.v1.js +116 -25
  33. package/dist/iife/loader.v1.js +9 -1
  34. package/dist/iife/plu.v1.cc853a2d.js +458 -0
  35. package/dist/iife/plu.v1.js +458 -0
  36. package/dist/iife/signalement.v1.d321dfde.js +518 -0
  37. package/dist/iife/signalement.v1.js +518 -0
  38. package/dist/iife/ui.v1.00abf020.js +895 -0
  39. package/dist/iife/ui.v1.0caedc90.js +1018 -0
  40. package/dist/iife/ui.v1.0ccf99d5.js +997 -0
  41. package/dist/iife/ui.v1.11a41f0d.js +1007 -0
  42. package/dist/iife/ui.v1.12986798.js +962 -0
  43. package/dist/iife/ui.v1.15b2cd71.js +986 -0
  44. package/dist/iife/ui.v1.163772e6.js +962 -0
  45. package/dist/iife/ui.v1.3a1d08a0.js +1007 -0
  46. package/dist/iife/ui.v1.3c0cf4da.js +947 -0
  47. package/dist/iife/ui.v1.3fcff562.js +997 -0
  48. package/dist/iife/ui.v1.44616ccb.js +986 -0
  49. package/dist/iife/ui.v1.4e202266.js +1007 -0
  50. package/dist/iife/ui.v1.6becaa84.js +895 -0
  51. package/dist/iife/ui.v1.6c9e4995.js +895 -0
  52. package/dist/iife/ui.v1.7432d2af.js +995 -0
  53. package/dist/iife/ui.v1.84d77387.js +1003 -0
  54. package/dist/iife/ui.v1.857a0e8b.js +986 -0
  55. package/dist/iife/ui.v1.88bf5494.js +898 -0
  56. package/dist/iife/ui.v1.89fbfdd9.js +963 -0
  57. package/dist/iife/ui.v1.a588a766.js +997 -0
  58. package/dist/iife/ui.v1.a8cfe724.js +900 -0
  59. package/dist/iife/ui.v1.cd72e7c3.js +1006 -0
  60. package/dist/iife/ui.v1.e007c7c4.js +926 -0
  61. package/dist/iife/ui.v1.e24ba2bd.js +903 -0
  62. package/dist/iife/ui.v1.e8703c6d.js +1006 -0
  63. package/dist/iife/ui.v1.f1d8e998.js +903 -0
  64. package/dist/iife/ui.v1.f6857351.js +995 -0
  65. package/dist/iife/ui.v1.fc52b520.js +895 -0
  66. package/dist/iife/ui.v1.js +145 -86
  67. package/dist/iife/widget-manifest.json +5 -3
  68. package/package.json +3 -1
@@ -0,0 +1,160 @@
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
+ try {
35
+ this.ui.showError("");
36
+ this.stream = await navigator.mediaDevices.getUserMedia({ audio: true });
37
+ this.chunks = [];
38
+ var mimeType = resolveRecorderMimeType();
39
+ this.recorder = new MediaRecorder(this.stream, mimeType ? { mimeType: mimeType } : {});
40
+ var self = this;
41
+ this.recorder.ondataavailable = function(event) {
42
+ if (event.data && event.data.size > 0) self.chunks.push(event.data);
43
+ };
44
+ this.recorder.start(250);
45
+ this._recording = true;
46
+ this.state.listening = true;
47
+ this.ui.setMicListening(true);
48
+ } catch (error) {
49
+ this.cleanupMedia();
50
+ this.state.listening = false;
51
+ this.ui.setMicListening(false);
52
+ this.ui.showError("Erreur démarrage micro : " + (error.message || error));
53
+ }
54
+ };
55
+
56
+ WidgetAudioManager.prototype.stop = async function() {
57
+ if (!this._recording) return;
58
+ this._recording = false;
59
+ this._processing = true;
60
+ this.state.listening = false;
61
+ this.ui.setMicListening(false);
62
+ this.ui.setComposerDisabled(true);
63
+ try {
64
+ var blob = await this.collectRecordedBlob();
65
+ this.cleanupMedia();
66
+ if (!blob || blob.size === 0) {
67
+ this.ui.showError("Aucun audio enregistré.");
68
+ return;
69
+ }
70
+ var base64 = encodeArrayBufferToBase64(await blob.arrayBuffer());
71
+ var sessionId = await this.api.ensureSession();
72
+ var transcriptionPromise = this.waitForTranscription(sessionId);
73
+ var audioSession = await this.api.startAudioSession(sessionId);
74
+ var audioSessionId = audioSession.audio_session_id || "";
75
+ await this.api.sendAudioChunk(sessionId, audioSessionId, base64);
76
+ await this.api.closeAudioSession(sessionId, audioSessionId);
77
+ var transcribedText = await transcriptionPromise;
78
+ if (transcribedText) {
79
+ this.ui.setInput(transcribedText);
80
+ }
81
+ } catch (error) {
82
+ this.cleanupMedia();
83
+ this.ui.showError("Erreur transcription audio : " + (error.message || error));
84
+ } finally {
85
+ this._processing = false;
86
+ this.ui.setComposerDisabled(false);
87
+ }
88
+ };
89
+
90
+ WidgetAudioManager.prototype.waitForTranscription = function(sessionId) {
91
+ var self = this;
92
+ return new Promise(function(resolve, reject) {
93
+ var resolved = false;
94
+ var timeout = setTimeout(function() {
95
+ if (!resolved) {
96
+ resolved = true;
97
+ if (stopStream) stopStream();
98
+ reject(new Error("Transcription audio : délai dépassé."));
99
+ }
100
+ }, 30000);
101
+ var stopStream = null;
102
+ self.api.streamAudioDraft(sessionId, {
103
+ onDelta: function() {},
104
+ onComplete: function(payload) {
105
+ if (resolved) return;
106
+ resolved = true;
107
+ clearTimeout(timeout);
108
+ if (stopStream) stopStream();
109
+ resolve(payload && payload.text ? String(payload.text).trim() : "");
110
+ },
111
+ onError: function(payload) {
112
+ if (resolved) return;
113
+ resolved = true;
114
+ clearTimeout(timeout);
115
+ if (stopStream) stopStream();
116
+ reject(new Error((payload && payload.message) || "Erreur transcription audio"));
117
+ },
118
+ onTransportError: function(message) {
119
+ if (resolved) return;
120
+ var normalized = String(message || "").toLowerCase();
121
+ var isAbort = normalized.indexOf("aborted") !== -1 || normalized.indexOf("aborterror") !== -1;
122
+ if (isAbort) return;
123
+ resolved = true;
124
+ clearTimeout(timeout);
125
+ if (stopStream) stopStream();
126
+ reject(new Error(message || "Erreur stream audio"));
127
+ }
128
+ }).then(function(stop) { stopStream = stop; });
129
+ });
130
+ };
131
+
132
+ WidgetAudioManager.prototype.collectRecordedBlob = function() {
133
+ var self = this;
134
+ if (!this.recorder) return Promise.resolve(new Blob());
135
+ return new Promise(function(resolve) {
136
+ var recorder = self.recorder;
137
+ function done() {
138
+ var blob = new Blob(self.chunks, { type: recorder.mimeType || "audio/webm" });
139
+ resolve(blob);
140
+ }
141
+ if (recorder.state === "inactive") return done();
142
+ recorder.addEventListener("stop", done, { once: true });
143
+ recorder.stop();
144
+ });
145
+ };
146
+
147
+ WidgetAudioManager.prototype.cleanupMedia = function() {
148
+ if (this.recorder) {
149
+ this.recorder.ondataavailable = null;
150
+ this.recorder = null;
151
+ }
152
+ if (this.stream) {
153
+ this.stream.getTracks().forEach(function(track) { track.stop(); });
154
+ this.stream = null;
155
+ }
156
+ this.chunks = [];
157
+ };
158
+
159
+ runtime.WidgetAudioManager = WidgetAudioManager;
160
+ })();
@@ -0,0 +1,183 @@
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 mergeTranscript = runtime.utils.mergeTranscript;
9
+ var resolveRecorderMimeType = runtime.utils.resolveRecorderMimeType;
10
+ var encodeArrayBufferToBase64 = runtime.utils.encodeArrayBufferToBase64;
11
+
12
+ function WidgetAudioManager(state, api, ui) {
13
+ this.state = state;
14
+ this.api = api;
15
+ this.ui = ui;
16
+ this.stream = null;
17
+ this.recorder = null;
18
+ this.chunks = [];
19
+ this.stopDraftStream = null;
20
+ this.activeAudioSessionId = "";
21
+ this.cleanupTimer = null;
22
+ this._starting = false;
23
+ this._stopping = false;
24
+ }
25
+
26
+ WidgetAudioManager.prototype.isSupported = function() {
27
+ return !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia && typeof MediaRecorder !== "undefined");
28
+ };
29
+
30
+ WidgetAudioManager.prototype.toggle = async function() {
31
+ if (this._starting || this._stopping) return;
32
+ if (this.state.listening) return this.stop();
33
+ return this.start();
34
+ };
35
+
36
+ WidgetAudioManager.prototype.start = async function() {
37
+ if (!this.isSupported() || this.state.sending || this._starting || this._stopping) return;
38
+ this._starting = true;
39
+ this.ui.setMicListening(true);
40
+ try {
41
+ this.ui.showError("");
42
+ this.clearDraftCleanup();
43
+ if (this.stopDraftStream) {
44
+ this.stopDraftStream();
45
+ this.stopDraftStream = null;
46
+ }
47
+ this.ui.setInput("");
48
+ this.stream = await navigator.mediaDevices.getUserMedia({ audio: true });
49
+ if (this.activeAudioSessionId && this.state.sessionId) {
50
+ try {
51
+ await this.api.closeAudioSession(this.state.sessionId, this.activeAudioSessionId);
52
+ } catch (_) { /* best-effort */ }
53
+ this.activeAudioSessionId = "";
54
+ }
55
+ var sessionId = await this.api.ensureSession();
56
+ this.stopDraftStream = await this.api.streamAudioDraft(sessionId, {
57
+ onDelta: this.handleDraftDelta.bind(this),
58
+ onComplete: this.handleDraftComplete.bind(this),
59
+ onError: this.handleDraftError.bind(this),
60
+ onTransportError: this.handleDraftTransportError.bind(this)
61
+ });
62
+ var audioSession = await this.api.startAudioSession(sessionId);
63
+ this.activeAudioSessionId = audioSession.audio_session_id || "";
64
+ this.chunks = [];
65
+ var mimeType = resolveRecorderMimeType();
66
+ this.recorder = new MediaRecorder(this.stream, mimeType ? { mimeType: mimeType } : {});
67
+ var self = this;
68
+ this.recorder.ondataavailable = function(event) {
69
+ if (event.data && event.data.size > 0) self.chunks.push(event.data);
70
+ };
71
+ this.recorder.start(250);
72
+ this.state.listening = true;
73
+ } catch (error) {
74
+ this.cleanupMedia();
75
+ this.activeAudioSessionId = "";
76
+ this.state.listening = false;
77
+ this.ui.setMicListening(false);
78
+ this.ui.showError("Erreur démarrage audio : " + (error.message || error));
79
+ } finally {
80
+ this._starting = false;
81
+ }
82
+ };
83
+
84
+ WidgetAudioManager.prototype.stop = async function() {
85
+ if (!this.state.listening || this._stopping) return;
86
+ this._stopping = true;
87
+ this.state.listening = false;
88
+ this.ui.setMicListening(false);
89
+ var sessionId = this.state.sessionId;
90
+ var audioSessionId = this.activeAudioSessionId;
91
+ try {
92
+ var blob = await this.stopRecorder();
93
+ if (blob && blob.size > 0 && sessionId && audioSessionId) {
94
+ var base64 = encodeArrayBufferToBase64(await blob.arrayBuffer());
95
+ await this.api.sendAudioChunk(sessionId, audioSessionId, base64);
96
+ }
97
+ if (sessionId && audioSessionId) await this.api.closeAudioSession(sessionId, audioSessionId);
98
+ } catch (error) {
99
+ this.ui.showError("Erreur fermeture audio : " + (error.message || error));
100
+ } finally {
101
+ this.activeAudioSessionId = "";
102
+ this._stopping = false;
103
+ this.scheduleDraftCleanup();
104
+ }
105
+ };
106
+
107
+ WidgetAudioManager.prototype.stopRecorder = function() {
108
+ var self = this;
109
+ if (!this.recorder) {
110
+ this.cleanupMedia();
111
+ return Promise.resolve(new Blob());
112
+ }
113
+ return new Promise(function(resolve) {
114
+ var recorder = self.recorder;
115
+ function done() {
116
+ var blob = new Blob(self.chunks, { type: recorder.mimeType || "audio/webm" });
117
+ self.cleanupMedia();
118
+ resolve(blob);
119
+ }
120
+ if (recorder.state === "inactive") return done();
121
+ recorder.addEventListener("stop", done, { once: true });
122
+ recorder.stop();
123
+ });
124
+ };
125
+
126
+ WidgetAudioManager.prototype.cleanupMedia = function() {
127
+ if (this.recorder) {
128
+ this.recorder.ondataavailable = null;
129
+ this.recorder = null;
130
+ }
131
+ if (this.stream) {
132
+ this.stream.getTracks().forEach(function(track) { track.stop(); });
133
+ this.stream = null;
134
+ }
135
+ this.chunks = [];
136
+ };
137
+
138
+ WidgetAudioManager.prototype.handleDraftDelta = function(payload) {
139
+ if (!payload || !payload.delta) return;
140
+ this.ui.setInput(mergeTranscript(this.ui.inputValue(), payload.delta));
141
+ };
142
+
143
+ WidgetAudioManager.prototype.handleDraftComplete = function(payload) {
144
+ if (payload && payload.text) this.ui.setInput(String(payload.text).trim());
145
+ this.scheduleDraftCleanup();
146
+ };
147
+
148
+ WidgetAudioManager.prototype.handleDraftError = function(payload) {
149
+ this.ui.showError((payload && payload.message) || "Erreur transcription audio");
150
+ this.scheduleDraftCleanup();
151
+ };
152
+
153
+ WidgetAudioManager.prototype.isExpectedStreamAbortMessage = function(message) {
154
+ var normalized = String(message || "").toLowerCase();
155
+ return normalized.indexOf("aborted") !== -1 ||
156
+ normalized.indexOf("aborterror") !== -1 ||
157
+ normalized.indexOf("body stream buffer was aborted") !== -1 ||
158
+ normalized.indexOf("the operation was aborted") !== -1;
159
+ };
160
+
161
+ WidgetAudioManager.prototype.handleDraftTransportError = function(message) {
162
+ if (this.isExpectedStreamAbortMessage(message)) return;
163
+ this.ui.showError(message || "Erreur stream audio");
164
+ };
165
+
166
+ WidgetAudioManager.prototype.clearDraftCleanup = function() {
167
+ if (!this.cleanupTimer) return;
168
+ clearTimeout(this.cleanupTimer);
169
+ this.cleanupTimer = null;
170
+ };
171
+
172
+ WidgetAudioManager.prototype.scheduleDraftCleanup = function() {
173
+ var self = this;
174
+ this.clearDraftCleanup();
175
+ this.cleanupTimer = setTimeout(function() {
176
+ if (self.stopDraftStream) self.stopDraftStream();
177
+ self.stopDraftStream = null;
178
+ self.cleanupTimer = null;
179
+ }, 3000);
180
+ };
181
+
182
+ runtime.WidgetAudioManager = WidgetAudioManager;
183
+ })();
@@ -0,0 +1,208 @@
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 mergeTranscript = runtime.utils.mergeTranscript;
9
+ var resolveRecorderMimeType = runtime.utils.resolveRecorderMimeType;
10
+ var encodeArrayBufferToBase64 = runtime.utils.encodeArrayBufferToBase64;
11
+
12
+ function WidgetAudioManager(state, api, ui) {
13
+ this.state = state;
14
+ this.api = api;
15
+ this.ui = ui;
16
+ this.stream = null;
17
+ this.recorder = null;
18
+ this.chunks = [];
19
+ this.stopDraftStream = null;
20
+ this.activeAudioSessionId = "";
21
+ this.cleanupTimer = null;
22
+ this._starting = false;
23
+ this._stopping = false;
24
+ this._startCancelled = false;
25
+ }
26
+
27
+ WidgetAudioManager.prototype.isSupported = function() {
28
+ return !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia && typeof MediaRecorder !== "undefined");
29
+ };
30
+
31
+ WidgetAudioManager.prototype.toggle = async function() {
32
+ if (this._stopping) return;
33
+ if (this._starting) {
34
+ this._startCancelled = true;
35
+ return;
36
+ }
37
+ if (this.state.listening) return this.stop();
38
+ return this.start();
39
+ };
40
+
41
+ WidgetAudioManager.prototype.start = async function() {
42
+ if (!this.isSupported() || this.state.sending || this._starting || this._stopping) return;
43
+ this._starting = true;
44
+ this._startCancelled = false;
45
+ this.ui.setMicListening(true);
46
+ try {
47
+ this.ui.showError("");
48
+ this.clearDraftCleanup();
49
+ var closePreviousPromise = Promise.resolve();
50
+ if (this.activeAudioSessionId && this.state.sessionId) {
51
+ var prevSessionId = this.state.sessionId;
52
+ var prevAudioSessionId = this.activeAudioSessionId;
53
+ this.activeAudioSessionId = "";
54
+ closePreviousPromise = this.api.closeAudioSession(prevSessionId, prevAudioSessionId).catch(function() {});
55
+ }
56
+ var results = await Promise.all([
57
+ navigator.mediaDevices.getUserMedia({ audio: true }),
58
+ closePreviousPromise.then(this.api.ensureSession.bind(this.api))
59
+ ]);
60
+ this.stream = results[0];
61
+ var sessionId = results[1];
62
+ if (this._startCancelled) {
63
+ this.cleanupMedia();
64
+ this.ui.setMicListening(false);
65
+ return;
66
+ }
67
+ if (this.stopDraftStream) this.stopDraftStream();
68
+ var draftCallbacks = {
69
+ onDelta: this.handleDraftDelta.bind(this),
70
+ onComplete: this.handleDraftComplete.bind(this),
71
+ onError: this.handleDraftError.bind(this),
72
+ onTransportError: this.handleDraftTransportError.bind(this)
73
+ };
74
+ var draftAndSessionResults = await Promise.all([
75
+ this.api.streamAudioDraft(sessionId, draftCallbacks),
76
+ this.api.startAudioSession(sessionId)
77
+ ]);
78
+ this.stopDraftStream = draftAndSessionResults[0];
79
+ this.activeAudioSessionId = draftAndSessionResults[1].audio_session_id || "";
80
+ if (this._startCancelled) {
81
+ this.cleanupMedia();
82
+ this.activeAudioSessionId = "";
83
+ this.ui.setMicListening(false);
84
+ return;
85
+ }
86
+ this.chunks = [];
87
+ var mimeType = resolveRecorderMimeType();
88
+ this.recorder = new MediaRecorder(this.stream, mimeType ? { mimeType: mimeType } : {});
89
+ var self = this;
90
+ this.recorder.ondataavailable = function(event) {
91
+ if (event.data && event.data.size > 0) self.chunks.push(event.data);
92
+ };
93
+ this.recorder.start(250);
94
+ this.state.listening = true;
95
+ } catch (error) {
96
+ this.cleanupMedia();
97
+ this.activeAudioSessionId = "";
98
+ this.state.listening = false;
99
+ this.ui.setMicListening(false);
100
+ if (!this._startCancelled) {
101
+ this.ui.showError("Erreur démarrage audio : " + (error.message || error));
102
+ }
103
+ } finally {
104
+ this._starting = false;
105
+ this._startCancelled = false;
106
+ }
107
+ };
108
+
109
+ WidgetAudioManager.prototype.stop = async function() {
110
+ if (!this.state.listening || this._stopping) return;
111
+ this._stopping = true;
112
+ this.state.listening = false;
113
+ this.ui.setMicListening(false);
114
+ var sessionId = this.state.sessionId;
115
+ var audioSessionId = this.activeAudioSessionId;
116
+ try {
117
+ var blob = await this.stopRecorder();
118
+ if (blob && blob.size > 0 && sessionId && audioSessionId) {
119
+ var base64 = encodeArrayBufferToBase64(await blob.arrayBuffer());
120
+ await this.api.sendAudioChunk(sessionId, audioSessionId, base64);
121
+ }
122
+ if (sessionId && audioSessionId) await this.api.closeAudioSession(sessionId, audioSessionId);
123
+ } catch (error) {
124
+ this.ui.showError("Erreur fermeture audio : " + (error.message || error));
125
+ } finally {
126
+ this.activeAudioSessionId = "";
127
+ this._stopping = false;
128
+ this.scheduleDraftCleanup();
129
+ }
130
+ };
131
+
132
+ WidgetAudioManager.prototype.stopRecorder = function() {
133
+ var self = this;
134
+ if (!this.recorder) {
135
+ this.cleanupMedia();
136
+ return Promise.resolve(new Blob());
137
+ }
138
+ return new Promise(function(resolve) {
139
+ var recorder = self.recorder;
140
+ function done() {
141
+ var blob = new Blob(self.chunks, { type: recorder.mimeType || "audio/webm" });
142
+ self.cleanupMedia();
143
+ resolve(blob);
144
+ }
145
+ if (recorder.state === "inactive") return done();
146
+ recorder.addEventListener("stop", done, { once: true });
147
+ recorder.stop();
148
+ });
149
+ };
150
+
151
+ WidgetAudioManager.prototype.cleanupMedia = function() {
152
+ if (this.recorder) {
153
+ this.recorder.ondataavailable = null;
154
+ this.recorder = null;
155
+ }
156
+ if (this.stream) {
157
+ this.stream.getTracks().forEach(function(track) { track.stop(); });
158
+ this.stream = null;
159
+ }
160
+ this.chunks = [];
161
+ };
162
+
163
+ WidgetAudioManager.prototype.handleDraftDelta = function(payload) {
164
+ if (!payload || !payload.delta) return;
165
+ this.ui.setInput(mergeTranscript(this.ui.inputValue(), payload.delta));
166
+ };
167
+
168
+ WidgetAudioManager.prototype.handleDraftComplete = function(payload) {
169
+ if (payload && payload.text) this.ui.setInput(String(payload.text).trim());
170
+ this.scheduleDraftCleanup();
171
+ };
172
+
173
+ WidgetAudioManager.prototype.handleDraftError = function(payload) {
174
+ this.ui.showError((payload && payload.message) || "Erreur transcription audio");
175
+ this.scheduleDraftCleanup();
176
+ };
177
+
178
+ WidgetAudioManager.prototype.isExpectedStreamAbortMessage = function(message) {
179
+ var normalized = String(message || "").toLowerCase();
180
+ return normalized.indexOf("aborted") !== -1 ||
181
+ normalized.indexOf("aborterror") !== -1 ||
182
+ normalized.indexOf("body stream buffer was aborted") !== -1 ||
183
+ normalized.indexOf("the operation was aborted") !== -1;
184
+ };
185
+
186
+ WidgetAudioManager.prototype.handleDraftTransportError = function(message) {
187
+ if (this.isExpectedStreamAbortMessage(message)) return;
188
+ this.ui.showError(message || "Erreur stream audio");
189
+ };
190
+
191
+ WidgetAudioManager.prototype.clearDraftCleanup = function() {
192
+ if (!this.cleanupTimer) return;
193
+ clearTimeout(this.cleanupTimer);
194
+ this.cleanupTimer = null;
195
+ };
196
+
197
+ WidgetAudioManager.prototype.scheduleDraftCleanup = function() {
198
+ var self = this;
199
+ this.clearDraftCleanup();
200
+ this.cleanupTimer = setTimeout(function() {
201
+ if (self.stopDraftStream) self.stopDraftStream();
202
+ self.stopDraftStream = null;
203
+ self.cleanupTimer = null;
204
+ }, 3000);
205
+ };
206
+
207
+ runtime.WidgetAudioManager = WidgetAudioManager;
208
+ })();