wm-menu 1.5.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,8 @@
1
1
  (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('@angular/common/http'), require('rxjs/operators'), require('rxjs'), require('@angular/platform-browser'), require('@angular/material/icon'), require('@auth0/angular-jwt'), require('@angular/material/toolbar'), require('@angular/material/sidenav'), require('@angular/material/list'), require('@angular/material/button'), require('@angular/material/menu'), require('@angular/material/table'), require('@angular/material/tooltip'), require('@angular/material/core'), require('@angular/forms')) :
3
- typeof define === 'function' && define.amd ? define('wm-menu', ['exports', '@angular/core', '@angular/common/http', 'rxjs/operators', 'rxjs', '@angular/platform-browser', '@angular/material/icon', '@auth0/angular-jwt', '@angular/material/toolbar', '@angular/material/sidenav', '@angular/material/list', '@angular/material/button', '@angular/material/menu', '@angular/material/table', '@angular/material/tooltip', '@angular/material/core', '@angular/forms'], factory) :
4
- (global = global || self, factory(global['wm-menu'] = {}, global.ng.core, global.ng.common.http, global.rxjs.operators, global.rxjs, global.ng.platformBrowser, global.ng.material.icon, global.angularJwt, global.ng.material.toolbar, global.ng.material.sidenav, global.ng.material.list, global.ng.material.button, global.ng.material.menu, global.ng.material.table, global.ng.material.tooltip, global.ng.material.core, global.ng.forms));
5
- }(this, (function (exports, core, http, operators, rxjs, platformBrowser, icon, angularJwt, toolbar, sidenav, list, button, menu, table, tooltip, core$1, forms) { 'use strict';
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('@angular/common/http'), require('rxjs/operators'), require('rxjs'), require('@angular/platform-browser'), require('@angular/material/icon'), require('@auth0/angular-jwt'), require('@angular/material/toolbar'), require('@angular/material/sidenav'), require('@angular/material/list'), require('@angular/material/button'), require('@angular/material/menu'), require('@angular/material/table'), require('@angular/material/tooltip'), require('@angular/material/core'), require('@angular/forms'), require('xlsx')) :
3
+ typeof define === 'function' && define.amd ? define('wm-menu', ['exports', '@angular/core', '@angular/common/http', 'rxjs/operators', 'rxjs', '@angular/platform-browser', '@angular/material/icon', '@auth0/angular-jwt', '@angular/material/toolbar', '@angular/material/sidenav', '@angular/material/list', '@angular/material/button', '@angular/material/menu', '@angular/material/table', '@angular/material/tooltip', '@angular/material/core', '@angular/forms', 'xlsx'], factory) :
4
+ (global = global || self, factory(global['wm-menu'] = {}, global.ng.core, global.ng.common.http, global.rxjs.operators, global.rxjs, global.ng.platformBrowser, global.ng.material.icon, global.angularJwt, global.ng.material.toolbar, global.ng.material.sidenav, global.ng.material.list, global.ng.material.button, global.ng.material.menu, global.ng.material.table, global.ng.material.tooltip, global.ng.material.core, global.ng.forms, global.xlsx));
5
+ }(this, (function (exports, core, http, operators, rxjs, platformBrowser, icon, angularJwt, toolbar, sidenav, list, button, menu, table, tooltip, core$1, forms, xlsx) { 'use strict';
6
6
 
7
7
  /*! *****************************************************************************
8
8
  Copyright (c) Microsoft Corporation.
@@ -825,10 +825,114 @@
825
825
  return AsistenteDatosApiService;
826
826
  }());
827
827
 
828
+ // Exporta/copia el resultado tabular del asistente ("Pregúntale a tus Datos"). El resultado ya
829
+ // llega como arreglo de objetos (una clave por columna, ver RespuestaAsistenteDatos.Resultado), así
830
+ // que no hay que parsear HTML: se recorre directo.
831
+ var TablaExportService = /** @class */ (function () {
832
+ function TablaExportService() {
833
+ // Separador del CSV: punto y coma. Excel en configuración regional ES (la de los usuarios WM)
834
+ // usa ';' como separador de lista, así que al abrir el .csv las columnas quedan separadas sin
835
+ // tener que importar con asistente. El TSV del portapapeles usa TAB, que Excel pega en columnas
836
+ // sin depender de la config regional.
837
+ this.SEPARADOR_CSV = ';';
838
+ }
839
+ TablaExportService.prototype.columnas = function (filas) {
840
+ return filas && filas.length ? Object.keys(filas[0]) : [];
841
+ };
842
+ TablaExportService.prototype.valorCelda = function (valor) {
843
+ if (valor === null || valor === undefined) {
844
+ return '';
845
+ }
846
+ return String(valor);
847
+ };
848
+ // TSV para el portapapeles: se pega directo en Excel/Sheets en columnas. Los TAB y saltos de
849
+ // línea dentro de una celda se neutralizan a espacio para no romper la cuadrícula al pegar.
850
+ TablaExportService.prototype.aTsv = function (filas) {
851
+ var _this = this;
852
+ var cols = this.columnas(filas);
853
+ var limpiar = function (v) { return v.replace(/\t/g, ' ').replace(/\r?\n/g, ' '); };
854
+ var encabezado = cols.map(limpiar).join('\t');
855
+ var cuerpo = filas.map(function (fila) { return cols.map(function (c) { return limpiar(_this.valorCelda(fila[c])); }).join('\t'); });
856
+ return __spread([encabezado], cuerpo).join('\n');
857
+ };
858
+ TablaExportService.prototype.copiar = function (filas) {
859
+ return __awaiter(this, void 0, void 0, function () {
860
+ var tsv, area;
861
+ return __generator(this, function (_a) {
862
+ switch (_a.label) {
863
+ case 0:
864
+ tsv = this.aTsv(filas);
865
+ if (!(navigator.clipboard && navigator.clipboard.writeText)) return [3 /*break*/, 2];
866
+ return [4 /*yield*/, navigator.clipboard.writeText(tsv)];
867
+ case 1:
868
+ _a.sent();
869
+ return [2 /*return*/];
870
+ case 2:
871
+ area = document.createElement('textarea');
872
+ area.value = tsv;
873
+ area.style.position = 'fixed';
874
+ area.style.left = '-9999px';
875
+ document.body.appendChild(area);
876
+ area.select();
877
+ try {
878
+ document.execCommand('copy');
879
+ }
880
+ finally {
881
+ document.body.removeChild(area);
882
+ }
883
+ return [2 /*return*/];
884
+ }
885
+ });
886
+ });
887
+ };
888
+ TablaExportService.prototype.escaparCsv = function (valor) {
889
+ if (valor.indexOf('"') >= 0 || valor.indexOf(this.SEPARADOR_CSV) >= 0 || /[\r\n]/.test(valor)) {
890
+ return '"' + valor.replace(/"/g, '""') + '"';
891
+ }
892
+ return valor;
893
+ };
894
+ TablaExportService.prototype.descargarCsv = function (filas, nombreBase) {
895
+ var _this = this;
896
+ var cols = this.columnas(filas);
897
+ var lineas = __spread([
898
+ cols.map(function (c) { return _this.escaparCsv(c); }).join(this.SEPARADOR_CSV)
899
+ ], filas.map(function (fila) { return cols.map(function (c) { return _this.escaparCsv(_this.valorCelda(fila[c])); }).join(_this.SEPARADOR_CSV); }));
900
+ // BOM UTF-8 () para que Excel muestre tildes y ñ correctamente.
901
+ var contenido = '' + lineas.join('\r\n');
902
+ var blob = new Blob([contenido], { type: 'text/csv;charset=utf-8;' });
903
+ this.descargarBlob(blob, nombreBase + '.csv');
904
+ };
905
+ TablaExportService.prototype.descargarXlsx = function (filas, nombreBase) {
906
+ var hoja = xlsx.utils.json_to_sheet(filas);
907
+ var libro = xlsx.utils.book_new();
908
+ xlsx.utils.book_append_sheet(libro, hoja, 'Datos');
909
+ // XLSX.writeFile arma el Blob y dispara la descarga en el navegador.
910
+ xlsx.writeFile(libro, nombreBase + '.xlsx');
911
+ };
912
+ TablaExportService.prototype.descargarBlob = function (blob, nombreArchivo) {
913
+ var url = URL.createObjectURL(blob);
914
+ var enlace = document.createElement('a');
915
+ enlace.href = url;
916
+ enlace.download = nombreArchivo;
917
+ document.body.appendChild(enlace);
918
+ enlace.click();
919
+ document.body.removeChild(enlace);
920
+ setTimeout(function () { return URL.revokeObjectURL(url); });
921
+ };
922
+ TablaExportService.ɵprov = core.ɵɵdefineInjectable({ factory: function TablaExportService_Factory() { return new TablaExportService(); }, token: TablaExportService, providedIn: "root" });
923
+ TablaExportService = __decorate([
924
+ core.Injectable({
925
+ providedIn: 'root',
926
+ })
927
+ ], TablaExportService);
928
+ return TablaExportService;
929
+ }());
930
+
828
931
  var AsistenteDatosComponent = /** @class */ (function () {
829
- function AsistenteDatosComponent(asistenteDatosApiService, reconocimientoVoz) {
932
+ function AsistenteDatosComponent(asistenteDatosApiService, reconocimientoVoz, tablaExport) {
830
933
  this.asistenteDatosApiService = asistenteDatosApiService;
831
934
  this.reconocimientoVoz = reconocimientoVoz;
935
+ this.tablaExport = tablaExport;
832
936
  // La apertura/cierre del panel la controla el padre (el menú). El panel solo se renderiza
833
937
  // cuando `abierto` es true y avisa, vía `cerrarChange`, cuando el usuario toca la X.
834
938
  this.abierto = false;
@@ -846,14 +950,28 @@
846
950
  this.proveedorSeleccionado = 'Gemini';
847
951
  // Dictado por voz (Web Speech API) — sin soporte en Firefox, inconsistente en Safari.
848
952
  this.escuchando = false;
849
- // Preguntas ya verificadas como respondibles hoy (whitelist + contrato de la IA — ver
850
- // ChatConsultaWhitelistServicio/ChatConsultaPromptServicio en el backend), mostradas como chips
851
- // de sugerencia en el estado vacío del chat.
852
- this.chipsSugeridos = [
953
+ // Las preguntas sugeridas se muestran con el chat vacío. Tras la primera pregunta (cuando ya
954
+ // hay mensajes) quedan plegadas en el botón "Sugerencias" que controla este flag, para que el
955
+ // área de conversación gane ese espacio; el usuario puede volver a abrirlas. Es solo UI, igual
956
+ // con Gemini y con Groq.
957
+ this.sugerenciasAbiertas = false;
958
+ // Tabla abierta en el modal "ampliar" (null = modal cerrado). El panel del chat es angosto y
959
+ // recorta las columnas; el modal muestra la misma tabla a todo el ancho de la pantalla.
960
+ this.tablaAmpliada = null;
961
+ this.descripcionAmpliada = '';
962
+ // Aviso efímero "Tabla copiada" tras copiar al portapapeles.
963
+ this.copiadoAviso = false;
964
+ // Sugerencias FIJAS: preguntas ya verificadas como respondibles (whitelist + contrato de la IA —
965
+ // ver ChatConsultaWhitelistServicio/ChatConsultaPromptServicio en el backend). Son el estado
966
+ // inicial (chat vacío) y el fallback cuando el backend no devuelve sugerencias contextuales.
967
+ this.chipsFijos = [
853
968
  '¿Cuántos formularios se diligenciaron hoy?',
854
969
  '¿Cuántos formularios se diligenciaron esta semana?',
855
970
  'Muéstrame los últimos 10 formularios diligenciados',
856
971
  ];
972
+ // Sugerencias que se muestran ahora: arrancan en las fijas y, tras cada respuesta, se reemplazan
973
+ // por las contextuales que devuelve el backend (Sugerencias); si no vienen, vuelven a las fijas.
974
+ this.chipsSugeridos = __spread(this.chipsFijos);
857
975
  }
858
976
  Object.defineProperty(AsistenteDatosComponent.prototype, "urlConsultar", {
859
977
  // URL del POST de consulta. Se refleja en el api service (propiedad `url`), que es donde se usa.
@@ -938,6 +1056,7 @@
938
1056
  resultado: (_a = respuesta.Resultado, (_a !== null && _a !== void 0 ? _a : [])),
939
1057
  proveedor: respuesta.Proveedor,
940
1058
  });
1059
+ _this.actualizarSugerencias(respuesta.Sugerencias);
941
1060
  _this.scrollAlFinal();
942
1061
  },
943
1062
  error: function (error) {
@@ -952,6 +1071,14 @@
952
1071
  },
953
1072
  });
954
1073
  };
1074
+ // Tras cada respuesta, el backend puede devolver sugerencias de seguimiento acotadas al rol. Si
1075
+ // llegan al menos 2, reemplazan a las que se muestran; si no, se vuelve a las fijas. El chat vacío
1076
+ // conserva las fijas: este método solo se llama al recibir una respuesta.
1077
+ AsistenteDatosComponent.prototype.actualizarSugerencias = function (sugerencias) {
1078
+ this.chipsSugeridos = sugerencias && sugerencias.length >= 2
1079
+ ? sugerencias
1080
+ : __spread(this.chipsFijos);
1081
+ };
955
1082
  // El nuevo mensaje recién se refleja en el DOM después del siguiente ciclo de detección de
956
1083
  // cambios — por eso el scrollTop se ajusta en un setTimeout(0), no inmediatamente después del
957
1084
  // push, para que scrollHeight ya incluya el mensaje agregado.
@@ -985,9 +1112,58 @@
985
1112
  var columna = this.columnasDe(resultado)[0];
986
1113
  return resultado.map(function (fila) { return fila[columna]; }).join(', ');
987
1114
  };
1115
+ // --- Acciones sobre la tabla de resultados (ampliar / copiar / exportar) ---
1116
+ AsistenteDatosComponent.prototype.ampliar = function (resultado, descripcion) {
1117
+ this.tablaAmpliada = resultado;
1118
+ this.descripcionAmpliada = descripcion || '';
1119
+ };
1120
+ AsistenteDatosComponent.prototype.cerrarAmpliada = function () {
1121
+ this.tablaAmpliada = null;
1122
+ this.descripcionAmpliada = '';
1123
+ };
1124
+ AsistenteDatosComponent.prototype.copiar = function (resultado) {
1125
+ return __awaiter(this, void 0, void 0, function () {
1126
+ var _a;
1127
+ var _this = this;
1128
+ return __generator(this, function (_b) {
1129
+ switch (_b.label) {
1130
+ case 0:
1131
+ _b.trys.push([0, 2, , 3]);
1132
+ return [4 /*yield*/, this.tablaExport.copiar(resultado)];
1133
+ case 1:
1134
+ _b.sent();
1135
+ this.copiadoAviso = true;
1136
+ setTimeout(function () { return (_this.copiadoAviso = false); }, 1800);
1137
+ return [3 /*break*/, 3];
1138
+ case 2:
1139
+ _a = _b.sent();
1140
+ return [3 /*break*/, 3];
1141
+ case 3: return [2 /*return*/];
1142
+ }
1143
+ });
1144
+ });
1145
+ };
1146
+ AsistenteDatosComponent.prototype.exportarCsv = function (resultado) {
1147
+ this.tablaExport.descargarCsv(resultado, this.nombreArchivo());
1148
+ };
1149
+ AsistenteDatosComponent.prototype.exportarXlsx = function (resultado) {
1150
+ this.tablaExport.descargarXlsx(resultado, this.nombreArchivo());
1151
+ };
1152
+ AsistenteDatosComponent.prototype.nombreArchivo = function () {
1153
+ var ahora = new Date();
1154
+ var dos = function (n) { return String(n).padStart(2, '0'); };
1155
+ return ('asistente-datos-' +
1156
+ ahora.getFullYear() +
1157
+ dos(ahora.getMonth() + 1) +
1158
+ dos(ahora.getDate()) +
1159
+ '-' +
1160
+ dos(ahora.getHours()) +
1161
+ dos(ahora.getMinutes()));
1162
+ };
988
1163
  AsistenteDatosComponent.ctorParameters = function () { return [
989
1164
  { type: AsistenteDatosApiService },
990
- { type: ReconocimientoVozService }
1165
+ { type: ReconocimientoVozService },
1166
+ { type: TablaExportService }
991
1167
  ]; };
992
1168
  __decorate([
993
1169
  core.ViewChild('mensajesContainer')
@@ -1007,8 +1183,8 @@
1007
1183
  AsistenteDatosComponent = __decorate([
1008
1184
  core.Component({
1009
1185
  selector: 'wm-asistente-datos',
1010
- template: "<!-- Panel (abierto) -->\r\n<div class=\"chat-ia-panel\" *ngIf=\"abierto\">\r\n <div class=\"chat-ia-header\">\r\n <div class=\"chat-ia-header-info\">\r\n <div class=\"chat-ia-header-icono\">\r\n <svg width=\"17\" height=\"17\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"1.8\">\r\n <path d=\"M4 5h16v10H9l-4 4V15H4z\"></path>\r\n </svg>\r\n </div>\r\n <div>\r\n <div class=\"chat-ia-titulo\">Preg\u00FAntale a tus Datos</div>\r\n <div class=\"chat-ia-subtitulo\">Consulta tus reportes en lenguaje natural</div>\r\n </div>\r\n </div>\r\n <div class=\"chat-ia-cerrar\" (click)=\"cerrar()\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" stroke=\"#fff\" stroke-width=\"2\" fill=\"none\" stroke-linecap=\"round\">\r\n <path d=\"M6 6l12 12M18 6L6 18\"></path>\r\n </svg>\r\n </div>\r\n </div>\r\n\r\n <div class=\"chat-ia-mensajes\" #mensajesContainer>\r\n <div class=\"chat-ia-bienvenida-texto\">Hola, soy tu asistente de datos de Snap Forms. Preg\u00FAntame lo que quieras saber sobre tu operaci\u00F3n.</div>\r\n\r\n <div class=\"chat-ia-mensaje\" *ngFor=\"let mensaje of mensajes\" [class.chat-ia-mensaje-usuario]=\"mensaje.deUsuario\">\r\n <div class=\"chat-ia-burbuja\" [class.chat-ia-burbuja-error]=\"mensaje.esError\">\r\n <span *ngIf=\"mensaje.texto\">{{ mensaje.texto }}</span>\r\n\r\n <ng-container *ngIf=\"mensaje.resultado\">\r\n <div class=\"chat-ia-descripcion\" *ngIf=\"mensaje.descripcion\">{{ mensaje.descripcion }}</div>\r\n\r\n <div class=\"chat-ia-sin-resultados\" *ngIf=\"mensaje.resultado.length === 0\">La consulta no encontr\u00F3 resultados.</div>\r\n\r\n <div class=\"chat-ia-stat\" *ngIf=\"esConteo(mensaje.resultado)\">\r\n <div class=\"chat-ia-stat-valor\">{{ mensaje.resultado[0].Resultado | number }}</div>\r\n </div>\r\n\r\n <div class=\"chat-ia-lista-simple\" *ngIf=\"esListaSimple(mensaje.resultado)\">{{ textoListaSimple(mensaje.resultado) }}</div>\r\n\r\n <div class=\"chat-ia-tabla-resultado\" *ngIf=\"mensaje.resultado.length > 0 && !esConteo(mensaje.resultado) && !esListaSimple(mensaje.resultado)\">\r\n <table mat-table [dataSource]=\"mensaje.resultado\">\r\n <ng-container *ngFor=\"let columna of columnasDe(mensaje.resultado)\" [matColumnDef]=\"columna\">\r\n <th mat-header-cell *matHeaderCellDef>{{ columna }}</th>\r\n <td mat-cell *matCellDef=\"let fila\">{{ fila[columna] }}</td>\r\n </ng-container>\r\n <tr mat-header-row *matHeaderRowDef=\"columnasDe(mensaje.resultado)\"></tr>\r\n <tr mat-row *matRowDef=\"let fila; columns: columnasDe(mensaje.resultado);\"></tr>\r\n </table>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n\r\n <div class=\"chat-ia-pensando\" *ngIf=\"consultando\">\r\n <span class=\"chat-ia-dot\"></span><span class=\"chat-ia-dot\"></span><span class=\"chat-ia-dot\"></span>\r\n </div>\r\n </div>\r\n\r\n <div class=\"chips-chat-ia\">\r\n <span class=\"chip-chat-ia\" *ngFor=\"let chip of chipsSugeridos\"\r\n (click)=\"onClickEnviar(chip)\">{{ chip }}</span>\r\n </div>\r\n\r\n <div class=\"chat-ia-entrada\">\r\n <div class=\"chat-ia-caja-entrada\">\r\n <mat-icon fontSet=\"wm-menu-iconos\" class=\"chat-ia-expandir\" (click)=\"onClickExpandirBorrador()\">{{ borradorExpandido ? 'close_fullscreen' : 'open_in_full' }}</mat-icon>\r\n <textarea [rows]=\"filasBorrador\" [(ngModel)]=\"borrador\" (keydown.enter)=\"$event.preventDefault(); onClickEnviar()\"\r\n [class.chat-ia-textarea-expandido]=\"borradorExpandido\"\r\n placeholder=\"Escribe tu pregunta...\"></textarea>\r\n <div class=\"chat-ia-pie-entrada\">\r\n <button class=\"chat-ia-selector-modelo\" [matMenuTriggerFor]=\"menuModeloChatIA\" type=\"button\">\r\n <span *ngIf=\"proveedorSeleccionado === 'Gemini'\">Gemini</span>\r\n <span *ngIf=\"proveedorSeleccionado === 'Groq'\">Groq</span>\r\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <path d=\"M6 9l6 6 6-6\"></path>\r\n </svg>\r\n </button>\r\n <button class=\"chat-ia-microfono\" [class.chat-ia-microfono-activo]=\"escuchando\" *ngIf=\"micDisponible\"\r\n (click)=\"onClickMicrofono()\" type=\"button\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <path d=\"M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z\"></path>\r\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"></path>\r\n <line x1=\"12\" y1=\"19\" x2=\"12\" y2=\"23\"></line>\r\n </svg>\r\n </button>\r\n <button class=\"chat-ia-enviar\" [disabled]=\"!borrador.trim() || consultando\" (click)=\"onClickEnviar()\" type=\"button\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <path d=\"M12 19V6\"></path>\r\n <path d=\"M5 13l7-7 7 7\"></path>\r\n </svg>\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<mat-menu #menuModeloChatIA=\"matMenu\" class=\"menu-modelo-chat-ia\">\r\n <button mat-menu-item [class.opcion-activa]=\"proveedorSeleccionado === 'Gemini'\"\r\n (click)=\"proveedorSeleccionado = 'Gemini'\">\r\n <span>Gemini</span>\r\n <svg *ngIf=\"proveedorSeleccionado === 'Gemini'\" class=\"chat-ia-check\" width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <path d=\"M20 6L9 17l-5-5\"></path>\r\n </svg>\r\n </button>\r\n <button mat-menu-item [class.opcion-activa]=\"proveedorSeleccionado === 'Groq'\"\r\n (click)=\"proveedorSeleccionado = 'Groq'\">\r\n <span>Groq</span>\r\n <svg *ngIf=\"proveedorSeleccionado === 'Groq'\" class=\"chat-ia-check\" width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <path d=\"M20 6L9 17l-5-5\"></path>\r\n </svg>\r\n </button>\r\n</mat-menu>\r\n",
1011
- styles: [":host{position:fixed;right:32px;bottom:32px;z-index:1000;font-family:Roboto,sans-serif}.chat-ia-panel{width:450px;height:650px;max-width:calc(100vw - 48px);max-height:calc(100vh - 96px);background:#fff;border-radius:18px;box-shadow:0 24px 60px rgba(20,25,30,.28);display:flex;flex-direction:column;overflow:hidden}.chat-ia-header{background:linear-gradient(135deg,#7c5ce0,#5f42bd);padding:16px 18px;display:flex;align-items:center;justify-content:space-between;flex-shrink:0}.chat-ia-header-info{display:flex;align-items:center;gap:10px;min-width:0}.chat-ia-header-icono{width:32px;height:32px;border-radius:50%;background:rgba(255,255,255,.22);display:flex;align-items:center;justify-content:center;flex:none}.chat-ia-titulo{color:#fff;font-size:14px;font-weight:500}.chat-ia-subtitulo{color:rgba(255,255,255,.75);font-size:11px;margin-top:1px}.chat-ia-cerrar{width:24px;height:24px;display:flex;align-items:center;justify-content:center;cursor:pointer;opacity:.85;flex:none}.chat-ia-cerrar:hover{opacity:1}.chat-ia-mensajes{flex:1;min-height:0;overflow-y:auto;display:flex;flex-direction:column;gap:12px;padding:16px;background:#fbfafc}.chat-ia-mensaje{display:flex}.chat-ia-mensaje .chat-ia-burbuja{max-width:88%;background:#f2eefb;color:#26313a;border-radius:14px 14px 14px 3px;padding:10px 13px;font-size:13px;line-height:1.5}.chat-ia-mensaje .chat-ia-burbuja-error{background:#fdecea;color:#7a1f14}.chat-ia-mensaje.chat-ia-mensaje-usuario{justify-content:flex-end}.chat-ia-mensaje.chat-ia-mensaje-usuario .chat-ia-burbuja{background:#7c5ce0;color:#fff;border-radius:14px 14px 3px}.chat-ia-bienvenida-texto{align-self:flex-start;max-width:88%;background:#f2eefb;color:#26313a;border-radius:14px 14px 14px 3px;padding:10px 13px;font-size:13px;line-height:1.5}.chips-chat-ia{flex:none;display:flex;flex-wrap:wrap;gap:6px;padding:0 16px 10px}.chip-chat-ia{border:1px solid #e7e2f5;background:#f2eefb;color:#5f42bd;border-radius:14px;padding:5px 10px;font-size:11px;cursor:pointer}.chip-chat-ia:hover{background:#e7ddfa}.chat-ia-descripcion{font-size:13px;line-height:1.5;margin-bottom:6px}.chat-ia-sin-resultados{color:#5a5a5a;font-style:italic}.chat-ia-stat{margin-top:4px}.chat-ia-stat-valor{font-size:30px;font-weight:600;color:#5f42bd;line-height:1.15;font-variant-numeric:proportional-nums}.chat-ia-lista-simple,.chat-ia-tabla-resultado{margin-top:4px;background:#fff;border-radius:10px;padding:8px 10px}.chat-ia-lista-simple{font-size:13px;line-height:1.5}.chat-ia-tabla-resultado{overflow-x:auto;max-width:100%;padding:4px 10px}.chat-ia-tabla-resultado table{font-size:12px;background:0 0}.chat-ia-tabla-resultado th.mat-header-cell{color:#6b6478;font-weight:600;padding:0 14px 5px 0;border-bottom:1px solid #f0edf7;white-space:nowrap}.chat-ia-tabla-resultado td.mat-cell{padding:7px 14px 0 0;white-space:nowrap;font-variant-numeric:tabular-nums}.chat-ia-pensando{align-self:flex-start;background:#f2eefb;padding:12px 16px;border-radius:14px 14px 14px 3px;display:flex;gap:5px;flex-shrink:0}.chat-ia-dot{width:6px;height:6px;border-radius:50%;background:#9a93ae;-webkit-animation:1.1s ease-in-out infinite chatIaBlink;animation:1.1s ease-in-out infinite chatIaBlink}.chat-ia-dot:nth-child(2){-webkit-animation-delay:.15s;animation-delay:.15s}.chat-ia-dot:nth-child(3){-webkit-animation-delay:.3s;animation-delay:.3s}@-webkit-keyframes chatIaBlink{0%,100%,80%{opacity:.25}40%{opacity:1}}@keyframes chatIaBlink{0%,100%,80%{opacity:.25}40%{opacity:1}}.chat-ia-entrada{padding:10px 14px 14px;border-top:1px solid #e9f2ee;flex-shrink:0}.chat-ia-caja-entrada{position:relative;background:#f2f7f5;border:1px solid #e6efea;border-radius:18px;padding:10px 12px 8px}.chat-ia-caja-entrada textarea{border:none;outline:0;background:0 0;width:100%;font:inherit;font-size:13.5px;color:#26313a;resize:none;line-height:1.45;max-height:110px;overflow-y:auto;display:block;padding:0 28px 0 0}.chat-ia-caja-entrada textarea.chat-ia-textarea-expandido{max-height:160px}.chat-ia-caja-entrada textarea::-webkit-input-placeholder{color:#9a93ae}.chat-ia-caja-entrada textarea::-moz-placeholder{color:#9a93ae}.chat-ia-caja-entrada textarea:-ms-input-placeholder{color:#9a93ae}.chat-ia-caja-entrada textarea::-ms-input-placeholder{color:#9a93ae}.chat-ia-caja-entrada textarea::placeholder{color:#9a93ae}.chat-ia-expandir{position:absolute;top:8px;right:10px;font-size:17px;height:17px;width:17px;color:#9a9a9a;cursor:pointer}.chat-ia-pie-entrada{display:flex;align-items:center;justify-content:flex-end;gap:8px;margin-top:8px}.chat-ia-selector-modelo{display:inline-flex;align-items:center;gap:4px;border:none;background:0 0;color:#4e5d57;border-radius:14px;padding:5px 9px;font-size:11.5px;font-weight:600;cursor:pointer}.chat-ia-enviar{width:32px;height:32px;border-radius:50%;border:none;background:#7c5ce0;color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer;flex:none}.chat-ia-enviar:disabled{background:#dce6e1;color:#a9bab3;cursor:default}.chat-ia-microfono{width:28px;height:28px;border-radius:50%;border:none;background:0 0;color:#6b6478;display:flex;align-items:center;justify-content:center;cursor:pointer;flex:none}.chat-ia-microfono:hover{background:#f2eefb}.chat-ia-microfono.chat-ia-microfono-activo{color:#fff;background:#e05a5a;-webkit-animation:1.1s ease-in-out infinite chatIaBlink;animation:1.1s ease-in-out infinite chatIaBlink}::ng-deep .mat-menu-panel.menu-modelo-chat-ia{min-width:132px;border:1px solid #e6efea;border-radius:10px;box-shadow:0 12px 26px rgba(20,25,30,.16)}::ng-deep .mat-menu-panel.menu-modelo-chat-ia .mat-menu-content{padding:4px}::ng-deep .mat-menu-panel.menu-modelo-chat-ia .mat-menu-item{display:flex;align-items:center;justify-content:space-between;gap:8px;height:auto;line-height:normal;padding:7px 10px;font-size:12px;color:#26313a;border-radius:7px}::ng-deep .mat-menu-panel.menu-modelo-chat-ia .mat-menu-item.opcion-activa{background:#f2f7f5}::ng-deep .mat-menu-panel.menu-modelo-chat-ia .mat-menu-item .chat-ia-check{color:#7c5ce0}"]
1186
+ template: "<!-- Panel (abierto) -->\r\n<div class=\"chat-ia-panel\" *ngIf=\"abierto\">\r\n <div class=\"chat-ia-header\">\r\n <div class=\"chat-ia-header-info\">\r\n <div class=\"chat-ia-header-icono\">\r\n <svg width=\"17\" height=\"17\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"1.8\">\r\n <path d=\"M4 5h16v10H9l-4 4V15H4z\"></path>\r\n </svg>\r\n </div>\r\n <div>\r\n <div class=\"chat-ia-titulo\">Preg\u00FAntale a tus Datos</div>\r\n <div class=\"chat-ia-subtitulo\">Consulta tus reportes en lenguaje natural</div>\r\n </div>\r\n </div>\r\n <div class=\"chat-ia-cerrar\" (click)=\"cerrar()\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" stroke=\"#fff\" stroke-width=\"2\" fill=\"none\" stroke-linecap=\"round\">\r\n <path d=\"M6 6l12 12M18 6L6 18\"></path>\r\n </svg>\r\n </div>\r\n </div>\r\n\r\n <div class=\"chat-ia-mensajes\" #mensajesContainer>\r\n <div class=\"chat-ia-bienvenida-texto\">Hola, soy tu asistente de datos de Snap Forms. Preg\u00FAntame lo que quieras saber sobre tu operaci\u00F3n.</div>\r\n\r\n <div class=\"chat-ia-mensaje\" *ngFor=\"let mensaje of mensajes\" [class.chat-ia-mensaje-usuario]=\"mensaje.deUsuario\">\r\n <div class=\"chat-ia-burbuja\" [class.chat-ia-burbuja-error]=\"mensaje.esError\">\r\n <span *ngIf=\"mensaje.texto\">{{ mensaje.texto }}</span>\r\n\r\n <ng-container *ngIf=\"mensaje.resultado\">\r\n <div class=\"chat-ia-descripcion\" *ngIf=\"mensaje.descripcion\">{{ mensaje.descripcion }}</div>\r\n\r\n <div class=\"chat-ia-sin-resultados\" *ngIf=\"mensaje.resultado.length === 0\">La consulta no encontr\u00F3 resultados.</div>\r\n\r\n <div class=\"chat-ia-stat\" *ngIf=\"esConteo(mensaje.resultado)\">\r\n <div class=\"chat-ia-stat-valor\">{{ mensaje.resultado[0].Resultado | number }}</div>\r\n </div>\r\n\r\n <div class=\"chat-ia-lista-simple\" *ngIf=\"esListaSimple(mensaje.resultado)\">{{ textoListaSimple(mensaje.resultado) }}</div>\r\n\r\n <div class=\"chat-ia-tabla-bloque\" *ngIf=\"mensaje.resultado.length > 0 && !esConteo(mensaje.resultado) && !esListaSimple(mensaje.resultado)\">\r\n <div class=\"chat-ia-tabla-acciones\">\r\n <button type=\"button\" class=\"chat-ia-accion\" matTooltip=\"Ampliar\"\r\n (click)=\"ampliar(mensaje.resultado, mensaje.descripcion)\">\r\n <mat-icon>open_in_full</mat-icon>\r\n </button>\r\n <button type=\"button\" class=\"chat-ia-accion\" matTooltip=\"Copiar\"\r\n (click)=\"copiar(mensaje.resultado)\">\r\n <mat-icon>content_copy</mat-icon>\r\n </button>\r\n <button type=\"button\" class=\"chat-ia-accion\" matTooltip=\"Exportar\"\r\n [matMenuTriggerFor]=\"menuExport\">\r\n <mat-icon>download</mat-icon>\r\n </button>\r\n <mat-menu #menuExport=\"matMenu\" class=\"menu-export-chat-ia\">\r\n <button mat-menu-item type=\"button\" (click)=\"exportarCsv(mensaje.resultado)\">CSV (.csv)</button>\r\n <button mat-menu-item type=\"button\" (click)=\"exportarXlsx(mensaje.resultado)\">Excel (.xlsx)</button>\r\n </mat-menu>\r\n </div>\r\n <div class=\"chat-ia-tabla-resultado\">\r\n <table mat-table [dataSource]=\"mensaje.resultado\">\r\n <ng-container *ngFor=\"let columna of columnasDe(mensaje.resultado)\" [matColumnDef]=\"columna\">\r\n <th mat-header-cell *matHeaderCellDef>{{ columna }}</th>\r\n <td mat-cell *matCellDef=\"let fila\">{{ fila[columna] }}</td>\r\n </ng-container>\r\n <tr mat-header-row *matHeaderRowDef=\"columnasDe(mensaje.resultado)\"></tr>\r\n <tr mat-row *matRowDef=\"let fila; columns: columnasDe(mensaje.resultado);\"></tr>\r\n </table>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n\r\n <div class=\"chat-ia-pensando\" *ngIf=\"consultando\">\r\n <span class=\"chat-ia-dot\"></span><span class=\"chat-ia-dot\"></span><span class=\"chat-ia-dot\"></span>\r\n </div>\r\n </div>\r\n\r\n <!-- Sugerencias: con el chat vac\u00EDo se muestran. Tras la primera pregunta (ya hay mensajes)\r\n quedan plegadas en el bot\u00F3n \"Sugerencias\", que las abre/cierra; al estar plegadas, el \u00E1rea\r\n de conversaci\u00F3n gana ese espacio. -->\r\n <button type=\"button\" class=\"chip-chat-ia-toggle\" *ngIf=\"mensajes.length > 0\"\r\n (click)=\"sugerenciasAbiertas = !sugerenciasAbiertas\">\r\n <svg class=\"chip-chat-ia-toggle-bombilla\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <path d=\"M9 18h6\"></path>\r\n <path d=\"M10 22h4\"></path>\r\n <path d=\"M12 2a7 7 0 0 0-4 12.7c.6.5 1 1.3 1 2.1V18h6v-1.2c0-.8.4-1.6 1-2.1A7 7 0 0 0 12 2z\"></path>\r\n </svg>\r\n <span>Sugerencias</span>\r\n <svg class=\"chip-chat-ia-toggle-chevron\" [class.abierto]=\"sugerenciasAbiertas\" width=\"11\" height=\"11\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <path d=\"M6 9l6 6 6-6\"></path>\r\n </svg>\r\n </button>\r\n <div class=\"chips-chat-ia\" *ngIf=\"mensajes.length === 0 || sugerenciasAbiertas\">\r\n <span class=\"chip-chat-ia\" *ngFor=\"let chip of chipsSugeridos\"\r\n (click)=\"onClickEnviar(chip)\">{{ chip }}</span>\r\n </div>\r\n\r\n <div class=\"chat-ia-entrada\">\r\n <div class=\"chat-ia-caja-entrada\">\r\n <mat-icon fontSet=\"wm-menu-iconos\" class=\"chat-ia-expandir\" (click)=\"onClickExpandirBorrador()\">{{ borradorExpandido ? 'close_fullscreen' : 'open_in_full' }}</mat-icon>\r\n <textarea [rows]=\"filasBorrador\" [(ngModel)]=\"borrador\" (keydown.enter)=\"$event.preventDefault(); onClickEnviar()\"\r\n [class.chat-ia-textarea-expandido]=\"borradorExpandido\"\r\n placeholder=\"Escribe tu pregunta...\"></textarea>\r\n <div class=\"chat-ia-pie-entrada\">\r\n <button class=\"chat-ia-selector-modelo\" [matMenuTriggerFor]=\"menuModeloChatIA\" type=\"button\">\r\n <span *ngIf=\"proveedorSeleccionado === 'Gemini'\">Gemini</span>\r\n <span *ngIf=\"proveedorSeleccionado === 'Groq'\">Groq</span>\r\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <path d=\"M6 9l6 6 6-6\"></path>\r\n </svg>\r\n </button>\r\n <button class=\"chat-ia-microfono\" [class.chat-ia-microfono-activo]=\"escuchando\" *ngIf=\"micDisponible\"\r\n (click)=\"onClickMicrofono()\" type=\"button\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <path d=\"M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z\"></path>\r\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"></path>\r\n <line x1=\"12\" y1=\"19\" x2=\"12\" y2=\"23\"></line>\r\n </svg>\r\n </button>\r\n <button class=\"chat-ia-enviar\" [disabled]=\"!borrador.trim() || consultando\" (click)=\"onClickEnviar()\" type=\"button\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <path d=\"M12 19V6\"></path>\r\n <path d=\"M5 13l7-7 7 7\"></path>\r\n </svg>\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<mat-menu #menuModeloChatIA=\"matMenu\" class=\"menu-modelo-chat-ia\">\r\n <button mat-menu-item [class.opcion-activa]=\"proveedorSeleccionado === 'Gemini'\"\r\n (click)=\"proveedorSeleccionado = 'Gemini'\">\r\n <span>Gemini</span>\r\n <svg *ngIf=\"proveedorSeleccionado === 'Gemini'\" class=\"chat-ia-check\" width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <path d=\"M20 6L9 17l-5-5\"></path>\r\n </svg>\r\n </button>\r\n <button mat-menu-item [class.opcion-activa]=\"proveedorSeleccionado === 'Groq'\"\r\n (click)=\"proveedorSeleccionado = 'Groq'\">\r\n <span>Groq</span>\r\n <svg *ngIf=\"proveedorSeleccionado === 'Groq'\" class=\"chat-ia-check\" width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <path d=\"M20 6L9 17l-5-5\"></path>\r\n </svg>\r\n </button>\r\n</mat-menu>\r\n\r\n<!-- Modal \"ampliar\": muestra la tabla a todo el ancho de la pantalla (el panel la recorta). Overlay\r\n propio (no CDK) porque el componente se monta como web-component y el overlay de CDK queda fuera\r\n de su contexto de estilos. -->\r\n<div class=\"chat-ia-modal-overlay\" *ngIf=\"tablaAmpliada\" (click)=\"cerrarAmpliada()\">\r\n <div class=\"chat-ia-modal\" (click)=\"$event.stopPropagation()\">\r\n <div class=\"chat-ia-modal-header\">\r\n <div class=\"chat-ia-modal-titulo\">{{ descripcionAmpliada || 'Resultados' }}</div>\r\n <div class=\"chat-ia-modal-acciones\">\r\n <button type=\"button\" class=\"chat-ia-accion chat-ia-accion-clara\" matTooltip=\"Copiar\"\r\n (click)=\"copiar(tablaAmpliada)\">\r\n <mat-icon>content_copy</mat-icon>\r\n </button>\r\n <button type=\"button\" class=\"chat-ia-accion chat-ia-accion-clara\" matTooltip=\"Exportar\"\r\n [matMenuTriggerFor]=\"menuExportModal\">\r\n <mat-icon>download</mat-icon>\r\n </button>\r\n <mat-menu #menuExportModal=\"matMenu\" class=\"menu-export-chat-ia\">\r\n <button mat-menu-item type=\"button\" (click)=\"exportarCsv(tablaAmpliada)\">CSV (.csv)</button>\r\n <button mat-menu-item type=\"button\" (click)=\"exportarXlsx(tablaAmpliada)\">Excel (.xlsx)</button>\r\n </mat-menu>\r\n <button type=\"button\" class=\"chat-ia-accion chat-ia-accion-clara\" matTooltip=\"Cerrar\"\r\n (click)=\"cerrarAmpliada()\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n <div class=\"chat-ia-modal-tabla\">\r\n <table mat-table [dataSource]=\"tablaAmpliada\">\r\n <ng-container *ngFor=\"let columna of columnasDe(tablaAmpliada)\" [matColumnDef]=\"columna\">\r\n <th mat-header-cell *matHeaderCellDef>{{ columna }}</th>\r\n <td mat-cell *matCellDef=\"let fila\">{{ fila[columna] }}</td>\r\n </ng-container>\r\n <tr mat-header-row *matHeaderRowDef=\"columnasDe(tablaAmpliada)\"></tr>\r\n <tr mat-row *matRowDef=\"let fila; columns: columnasDe(tablaAmpliada);\"></tr>\r\n </table>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<!-- Aviso ef\u00EDmero tras copiar al portapapeles. -->\r\n<div class=\"chat-ia-copiado-aviso\" *ngIf=\"copiadoAviso\">Tabla copiada</div>\r\n",
1187
+ styles: [":host{position:fixed;right:32px;bottom:32px;z-index:1000;font-family:Roboto,sans-serif}.chat-ia-panel{width:450px;height:650px;max-width:calc(100vw - 48px);max-height:calc(100vh - 96px);background:#fff;border-radius:18px;box-shadow:0 24px 60px rgba(20,25,30,.28);display:flex;flex-direction:column;overflow:hidden}.chat-ia-header{background:linear-gradient(135deg,#7c5ce0,#5f42bd);padding:16px 18px;display:flex;align-items:center;justify-content:space-between;flex-shrink:0}.chat-ia-header-info{display:flex;align-items:center;gap:10px;min-width:0}.chat-ia-header-icono{width:32px;height:32px;border-radius:50%;background:rgba(255,255,255,.22);display:flex;align-items:center;justify-content:center;flex:none}.chat-ia-titulo{color:#fff;font-size:14px;font-weight:500}.chat-ia-subtitulo{color:rgba(255,255,255,.75);font-size:11px;margin-top:1px}.chat-ia-cerrar{width:24px;height:24px;display:flex;align-items:center;justify-content:center;cursor:pointer;opacity:.85;flex:none}.chat-ia-cerrar:hover{opacity:1}.chat-ia-mensajes{flex:1;min-height:0;overflow-y:auto;display:flex;flex-direction:column;gap:12px;padding:16px;background:#fbfafc}.chat-ia-mensaje{display:flex}.chat-ia-mensaje .chat-ia-burbuja{max-width:88%;background:#f2eefb;color:#26313a;border-radius:14px 14px 14px 3px;padding:10px 13px;font-size:13px;line-height:1.5}.chat-ia-mensaje .chat-ia-burbuja-error{background:#fdecea;color:#7a1f14}.chat-ia-mensaje.chat-ia-mensaje-usuario{justify-content:flex-end}.chat-ia-mensaje.chat-ia-mensaje-usuario .chat-ia-burbuja{background:#7c5ce0;color:#fff;border-radius:14px 14px 3px}.chat-ia-bienvenida-texto{align-self:flex-start;max-width:88%;background:#f2eefb;color:#26313a;border-radius:14px 14px 14px 3px;padding:10px 13px;font-size:13px;line-height:1.5}.chips-chat-ia{flex:none;display:flex;flex-direction:column;align-items:flex-start;gap:6px;padding:0 16px 10px}.chip-chat-ia{max-width:100%;border:1px solid #e7e2f5;background:#f2eefb;color:#5f42bd;border-radius:14px;padding:5px 10px;font-size:11px;line-height:1.35;cursor:pointer;overflow-wrap:anywhere}.chip-chat-ia:hover{background:#e7ddfa}.chip-chat-ia-toggle{flex:none;align-self:flex-start;display:inline-flex;align-items:center;gap:6px;margin:10px 16px;border:1px solid #e7e2f5;background:#f2eefb;color:#5f42bd;border-radius:16px;padding:6px 12px;font-size:12px;cursor:pointer}.chip-chat-ia-toggle .chip-chat-ia-toggle-chevron{transition:transform .15s}.chip-chat-ia-toggle .chip-chat-ia-toggle-chevron.abierto{transform:rotate(180deg)}.chip-chat-ia-toggle:hover{background:#e7ddfa}.chat-ia-descripcion{font-size:13px;line-height:1.5;margin-bottom:6px}.chat-ia-sin-resultados{color:#5a5a5a;font-style:italic}.chat-ia-stat{margin-top:4px}.chat-ia-stat-valor{font-size:30px;font-weight:600;color:#5f42bd;line-height:1.15;font-variant-numeric:proportional-nums}.chat-ia-lista-simple,.chat-ia-tabla-bloque{margin-top:4px;background:#fff;border-radius:10px;padding:8px 10px}.chat-ia-lista-simple{font-size:13px;line-height:1.5}.chat-ia-tabla-acciones{display:flex;justify-content:flex-end;align-items:center;gap:2px;margin-bottom:2px}.chat-ia-accion{width:26px;height:26px;border:none;background:0 0;color:#6b6478;border-radius:7px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;padding:0;flex:none}.chat-ia-accion:hover{background:#f2eefb}.chat-ia-accion .mat-icon{font-size:17px;width:17px;height:17px;line-height:17px}.chat-ia-accion.chat-ia-accion-clara{color:rgba(255,255,255,.9)}.chat-ia-accion.chat-ia-accion-clara:hover{background:rgba(255,255,255,.18);color:#fff}.chat-ia-tabla-resultado{overflow-x:auto;max-width:100%}.chat-ia-tabla-resultado table{font-size:12px;background:0 0}.chat-ia-tabla-resultado th.mat-header-cell{color:#6b6478;font-weight:600;padding:0 14px 5px 0;border-bottom:1px solid #f0edf7;white-space:nowrap}.chat-ia-tabla-resultado td.mat-cell{padding:7px 14px 0 0;white-space:nowrap;font-variant-numeric:tabular-nums}.chat-ia-pensando{align-self:flex-start;background:#f2eefb;padding:12px 16px;border-radius:14px 14px 14px 3px;display:flex;gap:5px;flex-shrink:0}.chat-ia-dot{width:6px;height:6px;border-radius:50%;background:#9a93ae;-webkit-animation:1.1s ease-in-out infinite chatIaBlink;animation:1.1s ease-in-out infinite chatIaBlink}.chat-ia-dot:nth-child(2){-webkit-animation-delay:.15s;animation-delay:.15s}.chat-ia-dot:nth-child(3){-webkit-animation-delay:.3s;animation-delay:.3s}@-webkit-keyframes chatIaBlink{0%,100%,80%{opacity:.25}40%{opacity:1}}@keyframes chatIaBlink{0%,100%,80%{opacity:.25}40%{opacity:1}}.chat-ia-entrada{padding:10px 14px 14px;border-top:1px solid #e9f2ee;flex-shrink:0}.chat-ia-caja-entrada{position:relative;background:#f2f7f5;border:1px solid #e6efea;border-radius:18px;padding:10px 12px 8px}.chat-ia-caja-entrada textarea{border:none;outline:0;background:0 0;width:100%;font:inherit;font-size:13.5px;color:#26313a;resize:none;line-height:1.45;max-height:110px;overflow-y:auto;display:block;padding:0 28px 0 0}.chat-ia-caja-entrada textarea.chat-ia-textarea-expandido{max-height:160px}.chat-ia-caja-entrada textarea::-webkit-input-placeholder{color:#9a93ae}.chat-ia-caja-entrada textarea::-moz-placeholder{color:#9a93ae}.chat-ia-caja-entrada textarea:-ms-input-placeholder{color:#9a93ae}.chat-ia-caja-entrada textarea::-ms-input-placeholder{color:#9a93ae}.chat-ia-caja-entrada textarea::placeholder{color:#9a93ae}.chat-ia-expandir{position:absolute;top:8px;right:10px;font-size:17px;height:17px;width:17px;color:#9a9a9a;cursor:pointer}.chat-ia-pie-entrada{display:flex;align-items:center;justify-content:flex-end;gap:8px;margin-top:8px}.chat-ia-selector-modelo{display:inline-flex;align-items:center;gap:4px;border:none;background:0 0;color:#4e5d57;border-radius:14px;padding:5px 9px;font-size:11.5px;font-weight:600;cursor:pointer}.chat-ia-enviar{width:32px;height:32px;border-radius:50%;border:none;background:#7c5ce0;color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer;flex:none}.chat-ia-enviar:disabled{background:#dce6e1;color:#a9bab3;cursor:default}.chat-ia-microfono{width:28px;height:28px;border-radius:50%;border:none;background:0 0;color:#6b6478;display:flex;align-items:center;justify-content:center;cursor:pointer;flex:none}.chat-ia-microfono:hover{background:#f2eefb}.chat-ia-microfono.chat-ia-microfono-activo{color:#fff;background:#e05a5a;-webkit-animation:1.1s ease-in-out infinite chatIaBlink;animation:1.1s ease-in-out infinite chatIaBlink}::ng-deep .mat-menu-panel.menu-modelo-chat-ia{min-width:132px;border:1px solid #e6efea;border-radius:10px;box-shadow:0 12px 26px rgba(20,25,30,.16)}::ng-deep .mat-menu-panel.menu-modelo-chat-ia .mat-menu-content{padding:4px}::ng-deep .mat-menu-panel.menu-modelo-chat-ia .mat-menu-item{display:flex;align-items:center;justify-content:space-between;gap:8px;height:auto;line-height:normal;padding:7px 10px;font-size:12px;color:#26313a;border-radius:7px}::ng-deep .mat-menu-panel.menu-modelo-chat-ia .mat-menu-item.opcion-activa{background:#f2f7f5}::ng-deep .mat-menu-panel.menu-modelo-chat-ia .mat-menu-item .chat-ia-check{color:#7c5ce0}.chat-ia-modal-overlay{position:fixed;inset:0;background:rgba(20,25,30,.5);display:flex;align-items:center;justify-content:center;padding:24px;z-index:1100}.chat-ia-modal{background:#fff;border-radius:16px;box-shadow:0 24px 60px rgba(20,25,30,.32);width:auto;max-width:92vw;max-height:86vh;display:flex;flex-direction:column;overflow:hidden}.chat-ia-modal-header{background:linear-gradient(135deg,#7c5ce0,#5f42bd);padding:12px 16px;display:flex;align-items:center;justify-content:space-between;gap:16px;flex-shrink:0}.chat-ia-modal-titulo{color:#fff;font-size:14px;font-weight:500;line-height:1.4;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.chat-ia-modal-acciones{display:flex;align-items:center;gap:2px;flex:none}.chat-ia-modal-tabla{flex:1;min-height:0;overflow:auto;padding:10px 16px 16px}.chat-ia-modal-tabla table{font-size:13px;background:0 0;width:100%}.chat-ia-modal-tabla th.mat-header-cell{color:#6b6478;font-weight:600;padding:0 18px 8px 0;border-bottom:1px solid #f0edf7;white-space:nowrap}.chat-ia-modal-tabla td.mat-cell{padding:9px 18px 9px 0;white-space:nowrap;color:#26313a;border-bottom:1px solid #f6f4fb;font-variant-numeric:tabular-nums}.chat-ia-copiado-aviso{position:fixed;top:24px;left:50%;transform:translateX(-50%);background:#26313a;color:#fff;font-size:12.5px;padding:8px 16px;border-radius:20px;box-shadow:0 8px 20px rgba(20,25,30,.25);z-index:1200;-webkit-animation:.15s ease-out chatIaAvisoEntrada;animation:.15s ease-out chatIaAvisoEntrada}@-webkit-keyframes chatIaAvisoEntrada{from{opacity:0;transform:translate(-50%,-6px)}to{opacity:1;transform:translate(-50%,0)}}@keyframes chatIaAvisoEntrada{from{opacity:0;transform:translate(-50%,-6px)}to{opacity:1;transform:translate(-50%,0)}}::ng-deep .mat-menu-panel.menu-export-chat-ia{min-width:132px;border:1px solid #e6efea;border-radius:10px;box-shadow:0 12px 26px rgba(20,25,30,.16)}::ng-deep .mat-menu-panel.menu-export-chat-ia .mat-menu-content{padding:4px}::ng-deep .mat-menu-panel.menu-export-chat-ia .mat-menu-item{height:auto;line-height:normal;padding:8px 12px;font-size:12px;color:#26313a;border-radius:7px}::ng-deep .mat-menu-panel.menu-export-chat-ia .mat-menu-item:hover{background:#f2f7f5}"]
1012
1188
  })
1013
1189
  ], AsistenteDatosComponent);
1014
1190
  return AsistenteDatosComponent;
@@ -1049,6 +1225,7 @@
1049
1225
  exports.ɵb = AsistenteDatosComponent;
1050
1226
  exports.ɵc = AsistenteDatosApiService;
1051
1227
  exports.ɵd = ReconocimientoVozService;
1228
+ exports.ɵe = TablaExportService;
1052
1229
 
1053
1230
  Object.defineProperty(exports, '__esModule', { value: true });
1054
1231