zenit-sdk 0.0.7 → 0.0.9

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.
@@ -1,7 +1,7 @@
1
1
  // src/react/ZenitMap.tsx
2
- import React, { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState, forwardRef } from "react";
3
- import { GeoJSON, MapContainer, Marker, TileLayer, ZoomControl, useMap } from "react-leaflet";
4
- import L from "leaflet";
2
+ import React3, { useCallback as useCallback2, useEffect as useEffect4, useImperativeHandle, useMemo as useMemo2, useState as useState2, forwardRef } from "react";
3
+ import { MapContainer, Marker as Marker2, TileLayer, ZoomControl } from "react-leaflet";
4
+ import L4 from "leaflet";
5
5
 
6
6
  // src/maps/helpers.ts
7
7
  function toNumber(value) {
@@ -206,9 +206,9 @@ function getAccentByLayerId(layerId, mapLayers) {
206
206
 
207
207
  // src/react/zoomOpacity.ts
208
208
  var DEFAULT_OPTIONS = {
209
- minZoom: 10,
210
- maxZoom: 17,
211
- minFactor: 0.6,
209
+ minZoom: 11,
210
+ maxZoom: 15,
211
+ minFactor: 0.3,
212
212
  maxFactor: 1,
213
213
  minOpacity: 0.1,
214
214
  maxOpacity: 0.92
@@ -245,302 +245,215 @@ function getEffectiveLayerOpacity(baseOpacity, zoom, layerType, geometryType, op
245
245
  return clampNumber(effective, settings.minOpacity, settings.maxOpacity);
246
246
  }
247
247
 
248
- // src/react/ZenitMap.tsx
249
- import { jsx, jsxs } from "react/jsx-runtime";
250
- var DEFAULT_CENTER = [0, 0];
251
- var DEFAULT_ZOOM = 3;
252
- function computeBBoxFromGeojson(geojson) {
253
- if (!geojson) return null;
254
- if (!Array.isArray(geojson.features) || geojson.features.length === 0) return null;
255
- const coords = [];
256
- const collect = (candidate) => {
257
- if (!Array.isArray(candidate)) return;
258
- if (candidate.length === 2 && typeof candidate[0] === "number" && typeof candidate[1] === "number") {
259
- coords.push([candidate[0], candidate[1]]);
260
- return;
261
- }
262
- candidate.forEach((entry) => collect(entry));
263
- };
264
- geojson.features.forEach((feature) => {
265
- collect(feature.geometry?.coordinates);
266
- });
267
- if (coords.length === 0) return null;
268
- const [firstLon, firstLat] = coords[0];
269
- const bbox = { minLon: firstLon, minLat: firstLat, maxLon: firstLon, maxLat: firstLat };
270
- coords.forEach(([lon, lat]) => {
271
- bbox.minLon = Math.min(bbox.minLon, lon);
272
- bbox.minLat = Math.min(bbox.minLat, lat);
273
- bbox.maxLon = Math.max(bbox.maxLon, lon);
274
- bbox.maxLat = Math.max(bbox.maxLat, lat);
275
- });
276
- return bbox;
248
+ // src/react/map/layer-geojson.tsx
249
+ import { GeoJSON } from "react-leaflet";
250
+ import L from "leaflet";
251
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
252
+ var POINT_GEOMETRY_TYPES = /* @__PURE__ */ new Set(["Point", "MultiPoint"]);
253
+ function getGeometryType(feature) {
254
+ const t = feature?.geometry?.type;
255
+ return typeof t === "string" ? t : null;
277
256
  }
278
- function mergeBBoxes(bboxes) {
279
- const valid = bboxes.filter((bbox) => !!bbox);
280
- if (valid.length === 0) return null;
281
- const first = valid[0];
282
- return valid.slice(1).reduce(
283
- (acc, bbox) => ({
284
- minLon: Math.min(acc.minLon, bbox.minLon),
285
- minLat: Math.min(acc.minLat, bbox.minLat),
286
- maxLon: Math.max(acc.maxLon, bbox.maxLon),
287
- maxLat: Math.max(acc.maxLat, bbox.maxLat)
288
- }),
289
- { ...first }
290
- );
257
+ function isPointGeometry(feature) {
258
+ const geometryType = getGeometryType(feature);
259
+ return geometryType !== null && POINT_GEOMETRY_TYPES.has(geometryType);
291
260
  }
292
- function getFeatureLayerId(feature) {
293
- const layerId = feature?.properties?.__zenit_layerId ?? feature?.properties?.layerId ?? feature?.properties?.layer_id;
294
- if (layerId === void 0 || layerId === null) return null;
295
- return layerId;
261
+ function isNonPointGeometry(feature) {
262
+ const geometryType = getGeometryType(feature);
263
+ return geometryType !== null && !POINT_GEOMETRY_TYPES.has(geometryType);
296
264
  }
297
- function escapeHtml(value) {
298
- return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
265
+ function buildFeatureCollection(features) {
266
+ return {
267
+ type: "FeatureCollection",
268
+ features
269
+ };
299
270
  }
300
- var DESCRIPTION_KEYS = /* @__PURE__ */ new Set(["descripcion", "description"]);
301
- var POPUP_EXCLUDED_KEYS = /* @__PURE__ */ new Set(["geom", "geometry"]);
271
+ var LayerGeoJson = ({
272
+ layerId,
273
+ data,
274
+ baseOpacity,
275
+ isMobile,
276
+ panesReady,
277
+ mapInstance,
278
+ fillPaneName,
279
+ pointsPaneName,
280
+ layerType,
281
+ styleFn,
282
+ onEachFeature,
283
+ onPolygonLabel
284
+ }) => {
285
+ const features = data.features ?? [];
286
+ const fillFeatures = features.filter(isNonPointGeometry);
287
+ const pointFeatures = features.filter(isPointGeometry);
288
+ const fillData = fillFeatures.length > 0 ? buildFeatureCollection(fillFeatures) : null;
289
+ const pointsData = pointFeatures.length > 0 ? buildFeatureCollection(pointFeatures) : null;
290
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
291
+ fillData && /* @__PURE__ */ jsx(
292
+ GeoJSON,
293
+ {
294
+ data: fillData,
295
+ pane: panesReady && mapInstance?.getPane(fillPaneName) ? fillPaneName : void 0,
296
+ style: (feature) => styleFn(feature, layerType, baseOpacity),
297
+ onEachFeature: (feature, layer) => {
298
+ onEachFeature(feature, layer);
299
+ onPolygonLabel?.(feature, layer);
300
+ }
301
+ },
302
+ `fill-${layerId}`
303
+ ),
304
+ pointsData && /* @__PURE__ */ jsx(
305
+ GeoJSON,
306
+ {
307
+ data: pointsData,
308
+ pane: panesReady && mapInstance?.getPane(pointsPaneName) ? pointsPaneName : void 0,
309
+ pointToLayer: (feature, latlng) => L.circleMarker(latlng, {
310
+ radius: isMobile ? 8 : 6,
311
+ ...styleFn(feature, layerType, baseOpacity)
312
+ }),
313
+ onEachFeature
314
+ },
315
+ `points-${layerId}`
316
+ )
317
+ ] });
318
+ };
319
+
320
+ // src/react/map/location-control.tsx
321
+ import { useEffect as useEffect2, useMemo, useRef as useRef2 } from "react";
322
+ import { createPortal } from "react-dom";
323
+ import { Circle, Marker, useMap } from "react-leaflet";
324
+ import L3 from "leaflet";
325
+
326
+ // src/react/hooks/use-geolocation.ts
327
+ import { useCallback, useEffect, useRef, useState } from "react";
328
+ function useGeolocation(options) {
329
+ const [isTracking, setIsTracking] = useState(false);
330
+ const [location, setLocation] = useState(null);
331
+ const [error, setError] = useState(null);
332
+ const watchIdRef = useRef(null);
333
+ const stopTracking = useCallback(() => {
334
+ if (watchIdRef.current !== null && typeof navigator !== "undefined" && navigator.geolocation) {
335
+ navigator.geolocation.clearWatch(watchIdRef.current);
336
+ }
337
+ watchIdRef.current = null;
338
+ setIsTracking(false);
339
+ }, []);
340
+ const startTracking = useCallback(() => {
341
+ if (typeof navigator === "undefined" || !navigator.geolocation) {
342
+ setError({ code: 0, message: "La geolocalizaci\xF3n no est\xE1 disponible en este navegador." });
343
+ return;
344
+ }
345
+ setError(null);
346
+ watchIdRef.current = navigator.geolocation.watchPosition(
347
+ (position) => {
348
+ setLocation({
349
+ lat: position.coords.latitude,
350
+ lon: position.coords.longitude,
351
+ accuracy: position.coords.accuracy
352
+ });
353
+ setIsTracking(true);
354
+ },
355
+ (err) => {
356
+ setError({ code: err.code, message: err.message });
357
+ stopTracking();
358
+ },
359
+ {
360
+ enableHighAccuracy: options?.enableHighAccuracy ?? true,
361
+ timeout: options?.timeout ?? 12e3,
362
+ maximumAge: options?.maximumAge ?? 0
363
+ }
364
+ );
365
+ }, [options?.enableHighAccuracy, options?.maximumAge, options?.timeout, stopTracking]);
366
+ const toggleTracking = useCallback(() => {
367
+ if (isTracking) {
368
+ stopTracking();
369
+ } else {
370
+ startTracking();
371
+ }
372
+ }, [isTracking, startTracking, stopTracking]);
373
+ const clearError = useCallback(() => setError(null), []);
374
+ useEffect(() => {
375
+ return () => {
376
+ stopTracking();
377
+ };
378
+ }, [stopTracking]);
379
+ return {
380
+ isTracking,
381
+ location,
382
+ error,
383
+ startTracking,
384
+ stopTracking,
385
+ toggleTracking,
386
+ clearError
387
+ };
388
+ }
389
+
390
+ // src/react/map/map-utils.ts
391
+ import L2 from "leaflet";
302
392
  var POPUP_STYLE_ID = "zenit-leaflet-popup-styles";
303
- var DESKTOP_POPUP_DIMENSIONS = { maxWidth: 350, minWidth: 280, maxHeight: 480 };
304
- var MOBILE_POPUP_DIMENSIONS = { maxWidth: 280, minWidth: 240, maxHeight: 380 };
393
+ var POPUP_EXCLUDED_KEYS = /* @__PURE__ */ new Set(["geom", "geometry", "_private"]);
394
+ var POPUP_HEADER_KEYS = ["nombre", "name", "title", "titulo"];
395
+ var DESKTOP_POPUP_DIMENSIONS = { maxWidth: 360, minWidth: 280, maxHeight: 520 };
396
+ var MOBILE_POPUP_DIMENSIONS = { maxWidth: 300, minWidth: 240, maxHeight: 420 };
305
397
  var ZENIT_LEAFLET_POPUP_STYLES = `
306
- /* ===== Zenit Leaflet Popup - Modern Professional Styling ===== */
307
-
308
- /* Main popup wrapper */
309
- .zenit-leaflet-popup .leaflet-popup-content-wrapper {
398
+ .custom-leaflet-popup .leaflet-popup-content-wrapper {
310
399
  border-radius: 12px;
311
- box-shadow:
312
- 0 4px 6px -1px rgba(0, 0, 0, 0.1),
313
- 0 2px 4px -2px rgba(0, 0, 0, 0.1),
314
- 0 0 0 1px rgba(0, 0, 0, 0.05);
315
400
  padding: 0;
316
401
  background: #ffffff;
317
- overflow: hidden;
402
+ box-shadow: 0 12px 24px rgba(15, 23, 42, 0.18);
403
+ border: 1px solid rgba(15, 23, 42, 0.08);
318
404
  }
319
405
 
320
- /* Content area with scroll support */
321
- .zenit-leaflet-popup .leaflet-popup-content {
406
+ .custom-leaflet-popup .leaflet-popup-content {
322
407
  margin: 0;
323
- padding: 0;
408
+ padding: 12px 14px;
324
409
  font-size: 13px;
325
- line-height: 1.5;
326
- color: #374151;
327
- min-width: 100%;
328
- overflow-y: auto;
329
- overflow-x: hidden;
330
- scrollbar-width: thin;
331
- scrollbar-color: rgba(156, 163, 175, 0.5) transparent;
332
- }
333
-
334
- /* Popup tip/arrow shadow */
335
- .zenit-leaflet-popup .leaflet-popup-tip-container {
336
- filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1));
337
- }
338
-
339
- .zenit-leaflet-popup .leaflet-popup-tip {
340
- background: #ffffff;
341
- box-shadow: none;
342
- }
343
-
344
- /* Close button styling */
345
- .zenit-leaflet-popup .leaflet-popup-close-button {
346
- color: #9ca3af;
347
- font-size: 18px;
348
- font-weight: 400;
349
- width: 28px;
350
- height: 28px;
351
- padding: 0;
352
- margin: 8px 8px 0 0;
353
- display: flex;
354
- align-items: center;
355
- justify-content: center;
356
- border-radius: 6px;
357
- transition: all 0.15s ease;
358
- z-index: 10;
359
- }
360
-
361
- .zenit-leaflet-popup .leaflet-popup-close-button:hover {
362
- color: #374151;
363
- background-color: #f3f4f6;
364
- }
365
-
366
- .zenit-leaflet-popup .leaflet-popup-close-button:active {
367
- background-color: #e5e7eb;
368
- }
369
-
370
- /* Main card container */
371
- .zenit-popup-card {
372
- display: flex;
373
- flex-direction: column;
374
- gap: 0;
375
- padding: 16px;
376
- }
377
-
378
- /* Individual row styling with subtle separator */
379
- .zenit-popup-row {
380
- display: flex;
381
- flex-direction: column;
382
- gap: 2px;
383
- padding: 10px 0;
384
- border-bottom: 1px solid #f3f4f6;
385
- }
386
-
387
- .zenit-popup-row:first-child {
388
- padding-top: 0;
389
- }
390
-
391
- .zenit-popup-row:last-child {
392
- border-bottom: none;
393
- padding-bottom: 0;
394
- }
395
-
396
- /* Label styling - small, gray, uppercase */
397
- .zenit-popup-label {
398
- font-size: 10px;
399
- font-weight: 500;
400
- color: #9ca3af;
401
- text-transform: uppercase;
402
- letter-spacing: 0.05em;
403
410
  line-height: 1.4;
411
+ color: #0f172a;
412
+ max-height: min(70vh, 520px);
413
+ overflow: auto;
414
+ scrollbar-width: thin;
415
+ scrollbar-color: rgba(148, 163, 184, 0.6) transparent;
404
416
  }
405
417
 
406
- /* Value styling - darker, readable */
407
- .zenit-popup-value {
408
- font-size: 13px;
409
- font-weight: 400;
410
- color: #1f2937;
411
- overflow-wrap: break-word;
412
- word-break: break-word;
413
- line-height: 1.5;
414
- }
415
-
416
- /* Special styling for description field */
417
- .zenit-popup-row.zenit-popup-description {
418
- background-color: #f9fafb;
419
- margin: 0 -16px;
420
- padding: 12px 16px;
421
- border-bottom: 1px solid #e5e7eb;
422
- }
423
-
424
- .zenit-popup-row.zenit-popup-description:first-child {
425
- margin-top: 0;
426
- border-radius: 0;
427
- }
428
-
429
- .zenit-popup-row.zenit-popup-description .zenit-popup-value {
430
- font-size: 13px;
431
- line-height: 1.6;
432
- color: #374151;
433
- max-height: 150px;
434
- overflow-y: auto;
435
- padding-right: 4px;
436
- }
437
-
438
- /* Preformatted text (JSON objects) */
439
- .zenit-popup-pre {
440
- margin: 0;
441
- font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
442
- font-size: 11px;
443
- white-space: pre-wrap;
444
- word-break: break-word;
445
- color: #4b5563;
446
- background-color: #f9fafb;
447
- padding: 8px;
448
- border-radius: 6px;
449
- border: 1px solid #e5e7eb;
418
+ .custom-leaflet-popup .leaflet-popup-close-button {
419
+ color: #64748b;
420
+ font-size: 16px;
421
+ padding: 6px 8px;
422
+ border-radius: 8px;
423
+ transition: background-color 0.15s ease, color 0.15s ease;
450
424
  }
451
425
 
452
- /* Empty state styling */
453
- .zenit-popup-empty {
454
- font-size: 13px;
455
- color: #9ca3af;
456
- font-style: italic;
457
- text-align: center;
458
- padding: 20px 0;
426
+ .custom-leaflet-popup .leaflet-popup-close-button:hover {
427
+ color: #0f172a;
428
+ background: rgba(148, 163, 184, 0.2);
459
429
  }
460
430
 
461
- /* Webkit scrollbar styling */
462
- .zenit-leaflet-popup .leaflet-popup-content::-webkit-scrollbar {
431
+ .custom-leaflet-popup .leaflet-popup-content::-webkit-scrollbar {
463
432
  width: 6px;
464
433
  }
465
434
 
466
- .zenit-leaflet-popup .leaflet-popup-content::-webkit-scrollbar-track {
467
- background: transparent;
468
- }
469
-
470
- .zenit-leaflet-popup .leaflet-popup-content::-webkit-scrollbar-thumb {
471
- background-color: rgba(156, 163, 175, 0.4);
472
- border-radius: 3px;
473
- }
474
-
475
- .zenit-leaflet-popup .leaflet-popup-content::-webkit-scrollbar-thumb:hover {
476
- background-color: rgba(107, 114, 128, 0.6);
435
+ .custom-leaflet-popup .leaflet-popup-content::-webkit-scrollbar-thumb {
436
+ background: rgba(148, 163, 184, 0.5);
437
+ border-radius: 999px;
477
438
  }
478
439
 
479
- /* Scrollbar for description field */
480
- .zenit-popup-row.zenit-popup-description .zenit-popup-value::-webkit-scrollbar {
481
- width: 4px;
482
- }
483
-
484
- .zenit-popup-row.zenit-popup-description .zenit-popup-value::-webkit-scrollbar-track {
440
+ .custom-leaflet-popup .leaflet-popup-content::-webkit-scrollbar-track {
485
441
  background: transparent;
486
442
  }
487
443
 
488
- .zenit-popup-row.zenit-popup-description .zenit-popup-value::-webkit-scrollbar-thumb {
489
- background-color: rgba(156, 163, 175, 0.4);
490
- border-radius: 2px;
491
- }
492
-
493
- /* ===== Responsive: Mobile (<640px) ===== */
494
444
  @media (max-width: 640px) {
495
- .zenit-leaflet-popup .leaflet-popup-content-wrapper {
496
- border-radius: 10px;
497
- }
498
-
499
- .zenit-leaflet-popup .leaflet-popup-close-button {
500
- width: 26px;
501
- height: 26px;
502
- font-size: 16px;
503
- margin: 6px 6px 0 0;
504
- }
505
-
506
- .zenit-popup-card {
507
- padding: 12px;
508
- }
509
-
510
- .zenit-popup-row {
511
- padding: 8px 0;
512
- }
513
-
514
- .zenit-popup-label {
515
- font-size: 9px;
516
- }
517
-
518
- .zenit-popup-value {
445
+ .custom-leaflet-popup .leaflet-popup-content {
519
446
  font-size: 12px;
520
- }
521
-
522
- .zenit-popup-row.zenit-popup-description {
523
- margin: 0 -12px;
524
447
  padding: 10px 12px;
448
+ max-height: min(65vh, 420px);
525
449
  }
526
450
 
527
- .zenit-popup-row.zenit-popup-description .zenit-popup-value {
528
- font-size: 12px;
529
- max-height: 120px;
530
- }
531
-
532
- .zenit-popup-pre {
533
- font-size: 10px;
534
- padding: 6px;
535
- }
536
-
537
- .zenit-popup-empty {
538
- font-size: 12px;
539
- padding: 16px 0;
451
+ .custom-leaflet-popup .leaflet-popup-close-button {
452
+ font-size: 14px;
453
+ padding: 4px 6px;
540
454
  }
541
455
  }
542
456
 
543
- /* ===== Map tooltip styling ===== */
544
457
  .zenit-map-tooltip {
545
458
  background-color: rgba(31, 41, 55, 0.95);
546
459
  border: none;
@@ -555,7 +468,23 @@ var ZENIT_LEAFLET_POPUP_STYLES = `
555
468
  .zenit-map-tooltip::before {
556
469
  border-top-color: rgba(31, 41, 55, 0.95);
557
470
  }
471
+
472
+ .polygon-label-tooltip {
473
+ background: rgba(15, 23, 42, 0.92);
474
+ border: none;
475
+ border-radius: 6px;
476
+ color: #f8fafc;
477
+ font-weight: 600;
478
+ font-size: 12px;
479
+ padding: 4px 8px;
480
+ }
558
481
  `;
482
+ function clampNumber2(value, min, max) {
483
+ return Math.min(max, Math.max(min, value));
484
+ }
485
+ function clampOpacity4(value) {
486
+ return clampNumber2(value, 0, 1);
487
+ }
559
488
  function ensurePopupStyles() {
560
489
  if (typeof document === "undefined") return;
561
490
  if (document.getElementById(POPUP_STYLE_ID)) return;
@@ -565,47 +494,52 @@ function ensurePopupStyles() {
565
494
  document.head.appendChild(styleTag);
566
495
  }
567
496
  function getPopupDimensions() {
568
- if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
497
+ if (typeof window === "undefined") {
569
498
  return DESKTOP_POPUP_DIMENSIONS;
570
499
  }
571
- return window.matchMedia("(max-width: 640px)").matches ? MOBILE_POPUP_DIMENSIONS : DESKTOP_POPUP_DIMENSIONS;
572
- }
573
- function normalizeDescriptionValue(value) {
574
- if (value === void 0 || value === null) return null;
575
- if (typeof value === "string") {
576
- const trimmed = value.trim();
577
- return trimmed ? trimmed : null;
578
- }
579
- if (typeof value === "number" || typeof value === "boolean") {
580
- return String(value);
581
- }
582
- return null;
500
+ const isSmallWidth = window.innerWidth < 640;
501
+ const isShortHeight = window.innerHeight < 768;
502
+ const base = isSmallWidth ? MOBILE_POPUP_DIMENSIONS : DESKTOP_POPUP_DIMENSIONS;
503
+ const maxHeight = isShortHeight ? Math.min(base.maxHeight, 360) : base.maxHeight;
504
+ return { ...base, maxHeight };
583
505
  }
584
- function extractDescriptionValue(properties) {
585
- if (!properties) return null;
586
- const matches = Object.entries(properties).find(
587
- ([key]) => DESCRIPTION_KEYS.has(key.toLowerCase())
588
- );
589
- if (!matches) return null;
590
- return normalizeDescriptionValue(matches[1]);
506
+ function escapeHtml(value) {
507
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
591
508
  }
592
- function safeJsonStringify(value) {
593
- try {
594
- const json = JSON.stringify(value, null, 2);
595
- if (json !== void 0) return json;
596
- } catch {
597
- }
598
- return String(value);
509
+ function formatLabel(key) {
510
+ return key.replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").trim();
599
511
  }
600
512
  function renderPopupValue(value) {
601
- if (value === null || value === void 0) {
602
- return '<span class="zenit-popup-empty">Sin datos</span>';
513
+ if (value === null || value === void 0) return '<span style="color:#94a3b8;">Sin datos</span>';
514
+ if (value instanceof Date) {
515
+ return escapeHtml(value.toLocaleDateString("es-GT"));
516
+ }
517
+ if (typeof value === "number") {
518
+ return escapeHtml(value.toLocaleString("es-GT"));
519
+ }
520
+ if (typeof value === "string") {
521
+ const trimmed = value.trim();
522
+ if (!trimmed) return '<span style="color:#94a3b8;">Sin datos</span>';
523
+ return escapeHtml(trimmed);
603
524
  }
604
525
  if (typeof value === "object") {
605
- const json = safeJsonStringify(value);
606
- return `<pre class="zenit-popup-pre">${escapeHtml(json)}</pre>`;
526
+ try {
527
+ return `<pre style="margin:0; white-space:pre-wrap; font-size:11px; background:#f8fafc; border:1px solid #e2e8f0; padding:6px 8px; border-radius:6px;">${escapeHtml(
528
+ JSON.stringify(value, null, 2)
529
+ )}</pre>`;
530
+ } catch {
531
+ return escapeHtml(String(value));
532
+ }
607
533
  }
608
- return `<span>${escapeHtml(String(value))}</span>`;
534
+ return escapeHtml(String(value));
535
+ }
536
+ function extractPopupHeader(properties) {
537
+ const entry = Object.entries(properties).find(([key, value]) => {
538
+ if (typeof value !== "string") return false;
539
+ const normalized = key.trim().toLowerCase();
540
+ return POPUP_HEADER_KEYS.includes(normalized) && value.trim().length > 0;
541
+ });
542
+ return entry ? entry[1].trim() : null;
609
543
  }
610
544
  function shouldIncludePopupEntry(key, value) {
611
545
  if (!key) return false;
@@ -617,45 +551,89 @@ function shouldIncludePopupEntry(key, value) {
617
551
  if (typeof value === "string" && !value.trim()) return false;
618
552
  return true;
619
553
  }
620
- function isDescriptionKey(key) {
621
- const normalized = key.trim().toLowerCase();
622
- return DESCRIPTION_KEYS.has(normalized);
623
- }
624
- function formatLabel(key) {
625
- return key.replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").trim();
626
- }
627
554
  function createPopupContent(properties) {
628
- const entries = Object.entries(properties).filter(
629
- ([key, value]) => shouldIncludePopupEntry(key, value)
630
- );
555
+ const headerText = extractPopupHeader(properties);
556
+ const entries = Object.entries(properties).filter(([key, value]) => {
557
+ if (!shouldIncludePopupEntry(key, value)) return false;
558
+ if (headerText && POPUP_HEADER_KEYS.includes(key.trim().toLowerCase())) return false;
559
+ return true;
560
+ });
631
561
  if (entries.length === 0) {
632
- return `<div class="zenit-popup-card"><div class="zenit-popup-empty">Sin datos disponibles</div></div>`;
562
+ return '<div style="padding:8px 0; color:#64748b; text-align:center;">Sin datos disponibles</div>';
633
563
  }
634
- const descriptionEntry = entries.find(([key]) => isDescriptionKey(key));
635
- const otherEntries = entries.filter(([key]) => !isDescriptionKey(key));
636
- let rowsHtml = "";
637
- if (descriptionEntry) {
638
- const [key, value] = descriptionEntry;
639
- const label = escapeHtml(formatLabel(key));
640
- const valueHtml = renderPopupValue(value);
641
- rowsHtml += `
642
- <div class="zenit-popup-row zenit-popup-description">
643
- <div class="zenit-popup-label">${label}</div>
644
- <div class="zenit-popup-value">${valueHtml}</div>
645
- </div>
646
- `;
647
- }
648
- rowsHtml += otherEntries.map(([key, value]) => {
564
+ const headerHtml = headerText ? `<div style="font-weight:700; font-size:14px; margin-bottom:8px; color:#0f172a;">${escapeHtml(
565
+ headerText
566
+ )}</div>` : "";
567
+ const rowsHtml = entries.map(([key, value]) => {
649
568
  const label = escapeHtml(formatLabel(key));
650
569
  const valueHtml = renderPopupValue(value);
651
570
  return `
652
- <div class="zenit-popup-row">
653
- <div class="zenit-popup-label">${label}</div>
654
- <div class="zenit-popup-value">${valueHtml}</div>
571
+ <div style="display:grid; grid-template-columns:minmax(90px, 35%) 1fr; gap:8px; padding:6px 0; border-bottom:1px solid #e2e8f0;">
572
+ <div style="font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.04em; color:#64748b;">${label}</div>
573
+ <div style="font-size:13px; color:#0f172a; word-break:break-word;">${valueHtml}</div>
655
574
  </div>
656
575
  `;
657
576
  }).join("");
658
- return `<div class="zenit-popup-card">${rowsHtml}</div>`;
577
+ return `<div>${headerHtml}${rowsHtml}</div>`;
578
+ }
579
+ function isPolygonType(layerType, geometryType) {
580
+ const candidate = (layerType ?? geometryType ?? "").toLowerCase();
581
+ return candidate === "polygon" || candidate === "multipolygon";
582
+ }
583
+ function calculateZoomBasedOpacity(zoom, baseOpacity, layerType, geometryType) {
584
+ if (!isPolygonType(layerType, geometryType)) return clampOpacity4(baseOpacity);
585
+ const minZoom = 11;
586
+ const maxZoom = 15;
587
+ const minFactor = 0.3;
588
+ if (maxZoom <= minZoom) return clampOpacity4(baseOpacity * minFactor);
589
+ const t = clampNumber2((zoom - minZoom) / (maxZoom - minZoom), 0, 1);
590
+ const factor = 1 - (1 - minFactor) * t;
591
+ return clampOpacity4(baseOpacity * factor);
592
+ }
593
+ function layerStyleToLeaflet(options) {
594
+ const { baseOpacity, zoom, layerStyle, geometryType, layerType } = options;
595
+ const sanitizedOpacity = clampOpacity4(baseOpacity);
596
+ const zoomOpacity = calculateZoomBasedOpacity(zoom, sanitizedOpacity, layerType, geometryType);
597
+ const styleFillOpacity = typeof layerStyle?.fillOpacity === "number" ? clampOpacity4(layerStyle.fillOpacity) : 0.8;
598
+ return {
599
+ color: layerStyle?.color ?? layerStyle?.fillColor ?? "#2563eb",
600
+ weight: layerStyle?.weight ?? 2,
601
+ fillColor: layerStyle?.fillColor ?? layerStyle?.color ?? "#2563eb",
602
+ opacity: clampOpacity4(Math.max(0.35, zoomOpacity * 0.9)),
603
+ fillOpacity: clampOpacity4(zoomOpacity * styleFillOpacity)
604
+ };
605
+ }
606
+ function getRgbFromColor(color) {
607
+ const trimmed = color.trim();
608
+ if (trimmed.startsWith("#")) {
609
+ const hex = trimmed.replace("#", "");
610
+ const expanded = hex.length === 3 ? hex.split("").map((char) => `${char}${char}`).join("") : hex;
611
+ if (expanded.length === 6) {
612
+ const r = parseInt(expanded.slice(0, 2), 16);
613
+ const g = parseInt(expanded.slice(2, 4), 16);
614
+ const b = parseInt(expanded.slice(4, 6), 16);
615
+ return { r, g, b };
616
+ }
617
+ }
618
+ const rgbMatch = trimmed.match(/rgba?\(([^)]+)\)/i);
619
+ if (rgbMatch) {
620
+ const [r, g, b] = rgbMatch[1].split(",").map((value) => parseFloat(value.trim())).slice(0, 3);
621
+ if ([r, g, b].every((value) => Number.isFinite(value))) {
622
+ return { r, g, b };
623
+ }
624
+ }
625
+ return null;
626
+ }
627
+ function getLabelTextStyles(color) {
628
+ const rgb = getRgbFromColor(color);
629
+ if (!rgb) {
630
+ return { color: "#0f172a", shadow: "0 1px 2px rgba(255, 255, 255, 0.6)" };
631
+ }
632
+ const luminance = (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b) / 255;
633
+ if (luminance > 0.6) {
634
+ return { color: "#0f172a", shadow: "0 1px 2px rgba(255, 255, 255, 0.7)" };
635
+ }
636
+ return { color: "#ffffff", shadow: "0 1px 2px rgba(0, 0, 0, 0.4)" };
659
637
  }
660
638
  function withAlpha(color, alpha) {
661
639
  const trimmed = color.trim();
@@ -668,48 +646,385 @@ function withAlpha(color, alpha) {
668
646
  const b = parseInt(expanded.slice(4, 6), 16);
669
647
  return `rgba(${r}, ${g}, ${b}, ${alpha})`;
670
648
  }
671
- }
672
- if (trimmed.startsWith("rgb(")) {
673
- const inner = trimmed.slice(4, -1);
674
- return `rgba(${inner}, ${alpha})`;
675
- }
676
- if (trimmed.startsWith("rgba(")) {
677
- const inner = trimmed.slice(5, -1).split(",").slice(0, 3).map((value) => value.trim());
678
- return `rgba(${inner.join(", ")}, ${alpha})`;
679
- }
680
- return color;
681
- }
682
- function getRgbFromColor(color) {
683
- const trimmed = color.trim();
684
- if (trimmed.startsWith("#")) {
685
- const hex = trimmed.replace("#", "");
686
- const expanded = hex.length === 3 ? hex.split("").map((char) => `${char}${char}`).join("") : hex;
687
- if (expanded.length === 6) {
688
- const r = parseInt(expanded.slice(0, 2), 16);
689
- const g = parseInt(expanded.slice(2, 4), 16);
690
- const b = parseInt(expanded.slice(4, 6), 16);
691
- return { r, g, b };
649
+ }
650
+ if (trimmed.startsWith("rgb(")) {
651
+ const inner = trimmed.slice(4, -1);
652
+ return `rgba(${inner}, ${alpha})`;
653
+ }
654
+ if (trimmed.startsWith("rgba(")) {
655
+ const inner = trimmed.slice(5, -1).split(",").slice(0, 3).map((value) => value.trim());
656
+ return `rgba(${inner.join(", ")}, ${alpha})`;
657
+ }
658
+ return color;
659
+ }
660
+ function createCustomIcon(label, opacity, color) {
661
+ const size = 60;
662
+ const innerSize = 44;
663
+ const textStyles = getLabelTextStyles(color);
664
+ const safeLabel = escapeHtml(label);
665
+ const clampedOpacity = Math.min(1, Math.max(0.92, opacity));
666
+ const innerBackground = withAlpha(color, 0.9);
667
+ return L2.divIcon({
668
+ className: "zenit-label-marker",
669
+ iconSize: [size, size],
670
+ iconAnchor: [size / 2, size / 2],
671
+ html: `
672
+ <div
673
+ title="${safeLabel}"
674
+ style="
675
+ width:${size}px;
676
+ height:${size}px;
677
+ border-radius:9999px;
678
+ background:rgba(255, 255, 255, 0.95);
679
+ border:3px solid rgba(255, 255, 255, 1);
680
+ display:flex;
681
+ align-items:center;
682
+ justify-content:center;
683
+ opacity:${clampedOpacity};
684
+ box-shadow:0 2px 6px rgba(0, 0, 0, 0.25);
685
+ pointer-events:none;
686
+ "
687
+ >
688
+ <div
689
+ style="
690
+ width:${innerSize}px;
691
+ height:${innerSize}px;
692
+ border-radius:9999px;
693
+ background:${innerBackground};
694
+ display:flex;
695
+ align-items:center;
696
+ justify-content:center;
697
+ box-shadow:inset 0 0 0 1px rgba(15, 23, 42, 0.12);
698
+ "
699
+ >
700
+ <span
701
+ style="
702
+ color:${textStyles.color};
703
+ font-size:20px;
704
+ font-weight:800;
705
+ text-shadow:${textStyles.shadow};
706
+ "
707
+ >
708
+ ${safeLabel}
709
+ </span>
710
+ </div>
711
+ </div>
712
+ `
713
+ });
714
+ }
715
+ function createLocationIcon() {
716
+ return L2.divIcon({
717
+ className: "zenit-location-marker",
718
+ iconSize: [18, 18],
719
+ iconAnchor: [9, 9],
720
+ html: `
721
+ <div style="width:18px;height:18px;border-radius:9999px;background:#2563eb;border:2px solid #fff;box-shadow:0 0 0 4px rgba(37, 99, 235, 0.25);"></div>
722
+ `
723
+ });
724
+ }
725
+
726
+ // src/react/map/location-control.tsx
727
+ import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
728
+ var LOCATION_STYLE_ID = "zenit-location-control-styles";
729
+ var LOCATION_STYLES = `
730
+ .zenit-location-control {
731
+ display: flex;
732
+ flex-direction: column;
733
+ gap: 8px;
734
+ }
735
+
736
+ .zenit-location-button {
737
+ width: 42px;
738
+ height: 42px;
739
+ border-radius: 12px;
740
+ border: none;
741
+ background: #ffffff;
742
+ color: #0f172a;
743
+ box-shadow: 0 8px 18px rgba(15, 23, 42, 0.2);
744
+ display: inline-flex;
745
+ align-items: center;
746
+ justify-content: center;
747
+ cursor: pointer;
748
+ position: relative;
749
+ }
750
+
751
+ .zenit-location-button.zenit-location-button--tracking {
752
+ animation: zenitLocationPulse 1.8s infinite;
753
+ }
754
+
755
+ .zenit-location-button:hover {
756
+ background: #f8fafc;
757
+ }
758
+
759
+ .zenit-location-badge {
760
+ position: absolute;
761
+ top: -6px;
762
+ right: -6px;
763
+ width: 18px;
764
+ height: 18px;
765
+ border-radius: 999px;
766
+ background: #ef4444;
767
+ color: #fff;
768
+ font-size: 11px;
769
+ font-weight: 700;
770
+ display: inline-flex;
771
+ align-items: center;
772
+ justify-content: center;
773
+ box-shadow: 0 4px 10px rgba(239, 68, 68, 0.35);
774
+ }
775
+
776
+ .zenit-location-error {
777
+ background: rgba(15, 23, 42, 0.92);
778
+ color: #f8fafc;
779
+ border-radius: 10px;
780
+ padding: 8px 10px;
781
+ font-size: 12px;
782
+ max-width: 200px;
783
+ box-shadow: 0 10px 24px rgba(15, 23, 42, 0.3);
784
+ }
785
+
786
+ @keyframes zenitLocationPulse {
787
+ 0% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.35); }
788
+ 70% { box-shadow: 0 0 0 12px rgba(16, 185, 129, 0); }
789
+ 100% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); }
790
+ }
791
+ `;
792
+ var LocateIcon = () => /* @__PURE__ */ jsxs2("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
793
+ /* @__PURE__ */ jsx2("circle", { cx: "12", cy: "12", r: "3" }),
794
+ /* @__PURE__ */ jsx2("line", { x1: "12", y1: "2", x2: "12", y2: "5" }),
795
+ /* @__PURE__ */ jsx2("line", { x1: "12", y1: "19", x2: "12", y2: "22" }),
796
+ /* @__PURE__ */ jsx2("line", { x1: "2", y1: "12", x2: "5", y2: "12" }),
797
+ /* @__PURE__ */ jsx2("line", { x1: "19", y1: "12", x2: "22", y2: "12" })
798
+ ] });
799
+ var LocationControl = ({ position = "bottomleft", zoom = 16 }) => {
800
+ const map = useMap();
801
+ const controlRef = useRef2(null);
802
+ const hasCenteredRef = useRef2(false);
803
+ const { isTracking, location, error, toggleTracking, clearError } = useGeolocation();
804
+ useEffect2(() => {
805
+ if (typeof document === "undefined") return;
806
+ if (document.getElementById(LOCATION_STYLE_ID)) return;
807
+ const styleTag = document.createElement("style");
808
+ styleTag.id = LOCATION_STYLE_ID;
809
+ styleTag.textContent = LOCATION_STYLES;
810
+ document.head.appendChild(styleTag);
811
+ }, []);
812
+ useEffect2(() => {
813
+ const control = L3.control({ position });
814
+ control.onAdd = () => {
815
+ const container = L3.DomUtil.create("div", "zenit-location-control");
816
+ L3.DomEvent.disableClickPropagation(container);
817
+ controlRef.current = container;
818
+ return container;
819
+ };
820
+ control.addTo(map);
821
+ return () => {
822
+ control.remove();
823
+ controlRef.current = null;
824
+ };
825
+ }, [map, position]);
826
+ useEffect2(() => {
827
+ if (!location || !isTracking) return;
828
+ if (hasCenteredRef.current) return;
829
+ hasCenteredRef.current = true;
830
+ map.flyTo([location.lat, location.lon], zoom, { animate: true });
831
+ }, [isTracking, location, map, zoom]);
832
+ const markerIcon = useMemo(() => createLocationIcon(), []);
833
+ return /* @__PURE__ */ jsxs2(Fragment2, { children: [
834
+ controlRef.current && createPortal(
835
+ /* @__PURE__ */ jsxs2("div", { children: [
836
+ /* @__PURE__ */ jsxs2(
837
+ "button",
838
+ {
839
+ type: "button",
840
+ className: `zenit-location-button${isTracking ? " zenit-location-button--tracking" : ""}`,
841
+ onClick: () => {
842
+ if (error) {
843
+ clearError();
844
+ }
845
+ toggleTracking();
846
+ },
847
+ "aria-label": isTracking ? "Detener ubicaci\xF3n" : "Mostrar mi ubicaci\xF3n",
848
+ children: [
849
+ /* @__PURE__ */ jsx2(LocateIcon, {}),
850
+ error && /* @__PURE__ */ jsx2("span", { className: "zenit-location-badge", children: "!" })
851
+ ]
852
+ }
853
+ ),
854
+ error && /* @__PURE__ */ jsx2("div", { className: "zenit-location-error", children: error.message || "No se pudo acceder a tu ubicaci\xF3n." })
855
+ ] }),
856
+ controlRef.current
857
+ ),
858
+ location && /* @__PURE__ */ jsxs2(Fragment2, { children: [
859
+ /* @__PURE__ */ jsx2(Marker, { position: [location.lat, location.lon], icon: markerIcon }),
860
+ /* @__PURE__ */ jsx2(
861
+ Circle,
862
+ {
863
+ center: [location.lat, location.lon],
864
+ radius: location.accuracy,
865
+ pathOptions: { color: "#2563eb", fillColor: "#2563eb", fillOpacity: 0.15 }
866
+ }
867
+ )
868
+ ] })
869
+ ] });
870
+ };
871
+
872
+ // src/react/map/map-handlers.tsx
873
+ import { useEffect as useEffect3, useRef as useRef3 } from "react";
874
+ import { useMap as useMap2 } from "react-leaflet";
875
+ function computeBBoxFromGeojson(geojson) {
876
+ if (!geojson || !Array.isArray(geojson.features)) return null;
877
+ const coords = [];
878
+ const collect = (candidate) => {
879
+ if (!Array.isArray(candidate)) return;
880
+ if (candidate.length === 2 && typeof candidate[0] === "number" && typeof candidate[1] === "number" && Number.isFinite(candidate[0]) && Number.isFinite(candidate[1])) {
881
+ coords.push([candidate[0], candidate[1]]);
882
+ return;
883
+ }
884
+ candidate.forEach((item) => collect(item));
885
+ };
886
+ geojson.features.forEach((feature) => {
887
+ collect(feature.geometry?.coordinates);
888
+ });
889
+ if (coords.length === 0) return null;
890
+ const [firstLon, firstLat] = coords[0];
891
+ const bbox = { minLon: firstLon, minLat: firstLat, maxLon: firstLon, maxLat: firstLat };
892
+ coords.forEach(([lon, lat]) => {
893
+ bbox.minLon = Math.min(bbox.minLon, lon);
894
+ bbox.minLat = Math.min(bbox.minLat, lat);
895
+ bbox.maxLon = Math.max(bbox.maxLon, lon);
896
+ bbox.maxLat = Math.max(bbox.maxLat, lat);
897
+ });
898
+ return bbox;
899
+ }
900
+ function mergeBBoxes(bboxes) {
901
+ const valid = bboxes.filter((bbox) => !!bbox);
902
+ if (valid.length === 0) return null;
903
+ const first = valid[0];
904
+ return valid.slice(1).reduce(
905
+ (acc, bbox) => ({
906
+ minLon: Math.min(acc.minLon, bbox.minLon),
907
+ minLat: Math.min(acc.minLat, bbox.minLat),
908
+ maxLon: Math.max(acc.maxLon, bbox.maxLon),
909
+ maxLat: Math.max(acc.maxLat, bbox.maxLat)
910
+ }),
911
+ { ...first }
912
+ );
913
+ }
914
+ var BBoxZoomHandler = ({
915
+ bbox,
916
+ geojson,
917
+ autoGeojson = [],
918
+ enabled = true
919
+ }) => {
920
+ const map = useMap2();
921
+ const lastAppliedBBox = useRef3(null);
922
+ const lastUserInteracted = useRef3(false);
923
+ useEffect3(() => {
924
+ const handleInteraction = () => {
925
+ lastUserInteracted.current = true;
926
+ };
927
+ map.on("dragstart", handleInteraction);
928
+ map.on("zoomstart", handleInteraction);
929
+ return () => {
930
+ map.off("dragstart", handleInteraction);
931
+ map.off("zoomstart", handleInteraction);
932
+ };
933
+ }, [map]);
934
+ useEffect3(() => {
935
+ if (!enabled) return;
936
+ let resolvedBBox = bbox ?? null;
937
+ if (!resolvedBBox && geojson) {
938
+ resolvedBBox = computeBBoxFromGeojson(geojson);
939
+ }
940
+ if (!resolvedBBox && autoGeojson.length > 0) {
941
+ const bboxes = autoGeojson.map((collection) => computeBBoxFromGeojson(collection));
942
+ resolvedBBox = mergeBBoxes(bboxes);
692
943
  }
693
- }
694
- const rgbMatch = trimmed.match(/rgba?\(([^)]+)\)/i);
695
- if (rgbMatch) {
696
- const [r, g, b] = rgbMatch[1].split(",").map((value) => parseFloat(value.trim())).slice(0, 3);
697
- if ([r, g, b].every((value) => Number.isFinite(value))) {
698
- return { r, g, b };
944
+ if (!resolvedBBox) return;
945
+ const serialized = JSON.stringify(resolvedBBox);
946
+ if (lastAppliedBBox.current === serialized) return;
947
+ if (lastUserInteracted.current && !bbox && !geojson) {
948
+ lastUserInteracted.current = false;
949
+ return;
699
950
  }
700
- }
951
+ const bounds = [
952
+ [resolvedBBox.minLat, resolvedBBox.minLon],
953
+ [resolvedBBox.maxLat, resolvedBBox.maxLon]
954
+ ];
955
+ map.fitBounds(bounds, { padding: [12, 12] });
956
+ lastAppliedBBox.current = serialized;
957
+ }, [autoGeojson, bbox, enabled, geojson, map]);
958
+ return null;
959
+ };
960
+ var ZoomBasedOpacityHandler = ({ onZoomChange }) => {
961
+ const map = useMap2();
962
+ useEffect3(() => {
963
+ const handleZoom = () => {
964
+ onZoomChange(map.getZoom());
965
+ };
966
+ map.on("zoomend", handleZoom);
967
+ handleZoom();
968
+ return () => {
969
+ map.off("zoomend", handleZoom);
970
+ };
971
+ }, [map, onZoomChange]);
972
+ return null;
973
+ };
974
+ var MapInstanceBridge = ({ onReady }) => {
975
+ const map = useMap2();
976
+ useEffect3(() => {
977
+ onReady(map);
978
+ }, [map, onReady]);
701
979
  return null;
980
+ };
981
+
982
+ // src/react/ZenitMap.tsx
983
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
984
+ var DEFAULT_CENTER = [0, 0];
985
+ var DEFAULT_ZOOM = 3;
986
+ var LABELS_PANE_NAME = "zenit-labels-pane";
987
+ function isRecord(value) {
988
+ return typeof value === "object" && value !== null;
989
+ }
990
+ function isGeoJsonFeatureCollection(value) {
991
+ if (!isRecord(value)) return false;
992
+ const features = value.features;
993
+ if (!Array.isArray(features)) return false;
994
+ const type = value.type;
995
+ return type === void 0 || type === "FeatureCollection";
996
+ }
997
+ function extractGeoJsonFeatureCollection(value) {
998
+ if (isRecord(value) && "data" in value) {
999
+ const data = value.data;
1000
+ return isGeoJsonFeatureCollection(data) ? data : null;
1001
+ }
1002
+ return isGeoJsonFeatureCollection(value) ? value : null;
702
1003
  }
703
- function getLabelTextStyles(color) {
704
- const rgb = getRgbFromColor(color);
705
- if (!rgb) {
706
- return { color: "#0f172a", shadow: "0 1px 2px rgba(255, 255, 255, 0.6)" };
1004
+ function getFeatureLayerId(feature) {
1005
+ const layerId = feature?.properties?.__zenit_layerId ?? feature?.properties?.layerId ?? feature?.properties?.layer_id;
1006
+ if (layerId === void 0 || layerId === null) return null;
1007
+ return layerId;
1008
+ }
1009
+ var DESCRIPTION_KEYS = /* @__PURE__ */ new Set(["descripcion", "description"]);
1010
+ function normalizeDescriptionValue(value) {
1011
+ if (value === void 0 || value === null) return null;
1012
+ if (typeof value === "string") {
1013
+ const trimmed = value.trim();
1014
+ return trimmed ? trimmed : null;
707
1015
  }
708
- const luminance = (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b) / 255;
709
- if (luminance > 0.6) {
710
- return { color: "#0f172a", shadow: "0 1px 2px rgba(255, 255, 255, 0.7)" };
1016
+ if (typeof value === "number" || typeof value === "boolean") {
1017
+ return String(value);
711
1018
  }
712
- return { color: "#ffffff", shadow: "0 1px 2px rgba(0, 0, 0, 0.4)" };
1019
+ return null;
1020
+ }
1021
+ function extractDescriptionValue(properties) {
1022
+ if (!properties) return null;
1023
+ const matches = Object.entries(properties).find(
1024
+ ([key]) => DESCRIPTION_KEYS.has(key.toLowerCase())
1025
+ );
1026
+ if (!matches) return null;
1027
+ return normalizeDescriptionValue(matches[1]);
713
1028
  }
714
1029
  function getFeatureStyleOverrides(feature) {
715
1030
  const candidate = feature?.properties?._style;
@@ -728,25 +1043,6 @@ function buildFeaturePopupHtml(feature) {
728
1043
  const rendered = createPopupContent(properties);
729
1044
  return rendered ? rendered : null;
730
1045
  }
731
- var POINT_GEOMETRY_TYPES = /* @__PURE__ */ new Set(["Point", "MultiPoint"]);
732
- function getGeometryType(feature) {
733
- const t = feature?.geometry?.type;
734
- return typeof t === "string" ? t : null;
735
- }
736
- function isPointGeometry(feature) {
737
- const geometryType = getGeometryType(feature);
738
- return geometryType !== null && POINT_GEOMETRY_TYPES.has(geometryType);
739
- }
740
- function isNonPointGeometry(feature) {
741
- const geometryType = getGeometryType(feature);
742
- return geometryType !== null && !POINT_GEOMETRY_TYPES.has(geometryType);
743
- }
744
- function buildFeatureCollection(features) {
745
- return {
746
- type: "FeatureCollection",
747
- features
748
- };
749
- }
750
1046
  function pickIntersectFeature(baseFeature, candidates) {
751
1047
  if (!Array.isArray(candidates) || candidates.length === 0) return null;
752
1048
  const baseId = baseFeature?.id;
@@ -773,80 +1069,6 @@ function normalizeCenterTuple(center) {
773
1069
  }
774
1070
  return null;
775
1071
  }
776
- var FitToBounds = ({ bbox }) => {
777
- const mapInstance = useMap();
778
- const lastAppliedBBox = useRef(null);
779
- useEffect(() => {
780
- const targetBBox = bbox;
781
- if (!targetBBox) return;
782
- const serialized = JSON.stringify(targetBBox);
783
- if (lastAppliedBBox.current === serialized) return;
784
- const bounds = [
785
- [targetBBox.minLat, targetBBox.minLon],
786
- [targetBBox.maxLat, targetBBox.maxLon]
787
- ];
788
- mapInstance.fitBounds(bounds, { padding: [12, 12] });
789
- lastAppliedBBox.current = serialized;
790
- }, [bbox, mapInstance]);
791
- return null;
792
- };
793
- var AutoFitToBounds = ({
794
- bbox,
795
- enabled = true
796
- }) => {
797
- const mapInstance = useMap();
798
- const lastAutoBBoxApplied = useRef(null);
799
- const lastUserInteracted = useRef(false);
800
- useEffect(() => {
801
- if (!enabled) return;
802
- const handleInteraction = () => {
803
- lastUserInteracted.current = true;
804
- };
805
- mapInstance.on("dragstart", handleInteraction);
806
- mapInstance.on("zoomstart", handleInteraction);
807
- return () => {
808
- mapInstance.off("dragstart", handleInteraction);
809
- mapInstance.off("zoomstart", handleInteraction);
810
- };
811
- }, [enabled, mapInstance]);
812
- useEffect(() => {
813
- if (!enabled) return;
814
- if (!bbox) return;
815
- const serialized = JSON.stringify(bbox);
816
- if (lastAutoBBoxApplied.current === serialized) return;
817
- if (lastUserInteracted.current) {
818
- lastUserInteracted.current = false;
819
- }
820
- const bounds = [
821
- [bbox.minLat, bbox.minLon],
822
- [bbox.maxLat, bbox.maxLon]
823
- ];
824
- mapInstance.fitBounds(bounds, { padding: [12, 12] });
825
- lastAutoBBoxApplied.current = serialized;
826
- }, [bbox, enabled, mapInstance]);
827
- return null;
828
- };
829
- var ZoomBasedOpacityHandler = ({ onZoomChange }) => {
830
- const mapInstance = useMap();
831
- useEffect(() => {
832
- const handleZoom = () => {
833
- onZoomChange(mapInstance.getZoom());
834
- };
835
- mapInstance.on("zoomend", handleZoom);
836
- handleZoom();
837
- return () => {
838
- mapInstance.off("zoomend", handleZoom);
839
- };
840
- }, [mapInstance, onZoomChange]);
841
- return null;
842
- };
843
- var MapInstanceBridge = ({ onReady }) => {
844
- const mapInstance = useMap();
845
- useEffect(() => {
846
- onReady(mapInstance);
847
- }, [mapInstance, onReady]);
848
- return null;
849
- };
850
1072
  var ZenitMap = forwardRef(({
851
1073
  client,
852
1074
  mapId,
@@ -872,20 +1094,21 @@ var ZenitMap = forwardRef(({
872
1094
  onZoomChange,
873
1095
  onMapReady
874
1096
  }, ref) => {
875
- const [map, setMap] = useState(null);
876
- const [layers, setLayers] = useState([]);
877
- const [effectiveStates, setEffectiveStates] = useState([]);
878
- const [loadingMap, setLoadingMap] = useState(false);
879
- const [mapError, setMapError] = useState(null);
880
- const [mapInstance, setMapInstance] = useState(null);
881
- const [panesReady, setPanesReady] = useState(false);
882
- const [currentZoom, setCurrentZoom] = useState(initialZoom ?? DEFAULT_ZOOM);
883
- const [isMobile, setIsMobile] = useState(() => {
1097
+ const [map, setMap] = useState2(null);
1098
+ const [layers, setLayers] = useState2([]);
1099
+ const [effectiveStates, setEffectiveStates] = useState2([]);
1100
+ const [loadingMap, setLoadingMap] = useState2(false);
1101
+ const [mapError, setMapError] = useState2(null);
1102
+ const [mapInstance, setMapInstance] = useState2(null);
1103
+ const [panesReady, setPanesReady] = useState2(false);
1104
+ const [currentZoom, setCurrentZoom] = useState2(initialZoom ?? DEFAULT_ZOOM);
1105
+ const [isPopupOpen, setIsPopupOpen] = useState2(false);
1106
+ const [isMobile, setIsMobile] = useState2(() => {
884
1107
  if (typeof window === "undefined") return false;
885
1108
  return window.matchMedia("(max-width: 768px)").matches;
886
1109
  });
887
- const normalizedLayers = useMemo(() => normalizeMapLayers(map), [map]);
888
- useEffect(() => {
1110
+ const normalizedLayers = useMemo2(() => normalizeMapLayers(map), [map]);
1111
+ useEffect4(() => {
889
1112
  if (typeof window === "undefined") return;
890
1113
  const mql = window.matchMedia("(max-width: 768px)");
891
1114
  const onChange = (e) => {
@@ -903,12 +1126,37 @@ var ZenitMap = forwardRef(({
903
1126
  }
904
1127
  return;
905
1128
  }, []);
906
- useEffect(() => {
1129
+ useEffect4(() => {
907
1130
  if (featureInfoMode === "popup") {
908
1131
  ensurePopupStyles();
909
1132
  }
910
1133
  }, [featureInfoMode]);
911
- const layerStyleIndex = useMemo(() => {
1134
+ useEffect4(() => {
1135
+ if (featureInfoMode !== "popup") {
1136
+ setIsPopupOpen(false);
1137
+ }
1138
+ }, [featureInfoMode]);
1139
+ useEffect4(() => {
1140
+ if (!mapInstance) return;
1141
+ const popupPane = mapInstance.getPane("popupPane");
1142
+ if (popupPane) {
1143
+ popupPane.style.zIndex = "800";
1144
+ }
1145
+ const labelsPane = mapInstance.getPane(LABELS_PANE_NAME) ?? mapInstance.createPane(LABELS_PANE_NAME);
1146
+ labelsPane.style.zIndex = "600";
1147
+ }, [mapInstance]);
1148
+ useEffect4(() => {
1149
+ if (!mapInstance) return;
1150
+ const handlePopupOpen = () => setIsPopupOpen(true);
1151
+ const handlePopupClose = () => setIsPopupOpen(false);
1152
+ mapInstance.on("popupopen", handlePopupOpen);
1153
+ mapInstance.on("popupclose", handlePopupClose);
1154
+ return () => {
1155
+ mapInstance.off("popupopen", handlePopupOpen);
1156
+ mapInstance.off("popupclose", handlePopupClose);
1157
+ };
1158
+ }, [mapInstance]);
1159
+ const layerStyleIndex = useMemo2(() => {
912
1160
  const index = /* @__PURE__ */ new Map();
913
1161
  (map?.mapLayers ?? []).forEach((entry) => {
914
1162
  const layerStyle = entry.layer?.style ?? entry.mapLayer?.layer?.style ?? entry.style ?? null;
@@ -919,7 +1167,7 @@ var ZenitMap = forwardRef(({
919
1167
  });
920
1168
  return index;
921
1169
  }, [map]);
922
- const labelKeyIndex = useMemo(() => {
1170
+ const labelKeyIndex = useMemo2(() => {
923
1171
  const index = /* @__PURE__ */ new Map();
924
1172
  normalizedLayers.forEach((entry) => {
925
1173
  const label = entry.layer?.label ?? entry.mapLayer?.label ?? entry.mapLayer.layerConfig?.label;
@@ -929,7 +1177,7 @@ var ZenitMap = forwardRef(({
929
1177
  });
930
1178
  return index;
931
1179
  }, [normalizedLayers]);
932
- const layerMetaIndex = useMemo(() => {
1180
+ const layerMetaIndex = useMemo2(() => {
933
1181
  const index = /* @__PURE__ */ new Map();
934
1182
  normalizedLayers.forEach((entry) => {
935
1183
  index.set(String(entry.layerId), {
@@ -939,7 +1187,7 @@ var ZenitMap = forwardRef(({
939
1187
  });
940
1188
  return index;
941
1189
  }, [normalizedLayers]);
942
- const overlayStyleFunction = useMemo(() => {
1190
+ const overlayStyleFunction = useMemo2(() => {
943
1191
  return (feature) => {
944
1192
  const featureLayerId = getFeatureLayerId(feature);
945
1193
  const featureStyleOverrides = getFeatureStyleOverrides(feature);
@@ -958,11 +1206,15 @@ var ZenitMap = forwardRef(({
958
1206
  return defaultOptions;
959
1207
  };
960
1208
  }, [layerStyleIndex, mapLayers, overlayStyle]);
961
- const [baseStates, setBaseStates] = useState([]);
962
- const [mapOverrides, setMapOverrides] = useState([]);
963
- const [controlOverrides, setControlOverrides] = useState([]);
964
- const [uiOverrides, setUiOverrides] = useState([]);
965
- useEffect(() => {
1209
+ const overlayStyleFn = useCallback2(
1210
+ (feature, _layerType, _baseOpacity) => overlayStyleFunction(feature),
1211
+ [overlayStyleFunction]
1212
+ );
1213
+ const [baseStates, setBaseStates] = useState2([]);
1214
+ const [mapOverrides, setMapOverrides] = useState2([]);
1215
+ const [controlOverrides, setControlOverrides] = useState2([]);
1216
+ const [uiOverrides, setUiOverrides] = useState2([]);
1217
+ useEffect4(() => {
966
1218
  let isMounted = true;
967
1219
  setLoadingMap(true);
968
1220
  setMapError(null);
@@ -985,7 +1237,7 @@ var ZenitMap = forwardRef(({
985
1237
  isMounted = false;
986
1238
  };
987
1239
  }, [client.maps, mapId, onError, onLoadingChange]);
988
- useEffect(() => {
1240
+ useEffect4(() => {
989
1241
  if (normalizedLayers.length === 0) {
990
1242
  setLayers([]);
991
1243
  setBaseStates([]);
@@ -1016,7 +1268,7 @@ var ZenitMap = forwardRef(({
1016
1268
  setMapOverrides(initialOverrides);
1017
1269
  setUiOverrides([]);
1018
1270
  }, [normalizedLayers]);
1019
- useEffect(() => {
1271
+ useEffect4(() => {
1020
1272
  if (!layerControls) {
1021
1273
  setControlOverrides([]);
1022
1274
  return;
@@ -1028,7 +1280,7 @@ var ZenitMap = forwardRef(({
1028
1280
  }));
1029
1281
  setControlOverrides(overrides);
1030
1282
  }, [layerControls]);
1031
- useEffect(() => {
1283
+ useEffect4(() => {
1032
1284
  if (layerStates) {
1033
1285
  return;
1034
1286
  }
@@ -1038,12 +1290,12 @@ var ZenitMap = forwardRef(({
1038
1290
  onLayerStateChange?.(reset);
1039
1291
  }
1040
1292
  }, [baseStates, effectiveStates.length, layerControls, layerStates, onLayerStateChange]);
1041
- useEffect(() => {
1293
+ useEffect4(() => {
1042
1294
  if (layerStates) {
1043
1295
  setEffectiveStates(layerStates);
1044
1296
  }
1045
1297
  }, [layerStates]);
1046
- useEffect(() => {
1298
+ useEffect4(() => {
1047
1299
  if (layerStates) {
1048
1300
  return;
1049
1301
  }
@@ -1056,11 +1308,11 @@ var ZenitMap = forwardRef(({
1056
1308
  setEffectiveStates(next);
1057
1309
  onLayerStateChange?.(next);
1058
1310
  }, [baseStates, controlOverrides, layerStates, mapOverrides, onLayerStateChange, uiOverrides]);
1059
- useEffect(() => {
1311
+ useEffect4(() => {
1060
1312
  if (!Array.isArray(layerControls) || layerControls.length > 0) return;
1061
1313
  setUiOverrides([]);
1062
1314
  }, [layerControls]);
1063
- useEffect(() => {
1315
+ useEffect4(() => {
1064
1316
  if (layerStates) {
1065
1317
  return;
1066
1318
  }
@@ -1080,18 +1332,13 @@ var ZenitMap = forwardRef(({
1080
1332
  return [...filtered, nextEntry];
1081
1333
  });
1082
1334
  };
1083
- const updateOpacityFromUi = useCallback(
1335
+ const updateOpacityFromUi = useCallback2(
1084
1336
  (layerId, uiOpacity) => {
1085
1337
  const meta = layerMetaIndex.get(String(layerId));
1086
- const zoomFactor = getLayerZoomOpacityFactor(
1338
+ const baseOpacity = clampOpacity3(uiOpacity);
1339
+ const effectiveOpacity = calculateZoomBasedOpacity(
1087
1340
  currentZoom,
1088
- meta?.layerType,
1089
- meta?.geometryType
1090
- );
1091
- const baseOpacity = clampOpacity3(uiOpacity / zoomFactor);
1092
- const effectiveOpacity = getEffectiveLayerOpacity(
1093
1341
  baseOpacity,
1094
- currentZoom,
1095
1342
  meta?.layerType,
1096
1343
  meta?.geometryType
1097
1344
  );
@@ -1124,7 +1371,7 @@ var ZenitMap = forwardRef(({
1124
1371
  },
1125
1372
  [currentZoom, effectiveStates, layerMetaIndex, layerStates, onLayerStateChange]
1126
1373
  );
1127
- const center = useMemo(() => {
1374
+ const center = useMemo2(() => {
1128
1375
  if (initialCenter) {
1129
1376
  return initialCenter;
1130
1377
  }
@@ -1135,36 +1382,30 @@ var ZenitMap = forwardRef(({
1135
1382
  return DEFAULT_CENTER;
1136
1383
  }, [initialCenter, map?.settings?.center]);
1137
1384
  const zoom = initialZoom ?? map?.settings?.zoom ?? DEFAULT_ZOOM;
1138
- useEffect(() => {
1385
+ useEffect4(() => {
1139
1386
  setCurrentZoom(zoom);
1140
1387
  }, [zoom]);
1141
- const decoratedLayers = useMemo(() => {
1388
+ const decoratedLayers = useMemo2(() => {
1142
1389
  return layers.map((layer) => ({
1143
1390
  ...layer,
1144
1391
  effective: effectiveStates.find((state) => state.layerId === layer.mapLayer.layerId),
1145
1392
  data: layerGeojson?.[layer.mapLayer.layerId] ?? layerGeojson?.[String(layer.mapLayer.layerId)] ?? null
1146
1393
  }));
1147
1394
  }, [effectiveStates, layerGeojson, layers]);
1148
- const orderedLayers = useMemo(() => {
1395
+ const orderedLayers = useMemo2(() => {
1149
1396
  return [...decoratedLayers].filter((layer) => layer.effective?.visible && layer.data).sort((a, b) => a.displayOrder - b.displayOrder);
1150
1397
  }, [decoratedLayers]);
1151
- const explicitZoomBBox = useMemo(() => {
1152
- if (zoomToBbox) return zoomToBbox;
1153
- if (zoomToGeojson) return computeBBoxFromGeojson(zoomToGeojson);
1154
- return null;
1155
- }, [zoomToBbox, zoomToGeojson]);
1156
- const autoZoomBBox = useMemo(() => {
1157
- if (explicitZoomBBox) return null;
1158
- const visibleBBoxes = orderedLayers.map((layer) => computeBBoxFromGeojson(layer.data));
1159
- return mergeBBoxes(visibleBBoxes);
1160
- }, [explicitZoomBBox, orderedLayers]);
1161
- const resolveLayerStyle = useCallback(
1398
+ const autoZoomGeojson = useMemo2(
1399
+ () => orderedLayers.map((layer) => layer.data).filter((collection) => !!collection),
1400
+ [orderedLayers]
1401
+ );
1402
+ const resolveLayerStyle = useCallback2(
1162
1403
  (layerId) => {
1163
1404
  return getStyleByLayerId(layerId, mapLayers) ?? layerStyleIndex.get(String(layerId)) ?? null;
1164
1405
  },
1165
1406
  [layerStyleIndex, mapLayers]
1166
1407
  );
1167
- const labelMarkers = useMemo(() => {
1408
+ const labelMarkers = useMemo2(() => {
1168
1409
  const markers = [];
1169
1410
  decoratedLayers.forEach((layerState) => {
1170
1411
  if (!layerState.effective?.visible) return;
@@ -1174,7 +1415,14 @@ var ZenitMap = forwardRef(({
1174
1415
  if (!data) return;
1175
1416
  const resolvedStyle = resolveLayerStyle(layerState.mapLayer.layerId);
1176
1417
  const layerColor = resolvedStyle?.fillColor ?? resolvedStyle?.color ?? "rgba(37, 99, 235, 1)";
1177
- const opacity = layerState.effective?.opacity ?? 1;
1418
+ const meta = layerMetaIndex.get(String(layerState.mapLayer.layerId));
1419
+ const baseOpacity = layerState.effective?.baseOpacity ?? layerState.effective?.opacity ?? 1;
1420
+ const opacity = calculateZoomBasedOpacity(
1421
+ currentZoom,
1422
+ baseOpacity,
1423
+ meta?.layerType,
1424
+ meta?.geometryType
1425
+ );
1178
1426
  data.features.forEach((feature, index) => {
1179
1427
  const properties = feature.properties;
1180
1428
  const value = properties?.[labelKey];
@@ -1193,26 +1441,24 @@ var ZenitMap = forwardRef(({
1193
1441
  });
1194
1442
  });
1195
1443
  return markers;
1196
- }, [decoratedLayers, labelKeyIndex, resolveLayerStyle]);
1197
- const ensureLayerPanes = useCallback(
1444
+ }, [currentZoom, decoratedLayers, labelKeyIndex, layerMetaIndex, resolveLayerStyle]);
1445
+ const ensureLayerPanes = useCallback2(
1198
1446
  (targetMap, targetLayers) => {
1199
1447
  const baseZIndex = 400;
1200
1448
  targetLayers.forEach((layer) => {
1201
1449
  const order = Number.isFinite(layer.displayOrder) ? layer.displayOrder : 0;
1450
+ const orderOffset = Math.max(0, Math.min(order, 150));
1202
1451
  const fillPaneName = `zenit-layer-${layer.layerId}-fill`;
1203
1452
  const pointPaneName = `zenit-layer-${layer.layerId}-points`;
1204
- const labelPaneName = `zenit-layer-${layer.layerId}-labels`;
1205
1453
  const fillPane = targetMap.getPane(fillPaneName) ?? targetMap.createPane(fillPaneName);
1206
1454
  const pointPane = targetMap.getPane(pointPaneName) ?? targetMap.createPane(pointPaneName);
1207
- const labelPane = targetMap.getPane(labelPaneName) ?? targetMap.createPane(labelPaneName);
1208
- fillPane.style.zIndex = String(baseZIndex + order);
1209
- pointPane.style.zIndex = String(baseZIndex + order + 1e3);
1210
- labelPane.style.zIndex = String(baseZIndex + order + 2e3);
1455
+ fillPane.style.zIndex = String(baseZIndex + orderOffset);
1456
+ pointPane.style.zIndex = String(baseZIndex + orderOffset + 100);
1211
1457
  });
1212
1458
  },
1213
1459
  []
1214
1460
  );
1215
- const handleMapReady = useCallback(
1461
+ const handleMapReady = useCallback2(
1216
1462
  (instance) => {
1217
1463
  setPanesReady(false);
1218
1464
  setMapInstance(instance);
@@ -1220,7 +1466,7 @@ var ZenitMap = forwardRef(({
1220
1466
  },
1221
1467
  [onMapReady]
1222
1468
  );
1223
- useEffect(() => {
1469
+ useEffect4(() => {
1224
1470
  if (!mapInstance) {
1225
1471
  setPanesReady(false);
1226
1472
  return;
@@ -1234,24 +1480,25 @@ var ZenitMap = forwardRef(({
1234
1480
  }));
1235
1481
  ensureLayerPanes(mapInstance, layerTargets);
1236
1482
  const first = layerTargets[0];
1237
- const testPane = mapInstance.getPane(`zenit-layer-${first.layerId}-labels`);
1238
- if (testPane) {
1483
+ const testPane = mapInstance.getPane(`zenit-layer-${first.layerId}-fill`);
1484
+ const labelsPane = mapInstance.getPane(LABELS_PANE_NAME);
1485
+ if (testPane && labelsPane) {
1239
1486
  setPanesReady(true);
1240
1487
  }
1241
1488
  }, [mapInstance, orderedLayers, ensureLayerPanes]);
1242
- const overlayOnEachFeature = useMemo(() => {
1489
+ const overlayOnEachFeature = useMemo2(() => {
1243
1490
  return (feature, layer) => {
1244
1491
  const layerId = getFeatureLayerId(feature) ?? void 0;
1245
1492
  const geometryType = feature?.geometry?.type;
1246
- const isPointFeature = geometryType === "Point" || geometryType === "MultiPoint" || layer instanceof L.CircleMarker;
1247
- const originalStyle = layer instanceof L.Path ? {
1493
+ const isPointFeature = geometryType === "Point" || geometryType === "MultiPoint" || layer instanceof L4.CircleMarker;
1494
+ const originalStyle = layer instanceof L4.Path ? {
1248
1495
  color: layer.options.color,
1249
1496
  weight: layer.options.weight,
1250
1497
  fillColor: layer.options.fillColor,
1251
1498
  opacity: layer.options.opacity,
1252
1499
  fillOpacity: layer.options.fillOpacity
1253
1500
  } : null;
1254
- const originalRadius = layer instanceof L.CircleMarker ? layer.getRadius() : null;
1501
+ const originalRadius = layer instanceof L4.CircleMarker ? layer.getRadius() : null;
1255
1502
  if (featureInfoMode === "popup") {
1256
1503
  const content = buildFeaturePopupHtml(feature);
1257
1504
  if (content) {
@@ -1260,11 +1507,10 @@ var ZenitMap = forwardRef(({
1260
1507
  maxWidth,
1261
1508
  minWidth,
1262
1509
  maxHeight,
1263
- className: "zenit-leaflet-popup",
1510
+ className: "custom-leaflet-popup",
1264
1511
  autoPan: true,
1265
1512
  closeButton: true,
1266
- keepInView: true,
1267
- offset: L.point(0, -24)
1513
+ keepInView: true
1268
1514
  });
1269
1515
  }
1270
1516
  }
@@ -1285,7 +1531,8 @@ var ZenitMap = forwardRef(({
1285
1531
  id: layerId,
1286
1532
  geometry: feature.geometry
1287
1533
  }).then((response) => {
1288
- const candidates = response.data?.features ?? [];
1534
+ const geo = extractGeoJsonFeatureCollection(response);
1535
+ const candidates = geo?.features ?? [];
1289
1536
  const resolved = pickIntersectFeature(feature, candidates);
1290
1537
  if (!resolved?.properties) return;
1291
1538
  const mergedProperties = {
@@ -1308,7 +1555,7 @@ var ZenitMap = forwardRef(({
1308
1555
  onFeatureClick?.(feature, layerId);
1309
1556
  });
1310
1557
  layer.on("mouseover", () => {
1311
- if (layer instanceof L.Path && originalStyle) {
1558
+ if (layer instanceof L4.Path && originalStyle) {
1312
1559
  layer.setStyle({
1313
1560
  ...originalStyle,
1314
1561
  weight: (originalStyle.weight ?? 2) + 1,
@@ -1316,16 +1563,16 @@ var ZenitMap = forwardRef(({
1316
1563
  fillOpacity: Math.min(1, (originalStyle.fillOpacity ?? 0.8) + 0.1)
1317
1564
  });
1318
1565
  }
1319
- if (layer instanceof L.CircleMarker && typeof originalRadius === "number") {
1566
+ if (layer instanceof L4.CircleMarker && typeof originalRadius === "number") {
1320
1567
  layer.setRadius(originalRadius + 1);
1321
1568
  }
1322
1569
  onFeatureHover?.(feature, layerId);
1323
1570
  });
1324
1571
  layer.on("mouseout", () => {
1325
- if (layer instanceof L.Path && originalStyle) {
1572
+ if (layer instanceof L4.Path && originalStyle) {
1326
1573
  layer.setStyle(originalStyle);
1327
1574
  }
1328
- if (layer instanceof L.CircleMarker && typeof originalRadius === "number") {
1575
+ if (layer instanceof L4.CircleMarker && typeof originalRadius === "number") {
1329
1576
  layer.setRadius(originalRadius);
1330
1577
  }
1331
1578
  });
@@ -1337,79 +1584,19 @@ var ZenitMap = forwardRef(({
1337
1584
  const resolvedStyle = featureStyleOverrides ? { ...style ?? {}, ...featureStyleOverrides } : style;
1338
1585
  const geometryType = feature?.geometry?.type;
1339
1586
  const resolvedLayerType = layerType ?? geometryType;
1340
- const sanitizedBaseOpacity = clampOpacity3(baseOpacity);
1341
- const normalizedStyleFill = typeof resolvedStyle?.fillOpacity === "number" ? clampOpacity3(resolvedStyle.fillOpacity) : 0.8;
1342
- const effectiveOpacity = getEffectiveLayerOpacity(
1343
- sanitizedBaseOpacity,
1344
- currentZoom,
1345
- resolvedLayerType,
1346
- geometryType
1347
- );
1348
- const fillOpacity = clampOpacity3(effectiveOpacity * normalizedStyleFill);
1349
- const strokeOpacity = clampOpacity3(Math.max(0.35, effectiveOpacity * 0.9));
1350
- return {
1351
- color: resolvedStyle?.color ?? resolvedStyle?.fillColor ?? "#2563eb",
1352
- weight: resolvedStyle?.weight ?? 2,
1353
- fillColor: resolvedStyle?.fillColor ?? resolvedStyle?.color ?? "#2563eb",
1354
- opacity: strokeOpacity,
1355
- fillOpacity
1587
+ return layerStyleToLeaflet({
1588
+ baseOpacity,
1589
+ zoom: currentZoom,
1590
+ layerStyle: resolvedStyle,
1591
+ geometryType,
1592
+ layerType: resolvedLayerType
1593
+ });
1594
+ };
1595
+ const makeStyleFnForLayer = (layerId) => {
1596
+ return (feature, layerType, baseOpacity) => {
1597
+ return buildLayerStyle(layerId, baseOpacity ?? 1, feature, layerType);
1356
1598
  };
1357
1599
  };
1358
- const buildLabelIcon = useCallback((label, opacity, color) => {
1359
- const size = 60;
1360
- const innerSize = 44;
1361
- const textStyles = getLabelTextStyles(color);
1362
- const safeLabel = escapeHtml(label);
1363
- const clampedOpacity = Math.min(1, Math.max(0.92, opacity));
1364
- const innerBackground = withAlpha(color, 0.9);
1365
- return L.divIcon({
1366
- className: "zenit-label-marker",
1367
- iconSize: [size, size],
1368
- iconAnchor: [size / 2, size / 2],
1369
- html: `
1370
- <div
1371
- title="${safeLabel}"
1372
- style="
1373
- width:${size}px;
1374
- height:${size}px;
1375
- border-radius:9999px;
1376
- background:rgba(255, 255, 255, 0.95);
1377
- border:3px solid rgba(255, 255, 255, 1);
1378
- display:flex;
1379
- align-items:center;
1380
- justify-content:center;
1381
- opacity:${clampedOpacity};
1382
- box-shadow:0 2px 6px rgba(0, 0, 0, 0.25);
1383
- pointer-events:none;
1384
- "
1385
- >
1386
- <div
1387
- style="
1388
- width:${innerSize}px;
1389
- height:${innerSize}px;
1390
- border-radius:9999px;
1391
- background:${innerBackground};
1392
- display:flex;
1393
- align-items:center;
1394
- justify-content:center;
1395
- box-shadow:inset 0 0 0 1px rgba(15, 23, 42, 0.12);
1396
- "
1397
- >
1398
- <span
1399
- style="
1400
- color:${textStyles.color};
1401
- font-size:20px;
1402
- font-weight:800;
1403
- text-shadow:${textStyles.shadow};
1404
- "
1405
- >
1406
- ${safeLabel}
1407
- </span>
1408
- </div>
1409
- </div>
1410
- `
1411
- });
1412
- }, []);
1413
1600
  useImperativeHandle(ref, () => ({
1414
1601
  setLayerOpacity: (layerId, opacity) => {
1415
1602
  upsertUiOverride(layerId, { overrideOpacity: opacity });
@@ -1460,10 +1647,10 @@ var ZenitMap = forwardRef(({
1460
1647
  getMapInstance: () => mapInstance
1461
1648
  }), [effectiveStates, mapInstance]);
1462
1649
  if (loadingMap) {
1463
- return /* @__PURE__ */ jsx("div", { style: { padding: 16, height, width }, children: "Cargando mapa..." });
1650
+ return /* @__PURE__ */ jsx3("div", { style: { padding: 16, height, width }, children: "Cargando mapa..." });
1464
1651
  }
1465
1652
  if (mapError) {
1466
- return /* @__PURE__ */ jsxs("div", { style: { padding: 16, height, width, color: "red" }, children: [
1653
+ return /* @__PURE__ */ jsxs3("div", { style: { padding: 16, height, width, color: "red" }, children: [
1467
1654
  "Error al cargar mapa: ",
1468
1655
  mapError
1469
1656
  ] });
@@ -1475,7 +1662,7 @@ var ZenitMap = forwardRef(({
1475
1662
  setCurrentZoom(zoomValue);
1476
1663
  onZoomChange?.(zoomValue);
1477
1664
  };
1478
- return /* @__PURE__ */ jsxs(
1665
+ return /* @__PURE__ */ jsxs3(
1479
1666
  "div",
1480
1667
  {
1481
1668
  style: {
@@ -1488,88 +1675,111 @@ var ZenitMap = forwardRef(({
1488
1675
  boxSizing: "border-box"
1489
1676
  },
1490
1677
  children: [
1491
- /* @__PURE__ */ jsx("div", { style: { flex: 1, position: "relative" }, children: /* @__PURE__ */ jsxs(
1492
- MapContainer,
1678
+ /* @__PURE__ */ jsx3(
1679
+ "div",
1493
1680
  {
1494
- center,
1495
- zoom,
1496
- style: { height: "100%", width: "100%" },
1497
- scrollWheelZoom: true,
1498
- zoomControl: false,
1499
- children: [
1500
- /* @__PURE__ */ jsx(
1501
- TileLayer,
1502
- {
1503
- url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
1504
- attribution: "\xA9 OpenStreetMap contributors"
1505
- }
1506
- ),
1507
- /* @__PURE__ */ jsx(ZoomControl, { position: "topright" }),
1508
- /* @__PURE__ */ jsx(MapInstanceBridge, { onReady: handleMapReady }),
1509
- /* @__PURE__ */ jsx(FitToBounds, { bbox: explicitZoomBBox ?? void 0 }),
1510
- /* @__PURE__ */ jsx(AutoFitToBounds, { bbox: autoZoomBBox ?? void 0, enabled: !explicitZoomBBox }),
1511
- /* @__PURE__ */ jsx(ZoomBasedOpacityHandler, { onZoomChange: handleZoomChange }),
1512
- orderedLayers.map((layerState) => {
1513
- const baseOpacity = layerState.effective?.baseOpacity ?? layerState.effective?.opacity ?? 1;
1514
- const fillPaneName = `zenit-layer-${layerState.mapLayer.layerId}-fill`;
1515
- const pointsPaneName = `zenit-layer-${layerState.mapLayer.layerId}-points`;
1516
- const labelPaneName = `zenit-layer-${layerState.mapLayer.layerId}-labels`;
1517
- const layerType = layerState.layer?.layerType ?? layerState.mapLayer.layerType ?? void 0;
1518
- const data = layerState.data?.features ?? [];
1519
- const fillFeatures = data.filter(isNonPointGeometry);
1520
- const pointFeatures = data.filter(isPointGeometry);
1521
- const fillData = fillFeatures.length > 0 ? buildFeatureCollection(fillFeatures) : null;
1522
- const pointsData = pointFeatures.length > 0 ? buildFeatureCollection(pointFeatures) : null;
1523
- return /* @__PURE__ */ jsxs(React.Fragment, { children: [
1524
- fillData && /* @__PURE__ */ jsx(
1525
- GeoJSON,
1681
+ className: `zenit-map-shell${isPopupOpen ? " popup-open" : ""}`,
1682
+ style: { flex: 1, position: "relative" },
1683
+ children: /* @__PURE__ */ jsxs3(
1684
+ MapContainer,
1685
+ {
1686
+ center,
1687
+ zoom,
1688
+ style: { height: "100%", width: "100%" },
1689
+ scrollWheelZoom: true,
1690
+ zoomControl: false,
1691
+ children: [
1692
+ /* @__PURE__ */ jsx3(
1693
+ TileLayer,
1526
1694
  {
1527
- data: fillData,
1528
- pane: panesReady && mapInstance?.getPane(fillPaneName) ? fillPaneName : void 0,
1529
- style: (feature) => buildLayerStyle(layerState.mapLayer.layerId, baseOpacity, feature, layerType),
1530
- onEachFeature: overlayOnEachFeature
1695
+ url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
1696
+ attribution: "\xA9 OpenStreetMap contributors"
1531
1697
  }
1532
1698
  ),
1533
- pointsData && /* @__PURE__ */ jsx(
1534
- GeoJSON,
1699
+ /* @__PURE__ */ jsx3(ZoomControl, { position: "topright" }),
1700
+ /* @__PURE__ */ jsx3(MapInstanceBridge, { onReady: handleMapReady }),
1701
+ /* @__PURE__ */ jsx3(
1702
+ BBoxZoomHandler,
1535
1703
  {
1536
- data: pointsData,
1537
- pane: panesReady && mapInstance?.getPane(pointsPaneName) ? pointsPaneName : void 0,
1538
- pointToLayer: (feature, latlng) => L.circleMarker(latlng, {
1539
- radius: isMobile ? 8 : 6,
1540
- ...buildLayerStyle(layerState.mapLayer.layerId, baseOpacity, feature, layerType)
1541
- }),
1542
- onEachFeature: overlayOnEachFeature
1704
+ bbox: zoomToBbox ?? void 0,
1705
+ geojson: zoomToGeojson ?? void 0,
1706
+ autoGeojson: autoZoomGeojson
1543
1707
  }
1544
1708
  ),
1545
- panesReady && mapInstance?.getPane(labelPaneName) ? labelMarkers.filter(
1546
- (marker) => String(marker.layerId) === String(layerState.mapLayer.layerId)
1547
- ).map((marker) => /* @__PURE__ */ jsx(
1548
- Marker,
1709
+ /* @__PURE__ */ jsx3(ZoomBasedOpacityHandler, { onZoomChange: handleZoomChange }),
1710
+ /* @__PURE__ */ jsx3(LocationControl, {}),
1711
+ orderedLayers.map((layerState) => {
1712
+ const baseOpacity = layerState.effective?.baseOpacity ?? layerState.effective?.opacity ?? 1;
1713
+ const fillPaneName = `zenit-layer-${layerState.mapLayer.layerId}-fill`;
1714
+ const pointsPaneName = `zenit-layer-${layerState.mapLayer.layerId}-points`;
1715
+ const layerType = layerState.layer?.layerType ?? layerState.mapLayer.layerType ?? void 0;
1716
+ const labelKey = labelKeyIndex.get(String(layerState.mapLayer.layerId));
1717
+ return /* @__PURE__ */ jsxs3(React3.Fragment, { children: [
1718
+ layerState.data && /* @__PURE__ */ jsx3(
1719
+ LayerGeoJson,
1720
+ {
1721
+ layerId: layerState.mapLayer.layerId,
1722
+ data: layerState.data,
1723
+ baseOpacity,
1724
+ isMobile,
1725
+ panesReady,
1726
+ mapInstance,
1727
+ fillPaneName,
1728
+ pointsPaneName,
1729
+ layerType,
1730
+ styleFn: makeStyleFnForLayer(layerState.mapLayer.layerId),
1731
+ onEachFeature: overlayOnEachFeature,
1732
+ onPolygonLabel: labelKey ? (feature, layer) => {
1733
+ const geometryType = feature?.geometry?.type;
1734
+ if (geometryType !== "Polygon" && geometryType !== "MultiPolygon") return;
1735
+ const properties = feature?.properties;
1736
+ const value = properties?.[labelKey];
1737
+ if (!value || !layer.bindTooltip) return;
1738
+ layer.bindTooltip(String(value), {
1739
+ sticky: true,
1740
+ direction: "center",
1741
+ opacity: 0.9,
1742
+ className: "polygon-label-tooltip"
1743
+ });
1744
+ } : void 0
1745
+ }
1746
+ ),
1747
+ panesReady && mapInstance?.getPane(LABELS_PANE_NAME) ? labelMarkers.filter(
1748
+ (marker) => String(marker.layerId) === String(layerState.mapLayer.layerId)
1749
+ ).map((marker) => /* @__PURE__ */ jsx3(
1750
+ Marker2,
1751
+ {
1752
+ position: marker.position,
1753
+ icon: createCustomIcon(marker.label, marker.opacity, marker.color),
1754
+ interactive: false,
1755
+ pane: LABELS_PANE_NAME
1756
+ },
1757
+ marker.key
1758
+ )) : null
1759
+ ] }, layerState.mapLayer.layerId.toString());
1760
+ }),
1761
+ overlayGeojson && /* @__PURE__ */ jsx3(
1762
+ LayerGeoJson,
1549
1763
  {
1550
- position: marker.position,
1551
- icon: buildLabelIcon(marker.label, marker.opacity, marker.color),
1552
- interactive: false,
1553
- pane: labelPaneName
1554
- },
1555
- marker.key
1556
- )) : null
1557
- ] }, layerState.mapLayer.layerId.toString());
1558
- }),
1559
- overlayGeojson && /* @__PURE__ */ jsx(
1560
- GeoJSON,
1561
- {
1562
- data: overlayGeojson,
1563
- style: overlayStyleFunction,
1564
- onEachFeature: overlayOnEachFeature
1565
- },
1566
- "zenit-overlay-geojson"
1567
- )
1568
- ]
1569
- },
1570
- String(mapId)
1571
- ) }),
1572
- showLayerPanel && decoratedLayers.length > 0 && /* @__PURE__ */ jsxs(
1764
+ layerId: "overlay-geojson",
1765
+ data: overlayGeojson,
1766
+ baseOpacity: 1,
1767
+ isMobile,
1768
+ panesReady,
1769
+ mapInstance,
1770
+ fillPaneName: "zenit-overlay-fill",
1771
+ pointsPaneName: "zenit-overlay-points",
1772
+ styleFn: overlayStyleFn,
1773
+ onEachFeature: overlayOnEachFeature
1774
+ }
1775
+ )
1776
+ ]
1777
+ },
1778
+ String(mapId)
1779
+ )
1780
+ }
1781
+ ),
1782
+ showLayerPanel && decoratedLayers.length > 0 && /* @__PURE__ */ jsxs3(
1573
1783
  "div",
1574
1784
  {
1575
1785
  style: {
@@ -1582,7 +1792,7 @@ var ZenitMap = forwardRef(({
1582
1792
  overflowY: "auto"
1583
1793
  },
1584
1794
  children: [
1585
- overlayGeojson && /* @__PURE__ */ jsxs(
1795
+ overlayGeojson && /* @__PURE__ */ jsxs3(
1586
1796
  "div",
1587
1797
  {
1588
1798
  style: {
@@ -1594,8 +1804,8 @@ var ZenitMap = forwardRef(({
1594
1804
  marginBottom: 12
1595
1805
  },
1596
1806
  children: [
1597
- /* @__PURE__ */ jsx("div", { style: { fontWeight: 600, marginBottom: 4 }, children: "Overlay activo" }),
1598
- /* @__PURE__ */ jsxs("div", { style: { fontSize: 13 }, children: [
1807
+ /* @__PURE__ */ jsx3("div", { style: { fontWeight: 600, marginBottom: 4 }, children: "Overlay activo" }),
1808
+ /* @__PURE__ */ jsxs3("div", { style: { fontSize: 13 }, children: [
1599
1809
  "GeoJSON externo con ",
1600
1810
  (overlayGeojson.features?.length ?? 0).toLocaleString(),
1601
1811
  " elementos."
@@ -1603,14 +1813,14 @@ var ZenitMap = forwardRef(({
1603
1813
  ]
1604
1814
  }
1605
1815
  ),
1606
- /* @__PURE__ */ jsx("div", { style: { fontWeight: 600, marginBottom: 12 }, children: "Capas" }),
1607
- decoratedLayers.map((layerState) => /* @__PURE__ */ jsxs(
1816
+ /* @__PURE__ */ jsx3("div", { style: { fontWeight: 600, marginBottom: 12 }, children: "Capas" }),
1817
+ decoratedLayers.map((layerState) => /* @__PURE__ */ jsxs3(
1608
1818
  "div",
1609
1819
  {
1610
1820
  style: { borderBottom: "1px solid #e5e7eb", paddingBottom: 10, marginBottom: 10 },
1611
1821
  children: [
1612
- /* @__PURE__ */ jsxs("label", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
1613
- /* @__PURE__ */ jsx(
1822
+ /* @__PURE__ */ jsxs3("label", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
1823
+ /* @__PURE__ */ jsx3(
1614
1824
  "input",
1615
1825
  {
1616
1826
  type: "checkbox",
@@ -1621,17 +1831,17 @@ var ZenitMap = forwardRef(({
1621
1831
  }
1622
1832
  }
1623
1833
  ),
1624
- /* @__PURE__ */ jsx("span", { children: layerState.layer?.name ?? `Capa ${layerState.mapLayer.layerId}` })
1834
+ /* @__PURE__ */ jsx3("span", { children: layerState.layer?.name ?? `Capa ${layerState.mapLayer.layerId}` })
1625
1835
  ] }),
1626
- /* @__PURE__ */ jsxs("div", { style: { marginTop: 8 }, children: [
1627
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", marginBottom: 4 }, children: [
1628
- /* @__PURE__ */ jsx("span", { style: { color: "#4a5568" }, children: "Opacidad" }),
1629
- /* @__PURE__ */ jsxs("span", { children: [
1836
+ /* @__PURE__ */ jsxs3("div", { style: { marginTop: 8 }, children: [
1837
+ /* @__PURE__ */ jsxs3("div", { style: { display: "flex", justifyContent: "space-between", marginBottom: 4 }, children: [
1838
+ /* @__PURE__ */ jsx3("span", { style: { color: "#4a5568" }, children: "Opacidad" }),
1839
+ /* @__PURE__ */ jsxs3("span", { children: [
1630
1840
  Math.round((layerState.effective?.opacity ?? 1) * 100),
1631
1841
  "%"
1632
1842
  ] })
1633
1843
  ] }),
1634
- /* @__PURE__ */ jsx(
1844
+ /* @__PURE__ */ jsx3(
1635
1845
  "input",
1636
1846
  {
1637
1847
  type: "range",
@@ -1661,13 +1871,13 @@ var ZenitMap = forwardRef(({
1661
1871
  ZenitMap.displayName = "ZenitMap";
1662
1872
 
1663
1873
  // src/react/ZenitLayerManager.tsx
1664
- import React2, { useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
1874
+ import React4, { useEffect as useEffect5, useMemo as useMemo3, useRef as useRef5, useState as useState3 } from "react";
1665
1875
 
1666
1876
  // src/react/icons.tsx
1667
1877
  import { Eye, EyeOff, ChevronLeft, ChevronRight, Layers, Upload, X, ZoomIn } from "lucide-react";
1668
1878
 
1669
1879
  // src/react/ZenitLayerManager.tsx
1670
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
1880
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1671
1881
  var FLOAT_TOLERANCE = 1e-3;
1672
1882
  function areEffectiveStatesEqual(a, b) {
1673
1883
  if (a.length !== b.length) return false;
@@ -1707,15 +1917,15 @@ var ZenitLayerManager = ({
1707
1917
  layerFeatureCounts,
1708
1918
  mapLayers
1709
1919
  }) => {
1710
- const [map, setMap] = useState2(null);
1711
- const [loadingMap, setLoadingMap] = useState2(false);
1712
- const [mapError, setMapError] = useState2(null);
1713
- const [layers, setLayers] = useState2([]);
1714
- const [activeTab, setActiveTab] = useState2("layers");
1715
- const [panelVisible, setPanelVisible] = useState2(true);
1716
- const lastEmittedStatesRef = useRef2(null);
1920
+ const [map, setMap] = useState3(null);
1921
+ const [loadingMap, setLoadingMap] = useState3(false);
1922
+ const [mapError, setMapError] = useState3(null);
1923
+ const [layers, setLayers] = useState3([]);
1924
+ const [activeTab, setActiveTab] = useState3("layers");
1925
+ const [panelVisible, setPanelVisible] = useState3(true);
1926
+ const lastEmittedStatesRef = useRef5(null);
1717
1927
  const isControlled = Array.isArray(layerStates) && typeof onLayerStatesChange === "function";
1718
- const baseStates = useMemo2(
1928
+ const baseStates = useMemo3(
1719
1929
  () => initLayerStates(
1720
1930
  layers.map((entry) => ({
1721
1931
  ...entry.mapLayer,
@@ -1726,7 +1936,7 @@ var ZenitLayerManager = ({
1726
1936
  ),
1727
1937
  [layers]
1728
1938
  );
1729
- const overrideStates = useMemo2(
1939
+ const overrideStates = useMemo3(
1730
1940
  () => layers.map(
1731
1941
  (entry) => ({
1732
1942
  layerId: entry.mapLayer.layerId,
@@ -1736,11 +1946,11 @@ var ZenitLayerManager = ({
1736
1946
  ),
1737
1947
  [layers]
1738
1948
  );
1739
- const effectiveStates = useMemo2(
1949
+ const effectiveStates = useMemo3(
1740
1950
  () => layerStates ?? applyLayerOverrides(baseStates, overrideStates),
1741
1951
  [baseStates, layerStates, overrideStates]
1742
1952
  );
1743
- const layerMetaIndex = useMemo2(() => {
1953
+ const layerMetaIndex = useMemo3(() => {
1744
1954
  const index = /* @__PURE__ */ new Map();
1745
1955
  mapLayers?.forEach((entry) => {
1746
1956
  const key = String(entry.layerId);
@@ -1754,7 +1964,7 @@ var ZenitLayerManager = ({
1754
1964
  });
1755
1965
  return index;
1756
1966
  }, [map, mapLayers]);
1757
- const resolveUserOpacity = React2.useCallback((state) => {
1967
+ const resolveUserOpacity = React4.useCallback((state) => {
1758
1968
  if (typeof state.overrideOpacity === "number") return state.overrideOpacity;
1759
1969
  if (typeof state.overrideOpacity === "string") {
1760
1970
  const parsed = Number.parseFloat(state.overrideOpacity);
@@ -1762,7 +1972,7 @@ var ZenitLayerManager = ({
1762
1972
  }
1763
1973
  return state.opacity ?? 1;
1764
1974
  }, []);
1765
- const resolveEffectiveOpacity = React2.useCallback(
1975
+ const resolveEffectiveOpacity = React4.useCallback(
1766
1976
  (layerId, userOpacity) => {
1767
1977
  if (!autoOpacityOnZoom || typeof mapZoom !== "number") {
1768
1978
  return userOpacity;
@@ -1778,7 +1988,7 @@ var ZenitLayerManager = ({
1778
1988
  },
1779
1989
  [autoOpacityConfig, autoOpacityOnZoom, layerMetaIndex, mapZoom]
1780
1990
  );
1781
- const effectiveStatesWithZoom = useMemo2(() => {
1991
+ const effectiveStatesWithZoom = useMemo3(() => {
1782
1992
  if (!autoOpacityOnZoom || typeof mapZoom !== "number") {
1783
1993
  return effectiveStates;
1784
1994
  }
@@ -1792,7 +2002,7 @@ var ZenitLayerManager = ({
1792
2002
  };
1793
2003
  });
1794
2004
  }, [autoOpacityOnZoom, effectiveStates, mapZoom, resolveEffectiveOpacity, resolveUserOpacity]);
1795
- useEffect2(() => {
2005
+ useEffect5(() => {
1796
2006
  let cancelled = false;
1797
2007
  setLoadingMap(true);
1798
2008
  setMapError(null);
@@ -1824,12 +2034,12 @@ var ZenitLayerManager = ({
1824
2034
  cancelled = true;
1825
2035
  };
1826
2036
  }, [client.maps, mapId]);
1827
- useEffect2(() => {
2037
+ useEffect5(() => {
1828
2038
  if (!showUploadTab && activeTab === "upload") {
1829
2039
  setActiveTab("layers");
1830
2040
  }
1831
2041
  }, [activeTab, showUploadTab]);
1832
- useEffect2(() => {
2042
+ useEffect5(() => {
1833
2043
  if (isControlled) return;
1834
2044
  if (!onLayerStatesChange) return;
1835
2045
  const emitStates = autoOpacityOnZoom && typeof mapZoom === "number" ? effectiveStatesWithZoom : effectiveStates;
@@ -1847,7 +2057,7 @@ var ZenitLayerManager = ({
1847
2057
  mapZoom,
1848
2058
  onLayerStatesChange
1849
2059
  ]);
1850
- const updateLayerVisible = React2.useCallback(
2060
+ const updateLayerVisible = React4.useCallback(
1851
2061
  (layerId, visible) => {
1852
2062
  if (!onLayerStatesChange) return;
1853
2063
  const next = effectiveStates.map(
@@ -1857,7 +2067,7 @@ var ZenitLayerManager = ({
1857
2067
  },
1858
2068
  [effectiveStates, onLayerStatesChange]
1859
2069
  );
1860
- const updateLayerOpacity = React2.useCallback(
2070
+ const updateLayerOpacity = React4.useCallback(
1861
2071
  (layerId, opacity) => {
1862
2072
  if (!onLayerStatesChange) return;
1863
2073
  const adjustedOpacity = resolveEffectiveOpacity(layerId, opacity);
@@ -1868,7 +2078,7 @@ var ZenitLayerManager = ({
1868
2078
  },
1869
2079
  [effectiveStates, onLayerStatesChange, resolveEffectiveOpacity]
1870
2080
  );
1871
- const resolveFeatureCount = React2.useCallback(
2081
+ const resolveFeatureCount = React4.useCallback(
1872
2082
  (layerId, layer) => {
1873
2083
  const resolvedFeatureCount = layerFeatureCounts?.[layerId] ?? layerFeatureCounts?.[String(layerId)];
1874
2084
  if (typeof resolvedFeatureCount === "number") return resolvedFeatureCount;
@@ -1877,7 +2087,7 @@ var ZenitLayerManager = ({
1877
2087
  },
1878
2088
  [layerFeatureCounts]
1879
2089
  );
1880
- const decoratedLayers = useMemo2(() => {
2090
+ const decoratedLayers = useMemo3(() => {
1881
2091
  return layers.map((entry) => ({
1882
2092
  ...entry,
1883
2093
  effective: effectiveStates.find((state) => state.layerId === entry.mapLayer.layerId),
@@ -1906,7 +2116,7 @@ var ZenitLayerManager = ({
1906
2116
  return String(a.mapLayer.layerId).localeCompare(String(b.mapLayer.layerId));
1907
2117
  });
1908
2118
  }, [effectiveStates, layers, resolveFeatureCount]);
1909
- const resolveLayerStyle = React2.useCallback(
2119
+ const resolveLayerStyle = React4.useCallback(
1910
2120
  (layerId) => {
1911
2121
  const layerKey = String(layerId);
1912
2122
  const fromProp = mapLayers?.find((entry) => String(entry.layerId) === layerKey)?.style;
@@ -1936,10 +2146,10 @@ var ZenitLayerManager = ({
1936
2146
  ...height ? { height } : {}
1937
2147
  };
1938
2148
  if (loadingMap) {
1939
- return /* @__PURE__ */ jsx2("div", { className, style: panelStyle, children: "Cargando capas\u2026" });
2149
+ return /* @__PURE__ */ jsx4("div", { className, style: panelStyle, children: "Cargando capas\u2026" });
1940
2150
  }
1941
2151
  if (mapError) {
1942
- return /* @__PURE__ */ jsxs2("div", { className, style: { ...panelStyle, color: "#c53030" }, children: [
2152
+ return /* @__PURE__ */ jsxs4("div", { className, style: { ...panelStyle, color: "#c53030" }, children: [
1943
2153
  "Error al cargar mapa: ",
1944
2154
  mapError
1945
2155
  ] });
@@ -1957,7 +2167,7 @@ var ZenitLayerManager = ({
1957
2167
  boxShadow: "0 1px 0 rgba(148, 163, 184, 0.25)"
1958
2168
  };
1959
2169
  const renderLayerCards = () => {
1960
- return /* @__PURE__ */ jsx2("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: decoratedLayers.map((layerState) => {
2170
+ return /* @__PURE__ */ jsx4("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: decoratedLayers.map((layerState) => {
1961
2171
  const layerId = layerState.mapLayer.layerId;
1962
2172
  const layerName = layerState.layerName ?? `Capa ${layerId}`;
1963
2173
  const visible = layerState.effective?.visible ?? false;
@@ -1967,7 +2177,7 @@ var ZenitLayerManager = ({
1967
2177
  const muted = !visible;
1968
2178
  const opacityPercent = Math.round(userOpacity * 100);
1969
2179
  const sliderBackground = `linear-gradient(to right, ${layerColor} 0%, ${layerColor} ${opacityPercent}%, #e5e7eb ${opacityPercent}%, #e5e7eb 100%)`;
1970
- return /* @__PURE__ */ jsxs2(
2180
+ return /* @__PURE__ */ jsxs4(
1971
2181
  "div",
1972
2182
  {
1973
2183
  className: `zlm-card${muted ? " is-muted" : ""}`,
@@ -1982,9 +2192,9 @@ var ZenitLayerManager = ({
1982
2192
  width: "100%"
1983
2193
  },
1984
2194
  children: [
1985
- /* @__PURE__ */ jsxs2("div", { style: { display: "flex", justifyContent: "space-between", gap: 12 }, children: [
1986
- /* @__PURE__ */ jsxs2("div", { style: { display: "flex", gap: 10, alignItems: "flex-start", minWidth: 0, flex: 1 }, children: [
1987
- /* @__PURE__ */ jsx2(
2195
+ /* @__PURE__ */ jsxs4("div", { style: { display: "flex", justifyContent: "space-between", gap: 12 }, children: [
2196
+ /* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: 10, alignItems: "flex-start", minWidth: 0, flex: 1 }, children: [
2197
+ /* @__PURE__ */ jsx4(
1988
2198
  "div",
1989
2199
  {
1990
2200
  style: {
@@ -1999,7 +2209,7 @@ var ZenitLayerManager = ({
1999
2209
  title: "Color de la capa"
2000
2210
  }
2001
2211
  ),
2002
- showLayerVisibilityIcon && /* @__PURE__ */ jsx2(
2212
+ showLayerVisibilityIcon && /* @__PURE__ */ jsx4(
2003
2213
  "button",
2004
2214
  {
2005
2215
  type: "button",
@@ -2010,11 +2220,11 @@ var ZenitLayerManager = ({
2010
2220
  )
2011
2221
  ),
2012
2222
  "aria-label": visible ? "Ocultar capa" : "Mostrar capa",
2013
- children: visible ? /* @__PURE__ */ jsx2(Eye, { size: 16 }) : /* @__PURE__ */ jsx2(EyeOff, { size: 16 })
2223
+ children: visible ? /* @__PURE__ */ jsx4(Eye, { size: 16 }) : /* @__PURE__ */ jsx4(EyeOff, { size: 16 })
2014
2224
  }
2015
2225
  ),
2016
- /* @__PURE__ */ jsxs2("div", { style: { minWidth: 0, flex: 1 }, children: [
2017
- /* @__PURE__ */ jsx2(
2226
+ /* @__PURE__ */ jsxs4("div", { style: { minWidth: 0, flex: 1 }, children: [
2227
+ /* @__PURE__ */ jsx4(
2018
2228
  "div",
2019
2229
  {
2020
2230
  className: "zlm-layer-name",
@@ -2032,26 +2242,26 @@ var ZenitLayerManager = ({
2032
2242
  children: layerName
2033
2243
  }
2034
2244
  ),
2035
- /* @__PURE__ */ jsxs2("div", { style: { color: muted ? "#94a3b8" : "#64748b", fontSize: 12 }, children: [
2245
+ /* @__PURE__ */ jsxs4("div", { style: { color: muted ? "#94a3b8" : "#64748b", fontSize: 12 }, children: [
2036
2246
  "ID ",
2037
2247
  layerId
2038
2248
  ] })
2039
2249
  ] })
2040
2250
  ] }),
2041
- /* @__PURE__ */ jsx2("div", { style: { display: "flex", alignItems: "flex-start", gap: 6, flexShrink: 0 }, children: typeof featureCount === "number" && /* @__PURE__ */ jsxs2("span", { className: "zlm-badge", children: [
2251
+ /* @__PURE__ */ jsx4("div", { style: { display: "flex", alignItems: "flex-start", gap: 6, flexShrink: 0 }, children: typeof featureCount === "number" && /* @__PURE__ */ jsxs4("span", { className: "zlm-badge", children: [
2042
2252
  featureCount.toLocaleString(),
2043
2253
  " features"
2044
2254
  ] }) })
2045
2255
  ] }),
2046
- /* @__PURE__ */ jsx2("div", { style: { display: "flex", gap: 10, alignItems: "center" }, children: /* @__PURE__ */ jsxs2("div", { style: { flex: 1 }, children: [
2047
- /* @__PURE__ */ jsxs2("div", { style: { display: "flex", justifyContent: "space-between", marginBottom: 6, color: "#64748b", fontSize: 12 }, children: [
2048
- /* @__PURE__ */ jsx2("span", { children: "Opacidad" }),
2049
- /* @__PURE__ */ jsxs2("span", { children: [
2256
+ /* @__PURE__ */ jsx4("div", { style: { display: "flex", gap: 10, alignItems: "center" }, children: /* @__PURE__ */ jsxs4("div", { style: { flex: 1 }, children: [
2257
+ /* @__PURE__ */ jsxs4("div", { style: { display: "flex", justifyContent: "space-between", marginBottom: 6, color: "#64748b", fontSize: 12 }, children: [
2258
+ /* @__PURE__ */ jsx4("span", { children: "Opacidad" }),
2259
+ /* @__PURE__ */ jsxs4("span", { children: [
2050
2260
  opacityPercent,
2051
2261
  "%"
2052
2262
  ] })
2053
2263
  ] }),
2054
- /* @__PURE__ */ jsx2(
2264
+ /* @__PURE__ */ jsx4(
2055
2265
  "input",
2056
2266
  {
2057
2267
  className: "zlm-range",
@@ -2089,8 +2299,8 @@ var ZenitLayerManager = ({
2089
2299
  );
2090
2300
  }) });
2091
2301
  };
2092
- return /* @__PURE__ */ jsxs2("div", { className: ["zenit-layer-manager", className].filter(Boolean).join(" "), style: panelStyle, children: [
2093
- /* @__PURE__ */ jsx2("style", { children: `
2302
+ return /* @__PURE__ */ jsxs4("div", { className: ["zenit-layer-manager", className].filter(Boolean).join(" "), style: panelStyle, children: [
2303
+ /* @__PURE__ */ jsx4("style", { children: `
2094
2304
  .zenit-layer-manager .zlm-card {
2095
2305
  transition: box-shadow 0.2s ease, transform 0.2s ease, opacity 0.2s ease;
2096
2306
  box-shadow: 0 6px 16px rgba(15, 23, 42, 0.08);
@@ -2185,16 +2395,16 @@ var ZenitLayerManager = ({
2185
2395
  outline-offset: 2px;
2186
2396
  }
2187
2397
  ` }),
2188
- /* @__PURE__ */ jsxs2("div", { style: headerStyle, children: [
2189
- /* @__PURE__ */ jsxs2("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
2190
- /* @__PURE__ */ jsxs2("div", { children: [
2191
- /* @__PURE__ */ jsx2("div", { style: { fontWeight: 800, fontSize: 16, color: "#0f172a" }, children: "Gesti\xF3n de Capas" }),
2192
- /* @__PURE__ */ jsxs2("div", { style: { color: "#64748b", fontSize: 12 }, children: [
2398
+ /* @__PURE__ */ jsxs4("div", { style: headerStyle, children: [
2399
+ /* @__PURE__ */ jsxs4("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
2400
+ /* @__PURE__ */ jsxs4("div", { children: [
2401
+ /* @__PURE__ */ jsx4("div", { style: { fontWeight: 800, fontSize: 16, color: "#0f172a" }, children: "Gesti\xF3n de Capas" }),
2402
+ /* @__PURE__ */ jsxs4("div", { style: { color: "#64748b", fontSize: 12 }, children: [
2193
2403
  "Mapa #",
2194
2404
  map.id
2195
2405
  ] })
2196
2406
  ] }),
2197
- /* @__PURE__ */ jsxs2(
2407
+ /* @__PURE__ */ jsxs4(
2198
2408
  "button",
2199
2409
  {
2200
2410
  type: "button",
@@ -2202,13 +2412,13 @@ var ZenitLayerManager = ({
2202
2412
  className: "zlm-panel-toggle",
2203
2413
  "aria-label": panelVisible ? "Ocultar panel de capas" : "Mostrar panel de capas",
2204
2414
  children: [
2205
- panelVisible ? /* @__PURE__ */ jsx2(Eye, { size: 16 }) : /* @__PURE__ */ jsx2(EyeOff, { size: 16 }),
2415
+ panelVisible ? /* @__PURE__ */ jsx4(Eye, { size: 16 }) : /* @__PURE__ */ jsx4(EyeOff, { size: 16 }),
2206
2416
  panelVisible ? "Ocultar" : "Mostrar"
2207
2417
  ]
2208
2418
  }
2209
2419
  )
2210
2420
  ] }),
2211
- /* @__PURE__ */ jsxs2(
2421
+ /* @__PURE__ */ jsxs4(
2212
2422
  "div",
2213
2423
  {
2214
2424
  style: {
@@ -2221,26 +2431,26 @@ var ZenitLayerManager = ({
2221
2431
  background: "#f1f5f9"
2222
2432
  },
2223
2433
  children: [
2224
- /* @__PURE__ */ jsxs2(
2434
+ /* @__PURE__ */ jsxs4(
2225
2435
  "button",
2226
2436
  {
2227
2437
  type: "button",
2228
2438
  className: `zlm-tab${activeTab === "layers" ? " is-active" : ""}`,
2229
2439
  onClick: () => setActiveTab("layers"),
2230
2440
  children: [
2231
- /* @__PURE__ */ jsx2(Layers, { size: 16 }),
2441
+ /* @__PURE__ */ jsx4(Layers, { size: 16 }),
2232
2442
  "Capas"
2233
2443
  ]
2234
2444
  }
2235
2445
  ),
2236
- showUploadTab && /* @__PURE__ */ jsxs2(
2446
+ showUploadTab && /* @__PURE__ */ jsxs4(
2237
2447
  "button",
2238
2448
  {
2239
2449
  type: "button",
2240
2450
  className: `zlm-tab${activeTab === "upload" ? " is-active" : ""}`,
2241
2451
  onClick: () => setActiveTab("upload"),
2242
2452
  children: [
2243
- /* @__PURE__ */ jsx2(Upload, { size: 16 }),
2453
+ /* @__PURE__ */ jsx4(Upload, { size: 16 }),
2244
2454
  "Subir"
2245
2455
  ]
2246
2456
  }
@@ -2249,15 +2459,15 @@ var ZenitLayerManager = ({
2249
2459
  }
2250
2460
  )
2251
2461
  ] }),
2252
- panelVisible && /* @__PURE__ */ jsxs2("div", { style: { padding: "12px 10px 18px", overflowY: "auto", flex: 1, minHeight: 0 }, children: [
2462
+ panelVisible && /* @__PURE__ */ jsxs4("div", { style: { padding: "12px 10px 18px", overflowY: "auto", flex: 1, minHeight: 0 }, children: [
2253
2463
  activeTab === "layers" && renderLayerCards(),
2254
- showUploadTab && activeTab === "upload" && /* @__PURE__ */ jsx2("div", { style: { color: "#475569", fontSize: 13 }, children: "Pr\xF3ximamente podr\xE1s subir capas desde este panel." })
2464
+ showUploadTab && activeTab === "upload" && /* @__PURE__ */ jsx4("div", { style: { color: "#475569", fontSize: 13 }, children: "Pr\xF3ximamente podr\xE1s subir capas desde este panel." })
2255
2465
  ] })
2256
2466
  ] });
2257
2467
  };
2258
2468
 
2259
2469
  // src/react/ZenitFeatureFilterPanel.tsx
2260
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
2470
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
2261
2471
  var ZenitFeatureFilterPanel = ({
2262
2472
  title = "Filtros",
2263
2473
  description,
@@ -2265,7 +2475,7 @@ var ZenitFeatureFilterPanel = ({
2265
2475
  style,
2266
2476
  children
2267
2477
  }) => {
2268
- return /* @__PURE__ */ jsxs3(
2478
+ return /* @__PURE__ */ jsxs5(
2269
2479
  "section",
2270
2480
  {
2271
2481
  className,
@@ -2278,22 +2488,22 @@ var ZenitFeatureFilterPanel = ({
2278
2488
  ...style
2279
2489
  },
2280
2490
  children: [
2281
- /* @__PURE__ */ jsxs3("header", { style: { marginBottom: 12 }, children: [
2282
- /* @__PURE__ */ jsx3("h3", { style: { margin: 0, fontSize: 16 }, children: title }),
2283
- description && /* @__PURE__ */ jsx3("p", { style: { margin: "6px 0 0", color: "#475569", fontSize: 13 }, children: description })
2491
+ /* @__PURE__ */ jsxs5("header", { style: { marginBottom: 12 }, children: [
2492
+ /* @__PURE__ */ jsx5("h3", { style: { margin: 0, fontSize: 16 }, children: title }),
2493
+ description && /* @__PURE__ */ jsx5("p", { style: { margin: "6px 0 0", color: "#475569", fontSize: 13 }, children: description })
2284
2494
  ] }),
2285
- /* @__PURE__ */ jsx3("div", { children })
2495
+ /* @__PURE__ */ jsx5("div", { children })
2286
2496
  ]
2287
2497
  }
2288
2498
  );
2289
2499
  };
2290
2500
 
2291
2501
  // src/react/ai/FloatingChatBox.tsx
2292
- import { useCallback as useCallback3, useEffect as useEffect3, useMemo as useMemo3, useRef as useRef4, useState as useState4 } from "react";
2293
- import { createPortal } from "react-dom";
2502
+ import { useCallback as useCallback4, useEffect as useEffect6, useMemo as useMemo4, useRef as useRef7, useState as useState5 } from "react";
2503
+ import { createPortal as createPortal2 } from "react-dom";
2294
2504
 
2295
2505
  // src/react/hooks/use-chat.ts
2296
- import { useCallback as useCallback2, useRef as useRef3, useState as useState3 } from "react";
2506
+ import { useCallback as useCallback3, useRef as useRef6, useState as useState4 } from "react";
2297
2507
 
2298
2508
  // src/ai/chat.service.ts
2299
2509
  var DEFAULT_ERROR_MESSAGE = "No fue posible completar la solicitud al asistente.";
@@ -2429,9 +2639,9 @@ var createChatService = (config) => ({
2429
2639
 
2430
2640
  // src/react/hooks/use-chat.ts
2431
2641
  var useSendMessage = (config) => {
2432
- const [isLoading, setIsLoading] = useState3(false);
2433
- const [error, setError] = useState3(null);
2434
- const send = useCallback2(
2642
+ const [isLoading, setIsLoading] = useState4(false);
2643
+ const [error, setError] = useState4(null);
2644
+ const send = useCallback3(
2435
2645
  async (mapId, request, options) => {
2436
2646
  setIsLoading(true);
2437
2647
  setError(null);
@@ -2449,18 +2659,18 @@ var useSendMessage = (config) => {
2449
2659
  return { sendMessage: send, isLoading, error };
2450
2660
  };
2451
2661
  var useSendMessageStream = (config) => {
2452
- const [isStreaming, setIsStreaming] = useState3(false);
2453
- const [streamingText, setStreamingText] = useState3("");
2454
- const [completeResponse, setCompleteResponse] = useState3(null);
2455
- const [error, setError] = useState3(null);
2456
- const requestIdRef = useRef3(0);
2457
- const reset = useCallback2(() => {
2662
+ const [isStreaming, setIsStreaming] = useState4(false);
2663
+ const [streamingText, setStreamingText] = useState4("");
2664
+ const [completeResponse, setCompleteResponse] = useState4(null);
2665
+ const [error, setError] = useState4(null);
2666
+ const requestIdRef = useRef6(0);
2667
+ const reset = useCallback3(() => {
2458
2668
  setIsStreaming(false);
2459
2669
  setStreamingText("");
2460
2670
  setCompleteResponse(null);
2461
2671
  setError(null);
2462
2672
  }, []);
2463
- const send = useCallback2(
2673
+ const send = useCallback3(
2464
2674
  async (mapId, request, options) => {
2465
2675
  const requestId = requestIdRef.current + 1;
2466
2676
  requestIdRef.current = requestId;
@@ -2514,7 +2724,7 @@ var useSendMessageStream = (config) => {
2514
2724
  // src/react/components/MarkdownRenderer.tsx
2515
2725
  import ReactMarkdown from "react-markdown";
2516
2726
  import remarkGfm from "remark-gfm";
2517
- import { jsx as jsx4 } from "react/jsx-runtime";
2727
+ import { jsx as jsx6 } from "react/jsx-runtime";
2518
2728
  function normalizeAssistantMarkdown(text) {
2519
2729
  if (!text || typeof text !== "string") return "";
2520
2730
  let normalized = text;
@@ -2530,28 +2740,28 @@ var MarkdownRenderer = ({ content, className }) => {
2530
2740
  if (!normalizedContent) {
2531
2741
  return null;
2532
2742
  }
2533
- return /* @__PURE__ */ jsx4("div", { className, style: { wordBreak: "break-word" }, children: /* @__PURE__ */ jsx4(
2743
+ return /* @__PURE__ */ jsx6("div", { className, style: { wordBreak: "break-word" }, children: /* @__PURE__ */ jsx6(
2534
2744
  ReactMarkdown,
2535
2745
  {
2536
2746
  remarkPlugins: [remarkGfm],
2537
2747
  components: {
2538
2748
  // Headings with proper spacing
2539
- h1: ({ children, ...props }) => /* @__PURE__ */ jsx4("h1", { style: { fontSize: "1.5em", fontWeight: 700, marginTop: "1em", marginBottom: "0.5em" }, ...props, children }),
2540
- h2: ({ children, ...props }) => /* @__PURE__ */ jsx4("h2", { style: { fontSize: "1.3em", fontWeight: 700, marginTop: "0.9em", marginBottom: "0.45em" }, ...props, children }),
2541
- h3: ({ children, ...props }) => /* @__PURE__ */ jsx4("h3", { style: { fontSize: "1.15em", fontWeight: 600, marginTop: "0.75em", marginBottom: "0.4em" }, ...props, children }),
2542
- h4: ({ children, ...props }) => /* @__PURE__ */ jsx4("h4", { style: { fontSize: "1.05em", fontWeight: 600, marginTop: "0.6em", marginBottom: "0.35em" }, ...props, children }),
2543
- h5: ({ children, ...props }) => /* @__PURE__ */ jsx4("h5", { style: { fontSize: "1em", fontWeight: 600, marginTop: "0.5em", marginBottom: "0.3em" }, ...props, children }),
2544
- h6: ({ children, ...props }) => /* @__PURE__ */ jsx4("h6", { style: { fontSize: "0.95em", fontWeight: 600, marginTop: "0.5em", marginBottom: "0.3em" }, ...props, children }),
2749
+ h1: ({ children, ...props }) => /* @__PURE__ */ jsx6("h1", { style: { fontSize: "1.5em", fontWeight: 700, marginTop: "1em", marginBottom: "0.5em" }, ...props, children }),
2750
+ h2: ({ children, ...props }) => /* @__PURE__ */ jsx6("h2", { style: { fontSize: "1.3em", fontWeight: 700, marginTop: "0.9em", marginBottom: "0.45em" }, ...props, children }),
2751
+ h3: ({ children, ...props }) => /* @__PURE__ */ jsx6("h3", { style: { fontSize: "1.15em", fontWeight: 600, marginTop: "0.75em", marginBottom: "0.4em" }, ...props, children }),
2752
+ h4: ({ children, ...props }) => /* @__PURE__ */ jsx6("h4", { style: { fontSize: "1.05em", fontWeight: 600, marginTop: "0.6em", marginBottom: "0.35em" }, ...props, children }),
2753
+ h5: ({ children, ...props }) => /* @__PURE__ */ jsx6("h5", { style: { fontSize: "1em", fontWeight: 600, marginTop: "0.5em", marginBottom: "0.3em" }, ...props, children }),
2754
+ h6: ({ children, ...props }) => /* @__PURE__ */ jsx6("h6", { style: { fontSize: "0.95em", fontWeight: 600, marginTop: "0.5em", marginBottom: "0.3em" }, ...props, children }),
2545
2755
  // Paragraphs with comfortable line height
2546
- p: ({ children, ...props }) => /* @__PURE__ */ jsx4("p", { style: { marginTop: "0.5em", marginBottom: "0.5em", lineHeight: 1.6 }, ...props, children }),
2756
+ p: ({ children, ...props }) => /* @__PURE__ */ jsx6("p", { style: { marginTop: "0.5em", marginBottom: "0.5em", lineHeight: 1.6 }, ...props, children }),
2547
2757
  // Lists with proper indentation
2548
- ul: ({ children, ...props }) => /* @__PURE__ */ jsx4("ul", { style: { paddingLeft: "1.5em", marginTop: "0.5em", marginBottom: "0.5em" }, ...props, children }),
2549
- ol: ({ children, ...props }) => /* @__PURE__ */ jsx4("ol", { style: { paddingLeft: "1.5em", marginTop: "0.5em", marginBottom: "0.5em" }, ...props, children }),
2550
- li: ({ children, ...props }) => /* @__PURE__ */ jsx4("li", { style: { marginTop: "0.25em", marginBottom: "0.25em" }, ...props, children }),
2758
+ ul: ({ children, ...props }) => /* @__PURE__ */ jsx6("ul", { style: { paddingLeft: "1.5em", marginTop: "0.5em", marginBottom: "0.5em" }, ...props, children }),
2759
+ ol: ({ children, ...props }) => /* @__PURE__ */ jsx6("ol", { style: { paddingLeft: "1.5em", marginTop: "0.5em", marginBottom: "0.5em" }, ...props, children }),
2760
+ li: ({ children, ...props }) => /* @__PURE__ */ jsx6("li", { style: { marginTop: "0.25em", marginBottom: "0.25em" }, ...props, children }),
2551
2761
  // Code blocks
2552
2762
  code: ({ inline, children, ...props }) => {
2553
2763
  if (inline) {
2554
- return /* @__PURE__ */ jsx4(
2764
+ return /* @__PURE__ */ jsx6(
2555
2765
  "code",
2556
2766
  {
2557
2767
  style: {
@@ -2566,7 +2776,7 @@ var MarkdownRenderer = ({ content, className }) => {
2566
2776
  }
2567
2777
  );
2568
2778
  }
2569
- return /* @__PURE__ */ jsx4(
2779
+ return /* @__PURE__ */ jsx6(
2570
2780
  "code",
2571
2781
  {
2572
2782
  style: {
@@ -2586,9 +2796,9 @@ var MarkdownRenderer = ({ content, className }) => {
2586
2796
  );
2587
2797
  },
2588
2798
  // Pre (code block wrapper)
2589
- pre: ({ children, ...props }) => /* @__PURE__ */ jsx4("pre", { style: { margin: 0 }, ...props, children }),
2799
+ pre: ({ children, ...props }) => /* @__PURE__ */ jsx6("pre", { style: { margin: 0 }, ...props, children }),
2590
2800
  // Blockquotes
2591
- blockquote: ({ children, ...props }) => /* @__PURE__ */ jsx4(
2801
+ blockquote: ({ children, ...props }) => /* @__PURE__ */ jsx6(
2592
2802
  "blockquote",
2593
2803
  {
2594
2804
  style: {
@@ -2604,11 +2814,11 @@ var MarkdownRenderer = ({ content, className }) => {
2604
2814
  }
2605
2815
  ),
2606
2816
  // Strong/bold
2607
- strong: ({ children, ...props }) => /* @__PURE__ */ jsx4("strong", { style: { fontWeight: 600 }, ...props, children }),
2817
+ strong: ({ children, ...props }) => /* @__PURE__ */ jsx6("strong", { style: { fontWeight: 600 }, ...props, children }),
2608
2818
  // Emphasis/italic
2609
- em: ({ children, ...props }) => /* @__PURE__ */ jsx4("em", { style: { fontStyle: "italic" }, ...props, children }),
2819
+ em: ({ children, ...props }) => /* @__PURE__ */ jsx6("em", { style: { fontStyle: "italic" }, ...props, children }),
2610
2820
  // Horizontal rule
2611
- hr: (props) => /* @__PURE__ */ jsx4(
2821
+ hr: (props) => /* @__PURE__ */ jsx6(
2612
2822
  "hr",
2613
2823
  {
2614
2824
  style: {
@@ -2621,7 +2831,7 @@ var MarkdownRenderer = ({ content, className }) => {
2621
2831
  }
2622
2832
  ),
2623
2833
  // Tables (GFM)
2624
- table: ({ children, ...props }) => /* @__PURE__ */ jsx4("div", { style: { overflowX: "auto", marginTop: "0.5em", marginBottom: "0.5em" }, children: /* @__PURE__ */ jsx4(
2834
+ table: ({ children, ...props }) => /* @__PURE__ */ jsx6("div", { style: { overflowX: "auto", marginTop: "0.5em", marginBottom: "0.5em" }, children: /* @__PURE__ */ jsx6(
2625
2835
  "table",
2626
2836
  {
2627
2837
  style: {
@@ -2633,7 +2843,7 @@ var MarkdownRenderer = ({ content, className }) => {
2633
2843
  children
2634
2844
  }
2635
2845
  ) }),
2636
- th: ({ children, ...props }) => /* @__PURE__ */ jsx4(
2846
+ th: ({ children, ...props }) => /* @__PURE__ */ jsx6(
2637
2847
  "th",
2638
2848
  {
2639
2849
  style: {
@@ -2647,7 +2857,7 @@ var MarkdownRenderer = ({ content, className }) => {
2647
2857
  children
2648
2858
  }
2649
2859
  ),
2650
- td: ({ children, ...props }) => /* @__PURE__ */ jsx4(
2860
+ td: ({ children, ...props }) => /* @__PURE__ */ jsx6(
2651
2861
  "td",
2652
2862
  {
2653
2863
  style: {
@@ -2665,32 +2875,32 @@ var MarkdownRenderer = ({ content, className }) => {
2665
2875
  };
2666
2876
 
2667
2877
  // src/react/ai/FloatingChatBox.tsx
2668
- import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
2669
- var ChatIcon = () => /* @__PURE__ */ jsx5("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx5("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) });
2670
- var CloseIcon = () => /* @__PURE__ */ jsxs4("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2671
- /* @__PURE__ */ jsx5("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
2672
- /* @__PURE__ */ jsx5("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
2878
+ import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
2879
+ var ChatIcon = () => /* @__PURE__ */ jsx7("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx7("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) });
2880
+ var CloseIcon = () => /* @__PURE__ */ jsxs6("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2881
+ /* @__PURE__ */ jsx7("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
2882
+ /* @__PURE__ */ jsx7("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
2673
2883
  ] });
2674
- var ExpandIcon = () => /* @__PURE__ */ jsxs4("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2675
- /* @__PURE__ */ jsx5("polyline", { points: "15 3 21 3 21 9" }),
2676
- /* @__PURE__ */ jsx5("polyline", { points: "9 21 3 21 3 15" }),
2677
- /* @__PURE__ */ jsx5("line", { x1: "21", y1: "3", x2: "14", y2: "10" }),
2678
- /* @__PURE__ */ jsx5("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
2884
+ var ExpandIcon = () => /* @__PURE__ */ jsxs6("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2885
+ /* @__PURE__ */ jsx7("polyline", { points: "15 3 21 3 21 9" }),
2886
+ /* @__PURE__ */ jsx7("polyline", { points: "9 21 3 21 3 15" }),
2887
+ /* @__PURE__ */ jsx7("line", { x1: "21", y1: "3", x2: "14", y2: "10" }),
2888
+ /* @__PURE__ */ jsx7("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
2679
2889
  ] });
2680
- var CollapseIcon = () => /* @__PURE__ */ jsxs4("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2681
- /* @__PURE__ */ jsx5("polyline", { points: "4 14 10 14 10 20" }),
2682
- /* @__PURE__ */ jsx5("polyline", { points: "20 10 14 10 14 4" }),
2683
- /* @__PURE__ */ jsx5("line", { x1: "14", y1: "10", x2: "21", y2: "3" }),
2684
- /* @__PURE__ */ jsx5("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
2890
+ var CollapseIcon = () => /* @__PURE__ */ jsxs6("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2891
+ /* @__PURE__ */ jsx7("polyline", { points: "4 14 10 14 10 20" }),
2892
+ /* @__PURE__ */ jsx7("polyline", { points: "20 10 14 10 14 4" }),
2893
+ /* @__PURE__ */ jsx7("line", { x1: "14", y1: "10", x2: "21", y2: "3" }),
2894
+ /* @__PURE__ */ jsx7("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
2685
2895
  ] });
2686
- var SendIcon = () => /* @__PURE__ */ jsxs4("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2687
- /* @__PURE__ */ jsx5("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
2688
- /* @__PURE__ */ jsx5("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
2896
+ var SendIcon = () => /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2897
+ /* @__PURE__ */ jsx7("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
2898
+ /* @__PURE__ */ jsx7("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
2689
2899
  ] });
2690
- var LayersIcon = () => /* @__PURE__ */ jsxs4("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2691
- /* @__PURE__ */ jsx5("polygon", { points: "12 2 2 7 12 12 22 7 12 2" }),
2692
- /* @__PURE__ */ jsx5("polyline", { points: "2 17 12 22 22 17" }),
2693
- /* @__PURE__ */ jsx5("polyline", { points: "2 12 12 17 22 12" })
2900
+ var LayersIcon = () => /* @__PURE__ */ jsxs6("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2901
+ /* @__PURE__ */ jsx7("polygon", { points: "12 2 2 7 12 12 22 7 12 2" }),
2902
+ /* @__PURE__ */ jsx7("polyline", { points: "2 17 12 22 22 17" }),
2903
+ /* @__PURE__ */ jsx7("polyline", { points: "2 12 12 17 22 12" })
2694
2904
  ] });
2695
2905
  var styles = {
2696
2906
  root: {
@@ -2699,8 +2909,8 @@ var styles = {
2699
2909
  // Floating button (closed state - wide with text, open state - circular with X)
2700
2910
  floatingButton: {
2701
2911
  position: "fixed",
2702
- bottom: 24,
2703
- right: 24,
2912
+ bottom: 16,
2913
+ right: 16,
2704
2914
  borderRadius: "999px",
2705
2915
  border: "none",
2706
2916
  cursor: "pointer",
@@ -2710,30 +2920,30 @@ var styles = {
2710
2920
  display: "flex",
2711
2921
  alignItems: "center",
2712
2922
  justifyContent: "center",
2713
- fontSize: 15,
2923
+ fontSize: 14,
2714
2924
  fontWeight: 600,
2715
2925
  zIndex: 99999,
2716
2926
  transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
2717
2927
  },
2718
2928
  floatingButtonClosed: {
2719
- padding: "14px 24px",
2929
+ padding: "12px 20px",
2720
2930
  gap: 8
2721
2931
  },
2722
2932
  floatingButtonOpen: {
2723
- width: 56,
2724
- height: 56,
2933
+ width: 52,
2934
+ height: 52,
2725
2935
  padding: 0
2726
2936
  },
2727
2937
  floatingButtonMobile: {
2728
- width: 56,
2729
- height: 56,
2938
+ width: 52,
2939
+ height: 52,
2730
2940
  padding: 0
2731
2941
  },
2732
2942
  // Panel (expandable)
2733
2943
  panel: {
2734
2944
  position: "fixed",
2735
- bottom: 92,
2736
- right: 24,
2945
+ bottom: 80,
2946
+ right: 16,
2737
2947
  background: "#fff",
2738
2948
  borderRadius: 16,
2739
2949
  boxShadow: "0 20px 60px rgba(15, 23, 42, 0.3), 0 0 0 1px rgba(15, 23, 42, 0.05)",
@@ -2744,16 +2954,16 @@ var styles = {
2744
2954
  transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
2745
2955
  },
2746
2956
  panelNormal: {
2747
- width: 400,
2748
- height: 550
2957
+ width: 360,
2958
+ height: 520
2749
2959
  },
2750
2960
  panelExpanded: {
2751
- width: 520,
2752
- height: 700
2961
+ width: 480,
2962
+ height: 640
2753
2963
  },
2754
2964
  // Header with green gradient
2755
2965
  header: {
2756
- padding: "16px 18px",
2966
+ padding: "14px 16px",
2757
2967
  background: "linear-gradient(135deg, #10b981, #059669)",
2758
2968
  color: "#fff",
2759
2969
  display: "flex",
@@ -2762,7 +2972,7 @@ var styles = {
2762
2972
  },
2763
2973
  title: {
2764
2974
  margin: 0,
2765
- fontSize: 16,
2975
+ fontSize: 15,
2766
2976
  fontWeight: 600,
2767
2977
  letterSpacing: "-0.01em"
2768
2978
  },
@@ -2775,8 +2985,8 @@ var styles = {
2775
2985
  border: "none",
2776
2986
  background: "rgba(255, 255, 255, 0.15)",
2777
2987
  color: "#fff",
2778
- width: 32,
2779
- height: 32,
2988
+ width: 30,
2989
+ height: 30,
2780
2990
  borderRadius: 8,
2781
2991
  cursor: "pointer",
2782
2992
  display: "flex",
@@ -2787,7 +2997,7 @@ var styles = {
2787
2997
  // Messages area
2788
2998
  messages: {
2789
2999
  flex: 1,
2790
- padding: "20px 18px",
3000
+ padding: "16px",
2791
3001
  overflowY: "auto",
2792
3002
  background: "#f8fafc",
2793
3003
  display: "flex",
@@ -2802,10 +3012,10 @@ var styles = {
2802
3012
  },
2803
3013
  messageBubble: {
2804
3014
  maxWidth: "85%",
2805
- padding: "12px 14px",
3015
+ padding: "10px 12px",
2806
3016
  borderRadius: 16,
2807
3017
  lineHeight: 1.5,
2808
- fontSize: 14,
3018
+ fontSize: 13,
2809
3019
  whiteSpace: "pre-wrap",
2810
3020
  wordBreak: "break-word"
2811
3021
  },
@@ -2919,7 +3129,7 @@ var styles = {
2919
3129
  // Input area
2920
3130
  inputWrapper: {
2921
3131
  borderTop: "1px solid #e2e8f0",
2922
- padding: "14px 16px",
3132
+ padding: "10px 14px",
2923
3133
  display: "flex",
2924
3134
  gap: 10,
2925
3135
  alignItems: "flex-end",
@@ -2930,8 +3140,8 @@ var styles = {
2930
3140
  resize: "none",
2931
3141
  borderRadius: 12,
2932
3142
  border: "1.5px solid #cbd5e1",
2933
- padding: "10px 12px",
2934
- fontSize: 14,
3143
+ padding: "8px 10px",
3144
+ fontSize: 13,
2935
3145
  fontFamily: "inherit",
2936
3146
  lineHeight: 1.4,
2937
3147
  transition: "border-color 0.2s"
@@ -2943,18 +3153,18 @@ var styles = {
2943
3153
  sendButton: {
2944
3154
  borderRadius: 12,
2945
3155
  border: "none",
2946
- padding: "10px 14px",
3156
+ padding: "8px 12px",
2947
3157
  background: "linear-gradient(135deg, #10b981, #059669)",
2948
3158
  color: "#fff",
2949
3159
  cursor: "pointer",
2950
- fontSize: 14,
3160
+ fontSize: 13,
2951
3161
  fontWeight: 600,
2952
3162
  display: "flex",
2953
3163
  alignItems: "center",
2954
3164
  justifyContent: "center",
2955
3165
  transition: "opacity 0.2s, transform 0.2s",
2956
- minWidth: 44,
2957
- height: 44
3166
+ minWidth: 40,
3167
+ height: 40
2958
3168
  },
2959
3169
  // Status messages
2960
3170
  statusNote: {
@@ -2984,42 +3194,42 @@ var FloatingChatBox = ({
2984
3194
  open: openProp
2985
3195
  }) => {
2986
3196
  const isControlled = openProp !== void 0;
2987
- const [internalOpen, setInternalOpen] = useState4(false);
3197
+ const [internalOpen, setInternalOpen] = useState5(false);
2988
3198
  const open = isControlled ? openProp : internalOpen;
2989
- const setOpen = useCallback3((value) => {
3199
+ const setOpen = useCallback4((value) => {
2990
3200
  const newValue = typeof value === "function" ? value(open) : value;
2991
3201
  if (!isControlled) {
2992
3202
  setInternalOpen(newValue);
2993
3203
  }
2994
3204
  onOpenChange?.(newValue);
2995
3205
  }, [isControlled, open, onOpenChange]);
2996
- const [expanded, setExpanded] = useState4(false);
2997
- const [messages, setMessages] = useState4([]);
2998
- const [inputValue, setInputValue] = useState4("");
2999
- const [conversationId, setConversationId] = useState4();
3000
- const [errorMessage, setErrorMessage] = useState4(null);
3001
- const [isFocused, setIsFocused] = useState4(false);
3002
- const [isMobile, setIsMobile] = useState4(false);
3003
- const messagesEndRef = useRef4(null);
3004
- const messagesContainerRef = useRef4(null);
3005
- const chatBoxRef = useRef4(null);
3006
- const chatConfig = useMemo3(() => {
3206
+ const [expanded, setExpanded] = useState5(false);
3207
+ const [messages, setMessages] = useState5([]);
3208
+ const [inputValue, setInputValue] = useState5("");
3209
+ const [conversationId, setConversationId] = useState5();
3210
+ const [errorMessage, setErrorMessage] = useState5(null);
3211
+ const [isFocused, setIsFocused] = useState5(false);
3212
+ const [isMobile, setIsMobile] = useState5(false);
3213
+ const messagesEndRef = useRef7(null);
3214
+ const messagesContainerRef = useRef7(null);
3215
+ const chatBoxRef = useRef7(null);
3216
+ const chatConfig = useMemo4(() => {
3007
3217
  if (!baseUrl) return void 0;
3008
3218
  return { baseUrl, accessToken, getAccessToken };
3009
3219
  }, [accessToken, baseUrl, getAccessToken]);
3010
3220
  const { sendMessage: sendMessage2, isStreaming, streamingText, completeResponse } = useSendMessageStream(chatConfig);
3011
3221
  const canSend = Boolean(mapId) && Boolean(baseUrl) && inputValue.trim().length > 0 && !isStreaming;
3012
- useEffect3(() => {
3222
+ useEffect6(() => {
3013
3223
  if (open && isMobile) {
3014
3224
  setExpanded(true);
3015
3225
  }
3016
3226
  }, [open, isMobile]);
3017
- const scrollToBottom = useCallback3(() => {
3227
+ const scrollToBottom = useCallback4(() => {
3018
3228
  if (messagesEndRef.current) {
3019
3229
  messagesEndRef.current.scrollIntoView({ behavior: "smooth" });
3020
3230
  }
3021
3231
  }, []);
3022
- useEffect3(() => {
3232
+ useEffect6(() => {
3023
3233
  if (open && messages.length === 0) {
3024
3234
  setMessages([
3025
3235
  {
@@ -3030,10 +3240,10 @@ var FloatingChatBox = ({
3030
3240
  ]);
3031
3241
  }
3032
3242
  }, [open, messages.length]);
3033
- useEffect3(() => {
3243
+ useEffect6(() => {
3034
3244
  scrollToBottom();
3035
3245
  }, [messages, streamingText, scrollToBottom]);
3036
- useEffect3(() => {
3246
+ useEffect6(() => {
3037
3247
  if (!open) return;
3038
3248
  if (isMobile && expanded) return;
3039
3249
  const handleClickOutside = (event) => {
@@ -3046,7 +3256,7 @@ var FloatingChatBox = ({
3046
3256
  document.removeEventListener("mousedown", handleClickOutside);
3047
3257
  };
3048
3258
  }, [open, isMobile, expanded]);
3049
- useEffect3(() => {
3259
+ useEffect6(() => {
3050
3260
  if (typeof window === "undefined") return;
3051
3261
  const mediaQuery = window.matchMedia("(max-width: 768px)");
3052
3262
  const updateMobile = () => setIsMobile(mediaQuery.matches);
@@ -3064,7 +3274,7 @@ var FloatingChatBox = ({
3064
3274
  }
3065
3275
  };
3066
3276
  }, []);
3067
- useEffect3(() => {
3277
+ useEffect6(() => {
3068
3278
  if (typeof document === "undefined") return;
3069
3279
  if (!open || !isMobile) return;
3070
3280
  document.body.style.overflow = "hidden";
@@ -3072,10 +3282,10 @@ var FloatingChatBox = ({
3072
3282
  document.body.style.overflow = "";
3073
3283
  };
3074
3284
  }, [open, isMobile]);
3075
- const addMessage = useCallback3((message) => {
3285
+ const addMessage = useCallback4((message) => {
3076
3286
  setMessages((prev) => [...prev, message]);
3077
3287
  }, []);
3078
- const handleSend = useCallback3(async () => {
3288
+ const handleSend = useCallback4(async () => {
3079
3289
  if (!mapId) {
3080
3290
  setErrorMessage("Selecciona un mapa para usar el asistente.");
3081
3291
  return;
@@ -3110,11 +3320,11 @@ var FloatingChatBox = ({
3110
3320
  response
3111
3321
  });
3112
3322
  } catch (error) {
3113
- setErrorMessage(error instanceof Error ? error.message : "Ocurri\xF3 un error inesperado.");
3323
+ setErrorMessage("Ocurri\xF3 un error al generar la respuesta.");
3114
3324
  addMessage({
3115
3325
  id: `error-${Date.now()}`,
3116
3326
  role: "assistant",
3117
- content: `\u274C Error: ${error instanceof Error ? error.message : "Ocurri\xF3 un error inesperado."}`
3327
+ content: "\u274C Ocurri\xF3 un error al generar la respuesta."
3118
3328
  });
3119
3329
  }
3120
3330
  }, [
@@ -3128,7 +3338,7 @@ var FloatingChatBox = ({
3128
3338
  sendMessage2,
3129
3339
  userId
3130
3340
  ]);
3131
- const handleKeyDown = useCallback3(
3341
+ const handleKeyDown = useCallback4(
3132
3342
  (event) => {
3133
3343
  if (event.key === "Enter" && !event.shiftKey) {
3134
3344
  event.preventDefault();
@@ -3139,20 +3349,20 @@ var FloatingChatBox = ({
3139
3349
  },
3140
3350
  [canSend, handleSend]
3141
3351
  );
3142
- const handleFollowUpClick = useCallback3((question) => {
3352
+ const handleFollowUpClick = useCallback4((question) => {
3143
3353
  setInputValue(question);
3144
3354
  }, []);
3145
3355
  const renderMetadata = (response) => {
3146
3356
  if (!response?.metadata) return null;
3147
3357
  const referencedLayers = response.metadata.referencedLayers;
3148
3358
  if (!referencedLayers || referencedLayers.length === 0) return null;
3149
- return /* @__PURE__ */ jsxs4("div", { style: styles.metadataSection, children: [
3150
- /* @__PURE__ */ jsxs4("div", { style: styles.metadataTitle, children: [
3151
- /* @__PURE__ */ jsx5(LayersIcon, {}),
3359
+ return /* @__PURE__ */ jsxs6("div", { style: styles.metadataSection, children: [
3360
+ /* @__PURE__ */ jsxs6("div", { style: styles.metadataTitle, children: [
3361
+ /* @__PURE__ */ jsx7(LayersIcon, {}),
3152
3362
  "Capas Analizadas"
3153
3363
  ] }),
3154
- /* @__PURE__ */ jsx5("ul", { style: styles.metadataList, children: referencedLayers.map((layer, index) => /* @__PURE__ */ jsxs4("li", { style: styles.metadataItem, children: [
3155
- /* @__PURE__ */ jsx5("strong", { children: layer.layerName }),
3364
+ /* @__PURE__ */ jsx7("ul", { style: styles.metadataList, children: referencedLayers.map((layer, index) => /* @__PURE__ */ jsxs6("li", { style: styles.metadataItem, children: [
3365
+ /* @__PURE__ */ jsx7("strong", { children: layer.layerName }),
3156
3366
  " (",
3157
3367
  layer.featureCount,
3158
3368
  " ",
@@ -3161,7 +3371,7 @@ var FloatingChatBox = ({
3161
3371
  ] }, index)) })
3162
3372
  ] });
3163
3373
  };
3164
- const handleActionClick = useCallback3((action) => {
3374
+ const handleActionClick = useCallback4((action) => {
3165
3375
  if (isStreaming) return;
3166
3376
  setOpen(false);
3167
3377
  requestAnimationFrame(() => {
@@ -3170,9 +3380,9 @@ var FloatingChatBox = ({
3170
3380
  }, [isStreaming, setOpen, onActionClick]);
3171
3381
  const renderActions = (response) => {
3172
3382
  if (!response?.suggestedActions?.length) return null;
3173
- return /* @__PURE__ */ jsxs4("div", { style: styles.actionsSection, children: [
3174
- /* @__PURE__ */ jsx5("div", { style: styles.sectionLabel, children: "Acciones Sugeridas" }),
3175
- /* @__PURE__ */ jsx5("div", { style: styles.actionsGrid, children: response.suggestedActions.map((action, index) => /* @__PURE__ */ jsx5(
3383
+ return /* @__PURE__ */ jsxs6("div", { style: styles.actionsSection, children: [
3384
+ /* @__PURE__ */ jsx7("div", { style: styles.sectionLabel, children: "Acciones Sugeridas" }),
3385
+ /* @__PURE__ */ jsx7("div", { style: styles.actionsGrid, children: response.suggestedActions.map((action, index) => /* @__PURE__ */ jsx7(
3176
3386
  "button",
3177
3387
  {
3178
3388
  type: "button",
@@ -3203,9 +3413,9 @@ var FloatingChatBox = ({
3203
3413
  };
3204
3414
  const renderFollowUps = (response) => {
3205
3415
  if (!response?.followUpQuestions?.length) return null;
3206
- return /* @__PURE__ */ jsxs4("div", { style: styles.actionsSection, children: [
3207
- /* @__PURE__ */ jsx5("div", { style: styles.sectionLabel, children: "Preguntas Relacionadas" }),
3208
- /* @__PURE__ */ jsx5("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: response.followUpQuestions.map((question, index) => /* @__PURE__ */ jsx5(
3416
+ return /* @__PURE__ */ jsxs6("div", { style: styles.actionsSection, children: [
3417
+ /* @__PURE__ */ jsx7("div", { style: styles.sectionLabel, children: "Preguntas Relacionadas" }),
3418
+ /* @__PURE__ */ jsx7("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: response.followUpQuestions.map((question, index) => /* @__PURE__ */ jsx7(
3209
3419
  "button",
3210
3420
  {
3211
3421
  type: "button",
@@ -3234,8 +3444,8 @@ var FloatingChatBox = ({
3234
3444
  )) })
3235
3445
  ] });
3236
3446
  };
3237
- const chatContent = /* @__PURE__ */ jsxs4("div", { style: styles.root, children: [
3238
- /* @__PURE__ */ jsx5("style", { children: `
3447
+ const chatContent = /* @__PURE__ */ jsxs6("div", { style: styles.root, children: [
3448
+ /* @__PURE__ */ jsx7("style", { children: `
3239
3449
  @keyframes zenitBlink {
3240
3450
  0%, 49% { opacity: 1; }
3241
3451
  50%, 100% { opacity: 0; }
@@ -3278,11 +3488,13 @@ var FloatingChatBox = ({
3278
3488
  @media (max-width: 768px) {
3279
3489
  .zenit-chat-panel.zenit-chat-panel--fullscreen {
3280
3490
  position: fixed !important;
3281
- inset: 0 !important;
3282
- width: 100vw !important;
3283
- max-width: 100vw !important;
3284
- height: 100vh !important;
3285
- height: 100dvh !important;
3491
+ left: 0 !important;
3492
+ right: 0 !important;
3493
+ top: 4rem !important;
3494
+ bottom: 0 !important;
3495
+ width: 100% !important;
3496
+ max-width: 100% !important;
3497
+ height: auto !important;
3286
3498
  border-radius: 0 !important;
3287
3499
  display: flex !important;
3288
3500
  flex-direction: column !important;
@@ -3305,7 +3517,7 @@ var FloatingChatBox = ({
3305
3517
  }
3306
3518
  }
3307
3519
  ` }),
3308
- open && /* @__PURE__ */ jsxs4(
3520
+ open && /* @__PURE__ */ jsxs6(
3309
3521
  "div",
3310
3522
  {
3311
3523
  ref: chatBoxRef,
@@ -3315,10 +3527,10 @@ var FloatingChatBox = ({
3315
3527
  ...expanded ? styles.panelExpanded : styles.panelNormal
3316
3528
  },
3317
3529
  children: [
3318
- /* @__PURE__ */ jsxs4("header", { style: styles.header, children: [
3319
- /* @__PURE__ */ jsx5("h3", { style: styles.title, children: "Asistente Zenit AI" }),
3320
- /* @__PURE__ */ jsxs4("div", { style: styles.headerButtons, children: [
3321
- /* @__PURE__ */ jsx5(
3530
+ /* @__PURE__ */ jsxs6("header", { style: styles.header, children: [
3531
+ /* @__PURE__ */ jsx7("h3", { style: styles.title, children: "Asistente Zenit AI" }),
3532
+ /* @__PURE__ */ jsxs6("div", { style: styles.headerButtons, children: [
3533
+ /* @__PURE__ */ jsx7(
3322
3534
  "button",
3323
3535
  {
3324
3536
  type: "button",
@@ -3331,10 +3543,10 @@ var FloatingChatBox = ({
3331
3543
  e.currentTarget.style.background = "rgba(255, 255, 255, 0.15)";
3332
3544
  },
3333
3545
  "aria-label": expanded ? "Contraer" : "Expandir",
3334
- children: expanded ? /* @__PURE__ */ jsx5(CollapseIcon, {}) : /* @__PURE__ */ jsx5(ExpandIcon, {})
3546
+ children: expanded ? /* @__PURE__ */ jsx7(CollapseIcon, {}) : /* @__PURE__ */ jsx7(ExpandIcon, {})
3335
3547
  }
3336
3548
  ),
3337
- /* @__PURE__ */ jsx5(
3549
+ /* @__PURE__ */ jsx7(
3338
3550
  "button",
3339
3551
  {
3340
3552
  type: "button",
@@ -3347,20 +3559,20 @@ var FloatingChatBox = ({
3347
3559
  e.currentTarget.style.background = "rgba(255, 255, 255, 0.15)";
3348
3560
  },
3349
3561
  "aria-label": "Cerrar",
3350
- children: /* @__PURE__ */ jsx5(CloseIcon, {})
3562
+ children: /* @__PURE__ */ jsx7(CloseIcon, {})
3351
3563
  }
3352
3564
  )
3353
3565
  ] })
3354
3566
  ] }),
3355
- /* @__PURE__ */ jsxs4("div", { ref: messagesContainerRef, className: "zenit-ai-body", style: styles.messages, children: [
3356
- messages.map((message) => /* @__PURE__ */ jsx5(
3567
+ /* @__PURE__ */ jsxs6("div", { ref: messagesContainerRef, className: "zenit-ai-body", style: styles.messages, children: [
3568
+ messages.map((message) => /* @__PURE__ */ jsx7(
3357
3569
  "div",
3358
3570
  {
3359
3571
  style: {
3360
3572
  ...styles.messageWrapper,
3361
3573
  alignItems: message.role === "user" ? "flex-end" : "flex-start"
3362
3574
  },
3363
- children: /* @__PURE__ */ jsxs4(
3575
+ children: /* @__PURE__ */ jsxs6(
3364
3576
  "div",
3365
3577
  {
3366
3578
  style: {
@@ -3368,7 +3580,7 @@ var FloatingChatBox = ({
3368
3580
  ...message.role === "user" ? styles.userMessage : styles.assistantMessage
3369
3581
  },
3370
3582
  children: [
3371
- message.role === "assistant" ? /* @__PURE__ */ jsx5(MarkdownRenderer, { content: message.content }) : message.content,
3583
+ message.role === "assistant" ? /* @__PURE__ */ jsx7(MarkdownRenderer, { content: message.content }) : message.content,
3372
3584
  message.role === "assistant" && renderMetadata(message.response),
3373
3585
  message.role === "assistant" && renderActions(message.response),
3374
3586
  message.role === "assistant" && renderFollowUps(message.response)
@@ -3378,39 +3590,39 @@ var FloatingChatBox = ({
3378
3590
  },
3379
3591
  message.id
3380
3592
  )),
3381
- isStreaming && /* @__PURE__ */ jsx5(
3593
+ isStreaming && /* @__PURE__ */ jsx7(
3382
3594
  "div",
3383
3595
  {
3384
3596
  style: {
3385
3597
  ...styles.messageWrapper,
3386
3598
  alignItems: "flex-start"
3387
3599
  },
3388
- children: /* @__PURE__ */ jsx5(
3600
+ children: /* @__PURE__ */ jsx7(
3389
3601
  "div",
3390
3602
  {
3391
3603
  style: {
3392
3604
  ...styles.messageBubble,
3393
3605
  ...styles.assistantMessage
3394
3606
  },
3395
- children: streamingText ? /* @__PURE__ */ jsxs4(Fragment, { children: [
3396
- /* @__PURE__ */ jsx5(MarkdownRenderer, { content: streamingText }),
3397
- /* @__PURE__ */ jsx5("span", { style: styles.cursor })
3398
- ] }) : /* @__PURE__ */ jsxs4("div", { style: styles.thinkingText, children: [
3399
- /* @__PURE__ */ jsx5("span", { children: "Analizando" }),
3400
- /* @__PURE__ */ jsxs4("div", { style: styles.typingIndicator, children: [
3401
- /* @__PURE__ */ jsx5("div", { className: "zenit-typing-dot", style: styles.typingDot }),
3402
- /* @__PURE__ */ jsx5("div", { className: "zenit-typing-dot", style: styles.typingDot }),
3403
- /* @__PURE__ */ jsx5("div", { className: "zenit-typing-dot", style: styles.typingDot })
3607
+ children: streamingText ? /* @__PURE__ */ jsxs6(Fragment3, { children: [
3608
+ /* @__PURE__ */ jsx7(MarkdownRenderer, { content: streamingText }),
3609
+ /* @__PURE__ */ jsx7("span", { style: styles.cursor })
3610
+ ] }) : /* @__PURE__ */ jsxs6("div", { style: styles.thinkingText, children: [
3611
+ /* @__PURE__ */ jsx7("span", { children: "Pensando" }),
3612
+ /* @__PURE__ */ jsxs6("div", { style: styles.typingIndicator, children: [
3613
+ /* @__PURE__ */ jsx7("div", { className: "zenit-typing-dot", style: styles.typingDot }),
3614
+ /* @__PURE__ */ jsx7("div", { className: "zenit-typing-dot", style: styles.typingDot }),
3615
+ /* @__PURE__ */ jsx7("div", { className: "zenit-typing-dot", style: styles.typingDot })
3404
3616
  ] })
3405
3617
  ] })
3406
3618
  }
3407
3619
  )
3408
3620
  }
3409
3621
  ),
3410
- /* @__PURE__ */ jsx5("div", { ref: messagesEndRef })
3622
+ /* @__PURE__ */ jsx7("div", { ref: messagesEndRef })
3411
3623
  ] }),
3412
- /* @__PURE__ */ jsxs4("div", { className: "zenit-ai-input-area", style: styles.inputWrapper, children: [
3413
- /* @__PURE__ */ jsx5(
3624
+ /* @__PURE__ */ jsxs6("div", { className: "zenit-ai-input-area", style: styles.inputWrapper, children: [
3625
+ /* @__PURE__ */ jsx7(
3414
3626
  "textarea",
3415
3627
  {
3416
3628
  style: {
@@ -3427,7 +3639,7 @@ var FloatingChatBox = ({
3427
3639
  disabled: !mapId || !baseUrl || isStreaming
3428
3640
  }
3429
3641
  ),
3430
- /* @__PURE__ */ jsx5(
3642
+ /* @__PURE__ */ jsx7(
3431
3643
  "button",
3432
3644
  {
3433
3645
  type: "button",
@@ -3436,36 +3648,38 @@ var FloatingChatBox = ({
3436
3648
  onClick: () => void handleSend(),
3437
3649
  disabled: !canSend,
3438
3650
  "aria-label": "Enviar mensaje",
3439
- children: /* @__PURE__ */ jsx5(SendIcon, {})
3651
+ children: /* @__PURE__ */ jsx7(SendIcon, {})
3440
3652
  }
3441
3653
  )
3442
3654
  ] }),
3443
- errorMessage && /* @__PURE__ */ jsx5("div", { style: styles.errorText, children: errorMessage }),
3444
- !mapId && !errorMessage && /* @__PURE__ */ jsx5("div", { style: styles.statusNote, children: "Selecciona un mapa para usar el asistente" }),
3445
- !baseUrl && !errorMessage && /* @__PURE__ */ jsx5("div", { style: styles.statusNote, children: "Configura la baseUrl del SDK" })
3655
+ errorMessage && /* @__PURE__ */ jsx7("div", { style: styles.errorText, children: errorMessage }),
3656
+ isStreaming && !errorMessage && /* @__PURE__ */ jsx7("div", { style: styles.statusNote, children: "Generando sugerencias..." }),
3657
+ !mapId && !errorMessage && /* @__PURE__ */ jsx7("div", { style: styles.statusNote, children: "Selecciona un mapa para usar el asistente" }),
3658
+ !baseUrl && !errorMessage && /* @__PURE__ */ jsx7("div", { style: styles.statusNote, children: "Configura la baseUrl del SDK" })
3446
3659
  ]
3447
3660
  }
3448
3661
  ),
3449
- !(hideButton && !open) && /* @__PURE__ */ jsx5(
3662
+ !(hideButton && !open) && /* @__PURE__ */ jsx7(
3450
3663
  "button",
3451
3664
  {
3452
3665
  type: "button",
3453
3666
  className: `zenit-ai-button ${open ? "open" : ""}${open && isMobile ? " zenit-ai-button--hidden-mobile" : ""}`,
3454
3667
  style: {
3455
3668
  ...styles.floatingButton,
3456
- ...open ? styles.floatingButtonOpen : isMobile ? styles.floatingButtonMobile : styles.floatingButtonClosed
3669
+ ...open ? styles.floatingButtonOpen : isMobile ? styles.floatingButtonMobile : styles.floatingButtonClosed,
3670
+ zIndex: open ? 100001 : 99999
3457
3671
  },
3458
3672
  onClick: () => setOpen((prev) => !prev),
3459
3673
  "aria-label": open ? "Cerrar asistente" : "Abrir asistente Zenit AI",
3460
- children: open ? /* @__PURE__ */ jsx5(CloseIcon, {}) : /* @__PURE__ */ jsxs4(Fragment, { children: [
3461
- /* @__PURE__ */ jsx5(ChatIcon, {}),
3462
- !isMobile && /* @__PURE__ */ jsx5("span", { children: "Asistente IA" })
3674
+ children: open ? /* @__PURE__ */ jsx7(CloseIcon, {}) : /* @__PURE__ */ jsxs6(Fragment3, { children: [
3675
+ /* @__PURE__ */ jsx7(ChatIcon, {}),
3676
+ !isMobile && /* @__PURE__ */ jsx7("span", { children: "Asistente IA" })
3463
3677
  ] })
3464
3678
  }
3465
3679
  )
3466
3680
  ] });
3467
3681
  if (typeof document !== "undefined") {
3468
- return createPortal(chatContent, document.body);
3682
+ return createPortal2(chatContent, document.body);
3469
3683
  }
3470
3684
  return chatContent;
3471
3685
  };
@@ -3508,4 +3722,4 @@ export {
3508
3722
  useSendMessageStream,
3509
3723
  FloatingChatBox
3510
3724
  };
3511
- //# sourceMappingURL=chunk-ITF7QCUZ.mjs.map
3725
+ //# sourceMappingURL=chunk-PCTRVN4O.mjs.map