@supcoflow/chat 0.1.0 → 2.1.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.
Files changed (73) hide show
  1. package/LICENSES/orca.txt +25 -0
  2. package/README.md +127 -44
  3. package/dist/apariencia.d.ts +16 -0
  4. package/dist/apariencia.d.ts.map +1 -0
  5. package/dist/cliente.d.ts +25 -0
  6. package/dist/cliente.d.ts.map +1 -0
  7. package/dist/componentes/Adjuntos.d.ts +11 -0
  8. package/dist/componentes/Adjuntos.d.ts.map +1 -0
  9. package/dist/componentes/Burbuja.d.ts +6 -0
  10. package/dist/componentes/Burbuja.d.ts.map +1 -0
  11. package/dist/componentes/Compositor.d.ts +19 -0
  12. package/dist/componentes/Compositor.d.ts.map +1 -0
  13. package/dist/componentes/Markdown.d.ts +4 -0
  14. package/dist/componentes/Markdown.d.ts.map +1 -0
  15. package/dist/componentes/Mensajes.d.ts +8 -0
  16. package/dist/componentes/Mensajes.d.ts.map +1 -0
  17. package/dist/componentes/Panel.d.ts +11 -0
  18. package/dist/componentes/Panel.d.ts.map +1 -0
  19. package/dist/componentes/PreguntaHitl.d.ts +8 -0
  20. package/dist/componentes/PreguntaHitl.d.ts.map +1 -0
  21. package/dist/componentes/Widget.d.ts +21 -0
  22. package/dist/componentes/Widget.d.ts.map +1 -0
  23. package/dist/contexto.d.ts +10 -0
  24. package/dist/contexto.d.ts.map +1 -0
  25. package/dist/estado-del-widget.d.ts +18 -31
  26. package/dist/estado-del-widget.d.ts.map +1 -1
  27. package/dist/estado.d.ts +40 -0
  28. package/dist/estado.d.ts.map +1 -0
  29. package/dist/evidencia/adjuntos.d.ts +45 -0
  30. package/dist/evidencia/adjuntos.d.ts.map +1 -0
  31. package/dist/evidencia/captura.d.ts +56 -0
  32. package/dist/evidencia/captura.d.ts.map +1 -0
  33. package/dist/evidencia/elemento.d.ts +46 -0
  34. package/dist/evidencia/elemento.d.ts.map +1 -0
  35. package/dist/evidencia/extraer-elemento.d.ts +11 -0
  36. package/dist/evidencia/extraer-elemento.d.ts.map +1 -0
  37. package/dist/evidencia/resumen-de-evidencia.d.ts +11 -0
  38. package/dist/evidencia/resumen-de-evidencia.d.ts.map +1 -0
  39. package/dist/evidencia/selector-de-elementos.d.ts +11 -0
  40. package/dist/evidencia/selector-de-elementos.d.ts.map +1 -0
  41. package/dist/evidencia/subida.d.ts +17 -0
  42. package/dist/evidencia/subida.d.ts.map +1 -0
  43. package/dist/iife.d.ts +2 -0
  44. package/dist/iife.d.ts.map +1 -0
  45. package/dist/index.d.ts +4 -1
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +91 -2
  48. package/dist/index.js.map +7 -1
  49. package/dist/markdown.d.ts +31 -0
  50. package/dist/markdown.d.ts.map +1 -0
  51. package/dist/montar.d.ts +9 -0
  52. package/dist/montar.d.ts.map +1 -0
  53. package/dist/opciones.d.ts +80 -0
  54. package/dist/opciones.d.ts.map +1 -0
  55. package/dist/protocolo.d.ts +61 -0
  56. package/dist/protocolo.d.ts.map +1 -0
  57. package/dist/react.d.ts +11 -0
  58. package/dist/react.d.ts.map +1 -0
  59. package/dist/sesion-guardada.d.ts +5 -0
  60. package/dist/sesion-guardada.d.ts.map +1 -0
  61. package/dist/tema.d.ts +3 -0
  62. package/dist/tema.d.ts.map +1 -0
  63. package/dist/textos.d.ts +32 -0
  64. package/dist/textos.d.ts.map +1 -0
  65. package/dist/widget.iife.js +91 -0
  66. package/dist/widget.iife.js.map +7 -0
  67. package/package.json +14 -33
  68. package/dist/ChatDeIntake.d.ts +0 -11
  69. package/dist/ChatDeIntake.d.ts.map +0 -1
  70. package/dist/ChatDeIntake.js +0 -47
  71. package/dist/ChatDeIntake.js.map +0 -1
  72. package/dist/estado-del-widget.js +0 -40
  73. package/dist/estado-del-widget.js.map +0 -1
@@ -0,0 +1,25 @@
1
+ Orca — github.com/stablyai/orca
2
+ Código trasladado a TypeScript en packages/widget/src/evidencia/extraer-elemento.ts y
3
+ selector-de-elementos.ts (src/main/browser/grab-guest-*.ts del original).
4
+
5
+ MIT License
6
+
7
+ Copyright (c) 2026 Lovecast Inc.
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ of this software and associated documentation files (the "Software"), to deal
11
+ in the Software without restriction, including without limitation the rights
12
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ copies of the Software, and to permit persons to whom the Software is
14
+ furnished to do so, subject to the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be included in all
17
+ copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ SOFTWARE.
package/README.md CHANGED
@@ -1,67 +1,150 @@
1
1
  # @supcoflow/chat
2
2
 
3
- El chat de intake de Supcoflow, como componente de React. Se monta en el sitio de un cliente y
4
- conversa con un agente que orienta sobre el producto y, cuando hay un bug de verdad, abre un
5
- reporte en Supcoflow.
6
-
7
- ## Requisitos
8
-
9
- - **React 19** (`peerDependencies: react >=19`)
10
- - Un proyecto de Supcoflow con **el chat activado** — que exige una skill aprobada y la lista de
11
- Origins admitidos escrita. Si el chat está apagado, el widget **no se dibuja** (ver abajo).
3
+ El chat de intake de Supcoflow, embebible en cualquier sitio. Conversa con un asistente que
4
+ orienta sobre el producto y, cuando hay un bug de verdad, abre un reporte en Supcoflow.
12
5
 
13
6
  ## Instalación
14
7
 
8
+ ### Con npm (React, Vue, Svelte, vanilla)
9
+
15
10
  ```bash
16
11
  npm i @supcoflow/chat
17
12
  ```
18
13
 
19
- Mientras el paquete no esté publicado, se instala del tarball que produce `npm pack -w @supcoflow/chat`:
14
+ En React:
20
15
 
21
- ```bash
22
- npm i /ruta/al/supcoflow-chat-0.1.0.tgz
16
+ ```tsx
17
+ import { ChatDeIntake } from "@supcoflow/chat";
18
+
19
+ <ChatDeIntake tenantId="tnt_xxx" projectId="proj_xxx" />
23
20
  ```
24
21
 
25
- ## Uso
22
+ Sin React:
26
23
 
27
- ```tsx
28
- import { ChatDeIntake } from "@supcoflow/chat";
24
+ ```ts
25
+ import { montarChat } from "@supcoflow/chat";
26
+
27
+ const chat = montarChat(document.body, { tenantId: "tnt_xxx", projectId: "proj_xxx" });
28
+ // chat.desmontar() cuando haga falta
29
+ ```
30
+
31
+ ### Con una línea de `<script>` (WordPress, Webflow, HTML plano)
32
+
33
+ ```html
34
+ <script src="https://chat.supcoflow.dev/widget.js" data-tenant="tnt_xxx" data-project="proj_xxx" defer></script>
35
+ ```
36
+
37
+ Opciones por atributo: `data-backend`, `data-posicion` (`derecha`|`izquierda`), `data-titulo`,
38
+ `data-acento` (color), `data-version-app`, `data-contexto-automatico="false"`, `data-usuario-id`,
39
+ `data-usuario-rol`. También queda `window.SupcoflowChat.montar(opciones)` / `.desmontar()`.
40
+
41
+ Los identificadores **son públicos** (modelo DSN). La contención es la lista de Origins admitidos
42
+ del proyecto más el tope de mensajes, los dos del lado de Supcoflow.
43
+
44
+ ## Opciones
29
45
 
30
- <ChatDeIntake
31
- tenantId="tnt_xxx"
32
- projectId="proj_xxx"
33
- backendUrl="https://chat.supcoflow.dev"
34
- contexto={{ url: location.href, versionApp: "1.4.2" }} // opcional
35
- />
46
+ | opción | default | qué |
47
+ |---|---|---|
48
+ | `backendUrl` | `https://chat.supcoflow.dev` | el backend del chat |
49
+ | `modo` | `"burbuja"` | `"burbuja"` (botón flotante + panel) o `"inline"` (el panel donde lo montes) |
50
+ | `posicion` | `"derecha"` | lado de la burbuja |
51
+ | `titulo` | `"Ayuda"` | cabecera del panel |
52
+ | `tema` | neutro | `{ acento, fondo, texto, textoSuave, radio, fuente }`, cualquier subconjunto |
53
+ | `contexto` | `{}` | `{ versionApp }` — viaja con el primer mensaje |
54
+ | `contextoAutomatico` | `true` | manda la URL (sin query) y el navegador. Errores de consola: nunca |
55
+ | `onDiagnostico` | — | `({ visible, status, motivo }) => void`, ver abajo |
56
+ | `usuario` | — | `{ id, rol, atributos }` — quién es el usuario según tu app, para el diagnóstico. Ver abajo |
57
+ | `previewToken` | — | **interna**: la usa la consola de Supcoflow para el preview; en tu sitio no hace nada útil |
58
+
59
+ ## Quién es el usuario
60
+
61
+ ```tsx
62
+ <ChatDeIntake tenantId="tnt_xxx" projectId="proj_xxx" usuario={{ id: "u_123", rol: "admin", atributos: { plan: "pro" } }} />
36
63
  ```
37
64
 
38
- Los tres identificadores **son públicos** modelo DSN. La contención no es un secreto: es la
39
- lista de Origins admitidos del proyecto más el tope de mensajes por día, los dos del lado del
40
- motor. Poner el `tenantId` en el HTML de un sitio público es el uso previsto.
65
+ Todo opcional. `id` (hasta 100 caracteres), `rol` (60) y hasta 8 `atributos` (claves de 40, valores de
66
+ 200) viajan con el primer mensaje al asistente y, si se abre un reporte, al reporte: el rol es el
67
+ dato de diagnóstico más barato que hay (la mitad de los «no me aparece el botón» son permisos). Es
68
+ **declarado, no verificado**: no da permisos a nada, es contexto. Lo que se pasa de largo se
69
+ recorta; lo que no sea `id`, `rol` o `atributos` se descarta.
70
+
71
+ No hay `nombre` en esta versión: llega con la pantalla de inicio, que es lo único que lo va a usar,
72
+ y **nunca va a viajar** al asistente ni al reporte.
73
+
74
+ ## La marca desde la consola
75
+
76
+ El logo, el título y los colores del chat se editan desde `/proyectos/<slug>/sdk` en Supcoflow y el
77
+ widget los aplica solo, antes del primer dibujo. Lo que pases por código (`titulo`, `tema`) gana
78
+ sobre lo guardado en la consola, campo a campo.
79
+
80
+ ## Qué pasa cuando algo falla
81
+
82
+ **Al cargar**, si el chat del proyecto está apagado, el tope del día se agotó, el origen del
83
+ sitio no está admitido o el backend no contesta, el widget **no se dibuja**: quien está del otro
84
+ lado es un usuario final del producto, y no tiene nada que hacer con «el proyecto tiene el tope
85
+ agotado». Para saber por qué no aparece, pasá `onDiagnostico` o mirá la consola en `localhost`:
86
+
87
+ | `status` | `motivo` | qué hacer |
88
+ |---|---|---|
89
+ | 403 | `chat_deshabilitado` | activar el chat en `/proyectos/<slug>/sdk` |
90
+ | 403 | `origen_no_admitido` | agregar el origen del sitio en esa misma pantalla |
91
+ | 404 | `proyecto_desconocido` | revisar `tenantId` / `projectId` |
92
+ | 429 | `tope_de_mensajes` | esperar: el tope vuelve a abrir solo |
93
+ | 502 | `motor_inalcanzable` | es del lado de Supcoflow |
94
+ | 0 | `sin_respuesta` | red, CSP o `backendUrl` mal escrita |
95
+
96
+ **A mitad de conversación**, un error se muestra debajo del último mensaje con un botón de
97
+ reintentar, y lo que el usuario escribió se conserva. `onDiagnostico` lo recibe también, con el
98
+ código del error en `motivo`.
99
+
100
+ ## Lo que el widget hace y no hace
101
+
102
+ - La conversación sobrevive a una recarga **de la misma pestaña**.
103
+ - El asistente escribe con formato (negrita, listas, código, links) y pide la confirmación del
104
+ reporte con botones.
105
+ - El navegador habla sólo con el backend del chat, en un formato propio y chico; el motor de
106
+ agentes queda del lado del servidor.
107
+ - No le da seguimiento del reporte al que reportó. Es a propósito.
108
+ - No recolecta errores de consola ni nada que no esté en la tabla de opciones.
109
+ - Señalar, capturar y adjuntar son acciones del usuario: el widget no captura nada solo.
110
+
111
+ ## Señalar y adjuntar
112
+
113
+ El usuario final puede **señalar el elemento** de tu página que le falla, **capturar la pantalla**
114
+ y **adjuntar una imagen**. Eso viaja al reporte (y al agente que lo arregla), nunca al modelo del
115
+ chat, que sólo recibe una descripción corta («señaló el botón Guardar del formulario»).
116
+
117
+ Qué se recolecta al señalar, y qué no:
118
+
119
+ - Sí: la etiqueta, un selector CSS, la ruta en el DOM, el texto visible, los atributos `id class
120
+ name type role href src alt title placeholder for action method` y `aria-*`, dieciséis
121
+ propiedades de estilo, el texto de los elementos vecinos, y el HTML del elemento acotado a 4 KB.
122
+ - No: **el valor de ningún `input`, `textarea` o `select`**, ningún `<script>`, ninguna query ni
123
+ hash de URL; y cualquier atributo o clase con pinta de secreto (`access_token`, `csrf`,
124
+ `password`…) se reemplaza por `[redacted]`.
125
+
126
+ La captura la toma el usuario con el diálogo del navegador (`getDisplayMedia`) y la ve como
127
+ miniatura antes de que suba; puede quitarla. Algunos navegadores y iOS no soportan
128
+ `getDisplayMedia`: ahí sólo queda «Adjuntar imagen», que funciona en todos lados. Las imágenes se re-codifican
129
+ (WebP o PNG, hasta 1,5 MB) y no conservan metadatos del archivo original. Hasta **4 adjuntos por
130
+ conversación**.
41
131
 
42
- `contexto` es opcional y viaja al reporte: sirve para que el issue diga en qué URL y con qué
43
- versión pasó.
132
+ Quien señala ve un resaltado sobre tu página: es un overlay nuestro, a pantalla completa, que se
133
+ desmonta al elegir o al apretar Esc. El código del selector es de [Orca](https://github.com/stablyai/orca)
134
+ (MIT), trasladado a TypeScript; la licencia va en `LICENSES/orca.txt`.
44
135
 
45
- ## Lo que hace cuando algo falla: nada visible
136
+ ## Si tu sitio tiene CSP
46
137
 
47
- El widget **se oculta** —no muestra un error— si el chat está apagado, si el tope del día se
48
- agotó, si el Origin del sitio no está admitido o si el backend no contesta. Es deliberado: quien
49
- está del otro lado es un usuario final del producto del cliente, y no tiene nada que hacer con
50
- "el proyecto tiene el tope agotado". Si esperabas ver el chat y no aparece, mirá la respuesta de
51
- `GET {backendUrl}/api/chat/estado?tenant_id=…&project_id=…`, que dice la causa por status: 403
52
- (chat apagado u Origin no admitido), 429 (tope agotado), 404 (proyecto desconocido), 502 (backend
53
- sin poder hablar con el motor).
138
+ `connect-src` tiene que admitir el `backendUrl` (por defecto `https://chat.supcoflow.dev`). El
139
+ widget no carga fuentes ni scripts de terceros. Las miniaturas de las capturas son `data:` URLs
140
+ generadas en el navegador: si tu `img-src` no admite `data:`, no se ven las miniaturas (la subida
141
+ funciona igual).
54
142
 
55
- ## Lo que este paquete NO hace
143
+ ## Tamaño y requisitos
56
144
 
57
- - No persiste la conversación: el historial vive en el navegador y viaja entero en cada pedido.
58
- - No le da seguimiento del reporte al que reportó. Es a propósito — el único lugar donde un
59
- reporte se ve es la consola del operador.
60
- - No trae estilos configurables todavía: el panel usa estilos en línea y hereda la tipografía del
61
- sitio.
145
+ Un solo archivo por salida, sin dependencias que instalar. React es opcional (sólo para
146
+ `ChatDeIntake`). Sin webfonts. Funciona en navegadores con ES2022 y Shadow DOM.
62
147
 
63
- ## Nota para quien lo consuma sin bundler
148
+ ## Licencias
64
149
 
65
- El paquete emite ESM con imports relativos sin extensión (`./ChatDeIntake`), que cualquier bundler
66
- —Next, Vite, webpack— resuelve. Node ESM en crudo, sin bundler, no: es un componente de cliente,
67
- así que ese caso no existe en la práctica, pero queda dicho.
150
+ MIT. Ver `LICENSES/` por el código de terceros incluido.
@@ -0,0 +1,16 @@
1
+ import { type OpcionesDelChat, type TemaDelChat } from "./opciones";
2
+ /** La marca tal cual la manda el backend (`GET /api/chat/estado`, snake_case). Todo opcional: un
3
+ * backend viejo no la manda y el widget usa sus defaults — nunca se adivina, como con `motivo`. */
4
+ export interface MarcaDelMotor {
5
+ logo_url?: string | null;
6
+ titulo?: string | null;
7
+ saludo?: string | null;
8
+ tema?: Partial<TemaDelChat> | null;
9
+ }
10
+ export interface Apariencia {
11
+ titulo: string;
12
+ logoUrl: string | null;
13
+ tema: TemaDelChat;
14
+ }
15
+ export declare function resolverApariencia(o: Pick<OpcionesDelChat, "titulo" | "tema">, marca: unknown): Apariencia;
16
+ //# sourceMappingURL=apariencia.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apariencia.d.ts","sourceRoot":"","sources":["../src/apariencia.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,eAAe,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAWtF;mGACmG;AACnG,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,IAAI,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC;CACpC;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,IAAI,EAAE,WAAW,CAAC;CACnB;AAoBD,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,IAAI,CAAC,eAAe,EAAE,QAAQ,GAAG,MAAM,CAAC,EAAE,KAAK,EAAE,OAAO,GAAG,UAAU,CAQ1G"}
@@ -0,0 +1,25 @@
1
+ import type { OpcionesResueltas } from "./opciones";
2
+ import type { EventoDelWidget } from "./protocolo";
3
+ export type Respuesta = {
4
+ requestId: string;
5
+ optionId?: string;
6
+ text?: string;
7
+ };
8
+ export interface ClienteDelChat {
9
+ abrir(mensaje: string, contexto: Record<string, string> | undefined): AsyncIterable<EventoDelWidget>;
10
+ /** `contexto` en un mensaje de seguimiento (Revisión 2 punto 5): el resumen acumulado de la
11
+ * evidencia va en CADA turno, porque clientContext de Eve es por turno. */
12
+ seguir(id: string, indice: number, cuerpo: {
13
+ mensaje: string;
14
+ contexto?: Record<string, string>;
15
+ } | {
16
+ respuesta: Respuesta;
17
+ }): AsyncIterable<EventoDelWidget>;
18
+ recuperar(id: string, desde: number): AsyncIterable<EventoDelWidget>;
19
+ }
20
+ /** Una línea que no es un evento v1 es `null`: el lector la salta y sigue. Un `t` desconocido
21
+ * SÍ pasa — es la regla de compatibilidad: el reducer lo ignora. */
22
+ export declare function parsearLinea(linea: string): EventoDelWidget | null;
23
+ export declare function leerNdjson(cuerpo: ReadableStream<Uint8Array>): AsyncGenerator<EventoDelWidget>;
24
+ export declare function crearCliente(o: OpcionesResueltas, fetchFn?: typeof fetch): ClienteDelChat;
25
+ //# sourceMappingURL=cliente.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cliente.d.ts","sourceRoot":"","sources":["../src/cliente.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAOnD,MAAM,MAAM,SAAS,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhF,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,GAAG,aAAa,CAAC,eAAe,CAAC,CAAC;IACrG;+EAC2E;IAC3E,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,GAAG;QAAE,SAAS,EAAE,SAAS,CAAA;KAAE,GAAG,aAAa,CAAC,eAAe,CAAC,CAAC;IAC9J,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,aAAa,CAAC,eAAe,CAAC,CAAC;CACtE;AAED;oEACoE;AACpE,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CASlE;AAED,wBAAuB,UAAU,CAAC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,cAAc,CAAC,eAAe,CAAC,CAqBrG;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,iBAAiB,EAAE,OAAO,GAAE,OAAO,KAA6B,GAAG,cAAc,CAyChH"}
@@ -0,0 +1,11 @@
1
+ import type { Adjunto } from "../evidencia/adjuntos";
2
+ export declare function Adjuntos(props: {
3
+ adjuntos: Adjunto[];
4
+ ocupado: boolean;
5
+ aviso: string | null;
6
+ /** Se ofrece sólo si el ÚLTIMO adjunto es un elemento y el navegador sabe capturar (§3.2). */
7
+ onCapturarElemento: (() => void) | null;
8
+ onQuitar: (clave: number) => void;
9
+ onReintentar: (clave: number) => void;
10
+ }): import("preact").JSX.Element | null;
11
+ //# sourceMappingURL=Adjuntos.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Adjuntos.d.ts","sourceRoot":"","sources":["../../src/componentes/Adjuntos.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAqBrD,wBAAgB,QAAQ,CAAC,KAAK,EAAE;IAC9B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,8FAA8F;IAC9F,kBAAkB,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC;IACxC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC,uCAsBA"}
@@ -0,0 +1,6 @@
1
+ export declare function Burbuja(props: {
2
+ posicion: "derecha" | "izquierda";
3
+ abierto: boolean;
4
+ onClick: () => void;
5
+ }): import("preact").JSX.Element;
6
+ //# sourceMappingURL=Burbuja.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Burbuja.d.ts","sourceRoot":"","sources":["../../src/componentes/Burbuja.tsx"],"names":[],"mappings":"AAEA,wBAAgB,OAAO,CAAC,KAAK,EAAE;IAAE,QAAQ,EAAE,SAAS,GAAG,WAAW,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,gCAgB1G"}
@@ -0,0 +1,19 @@
1
+ export interface Herramientas {
2
+ onSenalar: () => void;
3
+ onCapturarPantalla: () => void;
4
+ onAdjuntarImagen: (archivo: File) => void;
5
+ /** §3.2: sin getDisplayMedia (Safari, iOS, http) el ítem de capturar NO se dibuja; adjuntar imagen siempre. */
6
+ puedeCapturar: boolean;
7
+ /** Falso con la cola llena (4): los botones se deshabilitan y el aviso lo dice la barra. */
8
+ puedeAdjuntar: boolean;
9
+ }
10
+ export declare function Compositor(props: {
11
+ valor: string;
12
+ onCambio: (v: string) => void;
13
+ onEnviar: () => void;
14
+ deshabilitado: boolean;
15
+ enfocarAlMontar: boolean;
16
+ /** `null` en preview (§2 del diseño de la marca): ni botones ni input de archivo — la subida está apagada, no deshabilitada. */
17
+ herramientas: Herramientas | null;
18
+ }): import("preact").JSX.Element;
19
+ //# sourceMappingURL=Compositor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Compositor.d.ts","sourceRoot":"","sources":["../../src/componentes/Compositor.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,gBAAgB,EAAE,CAAC,OAAO,EAAE,IAAI,KAAK,IAAI,CAAC;IAC1C,+GAA+G;IAC/G,aAAa,EAAE,OAAO,CAAC;IACvB,4FAA4F;IAC5F,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,EAAE,OAAO,CAAC;IACzB,gIAAgI;IAChI,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;CACnC,gCAoEA"}
@@ -0,0 +1,4 @@
1
+ export declare function Markdown({ texto }: {
2
+ texto: string;
3
+ }): import("preact").JSX.Element;
4
+ //# sourceMappingURL=Markdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Markdown.d.ts","sourceRoot":"","sources":["../../src/componentes/Markdown.tsx"],"names":[],"mappings":"AA+BA,wBAAgB,QAAQ,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,gCAEpD"}
@@ -0,0 +1,8 @@
1
+ import type { Respuesta } from "../cliente";
2
+ import { type EstadoDelChat } from "../estado";
3
+ export declare function Mensajes(props: {
4
+ estado: EstadoDelChat;
5
+ onReintentar: () => void;
6
+ onResponder: (r: Respuesta) => void;
7
+ }): import("preact").JSX.Element;
8
+ //# sourceMappingURL=Mensajes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Mensajes.d.ts","sourceRoot":"","sources":["../../src/componentes/Mensajes.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AAKjE,wBAAgB,QAAQ,CAAC,KAAK,EAAE;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,YAAY,EAAE,MAAM,IAAI,CAAC;IAAC,WAAW,EAAE,CAAC,CAAC,EAAE,SAAS,KAAK,IAAI,CAAA;CAAE,gCA+BvH"}
@@ -0,0 +1,11 @@
1
+ import type { ComponentChildren } from "preact";
2
+ export declare function Panel(props: {
3
+ titulo: string;
4
+ logoUrl: string | null;
5
+ enPreview: boolean;
6
+ modo: "burbuja" | "inline";
7
+ posicion: "derecha" | "izquierda";
8
+ onMinimizar: () => void;
9
+ children: ComponentChildren;
10
+ }): import("preact").JSX.Element;
11
+ //# sourceMappingURL=Panel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Panel.d.ts","sourceRoot":"","sources":["../../src/componentes/Panel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAGhD,wBAAgB,KAAK,CAAC,KAAK,EAAE;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC3B,QAAQ,EAAE,SAAS,GAAG,WAAW,CAAC;IAClC,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,QAAQ,EAAE,iBAAiB,CAAC;CAC7B,gCAyBA"}
@@ -0,0 +1,8 @@
1
+ import type { Respuesta } from "../cliente";
2
+ import type { PreguntaPendiente } from "../estado";
3
+ export declare function PreguntaHitl(props: {
4
+ pregunta: PreguntaPendiente;
5
+ ocupado: boolean;
6
+ onResponder: (r: Respuesta) => void;
7
+ }): import("preact").JSX.Element;
8
+ //# sourceMappingURL=PreguntaHitl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PreguntaHitl.d.ts","sourceRoot":"","sources":["../../src/componentes/PreguntaHitl.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAKnD,wBAAgB,YAAY,CAAC,KAAK,EAAE;IAAE,QAAQ,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,CAAC,CAAC,EAAE,SAAS,KAAK,IAAI,CAAA;CAAE,gCAqCzH"}
@@ -0,0 +1,21 @@
1
+ import type { ClienteDelChat } from "../cliente";
2
+ import { type ErrorVisible } from "../estado";
3
+ import type { OpcionesResueltas } from "../opciones";
4
+ import type { CursorDeSesion } from "../protocolo";
5
+ export default function Widget(props: {
6
+ cliente: ClienteDelChat;
7
+ opciones: OpcionesResueltas;
8
+ /** De la marca del motor (§3.3); `null` = sin logo. */
9
+ logoUrl: string | null;
10
+ /** §2/§5.3 del diseño de la marca: rótulo en la cabecera y la evidencia apagada — en el preview no
11
+ * hay `chat_conversaciones` que la reciba, y una captura de pantalla del modal no tiene que
12
+ * confundirse con el chat real. */
13
+ enPreview: boolean;
14
+ cursorInicial: CursorDeSesion | null;
15
+ alCambiarCursor: (c: CursorDeSesion | null) => void;
16
+ alError: (e: ErrorVisible) => void;
17
+ abiertoAlInicio: boolean;
18
+ /** El host de nuestro shadow root: el selector no lo puede elegir (señalar el chat no es un bug del sitio). */
19
+ hostDelWidget: Element | null;
20
+ }): import("preact").JSX.Element | null;
21
+ //# sourceMappingURL=Widget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Widget.d.ts","sourceRoot":"","sources":["../../src/componentes/Widget.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAa,MAAM,YAAY,CAAC;AAE5D,OAAO,EAAqG,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AAQjJ,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAmB,MAAM,cAAc,CAAC;AAQpE,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,KAAK,EAAE;IACpC,OAAO,EAAE,cAAc,CAAC;IACxB,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,uDAAuD;IACvD,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB;;uCAEmC;IACnC,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,cAAc,GAAG,IAAI,CAAC;IACrC,eAAe,EAAE,CAAC,CAAC,EAAE,cAAc,GAAG,IAAI,KAAK,IAAI,CAAC;IACpD,OAAO,EAAE,CAAC,CAAC,EAAE,YAAY,KAAK,IAAI,CAAC;IACnC,eAAe,EAAE,OAAO,CAAC;IACzB,+GAA+G;IAC/G,aAAa,EAAE,OAAO,GAAG,IAAI,CAAC;CAC/B,uCAyOA"}
@@ -0,0 +1,10 @@
1
+ import type { OpcionesResueltas } from "./opciones";
2
+ export declare function resumenDeContexto(o: OpcionesResueltas, ventana: {
3
+ location?: {
4
+ href: string;
5
+ };
6
+ navigator?: {
7
+ userAgent: string;
8
+ };
9
+ }): Record<string, string> | undefined;
10
+ //# sourceMappingURL=contexto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contexto.d.ts","sourceRoot":"","sources":["../src/contexto.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAapD,wBAAgB,iBAAiB,CAC/B,CAAC,EAAE,iBAAiB,EACpB,OAAO,EAAE;IAAE,QAAQ,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAAC,SAAS,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAC1E,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAuBpC"}
@@ -1,40 +1,27 @@
1
+ import type { Diagnostico } from "./opciones";
1
2
  export type Visibilidad = "visible" | "oculto";
2
- /** `crypto.randomUUID` no existe en un contexto no-seguro (un sitio embebido por HTTP
3
- * plano, no HTTPS) sin este chequeo, el `useMemo` del widget lanza en el render y
4
- * tira abajo el host entero, justo lo opuesto a la promesa de "se oculta" del §8. El
5
- * fallback no necesita ser criptográfico: es un id de canal anónimo, no un secreto. */
6
- export declare function generarCanalId(): string;
7
- /** Una barra final en `backendUrl` ("https://x.com/", la forma más natural de
8
- * escribirlo) deja `//` duplicado en las URLs armadas más abajo: el motor responde con
9
- * un 308 sin headers de CORS y el navegador lo bloquea sin explicar que el problema es
10
- * la barra. Mismo patrón que `motor.ts` usa para su propia base URL. */
3
+ /** Una barra final en `backendUrl` ("https://x.com/", la forma más natural de escribirlo) deja
4
+ * `//` duplicado en las URLs armadas: el motor responde con un 308 sin headers de CORS y el
5
+ * navegador lo bloquea sin explicar que el problema es la barra. */
11
6
  export declare function normalizarBackendUrl(backendUrl: string): string;
12
- /** §8 del diseño: sin backend, sin tope, o ante cualquier duda, el widget NO se dibuja.
13
- * Ocultarse es lo honesto: no hay nada que decirle al usuario final sobre el estado interno
14
- * del proyecto de otra empresa. */
7
+ /** §2.4 del diseño: sin backend, sin tope, o ante cualquier duda, el widget NO se dibuja al
8
+ * montar. Ocultarse es lo honesto: no hay nada que decirle al usuario final sobre el estado
9
+ * interno del proyecto de otra empresa. */
15
10
  export declare function decidirVisibilidad(h: {
16
11
  respuesta: {
17
12
  ok: boolean;
18
13
  } | null;
19
14
  huboError: boolean;
20
15
  }): Visibilidad;
21
- export type EstadoDelChat = "submitted" | "streaming" | "ready" | "error";
22
- interface ParteMinima {
23
- type: string;
24
- text?: string;
25
- }
26
- interface MensajeMinimo {
27
- role: string;
28
- parts: ParteMinima[];
29
- }
30
- /** Entre Enviar y el primer token del stream pasan 4-8 s en el peor caso (medido en la pasada
31
- * visual del demo) — el backend responde perfecto, pero un silencio así se lee como "se rompió
32
- * el botón", no como "está pensando". Mostramos un indicador mientras el pedido está en vuelo
33
- * ("submitted") y mientras ya empezó a transmitir pero todavía no llegó texto del asistente
34
- * ("streaming" sin parts de texto no vacíos en el último mensaje). */
35
- export declare function mostrarIndicadorDeEscritura(h: {
36
- status: EstadoDelChat;
37
- ultimoMensaje: MensajeMinimo | undefined;
38
- }): boolean;
39
- export {};
16
+ /** §2.5 del diseño: lo que el widget le cuenta al desarrollador. `decidirVisibilidad` sigue
17
+ * decidiendo si se dibuja; esto sólo agrega el porqué. `motivo` viene del motor vía
18
+ * `/api/chat/estado`; un backend viejo no lo manda y acá queda `null` — nunca se adivina. */
19
+ export declare function diagnosticoDesdeRespuesta(h: {
20
+ status: number;
21
+ cuerpo: {
22
+ ok?: boolean;
23
+ motivo?: string | null;
24
+ } | null;
25
+ huboError: boolean;
26
+ }): Diagnostico;
40
27
  //# sourceMappingURL=estado-del-widget.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"estado-del-widget.d.ts","sourceRoot":"","sources":["../src/estado-del-widget.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE/C;;;uFAGuF;AACvF,wBAAgB,cAAc,IAAI,MAAM,CAKvC;AAED;;;wEAGwE;AACxE,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED;;mCAEmC;AACnC,wBAAgB,kBAAkB,CAAC,CAAC,EAAE;IACpC,SAAS,EAAE;QAAE,EAAE,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IAClC,SAAS,EAAE,OAAO,CAAC;CACpB,GAAG,WAAW,CAGd;AAED,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,WAAW,GAAG,OAAO,GAAG,OAAO,CAAC;AAE1E,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAED;;;;sEAIsE;AACtE,wBAAgB,2BAA2B,CAAC,CAAC,EAAE;IAC7C,MAAM,EAAE,aAAa,CAAC;IACtB,aAAa,EAAE,aAAa,GAAG,SAAS,CAAC;CAC1C,GAAG,OAAO,CAOV"}
1
+ {"version":3,"file":"estado-del-widget.d.ts","sourceRoot":"","sources":["../src/estado-del-widget.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE/C;;oEAEoE;AACpE,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED;;2CAE2C;AAC3C,wBAAgB,kBAAkB,CAAC,CAAC,EAAE;IAAE,SAAS,EAAE;QAAE,EAAE,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,GAAG,WAAW,CAG5G;AAED;;6FAE6F;AAC7F,wBAAgB,yBAAyB,CAAC,CAAC,EAAE;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE;QAAE,EAAE,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;IACxD,SAAS,EAAE,OAAO,CAAC;CACpB,GAAG,WAAW,CAId"}
@@ -0,0 +1,40 @@
1
+ import type { CursorDeSesion, EventoDelWidget, OpcionDePregunta } from "./protocolo";
2
+ export interface Mensaje {
3
+ clave: number;
4
+ rol: "usuario" | "asistente";
5
+ texto: string;
6
+ /** asistente: todavía llegan deltas. usuario: mandado y todavía no confirmado por `usuario`. */
7
+ abierto: boolean;
8
+ }
9
+ export interface PreguntaPendiente {
10
+ requestId: string;
11
+ opciones: OpcionDePregunta[];
12
+ libre: boolean;
13
+ }
14
+ export interface ErrorVisible {
15
+ codigo: string;
16
+ mensaje: string;
17
+ terminal: boolean;
18
+ }
19
+ export interface EstadoDelChat {
20
+ mensajes: Mensaje[];
21
+ pregunta: PreguntaPendiente | null;
22
+ ocupado: boolean;
23
+ error: ErrorVisible | null;
24
+ /** `null` = el próximo mensaje abre una sesión nueva. */
25
+ cursor: CursorDeSesion | null;
26
+ siguienteClave: number;
27
+ }
28
+ export declare function estadoInicial(cursor: CursorDeSesion | null): EstadoDelChat;
29
+ /** Optimista: lo que el usuario escribió se ve ya, y `usuario` lo confirma después (§2.4: nunca
30
+ * se pierde lo escrito). */
31
+ export declare function alEnviar(e: EstadoDelChat, texto: string): EstadoDelChat;
32
+ export declare function alResponder(e: EstadoDelChat): EstadoDelChat;
33
+ export declare function alReintentar(e: EstadoDelChat): EstadoDelChat;
34
+ /** El cuerpo HTTP terminó, con borde o sin él: nada más va a llegar por este stream. */
35
+ export declare function alTerminarStream(e: EstadoDelChat): EstadoDelChat;
36
+ export declare function reducir(e: EstadoDelChat, ev: EventoDelWidget): EstadoDelChat;
37
+ /** Entre Enviar y el primer token pasan 4-8 s en el peor caso (medido en la 1.x): sin indicador
38
+ * se lee como "se rompió el botón". Se apaga con el primer texto del asistente. */
39
+ export declare function mostrarIndicador(e: EstadoDelChat): boolean;
40
+ //# sourceMappingURL=estado.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"estado.d.ts","sourceRoot":"","sources":["../src/estado.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAMrF,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,SAAS,GAAG,WAAW,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,gGAAgG;IAChG,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IAC7B,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IAC3B,yDAAyD;IACzD,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IAC9B,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,GAAG,aAAa,CAE1E;AA0BD;4BAC4B;AAC5B,wBAAgB,QAAQ,CAAC,CAAC,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,GAAG,aAAa,CAEvE;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,aAAa,GAAG,aAAa,CAE3D;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,aAAa,GAAG,aAAa,CAE5D;AAED,wFAAwF;AACxF,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,aAAa,GAAG,aAAa,CAEhE;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,aAAa,EAAE,EAAE,EAAE,eAAe,GAAG,aAAa,CAsC5E;AAED;mFACmF;AACnF,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,aAAa,GAAG,OAAO,CAI1D"}
@@ -0,0 +1,45 @@
1
+ import type { ImagenLista } from "./captura";
2
+ import type { ElementoSeleccionado } from "./elemento";
3
+ /** El motor cuenta 5 evidencias por sesión, de cualquier tipo, y el `contexto` que reenvía `abrir`
4
+ * ocupa una (Revisión 2 punto 4). Cuatro acá, antes que un quinto que siempre rebota con 429. */
5
+ export declare const MAX_ADJUNTOS = 4;
6
+ export type EstadoDeSubida = "pendiente" | "subiendo" | "listo" | "fallo";
7
+ interface AdjuntoBase {
8
+ clave: number;
9
+ estado: EstadoDeSubida;
10
+ evidenciaId: string | null;
11
+ motivo: string | null;
12
+ /** Con `fallo`: si vale la pena ofrecer «reintentar» (red, motor caído) o no (tope, rechazo). */
13
+ reintentable: boolean;
14
+ }
15
+ export type Adjunto = (AdjuntoBase & {
16
+ tipo: "elemento";
17
+ elemento: ElementoSeleccionado;
18
+ etiqueta: string;
19
+ }) | (AdjuntoBase & {
20
+ tipo: "captura";
21
+ imagen: ImagenLista;
22
+ });
23
+ export interface EstadoDeAdjuntos {
24
+ adjuntos: Adjunto[];
25
+ siguienteClave: number;
26
+ }
27
+ export declare function sinAdjuntos(): EstadoDeAdjuntos;
28
+ export declare function puedeAgregar(e: EstadoDeAdjuntos): boolean;
29
+ /** «button «Guardar»»: lo que el chip muestra. El nombre accesible antes que el texto, y corto. */
30
+ export declare function etiquetaDelElemento(el: ElementoSeleccionado): string;
31
+ export declare function agregarElemento(e: EstadoDeAdjuntos, elemento: ElementoSeleccionado): EstadoDeAdjuntos;
32
+ export declare function agregarCaptura(e: EstadoDeAdjuntos, imagen: ImagenLista): EstadoDeAdjuntos;
33
+ export declare function quitar(e: EstadoDeAdjuntos, clave: number): EstadoDeAdjuntos;
34
+ export declare function marcar(e: EstadoDeAdjuntos, clave: number, cambio: {
35
+ estado: EstadoDeSubida;
36
+ evidenciaId?: string;
37
+ motivo?: string;
38
+ reintentable?: boolean;
39
+ }): EstadoDeAdjuntos;
40
+ export declare function pendientes(e: EstadoDeAdjuntos): Adjunto[];
41
+ export declare function ultimoElemento(e: EstadoDeAdjuntos): Extract<Adjunto, {
42
+ tipo: "elemento";
43
+ }> | null;
44
+ export {};
45
+ //# sourceMappingURL=adjuntos.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adjuntos.d.ts","sourceRoot":"","sources":["../../src/evidencia/adjuntos.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAOvD;iGACiG;AACjG,eAAO,MAAM,YAAY,IAAI,CAAC;AAE9B,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,CAAC;AAE1E,UAAU,WAAW;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,cAAc,CAAC;IACvB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,iGAAiG;IACjG,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,MAAM,OAAO,GACf,CAAC,WAAW,GAAG;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,oBAAoB,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,GACtF,CAAC,WAAW,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,CAAC,CAAC;AAE7D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,WAAW,IAAI,gBAAgB,CAE9C;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAEzD;AAcD,mGAAmG;AACnG,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,oBAAoB,GAAG,MAAM,CAIpE;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,gBAAgB,EAAE,QAAQ,EAAE,oBAAoB,GAAG,gBAAgB,CAErG;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,gBAAgB,EAAE,MAAM,EAAE,WAAW,GAAG,gBAAgB,CAEzF;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,GAAG,gBAAgB,CAG3E;AAED,wBAAgB,MAAM,CACpB,CAAC,EAAE,gBAAgB,EACnB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE;IAAE,MAAM,EAAE,cAAc,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,GAChG,gBAAgB,CAelB;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,gBAAgB,GAAG,OAAO,EAAE,CAEzD;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,EAAE;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,GAAG,IAAI,CAMjG"}