@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,458 @@
1
+ (function() {
2
+ "use strict";
3
+ var runtime = window.__OurluWidgetRuntimeV1 || (window.__OurluWidgetRuntimeV1 = {});
4
+
5
+ var BAN_API_URL = "https://api-adresse.data.gouv.fr/search/";
6
+ var BAN_DEBOUNCE_MS = 300;
7
+ var BAN_MAX_RESULTS = 5;
8
+
9
+ function PluCssBuilder() {}
10
+
11
+ PluCssBuilder.prototype.build = function() {
12
+ return [
13
+ this.buildContent(), this.buildAddressInput(), this.buildSuggestions(),
14
+ this.buildSearchButton(), this.buildLoading(), this.buildResult(),
15
+ this.buildError(), this.buildPluButton(), this.buildMobileOverrides()
16
+ ].join("\n");
17
+ };
18
+
19
+ PluCssBuilder.prototype.buildContent = function() {
20
+ return [
21
+ ".cm-tab[data-tab='plu'].hidden{display:none}",
22
+ "#cm-plu-content{display:none;flex-direction:column;flex:1;overflow:hidden}",
23
+ "#cm-plu-content.active{display:flex}",
24
+ ".cm-plu-scroll{flex:1;overflow-y:auto;overflow-x:hidden;padding:20px 16px;display:flex;flex-direction:column;gap:16px;min-width:0;width:100%}",
25
+ ".cm-plu-scroll::-webkit-scrollbar{width:6px}",
26
+ ".cm-plu-scroll::-webkit-scrollbar-thumb{background:rgba(0,0,0,.1);border-radius:3px}",
27
+ ".cm-plu-title{display:flex;align-items:center;gap:8px;font-size:16px;font-weight:600;color:#1e293b;margin:0}",
28
+ ".cm-plu-title svg{width:20px;height:20px;flex-shrink:0}",
29
+ ".cm-plu-desc{font-size:14px;color:#64748b;line-height:1.5;margin:0}"
30
+ ].join("\n");
31
+ };
32
+
33
+ PluCssBuilder.prototype.buildAddressInput = function() {
34
+ return [
35
+ ".cm-plu-input-wrap{position:relative}",
36
+ ".cm-plu-input{width:100%;padding:12px 14px;border:1.5px solid #e2e8f0;border-radius:12px;font-size:14px;font-family:inherit;outline:none;transition:border-color .2s,box-shadow .2s;background:#fff;color:#1e293b;box-sizing:border-box}",
37
+ ".cm-plu-input:focus{border-color:#0066ff;box-shadow:0 0 0 3px rgba(0,102,255,.15)}",
38
+ ".cm-plu-input::placeholder{color:#9ca3af}"
39
+ ].join("\n");
40
+ };
41
+
42
+ PluCssBuilder.prototype.buildSuggestions = function() {
43
+ return [
44
+ ".cm-plu-suggestions{position:absolute;top:100%;left:0;right:0;margin-top:4px;background:#fff;border:1.5px solid #e2e8f0;border-radius:10px;box-shadow:0 8px 24px rgba(0,0,0,.12);z-index:10;overflow:hidden;display:none}",
45
+ ".cm-plu-suggestions.visible{display:block}",
46
+ ".cm-plu-suggestion{padding:10px 14px;font-size:14px;color:#334155;cursor:pointer;transition:background .1s;border-bottom:1px solid #f1f5f9;font-family:inherit}",
47
+ ".cm-plu-suggestion:last-child{border-bottom:none}",
48
+ ".cm-plu-suggestion:hover,.cm-plu-suggestion.highlighted{background:#eff6ff;color:#1d4ed8}"
49
+ ].join("\n");
50
+ };
51
+
52
+ PluCssBuilder.prototype.buildSearchButton = function() {
53
+ return [
54
+ ".cm-plu-search-btn{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;padding:12px 20px;border-radius:12px;border:none;background:#0066ff;color:#fff;font-size:14px;font-weight:600;cursor:pointer;transition:all .15s;font-family:inherit}",
55
+ ".cm-plu-search-btn svg{width:18px;height:18px;flex-shrink:0}",
56
+ ".cm-plu-search-btn:hover:not(:disabled){background:#0052cc;transform:translateY(-1px);box-shadow:0 4px 12px rgba(0,102,255,.3)}",
57
+ ".cm-plu-search-btn:active:not(:disabled){transform:scale(0.97)}",
58
+ ".cm-plu-search-btn:disabled{opacity:.5;cursor:default}"
59
+ ].join("\n");
60
+ };
61
+
62
+ PluCssBuilder.prototype.buildLoading = function() {
63
+ return [
64
+ ".cm-plu-loading{display:none;flex-direction:column;align-items:center;gap:12px;padding:24px 16px;text-align:center}",
65
+ ".cm-plu-loading.visible{display:flex}",
66
+ ".cm-plu-spinner{width:36px;height:36px;border:3px solid #e2e8f0;border-top-color:#0066ff;border-radius:50%;animation:cm-plu-spin 1s linear infinite}",
67
+ "@keyframes cm-plu-spin{to{transform:rotate(360deg)}}",
68
+ ".cm-plu-loading-text{font-size:14px;color:#64748b;font-weight:500}",
69
+ ".cm-plu-loading-sub{font-size:13px;color:#94a3b8}"
70
+ ].join("\n");
71
+ };
72
+
73
+ PluCssBuilder.prototype.buildResult = function() {
74
+ return [
75
+ ".cm-plu-result{display:none;flex-direction:column;gap:16px}",
76
+ ".cm-plu-result.visible{display:flex}",
77
+ ".cm-plu-result-addr{display:flex;align-items:flex-start;gap:8px;font-size:14px;color:#334155;line-height:1.5}",
78
+ ".cm-plu-result-addr svg{width:18px;height:18px;flex-shrink:0;color:#0066ff;margin-top:2px}",
79
+ ".cm-plu-zone-card{padding:14px 16px;border-radius:12px;background:#eff6ff;border:1.5px solid #bfdbfe;display:flex;flex-direction:column;gap:4px}",
80
+ ".cm-plu-zone-code{font-size:18px;font-weight:700;color:#1d4ed8}",
81
+ ".cm-plu-zone-label{font-size:14px;color:#3b82f6;font-weight:500}",
82
+ ".cm-plu-zone-doc{font-size:12px;color:#64748b;margin-top:2px}",
83
+ ".cm-plu-rules-title{font-size:13px;font-weight:600;color:#475569;text-transform:uppercase;letter-spacing:.04em;margin-bottom:4px}",
84
+ ".cm-plu-rules-list{list-style:disc;padding-left:20px;margin:0;display:flex;flex-direction:column;gap:6px}",
85
+ ".cm-plu-rules-list li{font-size:14px;color:#334155;line-height:1.5}",
86
+ ".cm-plu-sources{font-size:12px;color:#94a3b8;margin-top:4px}",
87
+ ".cm-plu-disclaimer{display:flex;align-items:flex-start;gap:8px;padding:12px 14px;border-radius:10px;background:#fffbeb;border:1px solid #fde68a;font-size:13px;color:#92400e;line-height:1.5}",
88
+ ".cm-plu-disclaimer svg{width:16px;height:16px;flex-shrink:0;margin-top:2px}",
89
+ ".cm-plu-new-search-btn{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;padding:10px 20px;border-radius:12px;border:1.5px solid #e2e8f0;background:#fff;color:#475569;font-size:14px;font-weight:500;cursor:pointer;transition:all .15s;font-family:inherit}",
90
+ ".cm-plu-new-search-btn:hover{border-color:#94a3b8;background:#f8fafc;color:#334155}"
91
+ ].join("\n");
92
+ };
93
+
94
+ PluCssBuilder.prototype.buildError = function() {
95
+ return [
96
+ ".cm-plu-error{display:none;flex-direction:column;align-items:center;gap:12px;padding:20px 16px;text-align:center}",
97
+ ".cm-plu-error.visible{display:flex}",
98
+ ".cm-plu-error-icon{width:40px;height:40px;border-radius:50%;background:#fef2f2;display:flex;align-items:center;justify-content:center}",
99
+ ".cm-plu-error-icon svg{width:22px;height:22px;color:#dc2626}",
100
+ ".cm-plu-error-msg{font-size:14px;color:#991b1b;font-weight:500;line-height:1.5}",
101
+ ".cm-plu-error-code{font-size:12px;color:#94a3b8;font-family:ui-monospace,monospace}",
102
+ ".cm-plu-retry-btn{display:flex;align-items:center;justify-content:center;gap:6px;padding:10px 20px;border-radius:12px;border:1.5px solid #fecaca;background:#fef2f2;color:#dc2626;font-size:14px;font-weight:500;cursor:pointer;transition:all .15s;font-family:inherit}",
103
+ ".cm-plu-retry-btn:hover{background:#dc2626;color:#fff;border-color:#dc2626}"
104
+ ].join("\n");
105
+ };
106
+
107
+ PluCssBuilder.prototype.buildPluButton = function() {
108
+ return [
109
+ ".cm-plu-open-btn{display:inline-flex;align-items:center;gap:8px;margin-top:10px;padding:10px 18px;border-radius:12px;border:1.5px solid #0066ff;background:#eff6ff;color:#0066ff;font-size:14px;font-weight:600;cursor:pointer;transition:all .15s;font-family:inherit}",
110
+ ".cm-plu-open-btn svg{width:18px;height:18px;flex-shrink:0}",
111
+ ".cm-plu-open-btn:hover{background:#0066ff;color:#fff;transform:translateY(-1px);box-shadow:0 4px 12px rgba(0,102,255,.3)}",
112
+ ".cm-plu-open-btn:active{transform:scale(0.97)}"
113
+ ].join("\n");
114
+ };
115
+
116
+ PluCssBuilder.prototype.buildMobileOverrides = function() {
117
+ return "@media(max-width:600px){.cm-plu-scroll{padding:16px 12px}.cm-plu-zone-card{padding:12px 14px}}";
118
+ };
119
+
120
+ function BanAutocompleteManager(inputEl, suggestionsEl) {
121
+ this.inputEl = inputEl;
122
+ this.suggestionsEl = suggestionsEl;
123
+ this.debounceTimer = null;
124
+ this.highlightedIndex = -1;
125
+ this.results = [];
126
+ this.selectedAddress = null;
127
+ this.onSelect = null;
128
+ this.bindEvents();
129
+ }
130
+
131
+ BanAutocompleteManager.prototype.bindEvents = function() {
132
+ var self = this;
133
+ this.inputEl.addEventListener("input", function() {
134
+ self.selectedAddress = null;
135
+ self.scheduleSearch(self.inputEl.value.trim());
136
+ });
137
+ this.inputEl.addEventListener("keydown", function(e) {
138
+ if (!self.results.length) return;
139
+ if (e.key === "ArrowDown") {
140
+ e.preventDefault();
141
+ self.highlightedIndex = Math.min(self.highlightedIndex + 1, self.results.length - 1);
142
+ self.renderHighlight();
143
+ } else if (e.key === "ArrowUp") {
144
+ e.preventDefault();
145
+ self.highlightedIndex = Math.max(self.highlightedIndex - 1, 0);
146
+ self.renderHighlight();
147
+ } else if (e.key === "Enter") {
148
+ e.preventDefault();
149
+ if (self.highlightedIndex >= 0) self.selectResult(self.results[self.highlightedIndex]);
150
+ } else if (e.key === "Escape") {
151
+ self.hideSuggestions();
152
+ }
153
+ });
154
+ document.addEventListener("click", function(e) {
155
+ if (!self.inputEl.contains(e.target) && !self.suggestionsEl.contains(e.target)) self.hideSuggestions();
156
+ });
157
+ };
158
+
159
+ BanAutocompleteManager.prototype.scheduleSearch = function(query) {
160
+ var self = this;
161
+ clearTimeout(this.debounceTimer);
162
+ if (query.length < 3) { this.hideSuggestions(); return; }
163
+ this.debounceTimer = setTimeout(function() { self.search(query); }, BAN_DEBOUNCE_MS);
164
+ };
165
+
166
+ BanAutocompleteManager.prototype.search = async function(query) {
167
+ var url = BAN_API_URL + "?q=" + encodeURIComponent(query) + "&limit=" + BAN_MAX_RESULTS;
168
+ var response = await fetch(url);
169
+ if (!response.ok) throw new Error("BAN API erreur HTTP " + response.status);
170
+ var data = await response.json();
171
+ this.results = (data.features || []).map(function(f) {
172
+ var props = f.properties || {};
173
+ var coords = f.geometry && f.geometry.coordinates ? f.geometry.coordinates : [0, 0];
174
+ return { label: props.label || "", city: props.city || "", postcode: props.postcode || "", citycode: props.citycode || "", longitude: coords[0], latitude: coords[1] };
175
+ });
176
+ this.highlightedIndex = -1;
177
+ this.renderSuggestions();
178
+ };
179
+
180
+ BanAutocompleteManager.prototype.renderSuggestions = function() {
181
+ var self = this;
182
+ if (!this.results.length) { this.hideSuggestions(); return; }
183
+ this.suggestionsEl.innerHTML = this.results.map(function(r, idx) {
184
+ return '<div class="cm-plu-suggestion" data-idx="' + idx + '">' + self.escapeHtml(r.label) + '</div>';
185
+ }).join("");
186
+ this.suggestionsEl.classList.add("visible");
187
+ this.suggestionsEl.querySelectorAll(".cm-plu-suggestion").forEach(function(el) {
188
+ el.addEventListener("click", function() { self.selectResult(self.results[Number(el.getAttribute("data-idx"))]); });
189
+ });
190
+ };
191
+
192
+ BanAutocompleteManager.prototype.renderHighlight = function() {
193
+ var self = this;
194
+ this.suggestionsEl.querySelectorAll(".cm-plu-suggestion").forEach(function(el, idx) {
195
+ el.classList.toggle("highlighted", idx === self.highlightedIndex);
196
+ });
197
+ };
198
+
199
+ BanAutocompleteManager.prototype.selectResult = function(result) {
200
+ if (!result) return;
201
+ this.selectedAddress = result;
202
+ this.inputEl.value = result.label;
203
+ this.hideSuggestions();
204
+ if (typeof this.onSelect === "function") this.onSelect(result);
205
+ };
206
+
207
+ BanAutocompleteManager.prototype.hideSuggestions = function() {
208
+ this.suggestionsEl.classList.remove("visible");
209
+ this.results = [];
210
+ this.highlightedIndex = -1;
211
+ };
212
+
213
+ BanAutocompleteManager.prototype.escapeHtml = function(str) {
214
+ var div = document.createElement("div");
215
+ div.textContent = str;
216
+ return div.innerHTML;
217
+ };
218
+
219
+ BanAutocompleteManager.prototype.reset = function() {
220
+ this.inputEl.value = "";
221
+ this.selectedAddress = null;
222
+ this.hideSuggestions();
223
+ };
224
+
225
+ var ICON_HOUSE = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>';
226
+ var ICON_PIN = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0118 0z"/><circle cx="12" cy="10" r="3"/></svg>';
227
+ var ICON_SEARCH = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>';
228
+ var ICON_WARN = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 9v4"/><path d="M12 17h.01"/><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/></svg>';
229
+ var ICON_X = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>';
230
+
231
+ function PluModuleManager(rootEl, events, apiManager, uiManager) {
232
+ this.root = rootEl;
233
+ this.events = events;
234
+ this.api = apiManager;
235
+ this.ui = uiManager;
236
+ this.autocomplete = null;
237
+ this.contentEl = null;
238
+ this.stateInputEl = null;
239
+ this.stateLoadingEl = null;
240
+ this.stateResultEl = null;
241
+ this.stateErrorEl = null;
242
+ this.searchBtnEl = null;
243
+ this.mounted = false;
244
+ }
245
+
246
+ PluModuleManager.prototype.injectCSS = function() {
247
+ var styleEl = document.createElement("style");
248
+ styleEl.id = "cm-plu-css";
249
+ styleEl.textContent = new PluCssBuilder().build();
250
+ this.root.appendChild(styleEl);
251
+ };
252
+
253
+ PluModuleManager.prototype.mount = function() {
254
+ if (this.mounted) return;
255
+ this.injectCSS();
256
+ var tabsBar = this.root.querySelector("#cm-tabs-bar");
257
+ if (tabsBar) {
258
+ var pluTab = document.createElement("button");
259
+ pluTab.className = "cm-tab hidden";
260
+ pluTab.setAttribute("data-tab", "plu");
261
+ pluTab.type = "button";
262
+ pluTab.innerHTML = ICON_HOUSE + 'Urbanisme<button class="cm-tab-close" type="button" aria-label="Fermer l\'onglet urbanisme">' + ICON_X + '</button>';
263
+ tabsBar.appendChild(pluTab);
264
+ var self = this;
265
+ pluTab.addEventListener("click", function() {
266
+ if (!pluTab.classList.contains("hidden")) self.switchTab("plu");
267
+ });
268
+ var closeBtn = pluTab.querySelector(".cm-tab-close");
269
+ if (closeBtn) closeBtn.addEventListener("click", function(e) { e.stopPropagation(); self.close(); });
270
+ }
271
+ var content = document.createElement("div");
272
+ content.id = "cm-plu-content";
273
+ content.innerHTML = this.buildHTML();
274
+ var panel = this.ui.getPanel();
275
+ var formEl = panel.querySelector("#cm-form");
276
+ if (formEl) { panel.insertBefore(content, formEl); } else { panel.appendChild(content); }
277
+ this.contentEl = content;
278
+ this.stateInputEl = content.querySelector(".cm-plu-state-input");
279
+ this.stateLoadingEl = content.querySelector(".cm-plu-loading");
280
+ this.stateResultEl = content.querySelector(".cm-plu-result");
281
+ this.stateErrorEl = content.querySelector(".cm-plu-error");
282
+ this.searchBtnEl = content.querySelector(".cm-plu-search-btn");
283
+ this.autocomplete = new BanAutocompleteManager(content.querySelector(".cm-plu-input"), content.querySelector(".cm-plu-suggestions"));
284
+ this.bindEvents();
285
+ this.mounted = true;
286
+ };
287
+
288
+ PluModuleManager.prototype.buildHTML = function() {
289
+ return [
290
+ '<div class="cm-plu-scroll">',
291
+ '<div class="cm-plu-state-input" style="display:flex;flex-direction:column;gap:16px">',
292
+ '<h3 class="cm-plu-title">' + ICON_HOUSE + ' Recherche PLU</h3>',
293
+ '<p class="cm-plu-desc">Saisissez votre adresse pour conna\u00eetre les r\u00e8gles d\'urbanisme applicables \u00e0 votre parcelle.</p>',
294
+ '<div class="cm-plu-input-wrap"><input class="cm-plu-input" type="text" placeholder="Tapez votre adresse\u2026" autocomplete="off"/><div class="cm-plu-suggestions"></div></div>',
295
+ '<button class="cm-plu-search-btn" type="button" disabled>' + ICON_SEARCH + ' Rechercher</button>',
296
+ '</div>',
297
+ '<div class="cm-plu-loading"><div class="cm-plu-spinner"></div><div class="cm-plu-loading-text">Recherche en cours\u2026</div><div class="cm-plu-loading-sub">Identification de votre parcelle et r\u00e9cup\u00e9ration des r\u00e8gles PLU\u2026</div></div>',
298
+ '<div class="cm-plu-result"></div>',
299
+ '<div class="cm-plu-error"></div>',
300
+ '</div>'
301
+ ].join("");
302
+ };
303
+
304
+ PluModuleManager.prototype.bindEvents = function() {
305
+ var self = this;
306
+ this.autocomplete.onSelect = function() { self.searchBtnEl.disabled = false; };
307
+ this.searchBtnEl.addEventListener("click", function() { self.submitQuery(); });
308
+ };
309
+
310
+ PluModuleManager.prototype.switchTab = function(tabName) {
311
+ var isPlu = tabName === "plu";
312
+ var tabsBar = this.root.querySelector("#cm-tabs-bar");
313
+ if (tabsBar) {
314
+ tabsBar.querySelectorAll(".cm-tab").forEach(function(t) {
315
+ t.classList.toggle("active", t.getAttribute("data-tab") === tabName);
316
+ });
317
+ }
318
+ this.root.querySelectorAll("#cm-messages, #cm-typing, #cm-error, #cm-disclaimer, #cm-transparency").forEach(function(el) { el.style.display = isPlu ? "none" : ""; });
319
+ var sigContent = this.root.querySelector("#cm-signalement-content");
320
+ if (sigContent) { sigContent.style.display = "none"; sigContent.classList.remove("active"); }
321
+ var sigFooter = this.root.querySelector(".cm-sig-footer");
322
+ if (sigFooter) sigFooter.classList.remove("active");
323
+ var chatForm = this.root.querySelector("#cm-form");
324
+ if (chatForm) chatForm.style.display = isPlu ? "none" : "";
325
+ this.contentEl.classList.toggle("active", isPlu);
326
+ this.contentEl.style.display = isPlu ? "flex" : "none";
327
+ };
328
+
329
+ PluModuleManager.prototype.open = function() {
330
+ var tabsBar = this.root.querySelector("#cm-tabs-bar");
331
+ if (tabsBar) {
332
+ var pluTab = tabsBar.querySelector('[data-tab="plu"]');
333
+ if (pluTab) pluTab.classList.remove("hidden");
334
+ tabsBar.classList.add("visible");
335
+ }
336
+ this.switchTab("plu");
337
+ this.showState("input");
338
+ this.events.emit("module:plu:open", {});
339
+ };
340
+
341
+ PluModuleManager.prototype.close = function() {
342
+ var tabsBar = this.root.querySelector("#cm-tabs-bar");
343
+ if (tabsBar) {
344
+ var pluTab = tabsBar.querySelector('[data-tab="plu"]');
345
+ if (pluTab) pluTab.classList.add("hidden");
346
+ }
347
+ this.switchTab("chat");
348
+ this.events.emit("module:plu:closed", {});
349
+ };
350
+
351
+ PluModuleManager.prototype.showState = function(state) {
352
+ this.stateInputEl.style.display = state === "input" ? "flex" : "none";
353
+ this.stateLoadingEl.classList.toggle("visible", state === "loading");
354
+ this.stateResultEl.classList.toggle("visible", state === "result");
355
+ this.stateErrorEl.classList.toggle("visible", state === "error");
356
+ };
357
+
358
+ PluModuleManager.prototype.submitQuery = async function() {
359
+ var address = this.autocomplete.selectedAddress;
360
+ if (!address) return;
361
+ this.showState("loading");
362
+ this.events.emit("module:plu:submitted", address);
363
+ try {
364
+ await this.api.ensureSession();
365
+ var url = this.api.resolveRuntimeUrl("/v1/modules/plu/query");
366
+ var headers = this.api.buildHeaders({ "Content-Type": "application/json" });
367
+ var body = JSON.stringify({
368
+ session_id: this.api.state.sessionId || "",
369
+ address_label: address.label,
370
+ latitude: address.latitude,
371
+ longitude: address.longitude,
372
+ city_insee_code: address.citycode
373
+ });
374
+ var response = await fetch(url, { method: "POST", headers: headers, body: body });
375
+ if (!response.ok) {
376
+ var errText = await response.text().catch(function() { return ""; });
377
+ var errObj = {};
378
+ try { errObj = JSON.parse(errText); } catch (_) {}
379
+ throw { message: errObj.message || errObj.error || "Erreur HTTP " + response.status, code: errObj.error_code || "PLU_HTTP_" + response.status };
380
+ }
381
+ var result = await response.json();
382
+ this.showResult(address, result);
383
+ this.events.emit("module:plu:resolved", result);
384
+ } catch (error) {
385
+ this.showError(
386
+ (error && error.message) || "Impossible de résoudre le PLU pour cette adresse.",
387
+ (error && error.code) || "PLU_UNKNOWN_ERROR"
388
+ );
389
+ this.events.emit("module:plu:error", { message: error && error.message, code: error && error.code });
390
+ }
391
+ };
392
+
393
+ PluModuleManager.prototype.showResult = function(address, data) {
394
+ var rulesHtml = "";
395
+ if (data.rules && data.rules.length) {
396
+ rulesHtml = '<div class="cm-plu-rules-title">Règles applicables :</div><ul class="cm-plu-rules-list">' +
397
+ data.rules.map(function(r) { return "<li>" + escapeHtml(typeof r === "string" ? r : (r.label || r.description || "")) + "</li>"; }).join("") + '</ul>';
398
+ }
399
+ var sourcesText = "";
400
+ if (data.sources && data.sources.length) {
401
+ sourcesText = "Sources : " + data.sources.map(function(s) { return escapeHtml(typeof s === "string" ? s : (s.name || "")); }).join(", ");
402
+ }
403
+ this.stateResultEl.innerHTML = [
404
+ '<h3 class="cm-plu-title">' + ICON_HOUSE + ' Résultat PLU</h3>',
405
+ '<div class="cm-plu-result-addr">' + ICON_PIN + '<div>' + escapeHtml(address.label) + '</div></div>',
406
+ '<div class="cm-plu-zone-card"><div class="cm-plu-zone-code">Zone : ' + escapeHtml(data.zone_code || "N/A") + '</div>',
407
+ '<div class="cm-plu-zone-label">' + escapeHtml(data.zone_label || "") + '</div>',
408
+ data.document_type ? '<div class="cm-plu-zone-doc">Document : ' + escapeHtml(data.document_type) + '</div>' : '',
409
+ '</div>', rulesHtml,
410
+ sourcesText ? '<div class="cm-plu-sources">' + sourcesText + '</div>' : '',
411
+ '<div class="cm-plu-disclaimer">' + ICON_WARN + '<div>' + escapeHtml(data.disclaimer || "Information indicative. Consultez le service urbanisme de votre mairie pour confirmation.") + '</div></div>',
412
+ '<button class="cm-plu-new-search-btn" type="button">' + ICON_SEARCH + ' Nouvelle recherche</button>'
413
+ ].join("");
414
+ var self = this;
415
+ var newBtn = this.stateResultEl.querySelector(".cm-plu-new-search-btn");
416
+ if (newBtn) newBtn.addEventListener("click", function() { self.resetToInput(); });
417
+ this.showState("result");
418
+ };
419
+
420
+ PluModuleManager.prototype.showError = function(message, code) {
421
+ this.stateErrorEl.innerHTML = [
422
+ '<div class="cm-plu-error-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg></div>',
423
+ '<div class="cm-plu-error-msg">' + escapeHtml(message) + '</div>',
424
+ code ? '<div class="cm-plu-error-code">' + escapeHtml(code) + '</div>' : '',
425
+ '<button class="cm-plu-retry-btn" type="button">' + ICON_SEARCH + ' Réessayer</button>'
426
+ ].join("");
427
+ var self = this;
428
+ var retryBtn = this.stateErrorEl.querySelector(".cm-plu-retry-btn");
429
+ if (retryBtn) retryBtn.addEventListener("click", function() { self.resetToInput(); });
430
+ this.showState("error");
431
+ };
432
+
433
+ PluModuleManager.prototype.resetToInput = function() {
434
+ this.autocomplete.reset();
435
+ this.searchBtnEl.disabled = true;
436
+ this.showState("input");
437
+ };
438
+
439
+ PluModuleManager.prototype.createOpenButton = function() {
440
+ var btn = document.createElement("button");
441
+ btn.className = "cm-plu-open-btn";
442
+ btn.type = "button";
443
+ btn.innerHTML = ICON_HOUSE + 'Urbanisme';
444
+ var self = this;
445
+ btn.addEventListener("click", function() { self.open(); });
446
+ return btn;
447
+ };
448
+
449
+ function escapeHtml(str) {
450
+ var div = document.createElement("div");
451
+ div.textContent = String(str || "");
452
+ return div.innerHTML;
453
+ }
454
+
455
+ runtime.PluModuleManager = PluModuleManager;
456
+ runtime.PluCssBuilder = PluCssBuilder;
457
+ runtime.BanAutocompleteManager = BanAutocompleteManager;
458
+ })();