@ourlu/assistant-sdk 0.2.2 → 0.2.4

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.
@@ -0,0 +1,919 @@
1
+ (function() {
2
+ function installColorUtils(ui) {
3
+ ui.clampAlpha = function clampAlpha(rawValue) {
4
+ var parsed = Number(rawValue);
5
+ if (!Number.isFinite(parsed)) return 1;
6
+ if (parsed < 0) return 0;
7
+ if (parsed > 1) return 1;
8
+ return parsed;
9
+ };
10
+
11
+ ui.normalizeHexColor = function normalizeHexColor(rawValue, fallbackColor) {
12
+ var color = String(rawValue || "").trim().toLowerCase();
13
+ if (!/^#[0-9a-f]{6}$/.test(color)) return fallbackColor;
14
+ return color;
15
+ };
16
+
17
+ ui.hexToRgba = function hexToRgba(hexColor, alphaValue) {
18
+ var normalized = ui.normalizeHexColor(hexColor, "#ffffff");
19
+ var alpha = ui.clampAlpha(alphaValue);
20
+ var r = parseInt(normalized.slice(1, 3), 16);
21
+ var g = parseInt(normalized.slice(3, 5), 16);
22
+ var b = parseInt(normalized.slice(5, 7), 16);
23
+ return "rgba(" + r + "," + g + "," + b + "," + alpha + ")";
24
+ };
25
+
26
+ ui.clampColorChannel = function clampColorChannel(channelValue) {
27
+ return Math.max(0, Math.min(255, channelValue));
28
+ };
29
+
30
+ ui.adjustHexBrightness = function adjustHexBrightness(hexColor, percentage) {
31
+ var normalizedColor = ui.normalizeHexColor(hexColor, "#000000");
32
+ var adjustment = Math.round((Number(percentage) / 100) * 255);
33
+ var red = ui.clampColorChannel(parseInt(normalizedColor.slice(1, 3), 16) + adjustment);
34
+ var green = ui.clampColorChannel(parseInt(normalizedColor.slice(3, 5), 16) + adjustment);
35
+ var blue = ui.clampColorChannel(parseInt(normalizedColor.slice(5, 7), 16) + adjustment);
36
+ return "#" + red.toString(16).padStart(2, "0") + green.toString(16).padStart(2, "0") + blue.toString(16).padStart(2, "0");
37
+ };
38
+ }
39
+
40
+ function installMascotTheme(ui) {
41
+ ui.mascotSvgSourceCache = {};
42
+
43
+ ui.escapeRegExp = function escapeRegExp(rawValue) {
44
+ return String(rawValue || "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
45
+ };
46
+
47
+ ui.replaceSvgColorTokens = function replaceSvgColorTokens(rawSvgMarkup, colorMap) {
48
+ var themedMarkup = String(rawSvgMarkup || "");
49
+ Object.keys(colorMap).forEach(function(token) {
50
+ var resolvedColor = String(colorMap[token] || "").trim();
51
+ if (!resolvedColor) return;
52
+ themedMarkup = themedMarkup.replace(new RegExp(ui.escapeRegExp(token), "gi"), resolvedColor);
53
+ });
54
+ return themedMarkup;
55
+ };
56
+
57
+ ui.assignMascotTokenGroup = function assignMascotTokenGroup(replacementMap, tokens, replacementColor) {
58
+ tokens.forEach(function(token) {
59
+ replacementMap[token] = replacementColor;
60
+ });
61
+ };
62
+
63
+ ui.buildMascotDerivedPalette = function buildMascotDerivedPalette(config) {
64
+ var teal600 = ui.normalizeHexColor(config.mascotSecondaryColor, "#58878d");
65
+ var teal700 = ui.normalizeHexColor(config.mascotSecondaryDarkColor, "#355c62");
66
+ var gold500 = ui.normalizeHexColor(config.mascotGoldColor, "#f4c934");
67
+ var eye500 = ui.normalizeHexColor(config.mascotEyeColor, "#040402");
68
+ var beak300 = ui.normalizeHexColor(config.mascotBeakColor, "#ab6d46");
69
+ var neutral200 = ui.normalizeHexColor(config.mascotNeutralColor, "#e6e6e6");
70
+ return {
71
+ teal900: ui.adjustHexBrightness(teal700, -18),
72
+ teal700: teal700,
73
+ teal600: teal600,
74
+ teal500: ui.adjustHexBrightness(teal600, 10),
75
+ teal400: ui.adjustHexBrightness(teal600, 22),
76
+ wing500: teal700,
77
+ wing700: ui.adjustHexBrightness(teal700, -10),
78
+ wing900: ui.adjustHexBrightness(teal700, -26),
79
+ gold500: gold500,
80
+ gold450: ui.adjustHexBrightness(gold500, -6),
81
+ gold700: ui.adjustHexBrightness(gold500, -24),
82
+ gold900: ui.adjustHexBrightness(gold500, -42),
83
+ eyeOutline: ui.adjustHexBrightness(eye500, -8),
84
+ eye500: eye500,
85
+ eye900: ui.adjustHexBrightness(eye500, -26),
86
+ beak300: beak300,
87
+ beak500: ui.adjustHexBrightness(beak300, 12),
88
+ beak700: ui.adjustHexBrightness(beak300, -24),
89
+ claw700: ui.adjustHexBrightness(beak300, -30),
90
+ claw400: ui.adjustHexBrightness(beak300, 18),
91
+ neutral200: neutral200,
92
+ neutral300: ui.adjustHexBrightness(neutral200, -18),
93
+ black: ui.adjustHexBrightness(eye500, -40),
94
+ white: "#ffffff"
95
+ };
96
+ };
97
+
98
+ ui.buildMascotColorReplacementMap = function buildMascotColorReplacementMap(config) {
99
+ var palette = ui.buildMascotDerivedPalette(config || {});
100
+ var replacementMap = {};
101
+ ui.assignMascotTokenGroup(replacementMap, ["#355c62", "#355b62"], palette.teal900);
102
+ ui.assignMascotTokenGroup(replacementMap, ["#5a898f", "#396769"], palette.teal700);
103
+ ui.assignMascotTokenGroup(replacementMap, ["#58878d", "#57868c", "#5d898f", "#4c8187", "#457d86", "#35696e"], palette.teal600);
104
+ ui.assignMascotTokenGroup(replacementMap, ["#669ca0", "#669b9f", "#619498", "#709092", "#55898b", "#63989c", "#53878c"], palette.teal500);
105
+ ui.assignMascotTokenGroup(replacementMap, ["#85b8bb"], palette.teal400);
106
+ ui.assignMascotTokenGroup(replacementMap, ["#548389", "#548085"], palette.wing500);
107
+ ui.assignMascotTokenGroup(replacementMap, ["#537577", "#41656a", "#406d74"], palette.wing700);
108
+ ui.assignMascotTokenGroup(replacementMap, ["#1b3b3c", "#1f464c", "#203d41", "#223f42", "#131f20", "#1a2729", "#295055"], palette.wing900);
109
+ ui.assignMascotTokenGroup(replacementMap, ["#f4c934", "#ffd166"], palette.gold500);
110
+ ui.assignMascotTokenGroup(replacementMap, ["#ebc132", "#efc433"], palette.gold450);
111
+ ui.assignMascotTokenGroup(replacementMap, ["#caa62b", "#a68823"], palette.gold700);
112
+ ui.assignMascotTokenGroup(replacementMap, ["#58470b", "#8e751e"], palette.gold900);
113
+ ui.assignMascotTokenGroup(replacementMap, ["#1f4d52", "#225b61"], palette.eyeOutline);
114
+ ui.assignMascotTokenGroup(replacementMap, ["#254147"], palette.eye500);
115
+ ui.assignMascotTokenGroup(replacementMap, ["#152326", "#0a2d2a", "#595331", "#040402", "#000000"], palette.eye900);
116
+ ui.assignMascotTokenGroup(replacementMap, ["#ab6d46"], palette.beak300);
117
+ ui.assignMascotTokenGroup(replacementMap, ["#be9f5f", "#b18835", "#d7b353", "#bca134"], palette.beak500);
118
+ ui.assignMascotTokenGroup(replacementMap, ["#2b4a4d", "#695a39", "#574c34", "#565249", "#ac9149", "#5b4518", "#514523"], palette.beak700);
119
+ ui.assignMascotTokenGroup(replacementMap, ["#7d6026", "#7d6025"], palette.claw700);
120
+ ui.assignMascotTokenGroup(replacementMap, ["#e3af46", "#dcaa44"], palette.claw400);
121
+ ui.assignMascotTokenGroup(replacementMap, ["#e6e6e6", "#e5e5e5", "#e8e8e8", "#e8e4e4"], palette.neutral200);
122
+ ui.assignMascotTokenGroup(replacementMap, ["#a2aaaa", "#b0baba"], palette.neutral300);
123
+ replacementMap.white = palette.white;
124
+ replacementMap.black = palette.black;
125
+ return replacementMap;
126
+ };
127
+
128
+ ui.isLikelySvgAsset = function isLikelySvgAsset(rawUrl) {
129
+ return /\.svg(?:[?#].*)?$/i.test(String(rawUrl || "").trim());
130
+ };
131
+
132
+ ui.resolveAbsoluteMascotUrl = function resolveAbsoluteMascotUrl(mascotUrl, apiBaseUrl) {
133
+ var normalized = String(mascotUrl || "").trim();
134
+ if (!normalized) return "";
135
+ if (/^https?:\/\//i.test(normalized)) return normalized;
136
+ var base = String(apiBaseUrl || "").trim().replace(/\/$/, "");
137
+ if (!base) {
138
+ throw new Error("[OurluMairie] mascot URL relative sans apiBaseUrl: " + normalized);
139
+ }
140
+ return normalized.startsWith("/") ? base + normalized : base + "/" + normalized;
141
+ };
142
+
143
+ ui.encodeSvgToDataUrl = function encodeSvgToDataUrl(svgMarkup) {
144
+ return "data:image/svg+xml;charset=utf-8," + encodeURIComponent(String(svgMarkup || ""));
145
+ };
146
+
147
+ ui.resolveMascotUrlWithTheme = function resolveMascotUrlWithTheme(mascotUrl, config) {
148
+ var normalizedMascotUrl = ui.resolveAbsoluteMascotUrl(mascotUrl, (config || {}).apiBaseUrl);
149
+ if (!normalizedMascotUrl || typeof fetch !== "function" || !ui.isLikelySvgAsset(normalizedMascotUrl)) {
150
+ return Promise.resolve(normalizedMascotUrl);
151
+ }
152
+ var replacementMap = ui.buildMascotColorReplacementMap(config || {});
153
+ var cacheKey = normalizedMascotUrl + "::" + [
154
+ ui.normalizeHexColor((config || {}).mascotSecondaryColor, "#58878d"),
155
+ ui.normalizeHexColor((config || {}).mascotSecondaryDarkColor, "#355c62"),
156
+ ui.normalizeHexColor((config || {}).mascotGoldColor, "#f4c934"),
157
+ ui.normalizeHexColor((config || {}).mascotEyeColor, "#040402"),
158
+ ui.normalizeHexColor((config || {}).mascotBeakColor, "#ab6d46"),
159
+ ui.normalizeHexColor((config || {}).mascotNeutralColor, "#e6e6e6")
160
+ ].join("::");
161
+ if (ui.mascotSvgSourceCache[cacheKey]) {
162
+ return Promise.resolve(ui.mascotSvgSourceCache[cacheKey]);
163
+ }
164
+ return fetch(normalizedMascotUrl, { method: "GET", credentials: "omit" })
165
+ .then(function(response) {
166
+ if (!response.ok) {
167
+ throw new Error("mascot fetch failed");
168
+ }
169
+ return response.text();
170
+ })
171
+ .then(function(rawSvgMarkup) {
172
+ if (!rawSvgMarkup || rawSvgMarkup.indexOf("<svg") === -1) {
173
+ throw new Error("mascot SVG invalide ou reponse non-SVG pour " + normalizedMascotUrl);
174
+ }
175
+ var themedSvgMarkup = ui.replaceSvgColorTokens(rawSvgMarkup, replacementMap);
176
+ var themedDataUrl = ui.encodeSvgToDataUrl(themedSvgMarkup);
177
+ ui.mascotSvgSourceCache[cacheKey] = themedDataUrl;
178
+ return themedDataUrl;
179
+ });
180
+ };
181
+ }
182
+
183
+ function installMarkdownRender(ui) {
184
+ ui.escapeHtml = function escapeHtml(rawValue) {
185
+ return String(rawValue || "")
186
+ .replace(/&/g, "&amp;")
187
+ .replace(/</g, "&lt;")
188
+ .replace(/>/g, "&gt;")
189
+ .replace(/"/g, "&quot;")
190
+ .replace(/'/g, "&#39;");
191
+ };
192
+
193
+ ui.escapeHtmlAttribute = function escapeHtmlAttribute(rawValue) {
194
+ return ui.escapeHtml(rawValue).replace(/`/g, "&#96;");
195
+ };
196
+
197
+ ui.renderInlineMarkdown = function renderInlineMarkdown(rawText) {
198
+ var escaped = ui.escapeHtml(rawText);
199
+ escaped = escaped.replace(/\[([^\]\n]+)\]\((https?:\/\/[^\s)]+)\)/g, function(_match, label, url) {
200
+ return '<a href="' + ui.escapeHtmlAttribute(url) + '" target="_blank" rel="noopener noreferrer">' + label + "</a>";
201
+ });
202
+ escaped = escaped.replace(/`([^`\n]+)`/g, "<code>$1</code>");
203
+ escaped = escaped.replace(/\*\*([^*\n]+)\*\*/g, "<strong>$1</strong>");
204
+ escaped = escaped.replace(/\*([^*\n]+)\*/g, "<em>$1</em>");
205
+ return escaped;
206
+ };
207
+
208
+ ui.renderAssistantMarkdown = function renderAssistantMarkdown(rawText) {
209
+ var normalized = String(rawText || "").replace(/\r\n?/g, "\n").trim();
210
+ if (!normalized) {
211
+ return "";
212
+ }
213
+
214
+ var codeBlocks = [];
215
+ normalized = normalized.replace(/```([\s\S]*?)```/g, function(_match, codeContent) {
216
+ var placeholder = "@@CM_CODE_BLOCK_" + codeBlocks.length + "@@";
217
+ codeBlocks.push(
218
+ '<pre><code>' + ui.escapeHtml(String(codeContent || "").replace(/^\n+|\n+$/g, "")) + "</code></pre>"
219
+ );
220
+ return placeholder;
221
+ });
222
+
223
+ var htmlParts = [];
224
+ var listType = "";
225
+ var listItems = [];
226
+ var lines = normalized.split("\n");
227
+
228
+ function flushList() {
229
+ if (!listType || listItems.length === 0) {
230
+ listType = "";
231
+ listItems = [];
232
+ return;
233
+ }
234
+ htmlParts.push("<" + listType + ">" + listItems.join("") + "</" + listType + ">");
235
+ listType = "";
236
+ listItems = [];
237
+ }
238
+
239
+ for (var index = 0; index < lines.length; index += 1) {
240
+ var rawLine = lines[index];
241
+ var trimmedLine = rawLine.trim();
242
+ if (!trimmedLine) {
243
+ flushList();
244
+ continue;
245
+ }
246
+
247
+ var unorderedMatch = trimmedLine.match(/^[-*]\s+(.+)$/);
248
+ if (unorderedMatch) {
249
+ if (listType && listType !== "ul") {
250
+ flushList();
251
+ }
252
+ listType = "ul";
253
+ listItems.push("<li>" + ui.renderInlineMarkdown(unorderedMatch[1]) + "</li>");
254
+ continue;
255
+ }
256
+
257
+ var orderedMatch = trimmedLine.match(/^\d+\.\s+(.+)$/);
258
+ if (orderedMatch) {
259
+ if (listType && listType !== "ol") {
260
+ flushList();
261
+ }
262
+ listType = "ol";
263
+ listItems.push("<li>" + ui.renderInlineMarkdown(orderedMatch[1]) + "</li>");
264
+ continue;
265
+ }
266
+
267
+ flushList();
268
+
269
+ var headingMatch = trimmedLine.match(/^(#{1,3})\s+(.+)$/);
270
+ if (headingMatch) {
271
+ var level = Math.min(4, headingMatch[1].length + 2);
272
+ htmlParts.push("<h" + level + ">" + ui.renderInlineMarkdown(headingMatch[2]) + "</h" + level + ">");
273
+ continue;
274
+ }
275
+
276
+ htmlParts.push("<p>" + ui.renderInlineMarkdown(trimmedLine) + "</p>");
277
+ }
278
+
279
+ flushList();
280
+
281
+ var rendered = htmlParts.join("");
282
+ rendered = rendered.replace(/@@CM_CODE_BLOCK_(\d+)@@/g, function(_match, rawIndex) {
283
+ var blockIndex = Number(rawIndex);
284
+ if (!Number.isInteger(blockIndex) || blockIndex < 0 || blockIndex >= codeBlocks.length) {
285
+ return "";
286
+ }
287
+ return codeBlocks[blockIndex];
288
+ });
289
+ return rendered || "<p>" + ui.renderInlineMarkdown(normalized) + "</p>";
290
+ };
291
+ }
292
+
293
+ function installMediaUtils(ui) {
294
+ ui.trimTrailingSlash = function trimTrailingSlash(value) {
295
+ return String(value || "").replace(/\/$/, "");
296
+ };
297
+
298
+ ui.mergeTranscript = function mergeTranscript(currentText, delta) {
299
+ var base = String(currentText || "");
300
+ var incoming = String(delta || "").replace(/^\s+/, "");
301
+ if (!incoming) return base;
302
+ if (!base) return incoming;
303
+ var lastChar = base[base.length - 1];
304
+ var firstChar = incoming[0];
305
+ var noSpaceAfter = [" ", "\n", "'", "\"", "(", "[", "{", "/", "-"];
306
+ var noSpaceBefore = [".", ",", ";", ":", "!", "?", ")", "]", "}", "/", "-"];
307
+ var needsSpace = noSpaceAfter.indexOf(lastChar) === -1 && noSpaceBefore.indexOf(firstChar) === -1;
308
+ return needsSpace ? base + " " + incoming : base + incoming;
309
+ };
310
+
311
+ ui.resolveRecorderMimeType = function resolveRecorderMimeType() {
312
+ if (typeof MediaRecorder === "undefined") return "";
313
+ var preferred = ["audio/webm;codecs=opus", "audio/webm", "audio/ogg;codecs=opus", "audio/ogg", "audio/mp4"];
314
+ for (var i = 0; i < preferred.length; i += 1) {
315
+ if (MediaRecorder.isTypeSupported(preferred[i])) return preferred[i];
316
+ }
317
+ return "";
318
+ };
319
+
320
+ ui.encodeArrayBufferToBase64 = function encodeArrayBufferToBase64(arrayBuffer) {
321
+ var bytes = new Uint8Array(arrayBuffer);
322
+ var chunkSize = 0x8000;
323
+ var chunks = [];
324
+ for (var offset = 0; offset < bytes.length; offset += chunkSize) {
325
+ var part = bytes.subarray(offset, offset + chunkSize);
326
+ chunks.push(String.fromCharCode.apply(null, part));
327
+ }
328
+ return btoa(chunks.join(""));
329
+ };
330
+ }
331
+
332
+ function installEventBus(ui) {
333
+ function EventBus() {
334
+ this.events = {};
335
+ }
336
+
337
+ EventBus.prototype.on = function(eventName, handler) {
338
+ if (!this.events[eventName]) this.events[eventName] = [];
339
+ this.events[eventName].push(handler);
340
+ };
341
+
342
+ EventBus.prototype.emit = function(eventName, payload) {
343
+ var handlers = this.events[eventName] || [];
344
+ handlers.forEach(function(handler) {
345
+ try { handler(payload); } catch (_) {}
346
+ });
347
+ };
348
+
349
+ ui.EventBus = EventBus;
350
+ }
351
+
352
+ function installWidgetCssBuilder(ui) {
353
+ function WidgetCssBuilder(config) {
354
+ this.config = config;
355
+ }
356
+
357
+ WidgetCssBuilder.prototype.resolveLayout = function() {
358
+ var panelWidth = Number(this.config.panelWidth);
359
+ if (!Number.isFinite(panelWidth)) panelWidth = 420;
360
+ panelWidth = Math.max(360, Math.min(960, Math.round(panelWidth)));
361
+
362
+ var panelHeight = Number(this.config.panelHeight);
363
+ if (!Number.isFinite(panelHeight)) panelHeight = 640;
364
+ panelHeight = Math.max(480, Math.min(1400, Math.round(panelHeight)));
365
+
366
+ var panelMaxHeightVh = Number(this.config.panelMaxHeightVh);
367
+ if (!Number.isFinite(panelMaxHeightVh)) panelMaxHeightVh = 85;
368
+ panelMaxHeightVh = Math.max(60, Math.min(98, Math.round(panelMaxHeightVh)));
369
+
370
+ var borderRadius = Number(this.config.panelBorderRadius);
371
+ if (!Number.isFinite(borderRadius)) borderRadius = 16;
372
+ borderRadius = Math.max(0, Math.min(32, Math.round(borderRadius)));
373
+
374
+ return {
375
+ panelWidth: panelWidth,
376
+ panelHeight: panelHeight,
377
+ panelMaxHeightVh: panelMaxHeightVh,
378
+ borderRadius: borderRadius
379
+ };
380
+ };
381
+
382
+ WidgetCssBuilder.prototype.build = function() {
383
+ var cfg = this.config;
384
+ var side = cfg.position === "bottom-left" ? "left" : "right";
385
+ var resizeCorner = cfg.position === "bottom-left" ? "right" : "left";
386
+ var gradient = "linear-gradient(135deg," + cfg.primaryColor + " 0%,#0047b3 100%)";
387
+ var panelBg = ui.hexToRgba(cfg.panelBackgroundColor, cfg.panelBackgroundAlpha);
388
+ var layout = this.resolveLayout();
389
+ var pw = layout.panelWidth + "px";
390
+ var ph = layout.panelHeight + "px";
391
+ var mhVh = layout.panelMaxHeightVh + "vh";
392
+ var br = layout.borderRadius + "px";
393
+
394
+ return [
395
+ this.buildResetAndBase(),
396
+ this.buildBubble(side, gradient, cfg.primaryColor),
397
+ this.buildPanel(side, pw, ph, mhVh, panelBg, br),
398
+ this.buildHeader(gradient, br),
399
+ this.buildNotes(),
400
+ this.buildMessages(),
401
+ this.buildMessageBubbles(cfg.primaryColor),
402
+ this.buildTypingAndError(),
403
+ this.buildComposer(cfg.primaryColor, br),
404
+ this.buildResizeHandle(resizeCorner),
405
+ this.buildMobileOverrides()
406
+ ].join("\n");
407
+ };
408
+
409
+ WidgetCssBuilder.prototype.buildResetAndBase = function() {
410
+ return ":host *{box-sizing:border-box;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,system-ui,sans-serif;-webkit-font-smoothing:antialiased}";
411
+ };
412
+
413
+ WidgetCssBuilder.prototype.buildBubble = function(side, gradient, primaryColor) {
414
+ return [
415
+ "#cm-bubble{position:fixed;" + side + ":24px;bottom:24px;width:64px;height:64px;border-radius:50%;background:" + gradient + ";border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;z-index:2147483000;box-shadow:0 8px 24px rgba(0,0,0,.28),0 2px 8px rgba(0,0,0,.12);transition:transform .2s cubic-bezier(.4,0,.2,1)}",
416
+ "#cm-bubble:hover{transform:scale(1.1)}",
417
+ "#cm-bubble:active{transform:scale(0.95)}",
418
+ "#cm-bubble:focus-visible{outline:3px solid " + primaryColor + ";outline-offset:3px}",
419
+ "#cm-bubble img{width:84px;height:84px;object-fit:contain;position:absolute;top:-14px;filter:drop-shadow(0 4px 8px rgba(0,0,0,.25))}"
420
+ ].join("\n");
421
+ };
422
+
423
+ WidgetCssBuilder.prototype.buildPanel = function(side, width, height, maxHeightVh, background, borderRadius) {
424
+ return [
425
+ "#cm-panel{position:fixed;" + side + ":24px;bottom:100px;width:" + width + ";max-width:calc(100vw - 32px);height:" + height + ";max-height:" + maxHeightVh + ";background:" + background + ";border-radius:" + borderRadius + ";box-shadow:0 16px 48px rgba(0,0,0,.22),0 4px 16px rgba(0,0,0,.1);display:none;flex-direction:column;overflow:visible;z-index:2147483000;transition:box-shadow .2s,bottom .25s cubic-bezier(.4,0,.2,1)}",
426
+ "#cm-panel.open{display:flex;bottom:24px}"
427
+ ].join("\n");
428
+ };
429
+
430
+ WidgetCssBuilder.prototype.buildHeader = function(gradient, borderRadius) {
431
+ return [
432
+ "#cm-header{display:flex;align-items:center;justify-content:space-between;padding:14px 16px 14px 80px;background:" + gradient + ";color:#fff;position:relative;border-radius:" + borderRadius + " " + borderRadius + " 0 0;min-height:52px}",
433
+ "#cm-header-mascot{width:72px;height:72px;position:absolute;left:4px;top:-12px;filter:drop-shadow(0 3px 6px rgba(0,0,0,.25));pointer-events:none}",
434
+ "#cm-title{margin:0;font-size:16px;font-weight:600;letter-spacing:-0.01em}",
435
+ "#cm-close{background:none;border:none;color:#fff;cursor:pointer;font-size:22px;line-height:1;padding:6px 10px;border-radius:6px;transition:background .15s}",
436
+ "#cm-close:hover{background:rgba(255,255,255,.18)}"
437
+ ].join("\n");
438
+ };
439
+
440
+ WidgetCssBuilder.prototype.buildNotes = function() {
441
+ return [
442
+ "#cm-disclaimer,#cm-transparency{font-size:13px;display:flex;align-items:center;gap:8px;padding:8px 14px}",
443
+ "#cm-disclaimer{background:#fff3cd;color:#6b5900;border-bottom:1px solid #ffc107}",
444
+ "#cm-transparency{color:#555;border-top:1px solid #eee}",
445
+ ".cm-close-note{background:none;border:none;cursor:pointer;border-radius:4px;padding:2px 6px;opacity:.7;transition:opacity .15s}",
446
+ ".cm-close-note:hover{opacity:1;background:rgba(0,0,0,.06)}"
447
+ ].join("\n");
448
+ };
449
+
450
+ WidgetCssBuilder.prototype.buildMessages = function() {
451
+ return [
452
+ "#cm-messages{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:12px;scroll-behavior:smooth;overscroll-behavior:contain}",
453
+ "#cm-messages::-webkit-scrollbar{width:6px}",
454
+ "#cm-messages::-webkit-scrollbar-track{background:transparent}",
455
+ "#cm-messages::-webkit-scrollbar-thumb{background:rgba(0,0,0,.12);border-radius:3px}",
456
+ "#cm-messages::-webkit-scrollbar-thumb:hover{background:rgba(0,0,0,.2)}",
457
+ "#cm-welcome{color:#555;font-size:15px;line-height:1.6;text-align:center;padding:24px 16px}"
458
+ ].join("\n");
459
+ };
460
+
461
+ WidgetCssBuilder.prototype.buildMessageBubbles = function(primaryColor) {
462
+ return [
463
+ ".cm-msg{max-width:88%;padding:12px 16px;border-radius:16px;font-size:15px;line-height:1.55;word-break:break-word;animation:cm-msg-in .25s cubic-bezier(.4,0,.2,1)}",
464
+ "@keyframes cm-msg-in{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}",
465
+ ".cm-msg.assistant{background:#f0f4f8;color:#1a1a2e;border-bottom-left-radius:4px;align-self:flex-start}",
466
+ ".cm-msg.assistant p{margin:0 0 10px}",
467
+ ".cm-msg.assistant p:last-child{margin-bottom:0}",
468
+ ".cm-msg.assistant ul,.cm-msg.assistant ol{margin:0 0 10px 20px;padding:0}",
469
+ ".cm-msg.assistant li{margin:0 0 4px}",
470
+ ".cm-msg.assistant a{color:#0b57d0;text-decoration:underline;word-break:break-all}",
471
+ ".cm-msg.assistant code{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;background:#e2e8f0;padding:2px 6px;border-radius:4px;font-size:13px}",
472
+ ".cm-msg.assistant pre{margin:0 0 10px;padding:10px 12px;background:#1f2937;color:#f9fafb;border-radius:10px;overflow-x:auto;font-size:13px}",
473
+ ".cm-msg.assistant pre code{background:transparent;color:inherit;padding:0;font-size:inherit}",
474
+ ".cm-msg.assistant h3,.cm-msg.assistant h4{margin:0 0 8px;font-size:15px;line-height:1.35;font-weight:600}",
475
+ ".cm-msg.user{background:" + primaryColor + ";color:#fff;border-bottom-right-radius:4px;align-self:flex-end}"
476
+ ].join("\n");
477
+ };
478
+
479
+ WidgetCssBuilder.prototype.buildTypingAndError = function() {
480
+ return [
481
+ "#cm-typing{padding:8px 16px;font-size:13px;color:#666;display:none}",
482
+ "#cm-typing.visible{display:flex;align-items:center;gap:8px}",
483
+ "#cm-typing::before{content:'';display:inline-block;width:6px;height:6px;background:#666;border-radius:50%;animation:cm-dot-pulse 1.2s infinite}",
484
+ "@keyframes cm-dot-pulse{0%,100%{opacity:.3;transform:scale(.8)}50%{opacity:1;transform:scale(1.1)}}",
485
+ "#cm-error{display:none;background:#f8d7da;color:#58151c;font-size:13px;padding:10px 14px}",
486
+ "#cm-error.visible{display:block}"
487
+ ].join("\n");
488
+ };
489
+
490
+ WidgetCssBuilder.prototype.buildComposer = function(primaryColor, borderRadius) {
491
+ return [
492
+ "#cm-form{display:flex;align-items:flex-end;gap:8px;padding:12px 16px;border-top:1px solid #eee;background:#fff;border-radius:0 0 " + borderRadius + " " + borderRadius + "}",
493
+ "#cm-input{flex:1;border:1.5px solid #d1d5db;border-radius:14px;padding:12px 16px;min-height:48px;max-height:200px;resize:none;outline:none;line-height:1.5;font-size:15px;transition:border-color .2s,box-shadow .2s;overflow-y:auto}",
494
+ "#cm-input:focus{border-color:" + primaryColor + ";box-shadow:0 0 0 3px rgba(0,102,255,.15)}",
495
+ "#cm-input::placeholder{color:#9ca3af}",
496
+ "#cm-send,#cm-mic{width:48px;height:48px;border-radius:50%;display:flex;align-items:center;justify-content:center;border:none;cursor:pointer;flex-shrink:0;transition:transform .15s,opacity .15s;font-size:18px}",
497
+ "#cm-send{background:" + primaryColor + ";color:#fff}",
498
+ "#cm-send:hover:not(:disabled){transform:scale(1.08)}",
499
+ "#cm-send:active:not(:disabled){transform:scale(0.94)}",
500
+ "#cm-send:disabled,#cm-mic:disabled{opacity:.4;cursor:not-allowed}",
501
+ "#cm-mic{background:transparent;color:#667085;border:1.5px solid #d1d5db}",
502
+ "#cm-mic:hover:not(:disabled){border-color:#9ca3af;background:rgba(0,0,0,.02)}",
503
+ "#cm-mic.listening{background:#ef4444;color:#fff;border-color:#ef4444;animation:cm-mic-pulse 1.5s infinite}",
504
+ "@keyframes cm-mic-pulse{0%,100%{box-shadow:0 0 0 0 rgba(239,68,68,.35)}50%{box-shadow:0 0 0 8px rgba(239,68,68,0)}}"
505
+ ].join("\n");
506
+ };
507
+
508
+ WidgetCssBuilder.prototype.buildResizeHandle = function(corner) {
509
+ var posX = corner === "left" ? "left:0" : "right:0";
510
+ var cursorType = corner === "left" ? "ne-resize" : "nw-resize";
511
+ return [
512
+ "#cm-resize-handle{position:absolute;top:0;" + posX + ";width:28px;height:28px;cursor:" + cursorType + ";z-index:10;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .2s}",
513
+ "#cm-panel:hover #cm-resize-handle{opacity:.5}",
514
+ "#cm-resize-handle:hover{opacity:1 !important}",
515
+ "#cm-resize-handle svg{width:14px;height:14px;pointer-events:none}"
516
+ ].join("\n");
517
+ };
518
+
519
+ WidgetCssBuilder.prototype.buildMobileOverrides = function() {
520
+ return [
521
+ "@media (max-width:600px){",
522
+ "#cm-panel{left:0 !important;right:0 !important;bottom:0 !important;top:0 !important;width:100% !important;max-width:none !important;height:100dvh !important;height:100vh !important;max-height:none !important;border-radius:0 !important}",
523
+ "#cm-header{border-radius:0 !important;padding:16px 16px 16px 80px;min-height:56px}",
524
+ "#cm-header-mascot{width:64px;height:64px;left:8px;top:-4px}",
525
+ "#cm-title{font-size:17px}",
526
+ "#cm-close{font-size:24px;padding:8px 12px}",
527
+ "#cm-messages{padding:16px;gap:14px}",
528
+ ".cm-msg{max-width:92%;padding:14px 16px;font-size:16px;border-radius:18px}",
529
+ "#cm-form{padding:12px 16px;padding-bottom:calc(12px + env(safe-area-inset-bottom,0px));border-radius:0 !important;gap:10px}",
530
+ "#cm-input{min-height:48px;font-size:16px;border-radius:16px;padding:12px 16px}",
531
+ "#cm-send,#cm-mic{width:48px;height:48px}",
532
+ "#cm-typing{padding:8px 16px;font-size:14px}",
533
+ "#cm-disclaimer,#cm-transparency{font-size:13px;padding:10px 16px}",
534
+ "#cm-resize-handle{display:none !important}",
535
+ "#cm-welcome{font-size:16px;padding:32px 20px}",
536
+ "}"
537
+ ].join("\n");
538
+ };
539
+
540
+ ui.WidgetCssBuilder = WidgetCssBuilder;
541
+ }
542
+
543
+ function installWidgetUIManager(ui) {
544
+ function WidgetUIManager(config) {
545
+ this.config = config;
546
+ this.root = null;
547
+ this.bubble = null;
548
+ this.bubbleMascot = null;
549
+ this.panel = null;
550
+ this.headerMascot = null;
551
+ this.messages = null;
552
+ this.input = null;
553
+ this.sendButton = null;
554
+ this.micButton = null;
555
+ this.typing = null;
556
+ this.error = null;
557
+ this.welcome = null;
558
+ this.streamingAssistantElement = null;
559
+ this.streamingAssistantBuffer = "";
560
+ this.themeOverrideStyle = null;
561
+ this._resizeDragState = null;
562
+ }
563
+
564
+ WidgetUIManager.prototype.mount = function() {
565
+ if (document.getElementById(ui.CONTAINER_ID)) return false;
566
+ var cssBuilder = new ui.WidgetCssBuilder(this.config);
567
+ var css = cssBuilder.build();
568
+ var esc = ui.escapeHtml;
569
+ var escAttr = ui.escapeHtmlAttribute;
570
+ var resizeCorner = this.config.position === "bottom-left" ? "right" : "left";
571
+ var resizeSvg = resizeCorner === "left"
572
+ ? '<svg viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><line x1="2" y1="12" x2="12" y2="2"/><line x1="2" y1="7" x2="7" y2="2"/></svg>'
573
+ : '<svg viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><line x1="12" y1="12" x2="2" y2="2"/><line x1="12" y1="7" x2="7" y2="2"/></svg>';
574
+
575
+ var host = document.createElement("div");
576
+ host.id = ui.CONTAINER_ID;
577
+ document.body.appendChild(host);
578
+ var shadow = host.attachShadow({ mode: "open" });
579
+ var root = document.createElement("div");
580
+ root.innerHTML = [
581
+ "<style>" + css + "</style>",
582
+ '<button id="cm-bubble" type="button" aria-label="Ouvrir l\'assistant">',
583
+ '<img id="cm-bubble-mascot" src="' + escAttr(this.config.mascotUrl) + '" alt="Mascotte assistant" />',
584
+ "</button>",
585
+ '<div id="cm-panel" role="dialog" aria-modal="true" aria-label="Chat ' + escAttr(this.config.assistantName) + '">',
586
+ '<div id="cm-resize-handle" aria-hidden="true">' + resizeSvg + '</div>',
587
+ '<div id="cm-header"><img id="cm-header-mascot" src="' + escAttr(this.config.mascotUrl) + '" alt="" /><h3 id="cm-title">' + esc(this.config.assistantName) + '</h3><button id="cm-close" type="button" aria-label="Fermer l\'assistant">\u00d7</button></div>',
588
+ this.config.disclaimer ? '<div id="cm-disclaimer"><span>' + esc(this.config.disclaimerText) + '</span><button class="cm-close-note" id="cm-disclaimer-close" type="button" aria-label="Fermer l\'avertissement">\u00d7</button></div>' : "",
589
+ '<div id="cm-messages" aria-live="polite" aria-relevant="additions"><p id="cm-welcome">' + esc(this.config.welcomeMessage) + "</p></div>",
590
+ '<div id="cm-typing">L\'assistant r\u00e9fl\u00e9chit\u2026</div>',
591
+ '<div id="cm-error"></div>',
592
+ '<div id="cm-transparency"><span>' + esc(this.config.transparencyText) + '</span><button class="cm-close-note" id="cm-transparency-close" type="button" aria-label="Fermer le message de transparence">\u00d7</button></div>',
593
+ '<form id="cm-form"><button id="cm-mic" type="button" aria-label="Dicter un message">\ud83c\udfa4</button><textarea id="cm-input" rows="1" aria-label="Votre message" placeholder="Posez votre question\u2026"></textarea><button id="cm-send" type="submit" aria-label="Envoyer">\u27a4</button></form>',
594
+ "</div>"
595
+ ].join("");
596
+ shadow.appendChild(root);
597
+ this.host = host;
598
+ this.root = root;
599
+ this.bubble = root.querySelector("#cm-bubble");
600
+ this.bubbleMascot = root.querySelector("#cm-bubble-mascot");
601
+ this.panel = root.querySelector("#cm-panel");
602
+ this.headerMascot = root.querySelector("#cm-header-mascot");
603
+ this.messages = root.querySelector("#cm-messages");
604
+ this.input = root.querySelector("#cm-input");
605
+ this.sendButton = root.querySelector("#cm-send");
606
+ this.micButton = root.querySelector("#cm-mic");
607
+ this.typing = root.querySelector("#cm-typing");
608
+ this.error = root.querySelector("#cm-error");
609
+ this.welcome = root.querySelector("#cm-welcome");
610
+ this.themeOverrideStyle = document.createElement("style");
611
+ this.themeOverrideStyle.id = "cm-theme-overrides";
612
+ root.appendChild(this.themeOverrideStyle);
613
+ this.bindMascotImageErrorHandlers();
614
+ this.bindAutoGrowInput();
615
+ this.bindResizeHandle();
616
+ this.applyThemeOverrides();
617
+ this.applyMascotTheme();
618
+ return true;
619
+ };
620
+
621
+ WidgetUIManager.prototype.bindAutoGrowInput = function() {
622
+ var inputEl = this.input;
623
+ if (!inputEl) return;
624
+ function adjustHeight() {
625
+ inputEl.style.height = "auto";
626
+ var scrollH = inputEl.scrollHeight;
627
+ var maxH = 200;
628
+ inputEl.style.height = Math.min(scrollH, maxH) + "px";
629
+ inputEl.style.overflowY = scrollH > maxH ? "auto" : "hidden";
630
+ }
631
+ inputEl.addEventListener("input", adjustHeight);
632
+ inputEl.style.overflowY = "hidden";
633
+ };
634
+
635
+ WidgetUIManager.prototype.bindResizeHandle = function() {
636
+ var handle = this.root ? this.root.querySelector("#cm-resize-handle") : null;
637
+ var panel = this.panel;
638
+ if (!handle || !panel) return;
639
+ var self = this;
640
+ var isLeftCorner = this.config.position !== "bottom-left";
641
+
642
+ handle.addEventListener("mousedown", function(startEvt) {
643
+ startEvt.preventDefault();
644
+ startEvt.stopPropagation();
645
+ var rect = panel.getBoundingClientRect();
646
+ self._resizeDragState = {
647
+ startX: startEvt.clientX,
648
+ startY: startEvt.clientY,
649
+ startW: rect.width,
650
+ startH: rect.height
651
+ };
652
+ document.addEventListener("mousemove", onMouseMove);
653
+ document.addEventListener("mouseup", onMouseUp);
654
+ });
655
+
656
+ function onMouseMove(evt) {
657
+ var st = self._resizeDragState;
658
+ if (!st) return;
659
+ var dx = evt.clientX - st.startX;
660
+ var dy = evt.clientY - st.startY;
661
+ var newW = isLeftCorner ? st.startW - dx : st.startW + dx;
662
+ var newH = st.startH - dy;
663
+ newW = Math.max(360, Math.min(960, newW));
664
+ newH = Math.max(480, Math.min(window.innerHeight - 40, newH));
665
+ panel.style.width = newW + "px";
666
+ panel.style.height = newH + "px";
667
+ }
668
+
669
+ function onMouseUp() {
670
+ self._resizeDragState = null;
671
+ document.removeEventListener("mousemove", onMouseMove);
672
+ document.removeEventListener("mouseup", onMouseUp);
673
+ }
674
+ };
675
+
676
+ WidgetUIManager.prototype.bindMascotImageErrorHandlers = function() {
677
+ var self = this;
678
+ [this.bubbleMascot, this.headerMascot].forEach(function(mascotNode) {
679
+ if (!mascotNode) return;
680
+ mascotNode.addEventListener("error", function() {
681
+ var failedUrl = mascotNode.getAttribute("src") || "";
682
+ var errorMessage = failedUrl ? "chargement échoué pour " + failedUrl : "chargement échoué";
683
+ console.error("[OurluMairie] mascot image error:", errorMessage);
684
+ self.showMascotLoadError(errorMessage);
685
+ });
686
+ });
687
+ };
688
+
689
+ WidgetUIManager.prototype.showMascotLoadError = function(message) {
690
+ var resolvedMessage = String(message || "Mascotte indisponible.").trim() || "Mascotte indisponible.";
691
+ this.showError("Mascotte indisponible: " + resolvedMessage);
692
+ if (this.bubbleMascot) {
693
+ this.bubbleMascot.style.display = "none";
694
+ this.bubbleMascot.alt = resolvedMessage;
695
+ }
696
+ if (this.headerMascot) {
697
+ this.headerMascot.style.display = "none";
698
+ }
699
+ };
700
+
701
+ WidgetUIManager.prototype.bind = function(callbacks) {
702
+ this.bubble.addEventListener("click", callbacks.onToggle);
703
+ this.root.querySelector("#cm-close").addEventListener("click", callbacks.onToggle);
704
+ this.root.querySelector("#cm-form").addEventListener("submit", callbacks.onSend);
705
+ this.micButton.addEventListener("click", callbacks.onMicToggle);
706
+ document.addEventListener("keydown", callbacks.onEscape);
707
+ var disclaimerClose = this.root.querySelector("#cm-disclaimer-close");
708
+ var transparencyClose = this.root.querySelector("#cm-transparency-close");
709
+ var rootRef = this.root;
710
+ if (disclaimerClose) disclaimerClose.addEventListener("click", function() { var p = rootRef.querySelector("#cm-disclaimer"); if (p) p.style.display = "none"; });
711
+ if (transparencyClose) transparencyClose.addEventListener("click", function() { var p = rootRef.querySelector("#cm-transparency"); if (p) p.style.display = "none"; });
712
+ };
713
+
714
+ WidgetUIManager.prototype.setMascotSource = function(sourceUrl) {
715
+ var normalizedSourceUrl = String(sourceUrl || "").trim();
716
+ if (!normalizedSourceUrl) return;
717
+ if (this.bubbleMascot) {
718
+ this.bubbleMascot.src = normalizedSourceUrl;
719
+ this.bubbleMascot.style.display = "block";
720
+ }
721
+ if (this.headerMascot) {
722
+ this.headerMascot.src = normalizedSourceUrl;
723
+ this.headerMascot.style.display = "block";
724
+ }
725
+ };
726
+
727
+ WidgetUIManager.prototype.applyMascotTheme = function() {
728
+ var self = this;
729
+ ui.resolveMascotUrlWithTheme(this.config.mascotUrl, this.config).then(function(themedMascotUrl) {
730
+ self.setMascotSource(themedMascotUrl);
731
+ }).catch(function(error) {
732
+ var message = (error && error.message) ? error.message : "Mascotte indisponible.";
733
+ console.error("[OurluMairie]", message, error);
734
+ self.showMascotLoadError(message);
735
+ });
736
+ };
737
+
738
+ WidgetUIManager.prototype.applyThemeOverrides = function() {
739
+ if (!this.root || !this.themeOverrideStyle) return;
740
+ var isDarkTheme = String(this.config.effectiveThemeScheme || "light").toLowerCase() === "dark";
741
+ var primaryColor = ui.normalizeHexColor(this.config.primaryColor, "#0066ff");
742
+ var panelBackgroundBaseColor = isDarkTheme ? "#111827" : this.config.panelBackgroundColor;
743
+ var panelBackgroundAlpha = isDarkTheme ? "0.96" : this.config.panelBackgroundAlpha;
744
+ var panelBackground = ui.hexToRgba(panelBackgroundBaseColor, panelBackgroundAlpha);
745
+ var borderRadius = Number(this.config.panelBorderRadius);
746
+ if (!Number.isFinite(borderRadius)) borderRadius = 16;
747
+ borderRadius = Math.max(0, Math.min(32, Math.round(borderRadius)));
748
+ var borderRadiusPx = borderRadius + "px";
749
+ var gradient = "linear-gradient(135deg," + primaryColor + " 0%,#0047b3 100%)";
750
+ var darkThemeRules = isDarkTheme ? [
751
+ "#cm-panel{color:#f3f4f6 !important}",
752
+ "#cm-messages{background:rgba(2,6,23,0.28) !important}",
753
+ ".cm-msg.assistant{background:rgba(30,41,59,0.94) !important;color:#f8fafc !important}",
754
+ ".cm-msg.assistant code{background:#334155 !important;color:#f8fafc !important}",
755
+ "#cm-form{background:#0f172a !important;border-top:1px solid #1f2937 !important}",
756
+ "#cm-input{background:#111827 !important;color:#f8fafc !important;border-color:#334155 !important}",
757
+ "#cm-input::placeholder{color:#94a3b8 !important}",
758
+ "#cm-error{background:#7f1d1d !important;color:#fee2e2 !important}",
759
+ "#cm-resize-handle{color:#94a3b8 !important}"
760
+ ] : [
761
+ "#cm-messages{background:transparent !important}",
762
+ ".cm-msg.assistant{background:#f0f4f8 !important;color:#1a1a2e !important}",
763
+ "#cm-form{background:#ffffff !important;border-top:1px solid #eee !important}",
764
+ "#cm-input{background:#ffffff !important;color:#0f172a !important;border-color:#d1d5db !important}",
765
+ "#cm-error{background:#f8d7da !important;color:#58151c !important}"
766
+ ];
767
+ this.themeOverrideStyle.textContent = [
768
+ "#cm-bubble{background:" + gradient + " !important}",
769
+ "#cm-bubble:focus-visible{outline-color:" + primaryColor + " !important}",
770
+ "#cm-panel{background:" + panelBackground + " !important;border-radius:" + borderRadiusPx + " !important}",
771
+ "#cm-header{background:" + gradient + " !important;border-radius:" + borderRadiusPx + " " + borderRadiusPx + " 0 0 !important}",
772
+ "#cm-form{border-radius:0 0 " + borderRadiusPx + " " + borderRadiusPx + " !important}",
773
+ "#cm-send{background:" + primaryColor + " !important}",
774
+ ".cm-msg.user{background:" + primaryColor + " !important}",
775
+ "#cm-input:focus{border-color:" + primaryColor + " !important;box-shadow:0 0 0 3px rgba(0,102,255,.15) !important}"
776
+ ].concat(darkThemeRules).join("\n");
777
+ };
778
+
779
+ WidgetUIManager.prototype.updateTheme = function(updatedConfig) {
780
+ this.config = Object.assign({}, this.config, updatedConfig || {});
781
+ if (this.root) {
782
+ var titleNode = this.root.querySelector("#cm-title");
783
+ if (titleNode) {
784
+ titleNode.textContent = this.config.assistantName || "Assistant mairie";
785
+ }
786
+ if (this.panel) {
787
+ this.panel.setAttribute("aria-label", "Chat " + (this.config.assistantName || "Assistant mairie"));
788
+ }
789
+ var userMessages = this.root.querySelectorAll(".cm-msg.user");
790
+ var userBubbleColor = ui.normalizeHexColor(this.config.primaryColor, "#0066ff");
791
+ userMessages.forEach(function(node) {
792
+ node.style.background = userBubbleColor;
793
+ });
794
+ if (this.welcome && this.config.welcomeMessage) {
795
+ this.welcome.textContent = this.config.welcomeMessage;
796
+ }
797
+ var disclaimerSpan = this.root.querySelector("#cm-disclaimer > span");
798
+ if (disclaimerSpan && this.config.disclaimerText) {
799
+ disclaimerSpan.textContent = this.config.disclaimerText;
800
+ }
801
+ var headerMascot = this.root.querySelector("#cm-header-mascot");
802
+ if (headerMascot && this.config.mascotUrl) {
803
+ headerMascot.setAttribute("src", this.config.mascotUrl);
804
+ headerMascot.style.display = "";
805
+ }
806
+ }
807
+ this.applyThemeOverrides();
808
+ this.applyMascotTheme();
809
+ };
810
+
811
+ WidgetUIManager.prototype.setOpen = function(opened) {
812
+ this.panel.classList.toggle("open", opened);
813
+ this.bubble.style.display = opened ? "none" : "flex";
814
+ if (opened) this.input.focus();
815
+ };
816
+
817
+ WidgetUIManager.prototype.pullInput = function() {
818
+ var value = this.input.value.trim();
819
+ this.input.value = "";
820
+ this.input.style.height = "auto";
821
+ return value;
822
+ };
823
+
824
+ WidgetUIManager.prototype.setInput = function(value) { this.input.value = String(value || ""); };
825
+ WidgetUIManager.prototype.inputValue = function() { return this.input.value; };
826
+ WidgetUIManager.prototype.showTyping = function(visible) { this.typing.classList.toggle("visible", Boolean(visible)); };
827
+
828
+ WidgetUIManager.prototype.setComposerDisabled = function(disabled) {
829
+ this.input.disabled = disabled;
830
+ this.sendButton.disabled = disabled;
831
+ this.micButton.disabled = disabled;
832
+ };
833
+
834
+ WidgetUIManager.prototype.showError = function(message) {
835
+ this.error.textContent = message;
836
+ this.error.classList.toggle("visible", Boolean(message));
837
+ };
838
+
839
+ WidgetUIManager.prototype.setAssistantDraftText = function(node, text) {
840
+ if (!node) return;
841
+ node.textContent = String(text || "");
842
+ };
843
+
844
+ WidgetUIManager.prototype.setAssistantFinalText = function(node, text) {
845
+ if (!node) return;
846
+ node.innerHTML = ui.renderAssistantMarkdown(String(text || ""));
847
+ };
848
+
849
+ WidgetUIManager.prototype.addMessage = function(role, text) {
850
+ if (this.welcome) { this.welcome.remove(); this.welcome = null; }
851
+ var node = document.createElement("div");
852
+ node.className = "cm-msg " + role;
853
+ if (role === "assistant") {
854
+ this.setAssistantDraftText(node, text);
855
+ } else {
856
+ node.textContent = text;
857
+ }
858
+ this.messages.appendChild(node);
859
+ this.messages.scrollTop = this.messages.scrollHeight;
860
+ return node;
861
+ };
862
+
863
+ WidgetUIManager.prototype.startAssistantStream = function() {
864
+ this.streamingAssistantElement = this.addMessage("assistant", "");
865
+ this.streamingAssistantBuffer = "";
866
+ };
867
+
868
+ WidgetUIManager.prototype.appendAssistantToken = function(token) {
869
+ if (!this.streamingAssistantElement) this.startAssistantStream();
870
+ var resolvedToken = String(token || "");
871
+ if (!resolvedToken) return;
872
+ this.streamingAssistantBuffer += resolvedToken;
873
+ this.streamingAssistantElement.textContent += resolvedToken;
874
+ this.messages.scrollTop = this.messages.scrollHeight;
875
+ };
876
+
877
+ WidgetUIManager.prototype.finalizeAssistantMessage = function(content) {
878
+ if (!this.streamingAssistantElement) this.startAssistantStream();
879
+ var resolvedContent = content || this.streamingAssistantBuffer || this.streamingAssistantElement.textContent || "";
880
+ this.setAssistantFinalText(this.streamingAssistantElement, resolvedContent);
881
+ this.streamingAssistantElement = null;
882
+ this.streamingAssistantBuffer = "";
883
+ };
884
+
885
+ WidgetUIManager.prototype.setMicListening = function(isListening) {
886
+ this.micButton.classList.toggle("listening", isListening);
887
+ this.micButton.textContent = isListening ? "\u23f9" : "\ud83c\udfa4";
888
+ this.micButton.setAttribute("aria-label", isListening ? "Arrêter la dictée vocale" : "Dicter un message");
889
+ this.input.placeholder = isListening ? "Transcription en cours\u2026" : "Posez votre question\u2026";
890
+ };
891
+
892
+ ui.WidgetUIManager = WidgetUIManager;
893
+ }
894
+
895
+ "use strict";
896
+ var runtime = window.__OurluWidgetRuntimeV1 || (window.__OurluWidgetRuntimeV1 = {});
897
+ var ui = {};
898
+
899
+ ui.CONTAINER_ID = "ourlu-widget-root";
900
+
901
+ installColorUtils(ui);
902
+ installMascotTheme(ui);
903
+ installMarkdownRender(ui);
904
+ installMediaUtils(ui);
905
+ installEventBus(ui);
906
+ installWidgetCssBuilder(ui);
907
+ installWidgetUIManager(ui);
908
+
909
+ runtime.constants = { CONTAINER_ID: ui.CONTAINER_ID };
910
+ runtime.EventBus = ui.EventBus;
911
+ runtime.WidgetUIManager = ui.WidgetUIManager;
912
+ runtime.utils = {
913
+ trimTrailingSlash: ui.trimTrailingSlash,
914
+ mergeTranscript: ui.mergeTranscript,
915
+ resolveRecorderMimeType: ui.resolveRecorderMimeType,
916
+ encodeArrayBufferToBase64: ui.encodeArrayBufferToBase64,
917
+ resolveAbsoluteMascotUrl: ui.resolveAbsoluteMascotUrl
918
+ };
919
+ })();