zenit-sdk 0.0.8 → 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.
@@ -58,9 +58,9 @@ __export(react_exports, {
58
58
  module.exports = __toCommonJS(react_exports);
59
59
 
60
60
  // src/react/ZenitMap.tsx
61
- var import_react = __toESM(require("react"));
62
- var import_react_leaflet = require("react-leaflet");
63
- var import_leaflet = __toESM(require("leaflet"));
61
+ var import_react4 = __toESM(require("react"));
62
+ var import_react_leaflet4 = require("react-leaflet");
63
+ var import_leaflet4 = __toESM(require("leaflet"));
64
64
 
65
65
  // src/maps/helpers.ts
66
66
  function toNumber(value) {
@@ -253,9 +253,9 @@ function getAccentByLayerId(layerId, mapLayers) {
253
253
 
254
254
  // src/react/zoomOpacity.ts
255
255
  var DEFAULT_OPTIONS = {
256
- minZoom: 10,
257
- maxZoom: 17,
258
- minFactor: 0.6,
256
+ minZoom: 11,
257
+ maxZoom: 15,
258
+ minFactor: 0.3,
259
259
  maxFactor: 1,
260
260
  minOpacity: 0.1,
261
261
  maxOpacity: 0.92
@@ -292,358 +292,215 @@ function getEffectiveLayerOpacity(baseOpacity, zoom, layerType, geometryType, op
292
292
  return clampNumber(effective, settings.minOpacity, settings.maxOpacity);
293
293
  }
294
294
 
295
- // src/react/ZenitMap.tsx
295
+ // src/react/map/layer-geojson.tsx
296
+ var import_react_leaflet = require("react-leaflet");
297
+ var import_leaflet = __toESM(require("leaflet"));
296
298
  var import_jsx_runtime = require("react/jsx-runtime");
297
- var DEFAULT_CENTER = [0, 0];
298
- var DEFAULT_ZOOM = 3;
299
- var LABELS_PANE_NAME = "zenit-labels-pane";
300
- function computeBBoxFromGeojson(geojson) {
301
- if (!geojson) return null;
302
- if (!Array.isArray(geojson.features) || geojson.features.length === 0) return null;
303
- const coords = [];
304
- const collect = (candidate) => {
305
- if (!Array.isArray(candidate)) return;
306
- if (candidate.length === 2 && typeof candidate[0] === "number" && typeof candidate[1] === "number") {
307
- coords.push([candidate[0], candidate[1]]);
308
- return;
309
- }
310
- candidate.forEach((entry) => collect(entry));
311
- };
312
- geojson.features.forEach((feature) => {
313
- collect(feature.geometry?.coordinates);
314
- });
315
- if (coords.length === 0) return null;
316
- const [firstLon, firstLat] = coords[0];
317
- const bbox = { minLon: firstLon, minLat: firstLat, maxLon: firstLon, maxLat: firstLat };
318
- coords.forEach(([lon, lat]) => {
319
- bbox.minLon = Math.min(bbox.minLon, lon);
320
- bbox.minLat = Math.min(bbox.minLat, lat);
321
- bbox.maxLon = Math.max(bbox.maxLon, lon);
322
- bbox.maxLat = Math.max(bbox.maxLat, lat);
323
- });
324
- return bbox;
325
- }
326
- function mergeBBoxes(bboxes) {
327
- const valid = bboxes.filter((bbox) => !!bbox);
328
- if (valid.length === 0) return null;
329
- const first = valid[0];
330
- return valid.slice(1).reduce(
331
- (acc, bbox) => ({
332
- minLon: Math.min(acc.minLon, bbox.minLon),
333
- minLat: Math.min(acc.minLat, bbox.minLat),
334
- maxLon: Math.max(acc.maxLon, bbox.maxLon),
335
- maxLat: Math.max(acc.maxLat, bbox.maxLat)
336
- }),
337
- { ...first }
338
- );
339
- }
340
- function isRecord(value) {
341
- return typeof value === "object" && value !== null;
299
+ var POINT_GEOMETRY_TYPES = /* @__PURE__ */ new Set(["Point", "MultiPoint"]);
300
+ function getGeometryType(feature) {
301
+ const t = feature?.geometry?.type;
302
+ return typeof t === "string" ? t : null;
342
303
  }
343
- function isGeoJsonFeatureCollection(value) {
344
- if (!isRecord(value)) return false;
345
- const features = value.features;
346
- if (!Array.isArray(features)) return false;
347
- const type = value.type;
348
- return type === void 0 || type === "FeatureCollection";
304
+ function isPointGeometry(feature) {
305
+ const geometryType = getGeometryType(feature);
306
+ return geometryType !== null && POINT_GEOMETRY_TYPES.has(geometryType);
349
307
  }
350
- function extractGeoJsonFeatureCollection(value) {
351
- if (isRecord(value) && "data" in value) {
352
- const data = value.data;
353
- return isGeoJsonFeatureCollection(data) ? data : null;
354
- }
355
- return isGeoJsonFeatureCollection(value) ? value : null;
308
+ function isNonPointGeometry(feature) {
309
+ const geometryType = getGeometryType(feature);
310
+ return geometryType !== null && !POINT_GEOMETRY_TYPES.has(geometryType);
356
311
  }
357
- function getFeatureLayerId(feature) {
358
- const layerId = feature?.properties?.__zenit_layerId ?? feature?.properties?.layerId ?? feature?.properties?.layer_id;
359
- if (layerId === void 0 || layerId === null) return null;
360
- return layerId;
312
+ function buildFeatureCollection(features) {
313
+ return {
314
+ type: "FeatureCollection",
315
+ features
316
+ };
361
317
  }
362
- function escapeHtml(value) {
363
- return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
318
+ var LayerGeoJson = ({
319
+ layerId,
320
+ data,
321
+ baseOpacity,
322
+ isMobile,
323
+ panesReady,
324
+ mapInstance,
325
+ fillPaneName,
326
+ pointsPaneName,
327
+ layerType,
328
+ styleFn,
329
+ onEachFeature,
330
+ onPolygonLabel
331
+ }) => {
332
+ const features = data.features ?? [];
333
+ const fillFeatures = features.filter(isNonPointGeometry);
334
+ const pointFeatures = features.filter(isPointGeometry);
335
+ const fillData = fillFeatures.length > 0 ? buildFeatureCollection(fillFeatures) : null;
336
+ const pointsData = pointFeatures.length > 0 ? buildFeatureCollection(pointFeatures) : null;
337
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
338
+ fillData && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
339
+ import_react_leaflet.GeoJSON,
340
+ {
341
+ data: fillData,
342
+ pane: panesReady && mapInstance?.getPane(fillPaneName) ? fillPaneName : void 0,
343
+ style: (feature) => styleFn(feature, layerType, baseOpacity),
344
+ onEachFeature: (feature, layer) => {
345
+ onEachFeature(feature, layer);
346
+ onPolygonLabel?.(feature, layer);
347
+ }
348
+ },
349
+ `fill-${layerId}`
350
+ ),
351
+ pointsData && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
352
+ import_react_leaflet.GeoJSON,
353
+ {
354
+ data: pointsData,
355
+ pane: panesReady && mapInstance?.getPane(pointsPaneName) ? pointsPaneName : void 0,
356
+ pointToLayer: (feature, latlng) => import_leaflet.default.circleMarker(latlng, {
357
+ radius: isMobile ? 8 : 6,
358
+ ...styleFn(feature, layerType, baseOpacity)
359
+ }),
360
+ onEachFeature
361
+ },
362
+ `points-${layerId}`
363
+ )
364
+ ] });
365
+ };
366
+
367
+ // src/react/map/location-control.tsx
368
+ var import_react2 = require("react");
369
+ var import_react_dom = require("react-dom");
370
+ var import_react_leaflet2 = require("react-leaflet");
371
+ var import_leaflet3 = __toESM(require("leaflet"));
372
+
373
+ // src/react/hooks/use-geolocation.ts
374
+ var import_react = require("react");
375
+ function useGeolocation(options) {
376
+ const [isTracking, setIsTracking] = (0, import_react.useState)(false);
377
+ const [location, setLocation] = (0, import_react.useState)(null);
378
+ const [error, setError] = (0, import_react.useState)(null);
379
+ const watchIdRef = (0, import_react.useRef)(null);
380
+ const stopTracking = (0, import_react.useCallback)(() => {
381
+ if (watchIdRef.current !== null && typeof navigator !== "undefined" && navigator.geolocation) {
382
+ navigator.geolocation.clearWatch(watchIdRef.current);
383
+ }
384
+ watchIdRef.current = null;
385
+ setIsTracking(false);
386
+ }, []);
387
+ const startTracking = (0, import_react.useCallback)(() => {
388
+ if (typeof navigator === "undefined" || !navigator.geolocation) {
389
+ setError({ code: 0, message: "La geolocalizaci\xF3n no est\xE1 disponible en este navegador." });
390
+ return;
391
+ }
392
+ setError(null);
393
+ watchIdRef.current = navigator.geolocation.watchPosition(
394
+ (position) => {
395
+ setLocation({
396
+ lat: position.coords.latitude,
397
+ lon: position.coords.longitude,
398
+ accuracy: position.coords.accuracy
399
+ });
400
+ setIsTracking(true);
401
+ },
402
+ (err) => {
403
+ setError({ code: err.code, message: err.message });
404
+ stopTracking();
405
+ },
406
+ {
407
+ enableHighAccuracy: options?.enableHighAccuracy ?? true,
408
+ timeout: options?.timeout ?? 12e3,
409
+ maximumAge: options?.maximumAge ?? 0
410
+ }
411
+ );
412
+ }, [options?.enableHighAccuracy, options?.maximumAge, options?.timeout, stopTracking]);
413
+ const toggleTracking = (0, import_react.useCallback)(() => {
414
+ if (isTracking) {
415
+ stopTracking();
416
+ } else {
417
+ startTracking();
418
+ }
419
+ }, [isTracking, startTracking, stopTracking]);
420
+ const clearError = (0, import_react.useCallback)(() => setError(null), []);
421
+ (0, import_react.useEffect)(() => {
422
+ return () => {
423
+ stopTracking();
424
+ };
425
+ }, [stopTracking]);
426
+ return {
427
+ isTracking,
428
+ location,
429
+ error,
430
+ startTracking,
431
+ stopTracking,
432
+ toggleTracking,
433
+ clearError
434
+ };
364
435
  }
365
- var DESCRIPTION_KEYS = /* @__PURE__ */ new Set(["descripcion", "description"]);
366
- var POPUP_EXCLUDED_KEYS = /* @__PURE__ */ new Set(["geom", "geometry"]);
367
- var POPUP_HEADER_KEYS = ["nombre", "name", "title", "titulo"];
436
+
437
+ // src/react/map/map-utils.ts
438
+ var import_leaflet2 = __toESM(require("leaflet"));
368
439
  var POPUP_STYLE_ID = "zenit-leaflet-popup-styles";
369
- var DESKTOP_POPUP_DIMENSIONS = { maxWidth: 350, minWidth: 280, maxHeight: 480 };
370
- var MOBILE_POPUP_DIMENSIONS = { maxWidth: 280, minWidth: 240, maxHeight: 380 };
440
+ var POPUP_EXCLUDED_KEYS = /* @__PURE__ */ new Set(["geom", "geometry", "_private"]);
441
+ var POPUP_HEADER_KEYS = ["nombre", "name", "title", "titulo"];
442
+ var DESKTOP_POPUP_DIMENSIONS = { maxWidth: 360, minWidth: 280, maxHeight: 520 };
443
+ var MOBILE_POPUP_DIMENSIONS = { maxWidth: 300, minWidth: 240, maxHeight: 420 };
371
444
  var ZENIT_LEAFLET_POPUP_STYLES = `
372
- /* ===== Zenit Leaflet Popup - Modern Professional Styling ===== */
373
-
374
- /* Main popup wrapper */
375
- .zenit-leaflet-popup .leaflet-popup-content-wrapper {
445
+ .custom-leaflet-popup .leaflet-popup-content-wrapper {
376
446
  border-radius: 12px;
377
- box-shadow:
378
- 0 4px 6px -1px rgba(0, 0, 0, 0.1),
379
- 0 2px 4px -2px rgba(0, 0, 0, 0.1),
380
- 0 0 0 1px rgba(0, 0, 0, 0.05);
381
447
  padding: 0;
382
448
  background: #ffffff;
383
- overflow: hidden;
449
+ box-shadow: 0 12px 24px rgba(15, 23, 42, 0.18);
450
+ border: 1px solid rgba(15, 23, 42, 0.08);
384
451
  }
385
452
 
386
- /* Content area with scroll support */
387
- .zenit-leaflet-popup .leaflet-popup-content {
453
+ .custom-leaflet-popup .leaflet-popup-content {
388
454
  margin: 0;
389
- padding: 0;
455
+ padding: 12px 14px;
390
456
  font-size: 13px;
391
- line-height: 1.5;
392
- color: #374151;
393
- min-width: 100%;
394
- max-height: min(70vh, 480px);
395
- overflow-y: auto;
396
- overflow-x: hidden;
397
- scrollbar-width: thin;
398
- scrollbar-color: rgba(156, 163, 175, 0.5) transparent;
399
- }
400
-
401
- /* Popup tip/arrow shadow */
402
- .zenit-leaflet-popup .leaflet-popup-tip-container {
403
- filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1));
404
- }
405
-
406
- .zenit-leaflet-popup .leaflet-popup-tip {
407
- background: #ffffff;
408
- box-shadow: none;
409
- }
410
-
411
- /* Close button styling */
412
- .zenit-leaflet-popup .leaflet-popup-close-button {
413
- color: #9ca3af;
414
- font-size: 18px;
415
- font-weight: 400;
416
- width: 28px;
417
- height: 28px;
418
- padding: 0;
419
- margin: 8px 8px 0 0;
420
- display: flex;
421
- align-items: center;
422
- justify-content: center;
423
- border-radius: 6px;
424
- transition: all 0.15s ease;
425
- z-index: 10;
426
- }
427
-
428
- .zenit-leaflet-popup .leaflet-popup-close-button:hover {
429
- color: #374151;
430
- background-color: #f3f4f6;
431
- }
432
-
433
- .zenit-leaflet-popup .leaflet-popup-close-button:active {
434
- background-color: #e5e7eb;
435
- }
436
-
437
- /* Main card container */
438
- .zenit-popup-card {
439
- display: flex;
440
- flex-direction: column;
441
- gap: 0;
442
- padding: 16px;
443
- }
444
-
445
- .zenit-popup-header {
446
- padding-bottom: 12px;
447
- border-bottom: 1px solid #e5e7eb;
448
- margin-bottom: 4px;
449
- }
450
-
451
- .zenit-popup-title {
452
- font-size: 14px;
453
- font-weight: 700;
454
- color: #111827;
455
- letter-spacing: 0.01em;
456
- line-height: 1.4;
457
- }
458
-
459
- /* Individual row styling with subtle separator */
460
- .zenit-popup-row {
461
- display: flex;
462
- flex-direction: column;
463
- gap: 2px;
464
- padding: 10px 0;
465
- border-bottom: 1px solid #f3f4f6;
466
- }
467
-
468
- .zenit-popup-row:first-child {
469
- padding-top: 0;
470
- }
471
-
472
- .zenit-popup-row:last-child {
473
- border-bottom: none;
474
- padding-bottom: 0;
475
- }
476
-
477
- /* Label styling - small, gray, uppercase */
478
- .zenit-popup-label {
479
- font-size: 10px;
480
- font-weight: 500;
481
- color: #9ca3af;
482
- text-transform: uppercase;
483
- letter-spacing: 0.05em;
484
457
  line-height: 1.4;
458
+ color: #0f172a;
459
+ max-height: min(70vh, 520px);
460
+ overflow: auto;
461
+ scrollbar-width: thin;
462
+ scrollbar-color: rgba(148, 163, 184, 0.6) transparent;
485
463
  }
486
464
 
487
- /* Value styling - darker, readable */
488
- .zenit-popup-value {
489
- font-size: 13px;
490
- font-weight: 400;
491
- color: #1f2937;
492
- overflow-wrap: break-word;
493
- word-break: break-word;
494
- line-height: 1.5;
495
- }
496
-
497
- .zenit-popup-link {
498
- color: #2563eb;
499
- text-decoration: underline;
500
- font-weight: 500;
501
- }
502
-
503
- .zenit-popup-link:hover {
504
- color: #1d4ed8;
505
- }
506
-
507
- /* Special styling for description field */
508
- .zenit-popup-row.zenit-popup-description {
509
- background-color: #f9fafb;
510
- margin: 0 -16px;
511
- padding: 12px 16px;
512
- border-bottom: 1px solid #e5e7eb;
513
- }
514
-
515
- .zenit-popup-row.zenit-popup-description:first-child {
516
- margin-top: 0;
517
- border-radius: 0;
518
- }
519
-
520
- .zenit-popup-row.zenit-popup-description .zenit-popup-value {
521
- font-size: 13px;
522
- line-height: 1.6;
523
- color: #374151;
524
- max-height: 150px;
525
- overflow-y: auto;
526
- padding-right: 4px;
465
+ .custom-leaflet-popup .leaflet-popup-close-button {
466
+ color: #64748b;
467
+ font-size: 16px;
468
+ padding: 6px 8px;
469
+ border-radius: 8px;
470
+ transition: background-color 0.15s ease, color 0.15s ease;
527
471
  }
528
472
 
529
- /* Preformatted text (JSON objects) */
530
- .zenit-popup-pre {
531
- margin: 0;
532
- font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
533
- font-size: 11px;
534
- white-space: pre-wrap;
535
- word-break: break-word;
536
- color: #4b5563;
537
- background-color: #f9fafb;
538
- padding: 8px;
539
- border-radius: 6px;
540
- border: 1px solid #e5e7eb;
473
+ .custom-leaflet-popup .leaflet-popup-close-button:hover {
474
+ color: #0f172a;
475
+ background: rgba(148, 163, 184, 0.2);
541
476
  }
542
477
 
543
- /* Empty state styling */
544
- .zenit-popup-empty {
545
- font-size: 13px;
546
- color: #9ca3af;
547
- font-style: italic;
548
- text-align: center;
549
- padding: 20px 0;
550
- }
551
-
552
- /* Webkit scrollbar styling */
553
- .zenit-leaflet-popup .leaflet-popup-content::-webkit-scrollbar {
478
+ .custom-leaflet-popup .leaflet-popup-content::-webkit-scrollbar {
554
479
  width: 6px;
555
480
  }
556
481
 
557
- .zenit-leaflet-popup .leaflet-popup-content::-webkit-scrollbar-track {
558
- background: transparent;
559
- }
560
-
561
- .zenit-leaflet-popup .leaflet-popup-content::-webkit-scrollbar-thumb {
562
- background-color: rgba(156, 163, 175, 0.4);
563
- border-radius: 3px;
564
- }
565
-
566
- .zenit-leaflet-popup .leaflet-popup-content::-webkit-scrollbar-thumb:hover {
567
- background-color: rgba(107, 114, 128, 0.6);
568
- }
569
-
570
- /* Scrollbar for description field */
571
- .zenit-popup-row.zenit-popup-description .zenit-popup-value::-webkit-scrollbar {
572
- width: 4px;
482
+ .custom-leaflet-popup .leaflet-popup-content::-webkit-scrollbar-thumb {
483
+ background: rgba(148, 163, 184, 0.5);
484
+ border-radius: 999px;
573
485
  }
574
486
 
575
- .zenit-popup-row.zenit-popup-description .zenit-popup-value::-webkit-scrollbar-track {
487
+ .custom-leaflet-popup .leaflet-popup-content::-webkit-scrollbar-track {
576
488
  background: transparent;
577
489
  }
578
490
 
579
- .zenit-popup-row.zenit-popup-description .zenit-popup-value::-webkit-scrollbar-thumb {
580
- background-color: rgba(156, 163, 175, 0.4);
581
- border-radius: 2px;
582
- }
583
-
584
- /* ===== Responsive: Mobile (<640px) ===== */
585
491
  @media (max-width: 640px) {
586
- .zenit-leaflet-popup .leaflet-popup-content-wrapper {
587
- border-radius: 10px;
588
- }
589
-
590
- .zenit-leaflet-popup .leaflet-popup-close-button {
591
- width: 26px;
592
- height: 26px;
593
- font-size: 16px;
594
- margin: 6px 6px 0 0;
595
- }
596
-
597
- .zenit-popup-card {
598
- padding: 12px;
599
- }
600
-
601
- .zenit-leaflet-popup .leaflet-popup-content {
602
- max-height: min(65vh, 380px);
603
- }
604
-
605
- .zenit-popup-header {
606
- padding-bottom: 10px;
607
- }
608
-
609
- .zenit-popup-title {
610
- font-size: 13px;
611
- }
612
-
613
- .zenit-popup-row {
614
- padding: 8px 0;
615
- }
616
-
617
- .zenit-popup-label {
618
- font-size: 9px;
619
- }
620
-
621
- .zenit-popup-value {
492
+ .custom-leaflet-popup .leaflet-popup-content {
622
493
  font-size: 12px;
623
- }
624
-
625
- .zenit-popup-row.zenit-popup-description {
626
- margin: 0 -12px;
627
494
  padding: 10px 12px;
495
+ max-height: min(65vh, 420px);
628
496
  }
629
497
 
630
- .zenit-popup-row.zenit-popup-description .zenit-popup-value {
631
- font-size: 12px;
632
- max-height: 120px;
633
- }
634
-
635
- .zenit-popup-pre {
636
- font-size: 10px;
637
- padding: 6px;
638
- }
639
-
640
- .zenit-popup-empty {
641
- font-size: 12px;
642
- padding: 16px 0;
498
+ .custom-leaflet-popup .leaflet-popup-close-button {
499
+ font-size: 14px;
500
+ padding: 4px 6px;
643
501
  }
644
502
  }
645
503
 
646
- /* ===== Map tooltip styling ===== */
647
504
  .zenit-map-tooltip {
648
505
  background-color: rgba(31, 41, 55, 0.95);
649
506
  border: none;
@@ -660,16 +517,21 @@ var ZENIT_LEAFLET_POPUP_STYLES = `
660
517
  }
661
518
 
662
519
  .polygon-label-tooltip {
663
- z-index: 600 !important;
664
- }
665
-
666
- .zenit-map-shell.popup-open .zenit-label-marker,
667
- .zenit-map-shell.popup-open .polygon-label-tooltip,
668
- .zenit-map-shell.popup-open .click-for-detail-hint,
669
- .zenit-map-shell.popup-open .zenit-map-tooltip {
670
- display: none !important;
520
+ background: rgba(15, 23, 42, 0.92);
521
+ border: none;
522
+ border-radius: 6px;
523
+ color: #f8fafc;
524
+ font-weight: 600;
525
+ font-size: 12px;
526
+ padding: 4px 8px;
671
527
  }
672
528
  `;
529
+ function clampNumber2(value, min, max) {
530
+ return Math.min(max, Math.max(min, value));
531
+ }
532
+ function clampOpacity4(value) {
533
+ return clampNumber2(value, 0, 1);
534
+ }
673
535
  function ensurePopupStyles() {
674
536
  if (typeof document === "undefined") return;
675
537
  if (document.getElementById(POPUP_STYLE_ID)) return;
@@ -679,72 +541,52 @@ function ensurePopupStyles() {
679
541
  document.head.appendChild(styleTag);
680
542
  }
681
543
  function getPopupDimensions() {
682
- if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
544
+ if (typeof window === "undefined") {
683
545
  return DESKTOP_POPUP_DIMENSIONS;
684
546
  }
685
- return window.matchMedia("(max-width: 640px)").matches ? MOBILE_POPUP_DIMENSIONS : DESKTOP_POPUP_DIMENSIONS;
686
- }
687
- function normalizeDescriptionValue(value) {
688
- if (value === void 0 || value === null) return null;
689
- if (typeof value === "string") {
690
- const trimmed = value.trim();
691
- return trimmed ? trimmed : null;
692
- }
693
- if (typeof value === "number" || typeof value === "boolean") {
694
- return String(value);
695
- }
696
- return null;
547
+ const isSmallWidth = window.innerWidth < 640;
548
+ const isShortHeight = window.innerHeight < 768;
549
+ const base = isSmallWidth ? MOBILE_POPUP_DIMENSIONS : DESKTOP_POPUP_DIMENSIONS;
550
+ const maxHeight = isShortHeight ? Math.min(base.maxHeight, 360) : base.maxHeight;
551
+ return { ...base, maxHeight };
697
552
  }
698
- function extractDescriptionValue(properties) {
699
- if (!properties) return null;
700
- const matches = Object.entries(properties).find(
701
- ([key]) => DESCRIPTION_KEYS.has(key.toLowerCase())
702
- );
703
- if (!matches) return null;
704
- return normalizeDescriptionValue(matches[1]);
553
+ function escapeHtml(value) {
554
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
705
555
  }
706
- function safeJsonStringify(value) {
707
- try {
708
- const json = JSON.stringify(value, null, 2);
709
- if (json !== void 0) return json;
710
- } catch {
711
- }
712
- return String(value);
556
+ function formatLabel(key) {
557
+ return key.replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").trim();
713
558
  }
714
559
  function renderPopupValue(value) {
715
- if (value === null || value === void 0) {
716
- return '<span class="zenit-popup-empty">Sin datos</span>';
717
- }
560
+ if (value === null || value === void 0) return '<span style="color:#94a3b8;">Sin datos</span>';
718
561
  if (value instanceof Date) {
719
- return `<span>${escapeHtml(value.toLocaleDateString("es-GT"))}</span>`;
562
+ return escapeHtml(value.toLocaleDateString("es-GT"));
720
563
  }
721
564
  if (typeof value === "number") {
722
- return `<span>${escapeHtml(value.toLocaleString("es-GT"))}</span>`;
565
+ return escapeHtml(value.toLocaleString("es-GT"));
723
566
  }
724
567
  if (typeof value === "string") {
725
568
  const trimmed = value.trim();
726
- const isLikelyDate = /^\d{4}-\d{2}-\d{2}/.test(trimmed) || trimmed.includes("T");
727
- if (isLikelyDate) {
728
- const parsed = Date.parse(trimmed);
729
- if (!Number.isNaN(parsed)) {
730
- return `<span>${escapeHtml(new Date(parsed).toLocaleDateString("es-GT"))}</span>`;
731
- }
732
- }
569
+ if (!trimmed) return '<span style="color:#94a3b8;">Sin datos</span>';
570
+ return escapeHtml(trimmed);
571
+ }
572
+ if (typeof value === "object") {
733
573
  try {
734
- const parsedUrl = new URL(trimmed);
735
- if (parsedUrl.protocol === "http:" || parsedUrl.protocol === "https:") {
736
- const safeHref = escapeHtml(parsedUrl.toString());
737
- return `<a class="zenit-popup-link" href="${safeHref}" target="_blank" rel="noopener noreferrer">${safeHref}</a>`;
738
- }
574
+ 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(
575
+ JSON.stringify(value, null, 2)
576
+ )}</pre>`;
739
577
  } catch {
578
+ return escapeHtml(String(value));
740
579
  }
741
- return `<span>${escapeHtml(trimmed || value)}</span>`;
742
- }
743
- if (typeof value === "object") {
744
- const json = safeJsonStringify(value);
745
- return `<pre class="zenit-popup-pre">${escapeHtml(json)}</pre>`;
746
580
  }
747
- return `<span>${escapeHtml(String(value))}</span>`;
581
+ return escapeHtml(String(value));
582
+ }
583
+ function extractPopupHeader(properties) {
584
+ const entry = Object.entries(properties).find(([key, value]) => {
585
+ if (typeof value !== "string") return false;
586
+ const normalized = key.trim().toLowerCase();
587
+ return POPUP_HEADER_KEYS.includes(normalized) && value.trim().length > 0;
588
+ });
589
+ return entry ? entry[1].trim() : null;
748
590
  }
749
591
  function shouldIncludePopupEntry(key, value) {
750
592
  if (!key) return false;
@@ -756,118 +598,480 @@ function shouldIncludePopupEntry(key, value) {
756
598
  if (typeof value === "string" && !value.trim()) return false;
757
599
  return true;
758
600
  }
759
- function isDescriptionKey(key) {
760
- const normalized = key.trim().toLowerCase();
761
- return DESCRIPTION_KEYS.has(normalized);
601
+ function createPopupContent(properties) {
602
+ const headerText = extractPopupHeader(properties);
603
+ const entries = Object.entries(properties).filter(([key, value]) => {
604
+ if (!shouldIncludePopupEntry(key, value)) return false;
605
+ if (headerText && POPUP_HEADER_KEYS.includes(key.trim().toLowerCase())) return false;
606
+ return true;
607
+ });
608
+ if (entries.length === 0) {
609
+ return '<div style="padding:8px 0; color:#64748b; text-align:center;">Sin datos disponibles</div>';
610
+ }
611
+ const headerHtml = headerText ? `<div style="font-weight:700; font-size:14px; margin-bottom:8px; color:#0f172a;">${escapeHtml(
612
+ headerText
613
+ )}</div>` : "";
614
+ const rowsHtml = entries.map(([key, value]) => {
615
+ const label = escapeHtml(formatLabel(key));
616
+ const valueHtml = renderPopupValue(value);
617
+ return `
618
+ <div style="display:grid; grid-template-columns:minmax(90px, 35%) 1fr; gap:8px; padding:6px 0; border-bottom:1px solid #e2e8f0;">
619
+ <div style="font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.04em; color:#64748b;">${label}</div>
620
+ <div style="font-size:13px; color:#0f172a; word-break:break-word;">${valueHtml}</div>
621
+ </div>
622
+ `;
623
+ }).join("");
624
+ return `<div>${headerHtml}${rowsHtml}</div>`;
762
625
  }
763
- function extractPopupHeader(properties) {
764
- if (!properties) return null;
765
- const entry = Object.entries(properties).find(
766
- (candidate) => {
767
- const [key, value] = candidate;
768
- return POPUP_HEADER_KEYS.includes(key.trim().toLowerCase()) && typeof value === "string" && value.trim().length > 0;
626
+ function isPolygonType(layerType, geometryType) {
627
+ const candidate = (layerType ?? geometryType ?? "").toLowerCase();
628
+ return candidate === "polygon" || candidate === "multipolygon";
629
+ }
630
+ function calculateZoomBasedOpacity(zoom, baseOpacity, layerType, geometryType) {
631
+ if (!isPolygonType(layerType, geometryType)) return clampOpacity4(baseOpacity);
632
+ const minZoom = 11;
633
+ const maxZoom = 15;
634
+ const minFactor = 0.3;
635
+ if (maxZoom <= minZoom) return clampOpacity4(baseOpacity * minFactor);
636
+ const t = clampNumber2((zoom - minZoom) / (maxZoom - minZoom), 0, 1);
637
+ const factor = 1 - (1 - minFactor) * t;
638
+ return clampOpacity4(baseOpacity * factor);
639
+ }
640
+ function layerStyleToLeaflet(options) {
641
+ const { baseOpacity, zoom, layerStyle, geometryType, layerType } = options;
642
+ const sanitizedOpacity = clampOpacity4(baseOpacity);
643
+ const zoomOpacity = calculateZoomBasedOpacity(zoom, sanitizedOpacity, layerType, geometryType);
644
+ const styleFillOpacity = typeof layerStyle?.fillOpacity === "number" ? clampOpacity4(layerStyle.fillOpacity) : 0.8;
645
+ return {
646
+ color: layerStyle?.color ?? layerStyle?.fillColor ?? "#2563eb",
647
+ weight: layerStyle?.weight ?? 2,
648
+ fillColor: layerStyle?.fillColor ?? layerStyle?.color ?? "#2563eb",
649
+ opacity: clampOpacity4(Math.max(0.35, zoomOpacity * 0.9)),
650
+ fillOpacity: clampOpacity4(zoomOpacity * styleFillOpacity)
651
+ };
652
+ }
653
+ function getRgbFromColor(color) {
654
+ const trimmed = color.trim();
655
+ if (trimmed.startsWith("#")) {
656
+ const hex = trimmed.replace("#", "");
657
+ const expanded = hex.length === 3 ? hex.split("").map((char) => `${char}${char}`).join("") : hex;
658
+ if (expanded.length === 6) {
659
+ const r = parseInt(expanded.slice(0, 2), 16);
660
+ const g = parseInt(expanded.slice(2, 4), 16);
661
+ const b = parseInt(expanded.slice(4, 6), 16);
662
+ return { r, g, b };
769
663
  }
664
+ }
665
+ const rgbMatch = trimmed.match(/rgba?\(([^)]+)\)/i);
666
+ if (rgbMatch) {
667
+ const [r, g, b] = rgbMatch[1].split(",").map((value) => parseFloat(value.trim())).slice(0, 3);
668
+ if ([r, g, b].every((value) => Number.isFinite(value))) {
669
+ return { r, g, b };
670
+ }
671
+ }
672
+ return null;
673
+ }
674
+ function getLabelTextStyles(color) {
675
+ const rgb = getRgbFromColor(color);
676
+ if (!rgb) {
677
+ return { color: "#0f172a", shadow: "0 1px 2px rgba(255, 255, 255, 0.6)" };
678
+ }
679
+ const luminance = (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b) / 255;
680
+ if (luminance > 0.6) {
681
+ return { color: "#0f172a", shadow: "0 1px 2px rgba(255, 255, 255, 0.7)" };
682
+ }
683
+ return { color: "#ffffff", shadow: "0 1px 2px rgba(0, 0, 0, 0.4)" };
684
+ }
685
+ function withAlpha(color, alpha) {
686
+ const trimmed = color.trim();
687
+ if (trimmed.startsWith("#")) {
688
+ const hex = trimmed.replace("#", "");
689
+ const expanded = hex.length === 3 ? hex.split("").map((char) => `${char}${char}`).join("") : hex;
690
+ if (expanded.length === 6) {
691
+ const r = parseInt(expanded.slice(0, 2), 16);
692
+ const g = parseInt(expanded.slice(2, 4), 16);
693
+ const b = parseInt(expanded.slice(4, 6), 16);
694
+ return `rgba(${r}, ${g}, ${b}, ${alpha})`;
695
+ }
696
+ }
697
+ if (trimmed.startsWith("rgb(")) {
698
+ const inner = trimmed.slice(4, -1);
699
+ return `rgba(${inner}, ${alpha})`;
700
+ }
701
+ if (trimmed.startsWith("rgba(")) {
702
+ const inner = trimmed.slice(5, -1).split(",").slice(0, 3).map((value) => value.trim());
703
+ return `rgba(${inner.join(", ")}, ${alpha})`;
704
+ }
705
+ return color;
706
+ }
707
+ function createCustomIcon(label, opacity, color) {
708
+ const size = 60;
709
+ const innerSize = 44;
710
+ const textStyles = getLabelTextStyles(color);
711
+ const safeLabel = escapeHtml(label);
712
+ const clampedOpacity = Math.min(1, Math.max(0.92, opacity));
713
+ const innerBackground = withAlpha(color, 0.9);
714
+ return import_leaflet2.default.divIcon({
715
+ className: "zenit-label-marker",
716
+ iconSize: [size, size],
717
+ iconAnchor: [size / 2, size / 2],
718
+ html: `
719
+ <div
720
+ title="${safeLabel}"
721
+ style="
722
+ width:${size}px;
723
+ height:${size}px;
724
+ border-radius:9999px;
725
+ background:rgba(255, 255, 255, 0.95);
726
+ border:3px solid rgba(255, 255, 255, 1);
727
+ display:flex;
728
+ align-items:center;
729
+ justify-content:center;
730
+ opacity:${clampedOpacity};
731
+ box-shadow:0 2px 6px rgba(0, 0, 0, 0.25);
732
+ pointer-events:none;
733
+ "
734
+ >
735
+ <div
736
+ style="
737
+ width:${innerSize}px;
738
+ height:${innerSize}px;
739
+ border-radius:9999px;
740
+ background:${innerBackground};
741
+ display:flex;
742
+ align-items:center;
743
+ justify-content:center;
744
+ box-shadow:inset 0 0 0 1px rgba(15, 23, 42, 0.12);
745
+ "
746
+ >
747
+ <span
748
+ style="
749
+ color:${textStyles.color};
750
+ font-size:20px;
751
+ font-weight:800;
752
+ text-shadow:${textStyles.shadow};
753
+ "
754
+ >
755
+ ${safeLabel}
756
+ </span>
757
+ </div>
758
+ </div>
759
+ `
760
+ });
761
+ }
762
+ function createLocationIcon() {
763
+ return import_leaflet2.default.divIcon({
764
+ className: "zenit-location-marker",
765
+ iconSize: [18, 18],
766
+ iconAnchor: [9, 9],
767
+ html: `
768
+ <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>
769
+ `
770
+ });
771
+ }
772
+
773
+ // src/react/map/location-control.tsx
774
+ var import_jsx_runtime2 = require("react/jsx-runtime");
775
+ var LOCATION_STYLE_ID = "zenit-location-control-styles";
776
+ var LOCATION_STYLES = `
777
+ .zenit-location-control {
778
+ display: flex;
779
+ flex-direction: column;
780
+ gap: 8px;
781
+ }
782
+
783
+ .zenit-location-button {
784
+ width: 42px;
785
+ height: 42px;
786
+ border-radius: 12px;
787
+ border: none;
788
+ background: #ffffff;
789
+ color: #0f172a;
790
+ box-shadow: 0 8px 18px rgba(15, 23, 42, 0.2);
791
+ display: inline-flex;
792
+ align-items: center;
793
+ justify-content: center;
794
+ cursor: pointer;
795
+ position: relative;
796
+ }
797
+
798
+ .zenit-location-button.zenit-location-button--tracking {
799
+ animation: zenitLocationPulse 1.8s infinite;
800
+ }
801
+
802
+ .zenit-location-button:hover {
803
+ background: #f8fafc;
804
+ }
805
+
806
+ .zenit-location-badge {
807
+ position: absolute;
808
+ top: -6px;
809
+ right: -6px;
810
+ width: 18px;
811
+ height: 18px;
812
+ border-radius: 999px;
813
+ background: #ef4444;
814
+ color: #fff;
815
+ font-size: 11px;
816
+ font-weight: 700;
817
+ display: inline-flex;
818
+ align-items: center;
819
+ justify-content: center;
820
+ box-shadow: 0 4px 10px rgba(239, 68, 68, 0.35);
821
+ }
822
+
823
+ .zenit-location-error {
824
+ background: rgba(15, 23, 42, 0.92);
825
+ color: #f8fafc;
826
+ border-radius: 10px;
827
+ padding: 8px 10px;
828
+ font-size: 12px;
829
+ max-width: 200px;
830
+ box-shadow: 0 10px 24px rgba(15, 23, 42, 0.3);
831
+ }
832
+
833
+ @keyframes zenitLocationPulse {
834
+ 0% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.35); }
835
+ 70% { box-shadow: 0 0 0 12px rgba(16, 185, 129, 0); }
836
+ 100% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); }
837
+ }
838
+ `;
839
+ var LocateIcon = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
840
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "12", cy: "12", r: "3" }),
841
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "12", y1: "2", x2: "12", y2: "5" }),
842
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "12", y1: "19", x2: "12", y2: "22" }),
843
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "2", y1: "12", x2: "5", y2: "12" }),
844
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "19", y1: "12", x2: "22", y2: "12" })
845
+ ] });
846
+ var LocationControl = ({ position = "bottomleft", zoom = 16 }) => {
847
+ const map = (0, import_react_leaflet2.useMap)();
848
+ const controlRef = (0, import_react2.useRef)(null);
849
+ const hasCenteredRef = (0, import_react2.useRef)(false);
850
+ const { isTracking, location, error, toggleTracking, clearError } = useGeolocation();
851
+ (0, import_react2.useEffect)(() => {
852
+ if (typeof document === "undefined") return;
853
+ if (document.getElementById(LOCATION_STYLE_ID)) return;
854
+ const styleTag = document.createElement("style");
855
+ styleTag.id = LOCATION_STYLE_ID;
856
+ styleTag.textContent = LOCATION_STYLES;
857
+ document.head.appendChild(styleTag);
858
+ }, []);
859
+ (0, import_react2.useEffect)(() => {
860
+ const control = import_leaflet3.default.control({ position });
861
+ control.onAdd = () => {
862
+ const container = import_leaflet3.default.DomUtil.create("div", "zenit-location-control");
863
+ import_leaflet3.default.DomEvent.disableClickPropagation(container);
864
+ controlRef.current = container;
865
+ return container;
866
+ };
867
+ control.addTo(map);
868
+ return () => {
869
+ control.remove();
870
+ controlRef.current = null;
871
+ };
872
+ }, [map, position]);
873
+ (0, import_react2.useEffect)(() => {
874
+ if (!location || !isTracking) return;
875
+ if (hasCenteredRef.current) return;
876
+ hasCenteredRef.current = true;
877
+ map.flyTo([location.lat, location.lon], zoom, { animate: true });
878
+ }, [isTracking, location, map, zoom]);
879
+ const markerIcon = (0, import_react2.useMemo)(() => createLocationIcon(), []);
880
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
881
+ controlRef.current && (0, import_react_dom.createPortal)(
882
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
883
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
884
+ "button",
885
+ {
886
+ type: "button",
887
+ className: `zenit-location-button${isTracking ? " zenit-location-button--tracking" : ""}`,
888
+ onClick: () => {
889
+ if (error) {
890
+ clearError();
891
+ }
892
+ toggleTracking();
893
+ },
894
+ "aria-label": isTracking ? "Detener ubicaci\xF3n" : "Mostrar mi ubicaci\xF3n",
895
+ children: [
896
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(LocateIcon, {}),
897
+ error && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "zenit-location-badge", children: "!" })
898
+ ]
899
+ }
900
+ ),
901
+ error && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "zenit-location-error", children: error.message || "No se pudo acceder a tu ubicaci\xF3n." })
902
+ ] }),
903
+ controlRef.current
904
+ ),
905
+ location && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
906
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_leaflet2.Marker, { position: [location.lat, location.lon], icon: markerIcon }),
907
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
908
+ import_react_leaflet2.Circle,
909
+ {
910
+ center: [location.lat, location.lon],
911
+ radius: location.accuracy,
912
+ pathOptions: { color: "#2563eb", fillColor: "#2563eb", fillOpacity: 0.15 }
913
+ }
914
+ )
915
+ ] })
916
+ ] });
917
+ };
918
+
919
+ // src/react/map/map-handlers.tsx
920
+ var import_react3 = require("react");
921
+ var import_react_leaflet3 = require("react-leaflet");
922
+ function computeBBoxFromGeojson(geojson) {
923
+ if (!geojson || !Array.isArray(geojson.features)) return null;
924
+ const coords = [];
925
+ const collect = (candidate) => {
926
+ if (!Array.isArray(candidate)) return;
927
+ if (candidate.length === 2 && typeof candidate[0] === "number" && typeof candidate[1] === "number" && Number.isFinite(candidate[0]) && Number.isFinite(candidate[1])) {
928
+ coords.push([candidate[0], candidate[1]]);
929
+ return;
930
+ }
931
+ candidate.forEach((item) => collect(item));
932
+ };
933
+ geojson.features.forEach((feature) => {
934
+ collect(feature.geometry?.coordinates);
935
+ });
936
+ if (coords.length === 0) return null;
937
+ const [firstLon, firstLat] = coords[0];
938
+ const bbox = { minLon: firstLon, minLat: firstLat, maxLon: firstLon, maxLat: firstLat };
939
+ coords.forEach(([lon, lat]) => {
940
+ bbox.minLon = Math.min(bbox.minLon, lon);
941
+ bbox.minLat = Math.min(bbox.minLat, lat);
942
+ bbox.maxLon = Math.max(bbox.maxLon, lon);
943
+ bbox.maxLat = Math.max(bbox.maxLat, lat);
944
+ });
945
+ return bbox;
946
+ }
947
+ function mergeBBoxes(bboxes) {
948
+ const valid = bboxes.filter((bbox) => !!bbox);
949
+ if (valid.length === 0) return null;
950
+ const first = valid[0];
951
+ return valid.slice(1).reduce(
952
+ (acc, bbox) => ({
953
+ minLon: Math.min(acc.minLon, bbox.minLon),
954
+ minLat: Math.min(acc.minLat, bbox.minLat),
955
+ maxLon: Math.max(acc.maxLon, bbox.maxLon),
956
+ maxLat: Math.max(acc.maxLat, bbox.maxLat)
957
+ }),
958
+ { ...first }
770
959
  );
771
- if (!entry) return null;
772
- return entry[1].trim();
773
960
  }
774
- function formatLabel(key) {
775
- return key.replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").trim();
961
+ var BBoxZoomHandler = ({
962
+ bbox,
963
+ geojson,
964
+ autoGeojson = [],
965
+ enabled = true
966
+ }) => {
967
+ const map = (0, import_react_leaflet3.useMap)();
968
+ const lastAppliedBBox = (0, import_react3.useRef)(null);
969
+ const lastUserInteracted = (0, import_react3.useRef)(false);
970
+ (0, import_react3.useEffect)(() => {
971
+ const handleInteraction = () => {
972
+ lastUserInteracted.current = true;
973
+ };
974
+ map.on("dragstart", handleInteraction);
975
+ map.on("zoomstart", handleInteraction);
976
+ return () => {
977
+ map.off("dragstart", handleInteraction);
978
+ map.off("zoomstart", handleInteraction);
979
+ };
980
+ }, [map]);
981
+ (0, import_react3.useEffect)(() => {
982
+ if (!enabled) return;
983
+ let resolvedBBox = bbox ?? null;
984
+ if (!resolvedBBox && geojson) {
985
+ resolvedBBox = computeBBoxFromGeojson(geojson);
986
+ }
987
+ if (!resolvedBBox && autoGeojson.length > 0) {
988
+ const bboxes = autoGeojson.map((collection) => computeBBoxFromGeojson(collection));
989
+ resolvedBBox = mergeBBoxes(bboxes);
990
+ }
991
+ if (!resolvedBBox) return;
992
+ const serialized = JSON.stringify(resolvedBBox);
993
+ if (lastAppliedBBox.current === serialized) return;
994
+ if (lastUserInteracted.current && !bbox && !geojson) {
995
+ lastUserInteracted.current = false;
996
+ return;
997
+ }
998
+ const bounds = [
999
+ [resolvedBBox.minLat, resolvedBBox.minLon],
1000
+ [resolvedBBox.maxLat, resolvedBBox.maxLon]
1001
+ ];
1002
+ map.fitBounds(bounds, { padding: [12, 12] });
1003
+ lastAppliedBBox.current = serialized;
1004
+ }, [autoGeojson, bbox, enabled, geojson, map]);
1005
+ return null;
1006
+ };
1007
+ var ZoomBasedOpacityHandler = ({ onZoomChange }) => {
1008
+ const map = (0, import_react_leaflet3.useMap)();
1009
+ (0, import_react3.useEffect)(() => {
1010
+ const handleZoom = () => {
1011
+ onZoomChange(map.getZoom());
1012
+ };
1013
+ map.on("zoomend", handleZoom);
1014
+ handleZoom();
1015
+ return () => {
1016
+ map.off("zoomend", handleZoom);
1017
+ };
1018
+ }, [map, onZoomChange]);
1019
+ return null;
1020
+ };
1021
+ var MapInstanceBridge = ({ onReady }) => {
1022
+ const map = (0, import_react_leaflet3.useMap)();
1023
+ (0, import_react3.useEffect)(() => {
1024
+ onReady(map);
1025
+ }, [map, onReady]);
1026
+ return null;
1027
+ };
1028
+
1029
+ // src/react/ZenitMap.tsx
1030
+ var import_jsx_runtime3 = require("react/jsx-runtime");
1031
+ var DEFAULT_CENTER = [0, 0];
1032
+ var DEFAULT_ZOOM = 3;
1033
+ var LABELS_PANE_NAME = "zenit-labels-pane";
1034
+ function isRecord(value) {
1035
+ return typeof value === "object" && value !== null;
1036
+ }
1037
+ function isGeoJsonFeatureCollection(value) {
1038
+ if (!isRecord(value)) return false;
1039
+ const features = value.features;
1040
+ if (!Array.isArray(features)) return false;
1041
+ const type = value.type;
1042
+ return type === void 0 || type === "FeatureCollection";
776
1043
  }
777
- function createPopupContent(properties) {
778
- const headerText = extractPopupHeader(properties);
779
- const entries = Object.entries(properties).filter(([key, value]) => {
780
- if (!shouldIncludePopupEntry(key, value)) return false;
781
- if (headerText && POPUP_HEADER_KEYS.includes(key.trim().toLowerCase())) {
782
- return false;
783
- }
784
- return true;
785
- });
786
- if (entries.length === 0) {
787
- return `<div class="zenit-popup-card"><div class="zenit-popup-empty">Sin datos disponibles</div></div>`;
788
- }
789
- const descriptionEntry = entries.find(([key]) => isDescriptionKey(key));
790
- const otherEntries = entries.filter(([key]) => !isDescriptionKey(key));
791
- let rowsHtml = "";
792
- if (descriptionEntry) {
793
- const [key, value] = descriptionEntry;
794
- const label = escapeHtml(formatLabel(key));
795
- const valueHtml = renderPopupValue(value);
796
- rowsHtml += `
797
- <div class="zenit-popup-row zenit-popup-description">
798
- <div class="zenit-popup-label">${label}</div>
799
- <div class="zenit-popup-value">${valueHtml}</div>
800
- </div>
801
- `;
1044
+ function extractGeoJsonFeatureCollection(value) {
1045
+ if (isRecord(value) && "data" in value) {
1046
+ const data = value.data;
1047
+ return isGeoJsonFeatureCollection(data) ? data : null;
802
1048
  }
803
- rowsHtml += otherEntries.map(([key, value]) => {
804
- const label = escapeHtml(formatLabel(key));
805
- const valueHtml = renderPopupValue(value);
806
- return `
807
- <div class="zenit-popup-row">
808
- <div class="zenit-popup-label">${label}</div>
809
- <div class="zenit-popup-value">${valueHtml}</div>
810
- </div>
811
- `;
812
- }).join("");
813
- const headerHtml = headerText ? `<div class="zenit-popup-header"><div class="zenit-popup-title">${escapeHtml(
814
- headerText
815
- )}</div></div>` : "";
816
- return `<div class="zenit-popup-card">${headerHtml}${rowsHtml}</div>`;
1049
+ return isGeoJsonFeatureCollection(value) ? value : null;
817
1050
  }
818
- function withAlpha(color, alpha) {
819
- const trimmed = color.trim();
820
- if (trimmed.startsWith("#")) {
821
- const hex = trimmed.replace("#", "");
822
- const expanded = hex.length === 3 ? hex.split("").map((char) => `${char}${char}`).join("") : hex;
823
- if (expanded.length === 6) {
824
- const r = parseInt(expanded.slice(0, 2), 16);
825
- const g = parseInt(expanded.slice(2, 4), 16);
826
- const b = parseInt(expanded.slice(4, 6), 16);
827
- return `rgba(${r}, ${g}, ${b}, ${alpha})`;
828
- }
829
- }
830
- if (trimmed.startsWith("rgb(")) {
831
- const inner = trimmed.slice(4, -1);
832
- return `rgba(${inner}, ${alpha})`;
833
- }
834
- if (trimmed.startsWith("rgba(")) {
835
- const inner = trimmed.slice(5, -1).split(",").slice(0, 3).map((value) => value.trim());
836
- return `rgba(${inner.join(", ")}, ${alpha})`;
837
- }
838
- return color;
1051
+ function getFeatureLayerId(feature) {
1052
+ const layerId = feature?.properties?.__zenit_layerId ?? feature?.properties?.layerId ?? feature?.properties?.layer_id;
1053
+ if (layerId === void 0 || layerId === null) return null;
1054
+ return layerId;
839
1055
  }
840
- function getRgbFromColor(color) {
841
- const trimmed = color.trim();
842
- if (trimmed.startsWith("#")) {
843
- const hex = trimmed.replace("#", "");
844
- const expanded = hex.length === 3 ? hex.split("").map((char) => `${char}${char}`).join("") : hex;
845
- if (expanded.length === 6) {
846
- const r = parseInt(expanded.slice(0, 2), 16);
847
- const g = parseInt(expanded.slice(2, 4), 16);
848
- const b = parseInt(expanded.slice(4, 6), 16);
849
- return { r, g, b };
850
- }
1056
+ var DESCRIPTION_KEYS = /* @__PURE__ */ new Set(["descripcion", "description"]);
1057
+ function normalizeDescriptionValue(value) {
1058
+ if (value === void 0 || value === null) return null;
1059
+ if (typeof value === "string") {
1060
+ const trimmed = value.trim();
1061
+ return trimmed ? trimmed : null;
851
1062
  }
852
- const rgbMatch = trimmed.match(/rgba?\(([^)]+)\)/i);
853
- if (rgbMatch) {
854
- const [r, g, b] = rgbMatch[1].split(",").map((value) => parseFloat(value.trim())).slice(0, 3);
855
- if ([r, g, b].every((value) => Number.isFinite(value))) {
856
- return { r, g, b };
857
- }
1063
+ if (typeof value === "number" || typeof value === "boolean") {
1064
+ return String(value);
858
1065
  }
859
1066
  return null;
860
1067
  }
861
- function getLabelTextStyles(color) {
862
- const rgb = getRgbFromColor(color);
863
- if (!rgb) {
864
- return { color: "#0f172a", shadow: "0 1px 2px rgba(255, 255, 255, 0.6)" };
865
- }
866
- const luminance = (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b) / 255;
867
- if (luminance > 0.6) {
868
- return { color: "#0f172a", shadow: "0 1px 2px rgba(255, 255, 255, 0.7)" };
869
- }
870
- return { color: "#ffffff", shadow: "0 1px 2px rgba(0, 0, 0, 0.4)" };
1068
+ function extractDescriptionValue(properties) {
1069
+ if (!properties) return null;
1070
+ const matches = Object.entries(properties).find(
1071
+ ([key]) => DESCRIPTION_KEYS.has(key.toLowerCase())
1072
+ );
1073
+ if (!matches) return null;
1074
+ return normalizeDescriptionValue(matches[1]);
871
1075
  }
872
1076
  function getFeatureStyleOverrides(feature) {
873
1077
  const candidate = feature?.properties?._style;
@@ -886,25 +1090,6 @@ function buildFeaturePopupHtml(feature) {
886
1090
  const rendered = createPopupContent(properties);
887
1091
  return rendered ? rendered : null;
888
1092
  }
889
- var POINT_GEOMETRY_TYPES = /* @__PURE__ */ new Set(["Point", "MultiPoint"]);
890
- function getGeometryType(feature) {
891
- const t = feature?.geometry?.type;
892
- return typeof t === "string" ? t : null;
893
- }
894
- function isPointGeometry(feature) {
895
- const geometryType = getGeometryType(feature);
896
- return geometryType !== null && POINT_GEOMETRY_TYPES.has(geometryType);
897
- }
898
- function isNonPointGeometry(feature) {
899
- const geometryType = getGeometryType(feature);
900
- return geometryType !== null && !POINT_GEOMETRY_TYPES.has(geometryType);
901
- }
902
- function buildFeatureCollection(features) {
903
- return {
904
- type: "FeatureCollection",
905
- features
906
- };
907
- }
908
1093
  function pickIntersectFeature(baseFeature, candidates) {
909
1094
  if (!Array.isArray(candidates) || candidates.length === 0) return null;
910
1095
  const baseId = baseFeature?.id;
@@ -931,81 +1116,7 @@ function normalizeCenterTuple(center) {
931
1116
  }
932
1117
  return null;
933
1118
  }
934
- var FitToBounds = ({ bbox }) => {
935
- const mapInstance = (0, import_react_leaflet.useMap)();
936
- const lastAppliedBBox = (0, import_react.useRef)(null);
937
- (0, import_react.useEffect)(() => {
938
- const targetBBox = bbox;
939
- if (!targetBBox) return;
940
- const serialized = JSON.stringify(targetBBox);
941
- if (lastAppliedBBox.current === serialized) return;
942
- const bounds = [
943
- [targetBBox.minLat, targetBBox.minLon],
944
- [targetBBox.maxLat, targetBBox.maxLon]
945
- ];
946
- mapInstance.fitBounds(bounds, { padding: [12, 12] });
947
- lastAppliedBBox.current = serialized;
948
- }, [bbox, mapInstance]);
949
- return null;
950
- };
951
- var AutoFitToBounds = ({
952
- bbox,
953
- enabled = true
954
- }) => {
955
- const mapInstance = (0, import_react_leaflet.useMap)();
956
- const lastAutoBBoxApplied = (0, import_react.useRef)(null);
957
- const lastUserInteracted = (0, import_react.useRef)(false);
958
- (0, import_react.useEffect)(() => {
959
- if (!enabled) return;
960
- const handleInteraction = () => {
961
- lastUserInteracted.current = true;
962
- };
963
- mapInstance.on("dragstart", handleInteraction);
964
- mapInstance.on("zoomstart", handleInteraction);
965
- return () => {
966
- mapInstance.off("dragstart", handleInteraction);
967
- mapInstance.off("zoomstart", handleInteraction);
968
- };
969
- }, [enabled, mapInstance]);
970
- (0, import_react.useEffect)(() => {
971
- if (!enabled) return;
972
- if (!bbox) return;
973
- const serialized = JSON.stringify(bbox);
974
- if (lastAutoBBoxApplied.current === serialized) return;
975
- if (lastUserInteracted.current) {
976
- lastUserInteracted.current = false;
977
- }
978
- const bounds = [
979
- [bbox.minLat, bbox.minLon],
980
- [bbox.maxLat, bbox.maxLon]
981
- ];
982
- mapInstance.fitBounds(bounds, { padding: [12, 12] });
983
- lastAutoBBoxApplied.current = serialized;
984
- }, [bbox, enabled, mapInstance]);
985
- return null;
986
- };
987
- var ZoomBasedOpacityHandler = ({ onZoomChange }) => {
988
- const mapInstance = (0, import_react_leaflet.useMap)();
989
- (0, import_react.useEffect)(() => {
990
- const handleZoom = () => {
991
- onZoomChange(mapInstance.getZoom());
992
- };
993
- mapInstance.on("zoomend", handleZoom);
994
- handleZoom();
995
- return () => {
996
- mapInstance.off("zoomend", handleZoom);
997
- };
998
- }, [mapInstance, onZoomChange]);
999
- return null;
1000
- };
1001
- var MapInstanceBridge = ({ onReady }) => {
1002
- const mapInstance = (0, import_react_leaflet.useMap)();
1003
- (0, import_react.useEffect)(() => {
1004
- onReady(mapInstance);
1005
- }, [mapInstance, onReady]);
1006
- return null;
1007
- };
1008
- var ZenitMap = (0, import_react.forwardRef)(({
1119
+ var ZenitMap = (0, import_react4.forwardRef)(({
1009
1120
  client,
1010
1121
  mapId,
1011
1122
  height = "500px",
@@ -1030,21 +1141,21 @@ var ZenitMap = (0, import_react.forwardRef)(({
1030
1141
  onZoomChange,
1031
1142
  onMapReady
1032
1143
  }, ref) => {
1033
- const [map, setMap] = (0, import_react.useState)(null);
1034
- const [layers, setLayers] = (0, import_react.useState)([]);
1035
- const [effectiveStates, setEffectiveStates] = (0, import_react.useState)([]);
1036
- const [loadingMap, setLoadingMap] = (0, import_react.useState)(false);
1037
- const [mapError, setMapError] = (0, import_react.useState)(null);
1038
- const [mapInstance, setMapInstance] = (0, import_react.useState)(null);
1039
- const [panesReady, setPanesReady] = (0, import_react.useState)(false);
1040
- const [currentZoom, setCurrentZoom] = (0, import_react.useState)(initialZoom ?? DEFAULT_ZOOM);
1041
- const [isPopupOpen, setIsPopupOpen] = (0, import_react.useState)(false);
1042
- const [isMobile, setIsMobile] = (0, import_react.useState)(() => {
1144
+ const [map, setMap] = (0, import_react4.useState)(null);
1145
+ const [layers, setLayers] = (0, import_react4.useState)([]);
1146
+ const [effectiveStates, setEffectiveStates] = (0, import_react4.useState)([]);
1147
+ const [loadingMap, setLoadingMap] = (0, import_react4.useState)(false);
1148
+ const [mapError, setMapError] = (0, import_react4.useState)(null);
1149
+ const [mapInstance, setMapInstance] = (0, import_react4.useState)(null);
1150
+ const [panesReady, setPanesReady] = (0, import_react4.useState)(false);
1151
+ const [currentZoom, setCurrentZoom] = (0, import_react4.useState)(initialZoom ?? DEFAULT_ZOOM);
1152
+ const [isPopupOpen, setIsPopupOpen] = (0, import_react4.useState)(false);
1153
+ const [isMobile, setIsMobile] = (0, import_react4.useState)(() => {
1043
1154
  if (typeof window === "undefined") return false;
1044
1155
  return window.matchMedia("(max-width: 768px)").matches;
1045
1156
  });
1046
- const normalizedLayers = (0, import_react.useMemo)(() => normalizeMapLayers(map), [map]);
1047
- (0, import_react.useEffect)(() => {
1157
+ const normalizedLayers = (0, import_react4.useMemo)(() => normalizeMapLayers(map), [map]);
1158
+ (0, import_react4.useEffect)(() => {
1048
1159
  if (typeof window === "undefined") return;
1049
1160
  const mql = window.matchMedia("(max-width: 768px)");
1050
1161
  const onChange = (e) => {
@@ -1062,17 +1173,17 @@ var ZenitMap = (0, import_react.forwardRef)(({
1062
1173
  }
1063
1174
  return;
1064
1175
  }, []);
1065
- (0, import_react.useEffect)(() => {
1176
+ (0, import_react4.useEffect)(() => {
1066
1177
  if (featureInfoMode === "popup") {
1067
1178
  ensurePopupStyles();
1068
1179
  }
1069
1180
  }, [featureInfoMode]);
1070
- (0, import_react.useEffect)(() => {
1181
+ (0, import_react4.useEffect)(() => {
1071
1182
  if (featureInfoMode !== "popup") {
1072
1183
  setIsPopupOpen(false);
1073
1184
  }
1074
1185
  }, [featureInfoMode]);
1075
- (0, import_react.useEffect)(() => {
1186
+ (0, import_react4.useEffect)(() => {
1076
1187
  if (!mapInstance) return;
1077
1188
  const popupPane = mapInstance.getPane("popupPane");
1078
1189
  if (popupPane) {
@@ -1081,7 +1192,7 @@ var ZenitMap = (0, import_react.forwardRef)(({
1081
1192
  const labelsPane = mapInstance.getPane(LABELS_PANE_NAME) ?? mapInstance.createPane(LABELS_PANE_NAME);
1082
1193
  labelsPane.style.zIndex = "600";
1083
1194
  }, [mapInstance]);
1084
- (0, import_react.useEffect)(() => {
1195
+ (0, import_react4.useEffect)(() => {
1085
1196
  if (!mapInstance) return;
1086
1197
  const handlePopupOpen = () => setIsPopupOpen(true);
1087
1198
  const handlePopupClose = () => setIsPopupOpen(false);
@@ -1092,7 +1203,7 @@ var ZenitMap = (0, import_react.forwardRef)(({
1092
1203
  mapInstance.off("popupclose", handlePopupClose);
1093
1204
  };
1094
1205
  }, [mapInstance]);
1095
- const layerStyleIndex = (0, import_react.useMemo)(() => {
1206
+ const layerStyleIndex = (0, import_react4.useMemo)(() => {
1096
1207
  const index = /* @__PURE__ */ new Map();
1097
1208
  (map?.mapLayers ?? []).forEach((entry) => {
1098
1209
  const layerStyle = entry.layer?.style ?? entry.mapLayer?.layer?.style ?? entry.style ?? null;
@@ -1103,7 +1214,7 @@ var ZenitMap = (0, import_react.forwardRef)(({
1103
1214
  });
1104
1215
  return index;
1105
1216
  }, [map]);
1106
- const labelKeyIndex = (0, import_react.useMemo)(() => {
1217
+ const labelKeyIndex = (0, import_react4.useMemo)(() => {
1107
1218
  const index = /* @__PURE__ */ new Map();
1108
1219
  normalizedLayers.forEach((entry) => {
1109
1220
  const label = entry.layer?.label ?? entry.mapLayer?.label ?? entry.mapLayer.layerConfig?.label;
@@ -1113,7 +1224,7 @@ var ZenitMap = (0, import_react.forwardRef)(({
1113
1224
  });
1114
1225
  return index;
1115
1226
  }, [normalizedLayers]);
1116
- const layerMetaIndex = (0, import_react.useMemo)(() => {
1227
+ const layerMetaIndex = (0, import_react4.useMemo)(() => {
1117
1228
  const index = /* @__PURE__ */ new Map();
1118
1229
  normalizedLayers.forEach((entry) => {
1119
1230
  index.set(String(entry.layerId), {
@@ -1123,7 +1234,7 @@ var ZenitMap = (0, import_react.forwardRef)(({
1123
1234
  });
1124
1235
  return index;
1125
1236
  }, [normalizedLayers]);
1126
- const overlayStyleFunction = (0, import_react.useMemo)(() => {
1237
+ const overlayStyleFunction = (0, import_react4.useMemo)(() => {
1127
1238
  return (feature) => {
1128
1239
  const featureLayerId = getFeatureLayerId(feature);
1129
1240
  const featureStyleOverrides = getFeatureStyleOverrides(feature);
@@ -1142,11 +1253,15 @@ var ZenitMap = (0, import_react.forwardRef)(({
1142
1253
  return defaultOptions;
1143
1254
  };
1144
1255
  }, [layerStyleIndex, mapLayers, overlayStyle]);
1145
- const [baseStates, setBaseStates] = (0, import_react.useState)([]);
1146
- const [mapOverrides, setMapOverrides] = (0, import_react.useState)([]);
1147
- const [controlOverrides, setControlOverrides] = (0, import_react.useState)([]);
1148
- const [uiOverrides, setUiOverrides] = (0, import_react.useState)([]);
1149
- (0, import_react.useEffect)(() => {
1256
+ const overlayStyleFn = (0, import_react4.useCallback)(
1257
+ (feature, _layerType, _baseOpacity) => overlayStyleFunction(feature),
1258
+ [overlayStyleFunction]
1259
+ );
1260
+ const [baseStates, setBaseStates] = (0, import_react4.useState)([]);
1261
+ const [mapOverrides, setMapOverrides] = (0, import_react4.useState)([]);
1262
+ const [controlOverrides, setControlOverrides] = (0, import_react4.useState)([]);
1263
+ const [uiOverrides, setUiOverrides] = (0, import_react4.useState)([]);
1264
+ (0, import_react4.useEffect)(() => {
1150
1265
  let isMounted = true;
1151
1266
  setLoadingMap(true);
1152
1267
  setMapError(null);
@@ -1169,7 +1284,7 @@ var ZenitMap = (0, import_react.forwardRef)(({
1169
1284
  isMounted = false;
1170
1285
  };
1171
1286
  }, [client.maps, mapId, onError, onLoadingChange]);
1172
- (0, import_react.useEffect)(() => {
1287
+ (0, import_react4.useEffect)(() => {
1173
1288
  if (normalizedLayers.length === 0) {
1174
1289
  setLayers([]);
1175
1290
  setBaseStates([]);
@@ -1200,7 +1315,7 @@ var ZenitMap = (0, import_react.forwardRef)(({
1200
1315
  setMapOverrides(initialOverrides);
1201
1316
  setUiOverrides([]);
1202
1317
  }, [normalizedLayers]);
1203
- (0, import_react.useEffect)(() => {
1318
+ (0, import_react4.useEffect)(() => {
1204
1319
  if (!layerControls) {
1205
1320
  setControlOverrides([]);
1206
1321
  return;
@@ -1212,7 +1327,7 @@ var ZenitMap = (0, import_react.forwardRef)(({
1212
1327
  }));
1213
1328
  setControlOverrides(overrides);
1214
1329
  }, [layerControls]);
1215
- (0, import_react.useEffect)(() => {
1330
+ (0, import_react4.useEffect)(() => {
1216
1331
  if (layerStates) {
1217
1332
  return;
1218
1333
  }
@@ -1222,12 +1337,12 @@ var ZenitMap = (0, import_react.forwardRef)(({
1222
1337
  onLayerStateChange?.(reset);
1223
1338
  }
1224
1339
  }, [baseStates, effectiveStates.length, layerControls, layerStates, onLayerStateChange]);
1225
- (0, import_react.useEffect)(() => {
1340
+ (0, import_react4.useEffect)(() => {
1226
1341
  if (layerStates) {
1227
1342
  setEffectiveStates(layerStates);
1228
1343
  }
1229
1344
  }, [layerStates]);
1230
- (0, import_react.useEffect)(() => {
1345
+ (0, import_react4.useEffect)(() => {
1231
1346
  if (layerStates) {
1232
1347
  return;
1233
1348
  }
@@ -1240,11 +1355,11 @@ var ZenitMap = (0, import_react.forwardRef)(({
1240
1355
  setEffectiveStates(next);
1241
1356
  onLayerStateChange?.(next);
1242
1357
  }, [baseStates, controlOverrides, layerStates, mapOverrides, onLayerStateChange, uiOverrides]);
1243
- (0, import_react.useEffect)(() => {
1358
+ (0, import_react4.useEffect)(() => {
1244
1359
  if (!Array.isArray(layerControls) || layerControls.length > 0) return;
1245
1360
  setUiOverrides([]);
1246
1361
  }, [layerControls]);
1247
- (0, import_react.useEffect)(() => {
1362
+ (0, import_react4.useEffect)(() => {
1248
1363
  if (layerStates) {
1249
1364
  return;
1250
1365
  }
@@ -1264,18 +1379,13 @@ var ZenitMap = (0, import_react.forwardRef)(({
1264
1379
  return [...filtered, nextEntry];
1265
1380
  });
1266
1381
  };
1267
- const updateOpacityFromUi = (0, import_react.useCallback)(
1382
+ const updateOpacityFromUi = (0, import_react4.useCallback)(
1268
1383
  (layerId, uiOpacity) => {
1269
1384
  const meta = layerMetaIndex.get(String(layerId));
1270
- const zoomFactor = getLayerZoomOpacityFactor(
1385
+ const baseOpacity = clampOpacity3(uiOpacity);
1386
+ const effectiveOpacity = calculateZoomBasedOpacity(
1271
1387
  currentZoom,
1272
- meta?.layerType,
1273
- meta?.geometryType
1274
- );
1275
- const baseOpacity = clampOpacity3(uiOpacity / zoomFactor);
1276
- const effectiveOpacity = getEffectiveLayerOpacity(
1277
1388
  baseOpacity,
1278
- currentZoom,
1279
1389
  meta?.layerType,
1280
1390
  meta?.geometryType
1281
1391
  );
@@ -1308,7 +1418,7 @@ var ZenitMap = (0, import_react.forwardRef)(({
1308
1418
  },
1309
1419
  [currentZoom, effectiveStates, layerMetaIndex, layerStates, onLayerStateChange]
1310
1420
  );
1311
- const center = (0, import_react.useMemo)(() => {
1421
+ const center = (0, import_react4.useMemo)(() => {
1312
1422
  if (initialCenter) {
1313
1423
  return initialCenter;
1314
1424
  }
@@ -1319,36 +1429,30 @@ var ZenitMap = (0, import_react.forwardRef)(({
1319
1429
  return DEFAULT_CENTER;
1320
1430
  }, [initialCenter, map?.settings?.center]);
1321
1431
  const zoom = initialZoom ?? map?.settings?.zoom ?? DEFAULT_ZOOM;
1322
- (0, import_react.useEffect)(() => {
1432
+ (0, import_react4.useEffect)(() => {
1323
1433
  setCurrentZoom(zoom);
1324
1434
  }, [zoom]);
1325
- const decoratedLayers = (0, import_react.useMemo)(() => {
1435
+ const decoratedLayers = (0, import_react4.useMemo)(() => {
1326
1436
  return layers.map((layer) => ({
1327
1437
  ...layer,
1328
1438
  effective: effectiveStates.find((state) => state.layerId === layer.mapLayer.layerId),
1329
1439
  data: layerGeojson?.[layer.mapLayer.layerId] ?? layerGeojson?.[String(layer.mapLayer.layerId)] ?? null
1330
1440
  }));
1331
1441
  }, [effectiveStates, layerGeojson, layers]);
1332
- const orderedLayers = (0, import_react.useMemo)(() => {
1442
+ const orderedLayers = (0, import_react4.useMemo)(() => {
1333
1443
  return [...decoratedLayers].filter((layer) => layer.effective?.visible && layer.data).sort((a, b) => a.displayOrder - b.displayOrder);
1334
1444
  }, [decoratedLayers]);
1335
- const explicitZoomBBox = (0, import_react.useMemo)(() => {
1336
- if (zoomToBbox) return zoomToBbox;
1337
- if (zoomToGeojson) return computeBBoxFromGeojson(zoomToGeojson);
1338
- return null;
1339
- }, [zoomToBbox, zoomToGeojson]);
1340
- const autoZoomBBox = (0, import_react.useMemo)(() => {
1341
- if (explicitZoomBBox) return null;
1342
- const visibleBBoxes = orderedLayers.map((layer) => computeBBoxFromGeojson(layer.data));
1343
- return mergeBBoxes(visibleBBoxes);
1344
- }, [explicitZoomBBox, orderedLayers]);
1345
- const resolveLayerStyle = (0, import_react.useCallback)(
1445
+ const autoZoomGeojson = (0, import_react4.useMemo)(
1446
+ () => orderedLayers.map((layer) => layer.data).filter((collection) => !!collection),
1447
+ [orderedLayers]
1448
+ );
1449
+ const resolveLayerStyle = (0, import_react4.useCallback)(
1346
1450
  (layerId) => {
1347
1451
  return getStyleByLayerId(layerId, mapLayers) ?? layerStyleIndex.get(String(layerId)) ?? null;
1348
1452
  },
1349
1453
  [layerStyleIndex, mapLayers]
1350
1454
  );
1351
- const labelMarkers = (0, import_react.useMemo)(() => {
1455
+ const labelMarkers = (0, import_react4.useMemo)(() => {
1352
1456
  const markers = [];
1353
1457
  decoratedLayers.forEach((layerState) => {
1354
1458
  if (!layerState.effective?.visible) return;
@@ -1358,7 +1462,14 @@ var ZenitMap = (0, import_react.forwardRef)(({
1358
1462
  if (!data) return;
1359
1463
  const resolvedStyle = resolveLayerStyle(layerState.mapLayer.layerId);
1360
1464
  const layerColor = resolvedStyle?.fillColor ?? resolvedStyle?.color ?? "rgba(37, 99, 235, 1)";
1361
- const opacity = layerState.effective?.opacity ?? 1;
1465
+ const meta = layerMetaIndex.get(String(layerState.mapLayer.layerId));
1466
+ const baseOpacity = layerState.effective?.baseOpacity ?? layerState.effective?.opacity ?? 1;
1467
+ const opacity = calculateZoomBasedOpacity(
1468
+ currentZoom,
1469
+ baseOpacity,
1470
+ meta?.layerType,
1471
+ meta?.geometryType
1472
+ );
1362
1473
  data.features.forEach((feature, index) => {
1363
1474
  const properties = feature.properties;
1364
1475
  const value = properties?.[labelKey];
@@ -1377,8 +1488,8 @@ var ZenitMap = (0, import_react.forwardRef)(({
1377
1488
  });
1378
1489
  });
1379
1490
  return markers;
1380
- }, [decoratedLayers, labelKeyIndex, resolveLayerStyle]);
1381
- const ensureLayerPanes = (0, import_react.useCallback)(
1491
+ }, [currentZoom, decoratedLayers, labelKeyIndex, layerMetaIndex, resolveLayerStyle]);
1492
+ const ensureLayerPanes = (0, import_react4.useCallback)(
1382
1493
  (targetMap, targetLayers) => {
1383
1494
  const baseZIndex = 400;
1384
1495
  targetLayers.forEach((layer) => {
@@ -1394,7 +1505,7 @@ var ZenitMap = (0, import_react.forwardRef)(({
1394
1505
  },
1395
1506
  []
1396
1507
  );
1397
- const handleMapReady = (0, import_react.useCallback)(
1508
+ const handleMapReady = (0, import_react4.useCallback)(
1398
1509
  (instance) => {
1399
1510
  setPanesReady(false);
1400
1511
  setMapInstance(instance);
@@ -1402,7 +1513,7 @@ var ZenitMap = (0, import_react.forwardRef)(({
1402
1513
  },
1403
1514
  [onMapReady]
1404
1515
  );
1405
- (0, import_react.useEffect)(() => {
1516
+ (0, import_react4.useEffect)(() => {
1406
1517
  if (!mapInstance) {
1407
1518
  setPanesReady(false);
1408
1519
  return;
@@ -1422,19 +1533,19 @@ var ZenitMap = (0, import_react.forwardRef)(({
1422
1533
  setPanesReady(true);
1423
1534
  }
1424
1535
  }, [mapInstance, orderedLayers, ensureLayerPanes]);
1425
- const overlayOnEachFeature = (0, import_react.useMemo)(() => {
1536
+ const overlayOnEachFeature = (0, import_react4.useMemo)(() => {
1426
1537
  return (feature, layer) => {
1427
1538
  const layerId = getFeatureLayerId(feature) ?? void 0;
1428
1539
  const geometryType = feature?.geometry?.type;
1429
- const isPointFeature = geometryType === "Point" || geometryType === "MultiPoint" || layer instanceof import_leaflet.default.CircleMarker;
1430
- const originalStyle = layer instanceof import_leaflet.default.Path ? {
1540
+ const isPointFeature = geometryType === "Point" || geometryType === "MultiPoint" || layer instanceof import_leaflet4.default.CircleMarker;
1541
+ const originalStyle = layer instanceof import_leaflet4.default.Path ? {
1431
1542
  color: layer.options.color,
1432
1543
  weight: layer.options.weight,
1433
1544
  fillColor: layer.options.fillColor,
1434
1545
  opacity: layer.options.opacity,
1435
1546
  fillOpacity: layer.options.fillOpacity
1436
1547
  } : null;
1437
- const originalRadius = layer instanceof import_leaflet.default.CircleMarker ? layer.getRadius() : null;
1548
+ const originalRadius = layer instanceof import_leaflet4.default.CircleMarker ? layer.getRadius() : null;
1438
1549
  if (featureInfoMode === "popup") {
1439
1550
  const content = buildFeaturePopupHtml(feature);
1440
1551
  if (content) {
@@ -1443,11 +1554,10 @@ var ZenitMap = (0, import_react.forwardRef)(({
1443
1554
  maxWidth,
1444
1555
  minWidth,
1445
1556
  maxHeight,
1446
- className: "zenit-leaflet-popup custom-leaflet-popup",
1557
+ className: "custom-leaflet-popup",
1447
1558
  autoPan: true,
1448
1559
  closeButton: true,
1449
- keepInView: true,
1450
- offset: import_leaflet.default.point(0, -24)
1560
+ keepInView: true
1451
1561
  });
1452
1562
  }
1453
1563
  }
@@ -1492,7 +1602,7 @@ var ZenitMap = (0, import_react.forwardRef)(({
1492
1602
  onFeatureClick?.(feature, layerId);
1493
1603
  });
1494
1604
  layer.on("mouseover", () => {
1495
- if (layer instanceof import_leaflet.default.Path && originalStyle) {
1605
+ if (layer instanceof import_leaflet4.default.Path && originalStyle) {
1496
1606
  layer.setStyle({
1497
1607
  ...originalStyle,
1498
1608
  weight: (originalStyle.weight ?? 2) + 1,
@@ -1500,16 +1610,16 @@ var ZenitMap = (0, import_react.forwardRef)(({
1500
1610
  fillOpacity: Math.min(1, (originalStyle.fillOpacity ?? 0.8) + 0.1)
1501
1611
  });
1502
1612
  }
1503
- if (layer instanceof import_leaflet.default.CircleMarker && typeof originalRadius === "number") {
1613
+ if (layer instanceof import_leaflet4.default.CircleMarker && typeof originalRadius === "number") {
1504
1614
  layer.setRadius(originalRadius + 1);
1505
1615
  }
1506
1616
  onFeatureHover?.(feature, layerId);
1507
1617
  });
1508
1618
  layer.on("mouseout", () => {
1509
- if (layer instanceof import_leaflet.default.Path && originalStyle) {
1619
+ if (layer instanceof import_leaflet4.default.Path && originalStyle) {
1510
1620
  layer.setStyle(originalStyle);
1511
1621
  }
1512
- if (layer instanceof import_leaflet.default.CircleMarker && typeof originalRadius === "number") {
1622
+ if (layer instanceof import_leaflet4.default.CircleMarker && typeof originalRadius === "number") {
1513
1623
  layer.setRadius(originalRadius);
1514
1624
  }
1515
1625
  });
@@ -1521,80 +1631,20 @@ var ZenitMap = (0, import_react.forwardRef)(({
1521
1631
  const resolvedStyle = featureStyleOverrides ? { ...style ?? {}, ...featureStyleOverrides } : style;
1522
1632
  const geometryType = feature?.geometry?.type;
1523
1633
  const resolvedLayerType = layerType ?? geometryType;
1524
- const sanitizedBaseOpacity = clampOpacity3(baseOpacity);
1525
- const normalizedStyleFill = typeof resolvedStyle?.fillOpacity === "number" ? clampOpacity3(resolvedStyle.fillOpacity) : 0.8;
1526
- const effectiveOpacity = getEffectiveLayerOpacity(
1527
- sanitizedBaseOpacity,
1528
- currentZoom,
1529
- resolvedLayerType,
1530
- geometryType
1531
- );
1532
- const fillOpacity = clampOpacity3(effectiveOpacity * normalizedStyleFill);
1533
- const strokeOpacity = clampOpacity3(Math.max(0.35, effectiveOpacity * 0.9));
1534
- return {
1535
- color: resolvedStyle?.color ?? resolvedStyle?.fillColor ?? "#2563eb",
1536
- weight: resolvedStyle?.weight ?? 2,
1537
- fillColor: resolvedStyle?.fillColor ?? resolvedStyle?.color ?? "#2563eb",
1538
- opacity: strokeOpacity,
1539
- fillOpacity
1634
+ return layerStyleToLeaflet({
1635
+ baseOpacity,
1636
+ zoom: currentZoom,
1637
+ layerStyle: resolvedStyle,
1638
+ geometryType,
1639
+ layerType: resolvedLayerType
1640
+ });
1641
+ };
1642
+ const makeStyleFnForLayer = (layerId) => {
1643
+ return (feature, layerType, baseOpacity) => {
1644
+ return buildLayerStyle(layerId, baseOpacity ?? 1, feature, layerType);
1540
1645
  };
1541
1646
  };
1542
- const buildLabelIcon = (0, import_react.useCallback)((label, opacity, color) => {
1543
- const size = 60;
1544
- const innerSize = 44;
1545
- const textStyles = getLabelTextStyles(color);
1546
- const safeLabel = escapeHtml(label);
1547
- const clampedOpacity = Math.min(1, Math.max(0.92, opacity));
1548
- const innerBackground = withAlpha(color, 0.9);
1549
- return import_leaflet.default.divIcon({
1550
- className: "zenit-label-marker",
1551
- iconSize: [size, size],
1552
- iconAnchor: [size / 2, size / 2],
1553
- html: `
1554
- <div
1555
- title="${safeLabel}"
1556
- style="
1557
- width:${size}px;
1558
- height:${size}px;
1559
- border-radius:9999px;
1560
- background:rgba(255, 255, 255, 0.95);
1561
- border:3px solid rgba(255, 255, 255, 1);
1562
- display:flex;
1563
- align-items:center;
1564
- justify-content:center;
1565
- opacity:${clampedOpacity};
1566
- box-shadow:0 2px 6px rgba(0, 0, 0, 0.25);
1567
- pointer-events:none;
1568
- "
1569
- >
1570
- <div
1571
- style="
1572
- width:${innerSize}px;
1573
- height:${innerSize}px;
1574
- border-radius:9999px;
1575
- background:${innerBackground};
1576
- display:flex;
1577
- align-items:center;
1578
- justify-content:center;
1579
- box-shadow:inset 0 0 0 1px rgba(15, 23, 42, 0.12);
1580
- "
1581
- >
1582
- <span
1583
- style="
1584
- color:${textStyles.color};
1585
- font-size:20px;
1586
- font-weight:800;
1587
- text-shadow:${textStyles.shadow};
1588
- "
1589
- >
1590
- ${safeLabel}
1591
- </span>
1592
- </div>
1593
- </div>
1594
- `
1595
- });
1596
- }, []);
1597
- (0, import_react.useImperativeHandle)(ref, () => ({
1647
+ (0, import_react4.useImperativeHandle)(ref, () => ({
1598
1648
  setLayerOpacity: (layerId, opacity) => {
1599
1649
  upsertUiOverride(layerId, { overrideOpacity: opacity });
1600
1650
  },
@@ -1644,10 +1694,10 @@ var ZenitMap = (0, import_react.forwardRef)(({
1644
1694
  getMapInstance: () => mapInstance
1645
1695
  }), [effectiveStates, mapInstance]);
1646
1696
  if (loadingMap) {
1647
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { padding: 16, height, width }, children: "Cargando mapa..." });
1697
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { padding: 16, height, width }, children: "Cargando mapa..." });
1648
1698
  }
1649
1699
  if (mapError) {
1650
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: 16, height, width, color: "red" }, children: [
1700
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { padding: 16, height, width, color: "red" }, children: [
1651
1701
  "Error al cargar mapa: ",
1652
1702
  mapError
1653
1703
  ] });
@@ -1659,7 +1709,7 @@ var ZenitMap = (0, import_react.forwardRef)(({
1659
1709
  setCurrentZoom(zoomValue);
1660
1710
  onZoomChange?.(zoomValue);
1661
1711
  };
1662
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1712
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1663
1713
  "div",
1664
1714
  {
1665
1715
  style: {
@@ -1672,13 +1722,13 @@ var ZenitMap = (0, import_react.forwardRef)(({
1672
1722
  boxSizing: "border-box"
1673
1723
  },
1674
1724
  children: [
1675
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1725
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1676
1726
  "div",
1677
1727
  {
1678
1728
  className: `zenit-map-shell${isPopupOpen ? " popup-open" : ""}`,
1679
1729
  style: { flex: 1, position: "relative" },
1680
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1681
- import_react_leaflet.MapContainer,
1730
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1731
+ import_react_leaflet4.MapContainer,
1682
1732
  {
1683
1733
  center,
1684
1734
  zoom,
@@ -1686,57 +1736,68 @@ var ZenitMap = (0, import_react.forwardRef)(({
1686
1736
  scrollWheelZoom: true,
1687
1737
  zoomControl: false,
1688
1738
  children: [
1689
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1690
- import_react_leaflet.TileLayer,
1739
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1740
+ import_react_leaflet4.TileLayer,
1691
1741
  {
1692
1742
  url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
1693
1743
  attribution: "\xA9 OpenStreetMap contributors"
1694
1744
  }
1695
1745
  ),
1696
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_leaflet.ZoomControl, { position: "topright" }),
1697
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MapInstanceBridge, { onReady: handleMapReady }),
1698
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FitToBounds, { bbox: explicitZoomBBox ?? void 0 }),
1699
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AutoFitToBounds, { bbox: autoZoomBBox ?? void 0, enabled: !explicitZoomBBox }),
1700
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ZoomBasedOpacityHandler, { onZoomChange: handleZoomChange }),
1746
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_leaflet4.ZoomControl, { position: "topright" }),
1747
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MapInstanceBridge, { onReady: handleMapReady }),
1748
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1749
+ BBoxZoomHandler,
1750
+ {
1751
+ bbox: zoomToBbox ?? void 0,
1752
+ geojson: zoomToGeojson ?? void 0,
1753
+ autoGeojson: autoZoomGeojson
1754
+ }
1755
+ ),
1756
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ZoomBasedOpacityHandler, { onZoomChange: handleZoomChange }),
1757
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(LocationControl, {}),
1701
1758
  orderedLayers.map((layerState) => {
1702
1759
  const baseOpacity = layerState.effective?.baseOpacity ?? layerState.effective?.opacity ?? 1;
1703
1760
  const fillPaneName = `zenit-layer-${layerState.mapLayer.layerId}-fill`;
1704
1761
  const pointsPaneName = `zenit-layer-${layerState.mapLayer.layerId}-points`;
1705
1762
  const layerType = layerState.layer?.layerType ?? layerState.mapLayer.layerType ?? void 0;
1706
- const data = layerState.data?.features ?? [];
1707
- const fillFeatures = data.filter(isNonPointGeometry);
1708
- const pointFeatures = data.filter(isPointGeometry);
1709
- const fillData = fillFeatures.length > 0 ? buildFeatureCollection(fillFeatures) : null;
1710
- const pointsData = pointFeatures.length > 0 ? buildFeatureCollection(pointFeatures) : null;
1711
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.default.Fragment, { children: [
1712
- fillData && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1713
- import_react_leaflet.GeoJSON,
1714
- {
1715
- data: fillData,
1716
- pane: panesReady && mapInstance?.getPane(fillPaneName) ? fillPaneName : void 0,
1717
- style: (feature) => buildLayerStyle(layerState.mapLayer.layerId, baseOpacity, feature, layerType),
1718
- onEachFeature: overlayOnEachFeature
1719
- }
1720
- ),
1721
- pointsData && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1722
- import_react_leaflet.GeoJSON,
1763
+ const labelKey = labelKeyIndex.get(String(layerState.mapLayer.layerId));
1764
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.default.Fragment, { children: [
1765
+ layerState.data && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1766
+ LayerGeoJson,
1723
1767
  {
1724
- data: pointsData,
1725
- pane: panesReady && mapInstance?.getPane(pointsPaneName) ? pointsPaneName : void 0,
1726
- pointToLayer: (feature, latlng) => import_leaflet.default.circleMarker(latlng, {
1727
- radius: isMobile ? 8 : 6,
1728
- ...buildLayerStyle(layerState.mapLayer.layerId, baseOpacity, feature, layerType)
1729
- }),
1730
- onEachFeature: overlayOnEachFeature
1768
+ layerId: layerState.mapLayer.layerId,
1769
+ data: layerState.data,
1770
+ baseOpacity,
1771
+ isMobile,
1772
+ panesReady,
1773
+ mapInstance,
1774
+ fillPaneName,
1775
+ pointsPaneName,
1776
+ layerType,
1777
+ styleFn: makeStyleFnForLayer(layerState.mapLayer.layerId),
1778
+ onEachFeature: overlayOnEachFeature,
1779
+ onPolygonLabel: labelKey ? (feature, layer) => {
1780
+ const geometryType = feature?.geometry?.type;
1781
+ if (geometryType !== "Polygon" && geometryType !== "MultiPolygon") return;
1782
+ const properties = feature?.properties;
1783
+ const value = properties?.[labelKey];
1784
+ if (!value || !layer.bindTooltip) return;
1785
+ layer.bindTooltip(String(value), {
1786
+ sticky: true,
1787
+ direction: "center",
1788
+ opacity: 0.9,
1789
+ className: "polygon-label-tooltip"
1790
+ });
1791
+ } : void 0
1731
1792
  }
1732
1793
  ),
1733
1794
  panesReady && mapInstance?.getPane(LABELS_PANE_NAME) ? labelMarkers.filter(
1734
1795
  (marker) => String(marker.layerId) === String(layerState.mapLayer.layerId)
1735
- ).map((marker) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1736
- import_react_leaflet.Marker,
1796
+ ).map((marker) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1797
+ import_react_leaflet4.Marker,
1737
1798
  {
1738
1799
  position: marker.position,
1739
- icon: buildLabelIcon(marker.label, marker.opacity, marker.color),
1800
+ icon: createCustomIcon(marker.label, marker.opacity, marker.color),
1740
1801
  interactive: false,
1741
1802
  pane: LABELS_PANE_NAME
1742
1803
  },
@@ -1744,14 +1805,20 @@ var ZenitMap = (0, import_react.forwardRef)(({
1744
1805
  )) : null
1745
1806
  ] }, layerState.mapLayer.layerId.toString());
1746
1807
  }),
1747
- overlayGeojson && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1748
- import_react_leaflet.GeoJSON,
1808
+ overlayGeojson && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1809
+ LayerGeoJson,
1749
1810
  {
1811
+ layerId: "overlay-geojson",
1750
1812
  data: overlayGeojson,
1751
- style: overlayStyleFunction,
1813
+ baseOpacity: 1,
1814
+ isMobile,
1815
+ panesReady,
1816
+ mapInstance,
1817
+ fillPaneName: "zenit-overlay-fill",
1818
+ pointsPaneName: "zenit-overlay-points",
1819
+ styleFn: overlayStyleFn,
1752
1820
  onEachFeature: overlayOnEachFeature
1753
- },
1754
- "zenit-overlay-geojson"
1821
+ }
1755
1822
  )
1756
1823
  ]
1757
1824
  },
@@ -1759,7 +1826,7 @@ var ZenitMap = (0, import_react.forwardRef)(({
1759
1826
  )
1760
1827
  }
1761
1828
  ),
1762
- showLayerPanel && decoratedLayers.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1829
+ showLayerPanel && decoratedLayers.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1763
1830
  "div",
1764
1831
  {
1765
1832
  style: {
@@ -1772,7 +1839,7 @@ var ZenitMap = (0, import_react.forwardRef)(({
1772
1839
  overflowY: "auto"
1773
1840
  },
1774
1841
  children: [
1775
- overlayGeojson && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1842
+ overlayGeojson && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1776
1843
  "div",
1777
1844
  {
1778
1845
  style: {
@@ -1784,8 +1851,8 @@ var ZenitMap = (0, import_react.forwardRef)(({
1784
1851
  marginBottom: 12
1785
1852
  },
1786
1853
  children: [
1787
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontWeight: 600, marginBottom: 4 }, children: "Overlay activo" }),
1788
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { fontSize: 13 }, children: [
1854
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { fontWeight: 600, marginBottom: 4 }, children: "Overlay activo" }),
1855
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { fontSize: 13 }, children: [
1789
1856
  "GeoJSON externo con ",
1790
1857
  (overlayGeojson.features?.length ?? 0).toLocaleString(),
1791
1858
  " elementos."
@@ -1793,14 +1860,14 @@ var ZenitMap = (0, import_react.forwardRef)(({
1793
1860
  ]
1794
1861
  }
1795
1862
  ),
1796
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontWeight: 600, marginBottom: 12 }, children: "Capas" }),
1797
- decoratedLayers.map((layerState) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1863
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { fontWeight: 600, marginBottom: 12 }, children: "Capas" }),
1864
+ decoratedLayers.map((layerState) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1798
1865
  "div",
1799
1866
  {
1800
1867
  style: { borderBottom: "1px solid #e5e7eb", paddingBottom: 10, marginBottom: 10 },
1801
1868
  children: [
1802
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
1803
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1869
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
1870
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1804
1871
  "input",
1805
1872
  {
1806
1873
  type: "checkbox",
@@ -1811,17 +1878,17 @@ var ZenitMap = (0, import_react.forwardRef)(({
1811
1878
  }
1812
1879
  }
1813
1880
  ),
1814
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: layerState.layer?.name ?? `Capa ${layerState.mapLayer.layerId}` })
1881
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: layerState.layer?.name ?? `Capa ${layerState.mapLayer.layerId}` })
1815
1882
  ] }),
1816
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginTop: 8 }, children: [
1817
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", marginBottom: 4 }, children: [
1818
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: "#4a5568" }, children: "Opacidad" }),
1819
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
1883
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { marginTop: 8 }, children: [
1884
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", marginBottom: 4 }, children: [
1885
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: "#4a5568" }, children: "Opacidad" }),
1886
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { children: [
1820
1887
  Math.round((layerState.effective?.opacity ?? 1) * 100),
1821
1888
  "%"
1822
1889
  ] })
1823
1890
  ] }),
1824
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1891
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1825
1892
  "input",
1826
1893
  {
1827
1894
  type: "range",
@@ -1851,13 +1918,13 @@ var ZenitMap = (0, import_react.forwardRef)(({
1851
1918
  ZenitMap.displayName = "ZenitMap";
1852
1919
 
1853
1920
  // src/react/ZenitLayerManager.tsx
1854
- var import_react2 = __toESM(require("react"));
1921
+ var import_react5 = __toESM(require("react"));
1855
1922
 
1856
1923
  // src/react/icons.tsx
1857
1924
  var import_lucide_react = require("lucide-react");
1858
1925
 
1859
1926
  // src/react/ZenitLayerManager.tsx
1860
- var import_jsx_runtime2 = require("react/jsx-runtime");
1927
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1861
1928
  var FLOAT_TOLERANCE = 1e-3;
1862
1929
  function areEffectiveStatesEqual(a, b) {
1863
1930
  if (a.length !== b.length) return false;
@@ -1897,15 +1964,15 @@ var ZenitLayerManager = ({
1897
1964
  layerFeatureCounts,
1898
1965
  mapLayers
1899
1966
  }) => {
1900
- const [map, setMap] = (0, import_react2.useState)(null);
1901
- const [loadingMap, setLoadingMap] = (0, import_react2.useState)(false);
1902
- const [mapError, setMapError] = (0, import_react2.useState)(null);
1903
- const [layers, setLayers] = (0, import_react2.useState)([]);
1904
- const [activeTab, setActiveTab] = (0, import_react2.useState)("layers");
1905
- const [panelVisible, setPanelVisible] = (0, import_react2.useState)(true);
1906
- const lastEmittedStatesRef = (0, import_react2.useRef)(null);
1967
+ const [map, setMap] = (0, import_react5.useState)(null);
1968
+ const [loadingMap, setLoadingMap] = (0, import_react5.useState)(false);
1969
+ const [mapError, setMapError] = (0, import_react5.useState)(null);
1970
+ const [layers, setLayers] = (0, import_react5.useState)([]);
1971
+ const [activeTab, setActiveTab] = (0, import_react5.useState)("layers");
1972
+ const [panelVisible, setPanelVisible] = (0, import_react5.useState)(true);
1973
+ const lastEmittedStatesRef = (0, import_react5.useRef)(null);
1907
1974
  const isControlled = Array.isArray(layerStates) && typeof onLayerStatesChange === "function";
1908
- const baseStates = (0, import_react2.useMemo)(
1975
+ const baseStates = (0, import_react5.useMemo)(
1909
1976
  () => initLayerStates(
1910
1977
  layers.map((entry) => ({
1911
1978
  ...entry.mapLayer,
@@ -1916,7 +1983,7 @@ var ZenitLayerManager = ({
1916
1983
  ),
1917
1984
  [layers]
1918
1985
  );
1919
- const overrideStates = (0, import_react2.useMemo)(
1986
+ const overrideStates = (0, import_react5.useMemo)(
1920
1987
  () => layers.map(
1921
1988
  (entry) => ({
1922
1989
  layerId: entry.mapLayer.layerId,
@@ -1926,11 +1993,11 @@ var ZenitLayerManager = ({
1926
1993
  ),
1927
1994
  [layers]
1928
1995
  );
1929
- const effectiveStates = (0, import_react2.useMemo)(
1996
+ const effectiveStates = (0, import_react5.useMemo)(
1930
1997
  () => layerStates ?? applyLayerOverrides(baseStates, overrideStates),
1931
1998
  [baseStates, layerStates, overrideStates]
1932
1999
  );
1933
- const layerMetaIndex = (0, import_react2.useMemo)(() => {
2000
+ const layerMetaIndex = (0, import_react5.useMemo)(() => {
1934
2001
  const index = /* @__PURE__ */ new Map();
1935
2002
  mapLayers?.forEach((entry) => {
1936
2003
  const key = String(entry.layerId);
@@ -1944,7 +2011,7 @@ var ZenitLayerManager = ({
1944
2011
  });
1945
2012
  return index;
1946
2013
  }, [map, mapLayers]);
1947
- const resolveUserOpacity = import_react2.default.useCallback((state) => {
2014
+ const resolveUserOpacity = import_react5.default.useCallback((state) => {
1948
2015
  if (typeof state.overrideOpacity === "number") return state.overrideOpacity;
1949
2016
  if (typeof state.overrideOpacity === "string") {
1950
2017
  const parsed = Number.parseFloat(state.overrideOpacity);
@@ -1952,7 +2019,7 @@ var ZenitLayerManager = ({
1952
2019
  }
1953
2020
  return state.opacity ?? 1;
1954
2021
  }, []);
1955
- const resolveEffectiveOpacity = import_react2.default.useCallback(
2022
+ const resolveEffectiveOpacity = import_react5.default.useCallback(
1956
2023
  (layerId, userOpacity) => {
1957
2024
  if (!autoOpacityOnZoom || typeof mapZoom !== "number") {
1958
2025
  return userOpacity;
@@ -1968,7 +2035,7 @@ var ZenitLayerManager = ({
1968
2035
  },
1969
2036
  [autoOpacityConfig, autoOpacityOnZoom, layerMetaIndex, mapZoom]
1970
2037
  );
1971
- const effectiveStatesWithZoom = (0, import_react2.useMemo)(() => {
2038
+ const effectiveStatesWithZoom = (0, import_react5.useMemo)(() => {
1972
2039
  if (!autoOpacityOnZoom || typeof mapZoom !== "number") {
1973
2040
  return effectiveStates;
1974
2041
  }
@@ -1982,7 +2049,7 @@ var ZenitLayerManager = ({
1982
2049
  };
1983
2050
  });
1984
2051
  }, [autoOpacityOnZoom, effectiveStates, mapZoom, resolveEffectiveOpacity, resolveUserOpacity]);
1985
- (0, import_react2.useEffect)(() => {
2052
+ (0, import_react5.useEffect)(() => {
1986
2053
  let cancelled = false;
1987
2054
  setLoadingMap(true);
1988
2055
  setMapError(null);
@@ -2014,12 +2081,12 @@ var ZenitLayerManager = ({
2014
2081
  cancelled = true;
2015
2082
  };
2016
2083
  }, [client.maps, mapId]);
2017
- (0, import_react2.useEffect)(() => {
2084
+ (0, import_react5.useEffect)(() => {
2018
2085
  if (!showUploadTab && activeTab === "upload") {
2019
2086
  setActiveTab("layers");
2020
2087
  }
2021
2088
  }, [activeTab, showUploadTab]);
2022
- (0, import_react2.useEffect)(() => {
2089
+ (0, import_react5.useEffect)(() => {
2023
2090
  if (isControlled) return;
2024
2091
  if (!onLayerStatesChange) return;
2025
2092
  const emitStates = autoOpacityOnZoom && typeof mapZoom === "number" ? effectiveStatesWithZoom : effectiveStates;
@@ -2037,7 +2104,7 @@ var ZenitLayerManager = ({
2037
2104
  mapZoom,
2038
2105
  onLayerStatesChange
2039
2106
  ]);
2040
- const updateLayerVisible = import_react2.default.useCallback(
2107
+ const updateLayerVisible = import_react5.default.useCallback(
2041
2108
  (layerId, visible) => {
2042
2109
  if (!onLayerStatesChange) return;
2043
2110
  const next = effectiveStates.map(
@@ -2047,7 +2114,7 @@ var ZenitLayerManager = ({
2047
2114
  },
2048
2115
  [effectiveStates, onLayerStatesChange]
2049
2116
  );
2050
- const updateLayerOpacity = import_react2.default.useCallback(
2117
+ const updateLayerOpacity = import_react5.default.useCallback(
2051
2118
  (layerId, opacity) => {
2052
2119
  if (!onLayerStatesChange) return;
2053
2120
  const adjustedOpacity = resolveEffectiveOpacity(layerId, opacity);
@@ -2058,7 +2125,7 @@ var ZenitLayerManager = ({
2058
2125
  },
2059
2126
  [effectiveStates, onLayerStatesChange, resolveEffectiveOpacity]
2060
2127
  );
2061
- const resolveFeatureCount = import_react2.default.useCallback(
2128
+ const resolveFeatureCount = import_react5.default.useCallback(
2062
2129
  (layerId, layer) => {
2063
2130
  const resolvedFeatureCount = layerFeatureCounts?.[layerId] ?? layerFeatureCounts?.[String(layerId)];
2064
2131
  if (typeof resolvedFeatureCount === "number") return resolvedFeatureCount;
@@ -2067,7 +2134,7 @@ var ZenitLayerManager = ({
2067
2134
  },
2068
2135
  [layerFeatureCounts]
2069
2136
  );
2070
- const decoratedLayers = (0, import_react2.useMemo)(() => {
2137
+ const decoratedLayers = (0, import_react5.useMemo)(() => {
2071
2138
  return layers.map((entry) => ({
2072
2139
  ...entry,
2073
2140
  effective: effectiveStates.find((state) => state.layerId === entry.mapLayer.layerId),
@@ -2096,7 +2163,7 @@ var ZenitLayerManager = ({
2096
2163
  return String(a.mapLayer.layerId).localeCompare(String(b.mapLayer.layerId));
2097
2164
  });
2098
2165
  }, [effectiveStates, layers, resolveFeatureCount]);
2099
- const resolveLayerStyle = import_react2.default.useCallback(
2166
+ const resolveLayerStyle = import_react5.default.useCallback(
2100
2167
  (layerId) => {
2101
2168
  const layerKey = String(layerId);
2102
2169
  const fromProp = mapLayers?.find((entry) => String(entry.layerId) === layerKey)?.style;
@@ -2126,10 +2193,10 @@ var ZenitLayerManager = ({
2126
2193
  ...height ? { height } : {}
2127
2194
  };
2128
2195
  if (loadingMap) {
2129
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className, style: panelStyle, children: "Cargando capas\u2026" });
2196
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className, style: panelStyle, children: "Cargando capas\u2026" });
2130
2197
  }
2131
2198
  if (mapError) {
2132
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className, style: { ...panelStyle, color: "#c53030" }, children: [
2199
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className, style: { ...panelStyle, color: "#c53030" }, children: [
2133
2200
  "Error al cargar mapa: ",
2134
2201
  mapError
2135
2202
  ] });
@@ -2147,7 +2214,7 @@ var ZenitLayerManager = ({
2147
2214
  boxShadow: "0 1px 0 rgba(148, 163, 184, 0.25)"
2148
2215
  };
2149
2216
  const renderLayerCards = () => {
2150
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: decoratedLayers.map((layerState) => {
2217
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: decoratedLayers.map((layerState) => {
2151
2218
  const layerId = layerState.mapLayer.layerId;
2152
2219
  const layerName = layerState.layerName ?? `Capa ${layerId}`;
2153
2220
  const visible = layerState.effective?.visible ?? false;
@@ -2157,7 +2224,7 @@ var ZenitLayerManager = ({
2157
2224
  const muted = !visible;
2158
2225
  const opacityPercent = Math.round(userOpacity * 100);
2159
2226
  const sliderBackground = `linear-gradient(to right, ${layerColor} 0%, ${layerColor} ${opacityPercent}%, #e5e7eb ${opacityPercent}%, #e5e7eb 100%)`;
2160
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2227
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2161
2228
  "div",
2162
2229
  {
2163
2230
  className: `zlm-card${muted ? " is-muted" : ""}`,
@@ -2172,9 +2239,9 @@ var ZenitLayerManager = ({
2172
2239
  width: "100%"
2173
2240
  },
2174
2241
  children: [
2175
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", gap: 12 }, children: [
2176
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", gap: 10, alignItems: "flex-start", minWidth: 0, flex: 1 }, children: [
2177
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2242
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", gap: 12 }, children: [
2243
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", gap: 10, alignItems: "flex-start", minWidth: 0, flex: 1 }, children: [
2244
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2178
2245
  "div",
2179
2246
  {
2180
2247
  style: {
@@ -2189,7 +2256,7 @@ var ZenitLayerManager = ({
2189
2256
  title: "Color de la capa"
2190
2257
  }
2191
2258
  ),
2192
- showLayerVisibilityIcon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2259
+ showLayerVisibilityIcon && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2193
2260
  "button",
2194
2261
  {
2195
2262
  type: "button",
@@ -2200,11 +2267,11 @@ var ZenitLayerManager = ({
2200
2267
  )
2201
2268
  ),
2202
2269
  "aria-label": visible ? "Ocultar capa" : "Mostrar capa",
2203
- children: visible ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.Eye, { size: 16 }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.EyeOff, { size: 16 })
2270
+ children: visible ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react.Eye, { size: 16 }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react.EyeOff, { size: 16 })
2204
2271
  }
2205
2272
  ),
2206
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { minWidth: 0, flex: 1 }, children: [
2207
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2273
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { minWidth: 0, flex: 1 }, children: [
2274
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2208
2275
  "div",
2209
2276
  {
2210
2277
  className: "zlm-layer-name",
@@ -2222,26 +2289,26 @@ var ZenitLayerManager = ({
2222
2289
  children: layerName
2223
2290
  }
2224
2291
  ),
2225
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { color: muted ? "#94a3b8" : "#64748b", fontSize: 12 }, children: [
2292
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { color: muted ? "#94a3b8" : "#64748b", fontSize: 12 }, children: [
2226
2293
  "ID ",
2227
2294
  layerId
2228
2295
  ] })
2229
2296
  ] })
2230
2297
  ] }),
2231
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { display: "flex", alignItems: "flex-start", gap: 6, flexShrink: 0 }, children: typeof featureCount === "number" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "zlm-badge", children: [
2298
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { display: "flex", alignItems: "flex-start", gap: 6, flexShrink: 0 }, children: typeof featureCount === "number" && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "zlm-badge", children: [
2232
2299
  featureCount.toLocaleString(),
2233
2300
  " features"
2234
2301
  ] }) })
2235
2302
  ] }),
2236
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { display: "flex", gap: 10, alignItems: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { flex: 1 }, children: [
2237
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", marginBottom: 6, color: "#64748b", fontSize: 12 }, children: [
2238
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "Opacidad" }),
2239
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
2303
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { display: "flex", gap: 10, alignItems: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { flex: 1 }, children: [
2304
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", marginBottom: 6, color: "#64748b", fontSize: 12 }, children: [
2305
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Opacidad" }),
2306
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { children: [
2240
2307
  opacityPercent,
2241
2308
  "%"
2242
2309
  ] })
2243
2310
  ] }),
2244
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2311
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2245
2312
  "input",
2246
2313
  {
2247
2314
  className: "zlm-range",
@@ -2279,8 +2346,8 @@ var ZenitLayerManager = ({
2279
2346
  );
2280
2347
  }) });
2281
2348
  };
2282
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: ["zenit-layer-manager", className].filter(Boolean).join(" "), style: panelStyle, children: [
2283
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: `
2349
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: ["zenit-layer-manager", className].filter(Boolean).join(" "), style: panelStyle, children: [
2350
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: `
2284
2351
  .zenit-layer-manager .zlm-card {
2285
2352
  transition: box-shadow 0.2s ease, transform 0.2s ease, opacity 0.2s ease;
2286
2353
  box-shadow: 0 6px 16px rgba(15, 23, 42, 0.08);
@@ -2375,16 +2442,16 @@ var ZenitLayerManager = ({
2375
2442
  outline-offset: 2px;
2376
2443
  }
2377
2444
  ` }),
2378
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: headerStyle, children: [
2379
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
2380
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
2381
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { fontWeight: 800, fontSize: 16, color: "#0f172a" }, children: "Gesti\xF3n de Capas" }),
2382
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { color: "#64748b", fontSize: 12 }, children: [
2445
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: headerStyle, children: [
2446
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
2447
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
2448
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontWeight: 800, fontSize: 16, color: "#0f172a" }, children: "Gesti\xF3n de Capas" }),
2449
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { color: "#64748b", fontSize: 12 }, children: [
2383
2450
  "Mapa #",
2384
2451
  map.id
2385
2452
  ] })
2386
2453
  ] }),
2387
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2454
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2388
2455
  "button",
2389
2456
  {
2390
2457
  type: "button",
@@ -2392,13 +2459,13 @@ var ZenitLayerManager = ({
2392
2459
  className: "zlm-panel-toggle",
2393
2460
  "aria-label": panelVisible ? "Ocultar panel de capas" : "Mostrar panel de capas",
2394
2461
  children: [
2395
- panelVisible ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.Eye, { size: 16 }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.EyeOff, { size: 16 }),
2462
+ panelVisible ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react.Eye, { size: 16 }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react.EyeOff, { size: 16 }),
2396
2463
  panelVisible ? "Ocultar" : "Mostrar"
2397
2464
  ]
2398
2465
  }
2399
2466
  )
2400
2467
  ] }),
2401
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2468
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2402
2469
  "div",
2403
2470
  {
2404
2471
  style: {
@@ -2411,26 +2478,26 @@ var ZenitLayerManager = ({
2411
2478
  background: "#f1f5f9"
2412
2479
  },
2413
2480
  children: [
2414
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2481
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2415
2482
  "button",
2416
2483
  {
2417
2484
  type: "button",
2418
2485
  className: `zlm-tab${activeTab === "layers" ? " is-active" : ""}`,
2419
2486
  onClick: () => setActiveTab("layers"),
2420
2487
  children: [
2421
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.Layers, { size: 16 }),
2488
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react.Layers, { size: 16 }),
2422
2489
  "Capas"
2423
2490
  ]
2424
2491
  }
2425
2492
  ),
2426
- showUploadTab && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2493
+ showUploadTab && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2427
2494
  "button",
2428
2495
  {
2429
2496
  type: "button",
2430
2497
  className: `zlm-tab${activeTab === "upload" ? " is-active" : ""}`,
2431
2498
  onClick: () => setActiveTab("upload"),
2432
2499
  children: [
2433
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.Upload, { size: 16 }),
2500
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react.Upload, { size: 16 }),
2434
2501
  "Subir"
2435
2502
  ]
2436
2503
  }
@@ -2439,15 +2506,15 @@ var ZenitLayerManager = ({
2439
2506
  }
2440
2507
  )
2441
2508
  ] }),
2442
- panelVisible && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { padding: "12px 10px 18px", overflowY: "auto", flex: 1, minHeight: 0 }, children: [
2509
+ panelVisible && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { padding: "12px 10px 18px", overflowY: "auto", flex: 1, minHeight: 0 }, children: [
2443
2510
  activeTab === "layers" && renderLayerCards(),
2444
- showUploadTab && activeTab === "upload" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { color: "#475569", fontSize: 13 }, children: "Pr\xF3ximamente podr\xE1s subir capas desde este panel." })
2511
+ showUploadTab && activeTab === "upload" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: "#475569", fontSize: 13 }, children: "Pr\xF3ximamente podr\xE1s subir capas desde este panel." })
2445
2512
  ] })
2446
2513
  ] });
2447
2514
  };
2448
2515
 
2449
2516
  // src/react/ZenitFeatureFilterPanel.tsx
2450
- var import_jsx_runtime3 = require("react/jsx-runtime");
2517
+ var import_jsx_runtime5 = require("react/jsx-runtime");
2451
2518
  var ZenitFeatureFilterPanel = ({
2452
2519
  title = "Filtros",
2453
2520
  description,
@@ -2455,7 +2522,7 @@ var ZenitFeatureFilterPanel = ({
2455
2522
  style,
2456
2523
  children
2457
2524
  }) => {
2458
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
2525
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2459
2526
  "section",
2460
2527
  {
2461
2528
  className,
@@ -2468,22 +2535,22 @@ var ZenitFeatureFilterPanel = ({
2468
2535
  ...style
2469
2536
  },
2470
2537
  children: [
2471
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("header", { style: { marginBottom: 12 }, children: [
2472
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { style: { margin: 0, fontSize: 16 }, children: title }),
2473
- description && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: { margin: "6px 0 0", color: "#475569", fontSize: 13 }, children: description })
2538
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("header", { style: { marginBottom: 12 }, children: [
2539
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h3", { style: { margin: 0, fontSize: 16 }, children: title }),
2540
+ description && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { style: { margin: "6px 0 0", color: "#475569", fontSize: 13 }, children: description })
2474
2541
  ] }),
2475
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { children })
2542
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { children })
2476
2543
  ]
2477
2544
  }
2478
2545
  );
2479
2546
  };
2480
2547
 
2481
2548
  // src/react/ai/FloatingChatBox.tsx
2482
- var import_react4 = require("react");
2483
- var import_react_dom = require("react-dom");
2549
+ var import_react7 = require("react");
2550
+ var import_react_dom2 = require("react-dom");
2484
2551
 
2485
2552
  // src/react/hooks/use-chat.ts
2486
- var import_react3 = require("react");
2553
+ var import_react6 = require("react");
2487
2554
 
2488
2555
  // src/ai/chat.service.ts
2489
2556
  var DEFAULT_ERROR_MESSAGE = "No fue posible completar la solicitud al asistente.";
@@ -2615,9 +2682,9 @@ var sendMessageStream = async (mapId, request, callbacks = {}, options, config)
2615
2682
 
2616
2683
  // src/react/hooks/use-chat.ts
2617
2684
  var useSendMessage = (config) => {
2618
- const [isLoading, setIsLoading] = (0, import_react3.useState)(false);
2619
- const [error, setError] = (0, import_react3.useState)(null);
2620
- const send = (0, import_react3.useCallback)(
2685
+ const [isLoading, setIsLoading] = (0, import_react6.useState)(false);
2686
+ const [error, setError] = (0, import_react6.useState)(null);
2687
+ const send = (0, import_react6.useCallback)(
2621
2688
  async (mapId, request, options) => {
2622
2689
  setIsLoading(true);
2623
2690
  setError(null);
@@ -2635,18 +2702,18 @@ var useSendMessage = (config) => {
2635
2702
  return { sendMessage: send, isLoading, error };
2636
2703
  };
2637
2704
  var useSendMessageStream = (config) => {
2638
- const [isStreaming, setIsStreaming] = (0, import_react3.useState)(false);
2639
- const [streamingText, setStreamingText] = (0, import_react3.useState)("");
2640
- const [completeResponse, setCompleteResponse] = (0, import_react3.useState)(null);
2641
- const [error, setError] = (0, import_react3.useState)(null);
2642
- const requestIdRef = (0, import_react3.useRef)(0);
2643
- const reset = (0, import_react3.useCallback)(() => {
2705
+ const [isStreaming, setIsStreaming] = (0, import_react6.useState)(false);
2706
+ const [streamingText, setStreamingText] = (0, import_react6.useState)("");
2707
+ const [completeResponse, setCompleteResponse] = (0, import_react6.useState)(null);
2708
+ const [error, setError] = (0, import_react6.useState)(null);
2709
+ const requestIdRef = (0, import_react6.useRef)(0);
2710
+ const reset = (0, import_react6.useCallback)(() => {
2644
2711
  setIsStreaming(false);
2645
2712
  setStreamingText("");
2646
2713
  setCompleteResponse(null);
2647
2714
  setError(null);
2648
2715
  }, []);
2649
- const send = (0, import_react3.useCallback)(
2716
+ const send = (0, import_react6.useCallback)(
2650
2717
  async (mapId, request, options) => {
2651
2718
  const requestId = requestIdRef.current + 1;
2652
2719
  requestIdRef.current = requestId;
@@ -2700,7 +2767,7 @@ var useSendMessageStream = (config) => {
2700
2767
  // src/react/components/MarkdownRenderer.tsx
2701
2768
  var import_react_markdown = __toESM(require("react-markdown"));
2702
2769
  var import_remark_gfm = __toESM(require("remark-gfm"));
2703
- var import_jsx_runtime4 = require("react/jsx-runtime");
2770
+ var import_jsx_runtime6 = require("react/jsx-runtime");
2704
2771
  function normalizeAssistantMarkdown(text) {
2705
2772
  if (!text || typeof text !== "string") return "";
2706
2773
  let normalized = text;
@@ -2716,28 +2783,28 @@ var MarkdownRenderer = ({ content, className }) => {
2716
2783
  if (!normalizedContent) {
2717
2784
  return null;
2718
2785
  }
2719
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className, style: { wordBreak: "break-word" }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2786
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className, style: { wordBreak: "break-word" }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2720
2787
  import_react_markdown.default,
2721
2788
  {
2722
2789
  remarkPlugins: [import_remark_gfm.default],
2723
2790
  components: {
2724
2791
  // Headings with proper spacing
2725
- h1: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h1", { style: { fontSize: "1.5em", fontWeight: 700, marginTop: "1em", marginBottom: "0.5em" }, ...props, children }),
2726
- h2: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { style: { fontSize: "1.3em", fontWeight: 700, marginTop: "0.9em", marginBottom: "0.45em" }, ...props, children }),
2727
- h3: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { style: { fontSize: "1.15em", fontWeight: 600, marginTop: "0.75em", marginBottom: "0.4em" }, ...props, children }),
2728
- h4: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h4", { style: { fontSize: "1.05em", fontWeight: 600, marginTop: "0.6em", marginBottom: "0.35em" }, ...props, children }),
2729
- h5: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h5", { style: { fontSize: "1em", fontWeight: 600, marginTop: "0.5em", marginBottom: "0.3em" }, ...props, children }),
2730
- h6: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h6", { style: { fontSize: "0.95em", fontWeight: 600, marginTop: "0.5em", marginBottom: "0.3em" }, ...props, children }),
2792
+ h1: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h1", { style: { fontSize: "1.5em", fontWeight: 700, marginTop: "1em", marginBottom: "0.5em" }, ...props, children }),
2793
+ h2: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h2", { style: { fontSize: "1.3em", fontWeight: 700, marginTop: "0.9em", marginBottom: "0.45em" }, ...props, children }),
2794
+ h3: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h3", { style: { fontSize: "1.15em", fontWeight: 600, marginTop: "0.75em", marginBottom: "0.4em" }, ...props, children }),
2795
+ h4: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h4", { style: { fontSize: "1.05em", fontWeight: 600, marginTop: "0.6em", marginBottom: "0.35em" }, ...props, children }),
2796
+ h5: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h5", { style: { fontSize: "1em", fontWeight: 600, marginTop: "0.5em", marginBottom: "0.3em" }, ...props, children }),
2797
+ h6: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h6", { style: { fontSize: "0.95em", fontWeight: 600, marginTop: "0.5em", marginBottom: "0.3em" }, ...props, children }),
2731
2798
  // Paragraphs with comfortable line height
2732
- p: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { style: { marginTop: "0.5em", marginBottom: "0.5em", lineHeight: 1.6 }, ...props, children }),
2799
+ p: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { style: { marginTop: "0.5em", marginBottom: "0.5em", lineHeight: 1.6 }, ...props, children }),
2733
2800
  // Lists with proper indentation
2734
- ul: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { style: { paddingLeft: "1.5em", marginTop: "0.5em", marginBottom: "0.5em" }, ...props, children }),
2735
- ol: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ol", { style: { paddingLeft: "1.5em", marginTop: "0.5em", marginBottom: "0.5em" }, ...props, children }),
2736
- li: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { style: { marginTop: "0.25em", marginBottom: "0.25em" }, ...props, children }),
2801
+ ul: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ul", { style: { paddingLeft: "1.5em", marginTop: "0.5em", marginBottom: "0.5em" }, ...props, children }),
2802
+ ol: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ol", { style: { paddingLeft: "1.5em", marginTop: "0.5em", marginBottom: "0.5em" }, ...props, children }),
2803
+ li: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("li", { style: { marginTop: "0.25em", marginBottom: "0.25em" }, ...props, children }),
2737
2804
  // Code blocks
2738
2805
  code: ({ inline, children, ...props }) => {
2739
2806
  if (inline) {
2740
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2807
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2741
2808
  "code",
2742
2809
  {
2743
2810
  style: {
@@ -2752,7 +2819,7 @@ var MarkdownRenderer = ({ content, className }) => {
2752
2819
  }
2753
2820
  );
2754
2821
  }
2755
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2822
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2756
2823
  "code",
2757
2824
  {
2758
2825
  style: {
@@ -2772,9 +2839,9 @@ var MarkdownRenderer = ({ content, className }) => {
2772
2839
  );
2773
2840
  },
2774
2841
  // Pre (code block wrapper)
2775
- pre: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("pre", { style: { margin: 0 }, ...props, children }),
2842
+ pre: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("pre", { style: { margin: 0 }, ...props, children }),
2776
2843
  // Blockquotes
2777
- blockquote: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2844
+ blockquote: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2778
2845
  "blockquote",
2779
2846
  {
2780
2847
  style: {
@@ -2790,11 +2857,11 @@ var MarkdownRenderer = ({ content, className }) => {
2790
2857
  }
2791
2858
  ),
2792
2859
  // Strong/bold
2793
- strong: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("strong", { style: { fontWeight: 600 }, ...props, children }),
2860
+ strong: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { style: { fontWeight: 600 }, ...props, children }),
2794
2861
  // Emphasis/italic
2795
- em: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("em", { style: { fontStyle: "italic" }, ...props, children }),
2862
+ em: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("em", { style: { fontStyle: "italic" }, ...props, children }),
2796
2863
  // Horizontal rule
2797
- hr: (props) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2864
+ hr: (props) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2798
2865
  "hr",
2799
2866
  {
2800
2867
  style: {
@@ -2807,7 +2874,7 @@ var MarkdownRenderer = ({ content, className }) => {
2807
2874
  }
2808
2875
  ),
2809
2876
  // Tables (GFM)
2810
- table: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { overflowX: "auto", marginTop: "0.5em", marginBottom: "0.5em" }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2877
+ table: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { overflowX: "auto", marginTop: "0.5em", marginBottom: "0.5em" }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2811
2878
  "table",
2812
2879
  {
2813
2880
  style: {
@@ -2819,7 +2886,7 @@ var MarkdownRenderer = ({ content, className }) => {
2819
2886
  children
2820
2887
  }
2821
2888
  ) }),
2822
- th: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2889
+ th: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2823
2890
  "th",
2824
2891
  {
2825
2892
  style: {
@@ -2833,7 +2900,7 @@ var MarkdownRenderer = ({ content, className }) => {
2833
2900
  children
2834
2901
  }
2835
2902
  ),
2836
- td: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2903
+ td: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2837
2904
  "td",
2838
2905
  {
2839
2906
  style: {
@@ -2851,32 +2918,32 @@ var MarkdownRenderer = ({ content, className }) => {
2851
2918
  };
2852
2919
 
2853
2920
  // src/react/ai/FloatingChatBox.tsx
2854
- var import_jsx_runtime5 = require("react/jsx-runtime");
2855
- var ChatIcon = () => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) });
2856
- var CloseIcon = () => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2857
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
2858
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
2921
+ var import_jsx_runtime7 = require("react/jsx-runtime");
2922
+ var ChatIcon = () => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) });
2923
+ var CloseIcon = () => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2924
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
2925
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
2859
2926
  ] });
2860
- var ExpandIcon = () => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2861
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polyline", { points: "15 3 21 3 21 9" }),
2862
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polyline", { points: "9 21 3 21 3 15" }),
2863
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: "21", y1: "3", x2: "14", y2: "10" }),
2864
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
2927
+ var ExpandIcon = () => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2928
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("polyline", { points: "15 3 21 3 21 9" }),
2929
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("polyline", { points: "9 21 3 21 3 15" }),
2930
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: "21", y1: "3", x2: "14", y2: "10" }),
2931
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
2865
2932
  ] });
2866
- var CollapseIcon = () => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2867
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polyline", { points: "4 14 10 14 10 20" }),
2868
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polyline", { points: "20 10 14 10 14 4" }),
2869
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: "14", y1: "10", x2: "21", y2: "3" }),
2870
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
2933
+ var CollapseIcon = () => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2934
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("polyline", { points: "4 14 10 14 10 20" }),
2935
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("polyline", { points: "20 10 14 10 14 4" }),
2936
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: "14", y1: "10", x2: "21", y2: "3" }),
2937
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
2871
2938
  ] });
2872
- var SendIcon = () => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2873
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
2874
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
2939
+ var SendIcon = () => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2940
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
2941
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
2875
2942
  ] });
2876
- var LayersIcon = () => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2877
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polygon", { points: "12 2 2 7 12 12 22 7 12 2" }),
2878
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polyline", { points: "2 17 12 22 22 17" }),
2879
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polyline", { points: "2 12 12 17 22 12" })
2943
+ var LayersIcon = () => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2944
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("polygon", { points: "12 2 2 7 12 12 22 7 12 2" }),
2945
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("polyline", { points: "2 17 12 22 22 17" }),
2946
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("polyline", { points: "2 12 12 17 22 12" })
2880
2947
  ] });
2881
2948
  var styles = {
2882
2949
  root: {
@@ -2885,8 +2952,8 @@ var styles = {
2885
2952
  // Floating button (closed state - wide with text, open state - circular with X)
2886
2953
  floatingButton: {
2887
2954
  position: "fixed",
2888
- bottom: 24,
2889
- right: 24,
2955
+ bottom: 16,
2956
+ right: 16,
2890
2957
  borderRadius: "999px",
2891
2958
  border: "none",
2892
2959
  cursor: "pointer",
@@ -2896,30 +2963,30 @@ var styles = {
2896
2963
  display: "flex",
2897
2964
  alignItems: "center",
2898
2965
  justifyContent: "center",
2899
- fontSize: 15,
2966
+ fontSize: 14,
2900
2967
  fontWeight: 600,
2901
2968
  zIndex: 99999,
2902
2969
  transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
2903
2970
  },
2904
2971
  floatingButtonClosed: {
2905
- padding: "14px 24px",
2972
+ padding: "12px 20px",
2906
2973
  gap: 8
2907
2974
  },
2908
2975
  floatingButtonOpen: {
2909
- width: 56,
2910
- height: 56,
2976
+ width: 52,
2977
+ height: 52,
2911
2978
  padding: 0
2912
2979
  },
2913
2980
  floatingButtonMobile: {
2914
- width: 56,
2915
- height: 56,
2981
+ width: 52,
2982
+ height: 52,
2916
2983
  padding: 0
2917
2984
  },
2918
2985
  // Panel (expandable)
2919
2986
  panel: {
2920
2987
  position: "fixed",
2921
- bottom: 92,
2922
- right: 24,
2988
+ bottom: 80,
2989
+ right: 16,
2923
2990
  background: "#fff",
2924
2991
  borderRadius: 16,
2925
2992
  boxShadow: "0 20px 60px rgba(15, 23, 42, 0.3), 0 0 0 1px rgba(15, 23, 42, 0.05)",
@@ -2930,16 +2997,16 @@ var styles = {
2930
2997
  transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
2931
2998
  },
2932
2999
  panelNormal: {
2933
- width: 400,
2934
- height: 550
3000
+ width: 360,
3001
+ height: 520
2935
3002
  },
2936
3003
  panelExpanded: {
2937
- width: 520,
2938
- height: 700
3004
+ width: 480,
3005
+ height: 640
2939
3006
  },
2940
3007
  // Header with green gradient
2941
3008
  header: {
2942
- padding: "16px 18px",
3009
+ padding: "14px 16px",
2943
3010
  background: "linear-gradient(135deg, #10b981, #059669)",
2944
3011
  color: "#fff",
2945
3012
  display: "flex",
@@ -2948,7 +3015,7 @@ var styles = {
2948
3015
  },
2949
3016
  title: {
2950
3017
  margin: 0,
2951
- fontSize: 16,
3018
+ fontSize: 15,
2952
3019
  fontWeight: 600,
2953
3020
  letterSpacing: "-0.01em"
2954
3021
  },
@@ -2961,8 +3028,8 @@ var styles = {
2961
3028
  border: "none",
2962
3029
  background: "rgba(255, 255, 255, 0.15)",
2963
3030
  color: "#fff",
2964
- width: 32,
2965
- height: 32,
3031
+ width: 30,
3032
+ height: 30,
2966
3033
  borderRadius: 8,
2967
3034
  cursor: "pointer",
2968
3035
  display: "flex",
@@ -2973,7 +3040,7 @@ var styles = {
2973
3040
  // Messages area
2974
3041
  messages: {
2975
3042
  flex: 1,
2976
- padding: "20px 18px",
3043
+ padding: "16px",
2977
3044
  overflowY: "auto",
2978
3045
  background: "#f8fafc",
2979
3046
  display: "flex",
@@ -2988,10 +3055,10 @@ var styles = {
2988
3055
  },
2989
3056
  messageBubble: {
2990
3057
  maxWidth: "85%",
2991
- padding: "12px 14px",
3058
+ padding: "10px 12px",
2992
3059
  borderRadius: 16,
2993
3060
  lineHeight: 1.5,
2994
- fontSize: 14,
3061
+ fontSize: 13,
2995
3062
  whiteSpace: "pre-wrap",
2996
3063
  wordBreak: "break-word"
2997
3064
  },
@@ -3105,7 +3172,7 @@ var styles = {
3105
3172
  // Input area
3106
3173
  inputWrapper: {
3107
3174
  borderTop: "1px solid #e2e8f0",
3108
- padding: "14px 16px",
3175
+ padding: "10px 14px",
3109
3176
  display: "flex",
3110
3177
  gap: 10,
3111
3178
  alignItems: "flex-end",
@@ -3116,8 +3183,8 @@ var styles = {
3116
3183
  resize: "none",
3117
3184
  borderRadius: 12,
3118
3185
  border: "1.5px solid #cbd5e1",
3119
- padding: "10px 12px",
3120
- fontSize: 14,
3186
+ padding: "8px 10px",
3187
+ fontSize: 13,
3121
3188
  fontFamily: "inherit",
3122
3189
  lineHeight: 1.4,
3123
3190
  transition: "border-color 0.2s"
@@ -3129,18 +3196,18 @@ var styles = {
3129
3196
  sendButton: {
3130
3197
  borderRadius: 12,
3131
3198
  border: "none",
3132
- padding: "10px 14px",
3199
+ padding: "8px 12px",
3133
3200
  background: "linear-gradient(135deg, #10b981, #059669)",
3134
3201
  color: "#fff",
3135
3202
  cursor: "pointer",
3136
- fontSize: 14,
3203
+ fontSize: 13,
3137
3204
  fontWeight: 600,
3138
3205
  display: "flex",
3139
3206
  alignItems: "center",
3140
3207
  justifyContent: "center",
3141
3208
  transition: "opacity 0.2s, transform 0.2s",
3142
- minWidth: 44,
3143
- height: 44
3209
+ minWidth: 40,
3210
+ height: 40
3144
3211
  },
3145
3212
  // Status messages
3146
3213
  statusNote: {
@@ -3170,42 +3237,42 @@ var FloatingChatBox = ({
3170
3237
  open: openProp
3171
3238
  }) => {
3172
3239
  const isControlled = openProp !== void 0;
3173
- const [internalOpen, setInternalOpen] = (0, import_react4.useState)(false);
3240
+ const [internalOpen, setInternalOpen] = (0, import_react7.useState)(false);
3174
3241
  const open = isControlled ? openProp : internalOpen;
3175
- const setOpen = (0, import_react4.useCallback)((value) => {
3242
+ const setOpen = (0, import_react7.useCallback)((value) => {
3176
3243
  const newValue = typeof value === "function" ? value(open) : value;
3177
3244
  if (!isControlled) {
3178
3245
  setInternalOpen(newValue);
3179
3246
  }
3180
3247
  onOpenChange?.(newValue);
3181
3248
  }, [isControlled, open, onOpenChange]);
3182
- const [expanded, setExpanded] = (0, import_react4.useState)(false);
3183
- const [messages, setMessages] = (0, import_react4.useState)([]);
3184
- const [inputValue, setInputValue] = (0, import_react4.useState)("");
3185
- const [conversationId, setConversationId] = (0, import_react4.useState)();
3186
- const [errorMessage, setErrorMessage] = (0, import_react4.useState)(null);
3187
- const [isFocused, setIsFocused] = (0, import_react4.useState)(false);
3188
- const [isMobile, setIsMobile] = (0, import_react4.useState)(false);
3189
- const messagesEndRef = (0, import_react4.useRef)(null);
3190
- const messagesContainerRef = (0, import_react4.useRef)(null);
3191
- const chatBoxRef = (0, import_react4.useRef)(null);
3192
- const chatConfig = (0, import_react4.useMemo)(() => {
3249
+ const [expanded, setExpanded] = (0, import_react7.useState)(false);
3250
+ const [messages, setMessages] = (0, import_react7.useState)([]);
3251
+ const [inputValue, setInputValue] = (0, import_react7.useState)("");
3252
+ const [conversationId, setConversationId] = (0, import_react7.useState)();
3253
+ const [errorMessage, setErrorMessage] = (0, import_react7.useState)(null);
3254
+ const [isFocused, setIsFocused] = (0, import_react7.useState)(false);
3255
+ const [isMobile, setIsMobile] = (0, import_react7.useState)(false);
3256
+ const messagesEndRef = (0, import_react7.useRef)(null);
3257
+ const messagesContainerRef = (0, import_react7.useRef)(null);
3258
+ const chatBoxRef = (0, import_react7.useRef)(null);
3259
+ const chatConfig = (0, import_react7.useMemo)(() => {
3193
3260
  if (!baseUrl) return void 0;
3194
3261
  return { baseUrl, accessToken, getAccessToken };
3195
3262
  }, [accessToken, baseUrl, getAccessToken]);
3196
3263
  const { sendMessage: sendMessage2, isStreaming, streamingText, completeResponse } = useSendMessageStream(chatConfig);
3197
3264
  const canSend = Boolean(mapId) && Boolean(baseUrl) && inputValue.trim().length > 0 && !isStreaming;
3198
- (0, import_react4.useEffect)(() => {
3265
+ (0, import_react7.useEffect)(() => {
3199
3266
  if (open && isMobile) {
3200
3267
  setExpanded(true);
3201
3268
  }
3202
3269
  }, [open, isMobile]);
3203
- const scrollToBottom = (0, import_react4.useCallback)(() => {
3270
+ const scrollToBottom = (0, import_react7.useCallback)(() => {
3204
3271
  if (messagesEndRef.current) {
3205
3272
  messagesEndRef.current.scrollIntoView({ behavior: "smooth" });
3206
3273
  }
3207
3274
  }, []);
3208
- (0, import_react4.useEffect)(() => {
3275
+ (0, import_react7.useEffect)(() => {
3209
3276
  if (open && messages.length === 0) {
3210
3277
  setMessages([
3211
3278
  {
@@ -3216,10 +3283,10 @@ var FloatingChatBox = ({
3216
3283
  ]);
3217
3284
  }
3218
3285
  }, [open, messages.length]);
3219
- (0, import_react4.useEffect)(() => {
3286
+ (0, import_react7.useEffect)(() => {
3220
3287
  scrollToBottom();
3221
3288
  }, [messages, streamingText, scrollToBottom]);
3222
- (0, import_react4.useEffect)(() => {
3289
+ (0, import_react7.useEffect)(() => {
3223
3290
  if (!open) return;
3224
3291
  if (isMobile && expanded) return;
3225
3292
  const handleClickOutside = (event) => {
@@ -3232,7 +3299,7 @@ var FloatingChatBox = ({
3232
3299
  document.removeEventListener("mousedown", handleClickOutside);
3233
3300
  };
3234
3301
  }, [open, isMobile, expanded]);
3235
- (0, import_react4.useEffect)(() => {
3302
+ (0, import_react7.useEffect)(() => {
3236
3303
  if (typeof window === "undefined") return;
3237
3304
  const mediaQuery = window.matchMedia("(max-width: 768px)");
3238
3305
  const updateMobile = () => setIsMobile(mediaQuery.matches);
@@ -3250,7 +3317,7 @@ var FloatingChatBox = ({
3250
3317
  }
3251
3318
  };
3252
3319
  }, []);
3253
- (0, import_react4.useEffect)(() => {
3320
+ (0, import_react7.useEffect)(() => {
3254
3321
  if (typeof document === "undefined") return;
3255
3322
  if (!open || !isMobile) return;
3256
3323
  document.body.style.overflow = "hidden";
@@ -3258,10 +3325,10 @@ var FloatingChatBox = ({
3258
3325
  document.body.style.overflow = "";
3259
3326
  };
3260
3327
  }, [open, isMobile]);
3261
- const addMessage = (0, import_react4.useCallback)((message) => {
3328
+ const addMessage = (0, import_react7.useCallback)((message) => {
3262
3329
  setMessages((prev) => [...prev, message]);
3263
3330
  }, []);
3264
- const handleSend = (0, import_react4.useCallback)(async () => {
3331
+ const handleSend = (0, import_react7.useCallback)(async () => {
3265
3332
  if (!mapId) {
3266
3333
  setErrorMessage("Selecciona un mapa para usar el asistente.");
3267
3334
  return;
@@ -3296,11 +3363,11 @@ var FloatingChatBox = ({
3296
3363
  response
3297
3364
  });
3298
3365
  } catch (error) {
3299
- setErrorMessage(error instanceof Error ? error.message : "Ocurri\xF3 un error inesperado.");
3366
+ setErrorMessage("Ocurri\xF3 un error al generar la respuesta.");
3300
3367
  addMessage({
3301
3368
  id: `error-${Date.now()}`,
3302
3369
  role: "assistant",
3303
- content: `\u274C Error: ${error instanceof Error ? error.message : "Ocurri\xF3 un error inesperado."}`
3370
+ content: "\u274C Ocurri\xF3 un error al generar la respuesta."
3304
3371
  });
3305
3372
  }
3306
3373
  }, [
@@ -3314,7 +3381,7 @@ var FloatingChatBox = ({
3314
3381
  sendMessage2,
3315
3382
  userId
3316
3383
  ]);
3317
- const handleKeyDown = (0, import_react4.useCallback)(
3384
+ const handleKeyDown = (0, import_react7.useCallback)(
3318
3385
  (event) => {
3319
3386
  if (event.key === "Enter" && !event.shiftKey) {
3320
3387
  event.preventDefault();
@@ -3325,20 +3392,20 @@ var FloatingChatBox = ({
3325
3392
  },
3326
3393
  [canSend, handleSend]
3327
3394
  );
3328
- const handleFollowUpClick = (0, import_react4.useCallback)((question) => {
3395
+ const handleFollowUpClick = (0, import_react7.useCallback)((question) => {
3329
3396
  setInputValue(question);
3330
3397
  }, []);
3331
3398
  const renderMetadata = (response) => {
3332
3399
  if (!response?.metadata) return null;
3333
3400
  const referencedLayers = response.metadata.referencedLayers;
3334
3401
  if (!referencedLayers || referencedLayers.length === 0) return null;
3335
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: styles.metadataSection, children: [
3336
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: styles.metadataTitle, children: [
3337
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(LayersIcon, {}),
3402
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: styles.metadataSection, children: [
3403
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: styles.metadataTitle, children: [
3404
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(LayersIcon, {}),
3338
3405
  "Capas Analizadas"
3339
3406
  ] }),
3340
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("ul", { style: styles.metadataList, children: referencedLayers.map((layer, index) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("li", { style: styles.metadataItem, children: [
3341
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("strong", { children: layer.layerName }),
3407
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("ul", { style: styles.metadataList, children: referencedLayers.map((layer, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("li", { style: styles.metadataItem, children: [
3408
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("strong", { children: layer.layerName }),
3342
3409
  " (",
3343
3410
  layer.featureCount,
3344
3411
  " ",
@@ -3347,7 +3414,7 @@ var FloatingChatBox = ({
3347
3414
  ] }, index)) })
3348
3415
  ] });
3349
3416
  };
3350
- const handleActionClick = (0, import_react4.useCallback)((action) => {
3417
+ const handleActionClick = (0, import_react7.useCallback)((action) => {
3351
3418
  if (isStreaming) return;
3352
3419
  setOpen(false);
3353
3420
  requestAnimationFrame(() => {
@@ -3356,9 +3423,9 @@ var FloatingChatBox = ({
3356
3423
  }, [isStreaming, setOpen, onActionClick]);
3357
3424
  const renderActions = (response) => {
3358
3425
  if (!response?.suggestedActions?.length) return null;
3359
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: styles.actionsSection, children: [
3360
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: styles.sectionLabel, children: "Acciones Sugeridas" }),
3361
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: styles.actionsGrid, children: response.suggestedActions.map((action, index) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3426
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: styles.actionsSection, children: [
3427
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: styles.sectionLabel, children: "Acciones Sugeridas" }),
3428
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: styles.actionsGrid, children: response.suggestedActions.map((action, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3362
3429
  "button",
3363
3430
  {
3364
3431
  type: "button",
@@ -3389,9 +3456,9 @@ var FloatingChatBox = ({
3389
3456
  };
3390
3457
  const renderFollowUps = (response) => {
3391
3458
  if (!response?.followUpQuestions?.length) return null;
3392
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: styles.actionsSection, children: [
3393
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: styles.sectionLabel, children: "Preguntas Relacionadas" }),
3394
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: response.followUpQuestions.map((question, index) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3459
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: styles.actionsSection, children: [
3460
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: styles.sectionLabel, children: "Preguntas Relacionadas" }),
3461
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: response.followUpQuestions.map((question, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3395
3462
  "button",
3396
3463
  {
3397
3464
  type: "button",
@@ -3420,8 +3487,8 @@ var FloatingChatBox = ({
3420
3487
  )) })
3421
3488
  ] });
3422
3489
  };
3423
- const chatContent = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: styles.root, children: [
3424
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("style", { children: `
3490
+ const chatContent = /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: styles.root, children: [
3491
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("style", { children: `
3425
3492
  @keyframes zenitBlink {
3426
3493
  0%, 49% { opacity: 1; }
3427
3494
  50%, 100% { opacity: 0; }
@@ -3464,11 +3531,13 @@ var FloatingChatBox = ({
3464
3531
  @media (max-width: 768px) {
3465
3532
  .zenit-chat-panel.zenit-chat-panel--fullscreen {
3466
3533
  position: fixed !important;
3467
- inset: 0 !important;
3468
- width: 100vw !important;
3469
- max-width: 100vw !important;
3470
- height: 100vh !important;
3471
- height: 100dvh !important;
3534
+ left: 0 !important;
3535
+ right: 0 !important;
3536
+ top: 4rem !important;
3537
+ bottom: 0 !important;
3538
+ width: 100% !important;
3539
+ max-width: 100% !important;
3540
+ height: auto !important;
3472
3541
  border-radius: 0 !important;
3473
3542
  display: flex !important;
3474
3543
  flex-direction: column !important;
@@ -3491,7 +3560,7 @@ var FloatingChatBox = ({
3491
3560
  }
3492
3561
  }
3493
3562
  ` }),
3494
- open && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3563
+ open && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
3495
3564
  "div",
3496
3565
  {
3497
3566
  ref: chatBoxRef,
@@ -3501,10 +3570,10 @@ var FloatingChatBox = ({
3501
3570
  ...expanded ? styles.panelExpanded : styles.panelNormal
3502
3571
  },
3503
3572
  children: [
3504
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("header", { style: styles.header, children: [
3505
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h3", { style: styles.title, children: "Asistente Zenit AI" }),
3506
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: styles.headerButtons, children: [
3507
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3573
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("header", { style: styles.header, children: [
3574
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h3", { style: styles.title, children: "Asistente Zenit AI" }),
3575
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: styles.headerButtons, children: [
3576
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3508
3577
  "button",
3509
3578
  {
3510
3579
  type: "button",
@@ -3517,10 +3586,10 @@ var FloatingChatBox = ({
3517
3586
  e.currentTarget.style.background = "rgba(255, 255, 255, 0.15)";
3518
3587
  },
3519
3588
  "aria-label": expanded ? "Contraer" : "Expandir",
3520
- children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CollapseIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ExpandIcon, {})
3589
+ children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CollapseIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ExpandIcon, {})
3521
3590
  }
3522
3591
  ),
3523
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3592
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3524
3593
  "button",
3525
3594
  {
3526
3595
  type: "button",
@@ -3533,20 +3602,20 @@ var FloatingChatBox = ({
3533
3602
  e.currentTarget.style.background = "rgba(255, 255, 255, 0.15)";
3534
3603
  },
3535
3604
  "aria-label": "Cerrar",
3536
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CloseIcon, {})
3605
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CloseIcon, {})
3537
3606
  }
3538
3607
  )
3539
3608
  ] })
3540
3609
  ] }),
3541
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { ref: messagesContainerRef, className: "zenit-ai-body", style: styles.messages, children: [
3542
- messages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3610
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { ref: messagesContainerRef, className: "zenit-ai-body", style: styles.messages, children: [
3611
+ messages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3543
3612
  "div",
3544
3613
  {
3545
3614
  style: {
3546
3615
  ...styles.messageWrapper,
3547
3616
  alignItems: message.role === "user" ? "flex-end" : "flex-start"
3548
3617
  },
3549
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3618
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
3550
3619
  "div",
3551
3620
  {
3552
3621
  style: {
@@ -3554,7 +3623,7 @@ var FloatingChatBox = ({
3554
3623
  ...message.role === "user" ? styles.userMessage : styles.assistantMessage
3555
3624
  },
3556
3625
  children: [
3557
- message.role === "assistant" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MarkdownRenderer, { content: message.content }) : message.content,
3626
+ message.role === "assistant" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MarkdownRenderer, { content: message.content }) : message.content,
3558
3627
  message.role === "assistant" && renderMetadata(message.response),
3559
3628
  message.role === "assistant" && renderActions(message.response),
3560
3629
  message.role === "assistant" && renderFollowUps(message.response)
@@ -3564,39 +3633,39 @@ var FloatingChatBox = ({
3564
3633
  },
3565
3634
  message.id
3566
3635
  )),
3567
- isStreaming && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3636
+ isStreaming && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3568
3637
  "div",
3569
3638
  {
3570
3639
  style: {
3571
3640
  ...styles.messageWrapper,
3572
3641
  alignItems: "flex-start"
3573
3642
  },
3574
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3643
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3575
3644
  "div",
3576
3645
  {
3577
3646
  style: {
3578
3647
  ...styles.messageBubble,
3579
3648
  ...styles.assistantMessage
3580
3649
  },
3581
- children: streamingText ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
3582
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MarkdownRenderer, { content: streamingText }),
3583
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: styles.cursor })
3584
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: styles.thinkingText, children: [
3585
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Analizando" }),
3586
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: styles.typingIndicator, children: [
3587
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "zenit-typing-dot", style: styles.typingDot }),
3588
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "zenit-typing-dot", style: styles.typingDot }),
3589
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "zenit-typing-dot", style: styles.typingDot })
3650
+ children: streamingText ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
3651
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MarkdownRenderer, { content: streamingText }),
3652
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: styles.cursor })
3653
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: styles.thinkingText, children: [
3654
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Pensando" }),
3655
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: styles.typingIndicator, children: [
3656
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "zenit-typing-dot", style: styles.typingDot }),
3657
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "zenit-typing-dot", style: styles.typingDot }),
3658
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "zenit-typing-dot", style: styles.typingDot })
3590
3659
  ] })
3591
3660
  ] })
3592
3661
  }
3593
3662
  )
3594
3663
  }
3595
3664
  ),
3596
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { ref: messagesEndRef })
3665
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { ref: messagesEndRef })
3597
3666
  ] }),
3598
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "zenit-ai-input-area", style: styles.inputWrapper, children: [
3599
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3667
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "zenit-ai-input-area", style: styles.inputWrapper, children: [
3668
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3600
3669
  "textarea",
3601
3670
  {
3602
3671
  style: {
@@ -3613,7 +3682,7 @@ var FloatingChatBox = ({
3613
3682
  disabled: !mapId || !baseUrl || isStreaming
3614
3683
  }
3615
3684
  ),
3616
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3685
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3617
3686
  "button",
3618
3687
  {
3619
3688
  type: "button",
@@ -3622,36 +3691,38 @@ var FloatingChatBox = ({
3622
3691
  onClick: () => void handleSend(),
3623
3692
  disabled: !canSend,
3624
3693
  "aria-label": "Enviar mensaje",
3625
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SendIcon, {})
3694
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SendIcon, {})
3626
3695
  }
3627
3696
  )
3628
3697
  ] }),
3629
- errorMessage && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: styles.errorText, children: errorMessage }),
3630
- !mapId && !errorMessage && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: styles.statusNote, children: "Selecciona un mapa para usar el asistente" }),
3631
- !baseUrl && !errorMessage && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: styles.statusNote, children: "Configura la baseUrl del SDK" })
3698
+ errorMessage && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: styles.errorText, children: errorMessage }),
3699
+ isStreaming && !errorMessage && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: styles.statusNote, children: "Generando sugerencias..." }),
3700
+ !mapId && !errorMessage && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: styles.statusNote, children: "Selecciona un mapa para usar el asistente" }),
3701
+ !baseUrl && !errorMessage && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: styles.statusNote, children: "Configura la baseUrl del SDK" })
3632
3702
  ]
3633
3703
  }
3634
3704
  ),
3635
- !(hideButton && !open) && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3705
+ !(hideButton && !open) && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3636
3706
  "button",
3637
3707
  {
3638
3708
  type: "button",
3639
3709
  className: `zenit-ai-button ${open ? "open" : ""}${open && isMobile ? " zenit-ai-button--hidden-mobile" : ""}`,
3640
3710
  style: {
3641
3711
  ...styles.floatingButton,
3642
- ...open ? styles.floatingButtonOpen : isMobile ? styles.floatingButtonMobile : styles.floatingButtonClosed
3712
+ ...open ? styles.floatingButtonOpen : isMobile ? styles.floatingButtonMobile : styles.floatingButtonClosed,
3713
+ zIndex: open ? 100001 : 99999
3643
3714
  },
3644
3715
  onClick: () => setOpen((prev) => !prev),
3645
3716
  "aria-label": open ? "Cerrar asistente" : "Abrir asistente Zenit AI",
3646
- children: open ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CloseIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
3647
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ChatIcon, {}),
3648
- !isMobile && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Asistente IA" })
3717
+ children: open ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CloseIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
3718
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChatIcon, {}),
3719
+ !isMobile && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Asistente IA" })
3649
3720
  ] })
3650
3721
  }
3651
3722
  )
3652
3723
  ] });
3653
3724
  if (typeof document !== "undefined") {
3654
- return (0, import_react_dom.createPortal)(chatContent, document.body);
3725
+ return (0, import_react_dom2.createPortal)(chatContent, document.body);
3655
3726
  }
3656
3727
  return chatContent;
3657
3728
  };