xonecode 0.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.
- package/README.md +167 -0
- package/dist/agent/authEnDisco.js +56 -0
- package/dist/agent/authEnDisco.js.map +1 -0
- package/dist/agent/configEnDisco.js +109 -0
- package/dist/agent/configEnDisco.js.map +1 -0
- package/dist/agent/crearProyecto.js +34 -0
- package/dist/agent/crearProyecto.js.map +1 -0
- package/dist/agent/entorno.js +124 -0
- package/dist/agent/entorno.js.map +1 -0
- package/dist/agent/guionizado.js +53 -0
- package/dist/agent/guionizado.js.map +1 -0
- package/dist/agent/instantanea.js +129 -0
- package/dist/agent/instantanea.js.map +1 -0
- package/dist/agent/interrupts.js +83 -0
- package/dist/agent/interrupts.js.map +1 -0
- package/dist/agent/mensajes.js +24 -0
- package/dist/agent/mensajes.js.map +1 -0
- package/dist/agent/modelos.js +42 -0
- package/dist/agent/modelos.js.map +1 -0
- package/dist/agent/normalizar.js +20 -0
- package/dist/agent/normalizar.js.map +1 -0
- package/dist/agent/perfiles.js +103 -0
- package/dist/agent/perfiles.js.map +1 -0
- package/dist/agent/proyecto.js +107 -0
- package/dist/agent/proyecto.js.map +1 -0
- package/dist/agent/puente.js +142 -0
- package/dist/agent/puente.js.map +1 -0
- package/dist/agent/resumenDeTool.js +48 -0
- package/dist/agent/resumenDeTool.js.map +1 -0
- package/dist/agent/skills.js +65 -0
- package/dist/agent/skills.js.map +1 -0
- package/dist/agent/textoDeTool.js +113 -0
- package/dist/agent/textoDeTool.js.map +1 -0
- package/dist/agent/turnoReal.js +173 -0
- package/dist/agent/turnoReal.js.map +1 -0
- package/dist/agent/verificador.js +65 -0
- package/dist/agent/verificador.js.map +1 -0
- package/dist/agent/xoneAgent.js +106 -0
- package/dist/agent/xoneAgent.js.map +1 -0
- package/dist/bin.js +9 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli/aprobar.js +82 -0
- package/dist/cli/aprobar.js.map +1 -0
- package/dist/cli/config.js +133 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/consola.js +346 -0
- package/dist/cli/consola.js.map +1 -0
- package/dist/cli/describe.js +53 -0
- package/dist/cli/describe.js.map +1 -0
- package/dist/cli/doctor.js +45 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/main.js +520 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/markdown.js +90 -0
- package/dist/cli/markdown.js.map +1 -0
- package/dist/cli/run.js +164 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/spinner.js +89 -0
- package/dist/cli/spinner.js.map +1 -0
- package/dist/cli/stdio.js +193 -0
- package/dist/cli/stdio.js.map +1 -0
- package/dist/cli/tema.js +40 -0
- package/dist/cli/tema.js.map +1 -0
- package/dist/cli/verify.js +62 -0
- package/dist/cli/verify.js.map +1 -0
- package/dist/core/bitacora.js +30 -0
- package/dist/core/bitacora.js.map +1 -0
- package/dist/core/config.js +236 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/contextos.js +56 -0
- package/dist/core/contextos.js.map +1 -0
- package/dist/core/deps.js +51 -0
- package/dist/core/deps.js.map +1 -0
- package/dist/core/diff.js +79 -0
- package/dist/core/diff.js.map +1 -0
- package/dist/core/esqueleto.js +366 -0
- package/dist/core/esqueleto.js.map +1 -0
- package/dist/core/events.js +2 -0
- package/dist/core/events.js.map +1 -0
- package/dist/core/modelos.js +85 -0
- package/dist/core/modelos.js.map +1 -0
- package/dist/core/notify.js +106 -0
- package/dist/core/notify.js.map +1 -0
- package/dist/core/ports.js +114 -0
- package/dist/core/ports.js.map +1 -0
- package/dist/core/turno.js +127 -0
- package/dist/core/turno.js.map +1 -0
- package/dist/vendor/hitl.js +103 -0
- package/dist/vendor/hitl.js.map +1 -0
- package/dist/vendor/skillLoaders/catalog.js +74 -0
- package/dist/vendor/skillLoaders/catalog.js.map +1 -0
- package/dist/vendor/tokenTracking.js +58 -0
- package/dist/vendor/tokenTracking.js.map +1 -0
- package/package.json +41 -0
- package/skills/xone-debugging/SKILL.md +52 -0
- package/skills/xone-debugging/references/faq.md +561 -0
- package/skills/xone-debugging/references/troubleshooting-y-glosario.md +429 -0
- package/skills/xone-development/SKILL.md +105 -0
- package/skills/xone-development/references/anti-patrones.md +94 -0
- package/skills/xone-development/references/css/atributos-por-categoria.md +509 -0
- package/skills/xone-development/references/css/buenas-practicas-y-parser.md +419 -0
- package/skills/xone-development/references/css/dinamicos-cascada-y-componentes.md +550 -0
- package/skills/xone-development/references/css/patrones-material-y-temas.md +1140 -0
- package/skills/xone-development/references/css/propiedades-y-herencia.md +885 -0
- package/skills/xone-development/references/css/selectores-unidades-colores.md +840 -0
- package/skills/xone-development/references/datos/appdata-referencia-ampliada.md +457 -0
- package/skills/xone-development/references/datos/appdata.md +611 -0
- package/skills/xone-development/references/datos/http-sqlmanager-y-crypto.md +720 -0
- package/skills/xone-development/references/datos/http.md +366 -0
- package/skills/xone-development/references/datos/oauth2-y-replica.md +156 -0
- package/skills/xone-development/references/device/biometria-imagedrawing-y-otros.md +544 -0
- package/skills/xone-development/references/device/objetos-de-dispositivo.md +583 -0
- package/skills/xone-development/references/device/systemsettings-referencia-ampliada.md +396 -0
- package/skills/xone-development/references/device/systemsettings-y-permisos.md +342 -0
- package/skills/xone-development/references/fundamentos/conceptos-clave.md +695 -0
- package/skills/xone-development/references/fundamentos/configuracion-app-xml-ini-mappings.md +668 -0
- package/skills/xone-development/references/fundamentos/errores-comunes.md +312 -0
- package/skills/xone-development/references/fundamentos/navegacion-convenciones-y-primer-proyecto.md +576 -0
- package/skills/xone-development/references/fundamentos/plataforma-y-anatomia-de-proyecto.md +387 -0
- package/skills/xone-development/references/indice-completo.md +77 -0
- package/skills/xone-development/references/javascript/coleccion-error-y-usuario.md +260 -0
- package/skills/xone-development/references/javascript/debugging-y-best-practices.md +245 -0
- package/skills/xone-development/references/javascript/metodos-de-los-controles.md +556 -0
- package/skills/xone-development/references/javascript/metodos-nativos-de-la-vista.md +114 -0
- package/skills/xone-development/references/javascript/motor-js-y-contexto-de-ejecucion.md +319 -0
- package/skills/xone-development/references/javascript/objeto-ai-llm-en-dispositivo.md +358 -0
- package/skills/xone-development/references/javascript/objetos-creables-a-m.md +527 -0
- package/skills/xone-development/references/javascript/objetos-creables-n-z.md +385 -0
- package/skills/xone-development/references/javascript/patrones-criticos-seguridad-y-rendimiento.md +595 -0
- package/skills/xone-development/references/javascript/patrones-de-navegacion-datos-y-codigo.md +751 -0
- package/skills/xone-development/references/javascript/patrones-de-ui-voz-integracion-y-seguridad.md +701 -0
- package/skills/xone-development/references/javascript/plantillas-y-funciones-utilitarias.md +647 -0
- package/skills/xone-development/references/javascript/self-y-dataobject.md +449 -0
- package/skills/xone-development/references/javascript/singletons-globales.md +361 -0
- package/skills/xone-development/references/javascript/ui-catalogo-de-metodos.md +464 -0
- package/skills/xone-development/references/javascript/ui-gps-camara-y-multimedia.md +506 -0
- package/skills/xone-development/references/javascript/ui-navegacion-mensajes-y-vista.md +422 -0
- package/skills/xone-development/references/resumen-js-datos-dispositivo.md +80 -0
- package/skills/xone-development/references/resumen-xml-y-css.md +74 -0
- package/skills/xone-development/references/tipos-de-prop.md +28 -0
- package/skills/xone-development/references/xml-ui/asfilter-visibilidad-eventos-y-macros.md +576 -0
- package/skills/xone-development/references/xml-ui/atributos-coll-group-frame.md +235 -0
- package/skills/xone-development/references/xml-ui/atributos-method-macro-script-event-app.md +244 -0
- package/skills/xone-development/references/xml-ui/atributos-prop.md +439 -0
- package/skills/xone-development/references/xml-ui/contents-y-macros.md +392 -0
- package/skills/xone-development/references/xml-ui/errores-comunes-xml.md +245 -0
- package/skills/xone-development/references/xml-ui/estructura-y-nodo-coll.md +326 -0
- package/skills/xone-development/references/xml-ui/eventos-ciclo-de-vida-e-interaccion.md +704 -0
- package/skills/xone-development/references/xml-ui/eventos-sistema-login-y-personalizados.md +944 -0
- package/skills/xone-development/references/xml-ui/layouts-herencia-y-buenas-practicas.md +618 -0
- package/skills/xone-development/references/xml-ui/mapas.md +685 -0
- package/skills/xone-development/references/xml-ui/mappings-y-colecciones-separadas.md +148 -0
- package/skills/xone-development/references/xml-ui/nodos-group-y-frame.md +666 -0
- package/skills/xone-development/references/xml-ui/patrones-de-pantalla.md +643 -0
- package/skills/xone-development/references/xml-ui/prop-atributos-y-condiciones.md +298 -0
- package/skills/xone-development/references/xml-ui/prop-tipos-basicos.md +493 -0
- package/skills/xone-development/references/xml-ui/prop-tipos-combos-y-controles.md +664 -0
- package/skills/xone-development/references/xml-ui/prop-tipos-listas-y-mapas.md +412 -0
- package/skills/xone-plan-builder/SKILL.md +183 -0
- package/skills/xone-plan-builder/agents/openai.yaml +5 -0
- package/skills/xone-plan-builder/references/TASKS-FORMAT.md +132 -0
- package/skills/xone-project-generator/SKILL.md +111 -0
- package/skills/xone-project-generator/references/canonical-sizes.md +292 -0
- package/skills/xone-project-generator/references/colecciones-base.md +16 -0
- package/skills/xone-project-generator/references/convenciones-de-nombres.md +16 -0
- package/skills/xone-project-generator/references/ejemplos-por-sector-y-prohibiciones.md +140 -0
- package/skills/xone-project-generator/references/fase-3-estilos-css.md +597 -0
- package/skills/xone-project-generator/references/fase-6-colecciones.md +762 -0
- package/skills/xone-project-generator/references/fase-7-asfilter-e-integraciones.md +142 -0
- package/skills/xone-project-generator/references/fase-7-entidades-y-estructura-de-pantalla.md +494 -0
- package/skills/xone-project-generator/references/fase-7-plantilla-consola.md +477 -0
- package/skills/xone-project-generator/references/fase-7-plantillas-de-pantalla.md +302 -0
- package/skills/xone-project-generator/references/fase-7-viewmodes-graficos-y-listas.md +681 -0
- package/skills/xone-project-generator/references/fase-7-viewmodes-mapa-y-calendario.md +479 -0
- package/skills/xone-project-generator/references/fases-0-2-analisis-y-modelo-de-datos.md +350 -0
- package/skills/xone-project-generator/references/fases-10-12-readmes-y-validacion.md +194 -0
- package/skills/xone-project-generator/references/fases-4-5-estructura-y-configuracion.md +587 -0
- package/skills/xone-project-generator/references/fases-8-9-eventos-y-javascript.md +861 -0
- package/skills/xone-project-generator/references/flujo-de-generacion.md +67 -0
- package/skills/xone-project-generator/references/plantillas-estandar.md +98 -0
- package/skills/xone-project-generator/references/tamanos-canonicos.md +49 -0
- package/skills/xone-review/SKILL.md +189 -0
- package/skills/xone-spec-builder/SKILL.md +235 -0
- package/skills/xone-spec-builder/agents/openai.yaml +5 -0
- package/skills/xone-spec-builder/references/ADR-FORMAT.md +55 -0
- package/skills/xone-spec-builder/references/CONTEXT-FORMAT.md +42 -0
- package/skills/xone-spec-builder/references/PLAN-FORMAT.md +120 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
# XOne JavaScript — Motor, contexto de ejecución y escape XML
|
|
2
|
+
|
|
3
|
+
> Fuente: `xone/v2/xone-help-docs/topics/03a-js-self.md` §1. Referencia de la skill; el índice está en [../../SKILL.md](../../SKILL.md).
|
|
4
|
+
|
|
5
|
+
Contenido: §1 motor JS embebido, objetos globales disponibles, cómo se ejecuta el JS desde eventos XML, diferencias con JS web, archivos JS del proyecto, alcance y persistencia de variables, buenas prácticas y escape XML/CDATA dentro de .xne
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. Introduccion al JavaScript de XOne
|
|
10
|
+
|
|
11
|
+
### 1.1 Motor JS Embebido
|
|
12
|
+
|
|
13
|
+
El JavaScript de XOne **NO se ejecuta en un navegador web ni en Node.js**. Se ejecuta dentro de un motor JavaScript nativo embebido en la aplicación móvil (Rhino/V8 según la plataforma). Esto implica limitaciones fundamentales que todo desarrollador debe conocer.
|
|
14
|
+
|
|
15
|
+
Los scripts se colocan dentro de nodos `<script>` en archivos `.xne`, vinculados a eventos del ciclo de vida de la pantalla:
|
|
16
|
+
|
|
17
|
+
```xml
|
|
18
|
+
<coll name="MiPantalla" title="Mi Pantalla" class="xnCollBase">
|
|
19
|
+
|
|
20
|
+
<!-- Se ejecuta UNA sola vez al crear el objeto (primera apertura) -->
|
|
21
|
+
<create>
|
|
22
|
+
<action name="runscript">
|
|
23
|
+
<script language="javascript">
|
|
24
|
+
inicializar();
|
|
25
|
+
</script>
|
|
26
|
+
</action>
|
|
27
|
+
</create>
|
|
28
|
+
|
|
29
|
+
<!-- Al abrir el objeto para edicion — evento principal para inicializar la pantalla -->
|
|
30
|
+
<before-edit>
|
|
31
|
+
<action name="runscript">
|
|
32
|
+
<script language="javascript">
|
|
33
|
+
cargarDatos();
|
|
34
|
+
</script>
|
|
35
|
+
</action>
|
|
36
|
+
</before-edit>
|
|
37
|
+
|
|
38
|
+
<!-- Al pulsar el botón atrás -->
|
|
39
|
+
<onback>
|
|
40
|
+
<action name="runscript">
|
|
41
|
+
<script language="javascript">
|
|
42
|
+
manejarAtras();
|
|
43
|
+
</script>
|
|
44
|
+
</action>
|
|
45
|
+
</onback>
|
|
46
|
+
|
|
47
|
+
</coll>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
> **Crítico**: `<load>` NO se ejecuta al mostrar la pantalla — se dispara **por cada DataObject** al cargarse desde la BD: tanto al recorrer la coleccion (`startBrowse()`/`loadAll()`) como al hidratar items de un `<contents>` o cargas individuales. **NO se recomienda usarlo** porque el rendimiento puede verse seriamente afectado (se ejecuta una vez por item cargado). Para inicializar una pantalla usar `<before-edit>` (al abrir para editar) o `<create>` (primera apertura).
|
|
51
|
+
|
|
52
|
+
> **Referencia cruzada:** Para la estructura completa de eventos en nodos XML, consultar el tópico 02 - Estructura XML. Para los estilos CSS aplicables a controles manipulados desde JS, ver el tópico 04 - Estilos CSS.
|
|
53
|
+
|
|
54
|
+
### 1.2 Objetos Globales Disponibles
|
|
55
|
+
|
|
56
|
+
XOne expone los siguientes objetos globales accesibles desde cualquier script:
|
|
57
|
+
|
|
58
|
+
| Objeto | Descripción |
|
|
59
|
+
|--------|-------------|
|
|
60
|
+
| `ui` | Interfaz de usuario: dialogos, toasts, navegación, GPS, camara |
|
|
61
|
+
| `appData` | Datos de la aplicación: colecciones, autenticación, macros, rutas |
|
|
62
|
+
| `self` | Objeto de datos actual (DataObject) en el contexto del script |
|
|
63
|
+
| `crypto` | Funciones criptograficas: hashing, cifrado AES, firma digital, encoding |
|
|
64
|
+
| `$http` | Cliente HTTP asíncrono: GET, POST, PUT, DELETE, PATCH, download |
|
|
65
|
+
| `console` | Logging WHATWG completo: `console.{log,info,debug,warn,error,trace,assert,group,groupCollapsed,groupEnd,time,timeLog,timeEnd,count,countReset,dir,dirxml,clear,table}` con formato `%s/%d/%j/...` |
|
|
66
|
+
| `biometricsManager` | Autenticación biometrica (huella, face) y firma biometrica |
|
|
67
|
+
| `fingerprintManager` | Huella dactilar (API legacy, preferir `biometricsManager`) |
|
|
68
|
+
| `bluetoothSerial` | Comunicación por puerto serie Bluetooth |
|
|
69
|
+
| `replica` | Control de sincronización/replica con servidor |
|
|
70
|
+
| `systemSettings` | Singleton global: brillo, permisos, memoria, MDM, batería, rutas, Intune |
|
|
71
|
+
| `deviceInfo` | Singleton global: batería, red móvil, trafico de bytes |
|
|
72
|
+
|
|
73
|
+
### 1.3 Como se Ejecuta el JS: Eventos XML a Acciones a Script
|
|
74
|
+
|
|
75
|
+
El flujo de ejecución en XOne sigue este patron:
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
1. Usuario interactua con la UI (pulsa boton, cambia campo, etc.)
|
|
79
|
+
|
|
|
80
|
+
2. El framework detecta el evento asociado al nodo XML
|
|
81
|
+
|
|
|
82
|
+
3. Se ejecuta el bloque <script> vinculado al evento
|
|
83
|
+
|
|
|
84
|
+
4. El script accede a objetos globales (self, ui, appData)
|
|
85
|
+
|
|
|
86
|
+
5. Las acciones del script modifican datos y/o la interfaz
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Eventos disponibles en nodos XML:**
|
|
90
|
+
|
|
91
|
+
| Evento | Momento de Ejecución | Ubicación |
|
|
92
|
+
|--------|---------------------|-----------|
|
|
93
|
+
| `<create>` | Una sola vez al crear el objeto (primera apertura) | Dentro de `<coll>` |
|
|
94
|
+
| `<before-edit>` | Al abrir un objeto para edición — **el más usado para inicializar la pantalla** | Dentro de `<coll>` |
|
|
95
|
+
| `<after-edit>` | Después de entrar en modo edición, con la UI ya montada | Dentro de `<coll>` |
|
|
96
|
+
| `<load>` | Se dispara **por cada DataObject** al cargarse desde la BD (startBrowse/loadAll/`<contents>`/cargas individuales). **NO es evento de pantalla** y **NO recomendado** por impacto en rendimiento | Dentro de `<coll>` |
|
|
97
|
+
| `<onchange>` | Cuando cambia el valor de una propiedad | Dentro de `<coll>`, nodo `<field>` |
|
|
98
|
+
| `<selecteditem>` | Cuando se selecciona un item en una lista | Dentro de `<coll>` |
|
|
99
|
+
| `<onlongpressitem>` | Pulsacion larga en un item de lista | Dentro de `<coll>` |
|
|
100
|
+
| `<onback>` | Cuando el usuario pulsa el botón atrás | Dentro de `<coll>` |
|
|
101
|
+
| `<miNodo>` | Nodo custom invocado con `ExecuteNode(miNodo())` o `method="executenode(miNodo)"` | Dentro de `<coll>` |
|
|
102
|
+
|
|
103
|
+
### 1.4 Diferencias con JS Web
|
|
104
|
+
|
|
105
|
+
**APIs que NO están disponibles en XOne:**
|
|
106
|
+
|
|
107
|
+
| API Web | Alternativa XOne |
|
|
108
|
+
|---------|------------------|
|
|
109
|
+
| `document`, `window` (DOM) | `ui.getView(self)` para acceder a controles |
|
|
110
|
+
| `localStorage` / `sessionStorage` | `appData.getGlobalMacro()` / `appData.setGlobalMacro()` |
|
|
111
|
+
| `XMLHttpRequest` | `$http.get()`, `$http.post()`, etc. (también existe `fetch` custom). |
|
|
112
|
+
| `navigator.geolocation` | `ui.startGps()` / `ui.checkGpsStatus()` |
|
|
113
|
+
| `alert()` / `confirm()` / `prompt()` | `ui.msgBox()` / `ui.showToast()` |
|
|
114
|
+
| `require()` / `import` | No hay sistema de módulos; todo va en `functions.js` |
|
|
115
|
+
| `async` / `await` | Callbacks o `Promise` (sí soportado vía implementación custom). |
|
|
116
|
+
|
|
117
|
+
**Sintaxis ES6+ NO soportada:**
|
|
118
|
+
|
|
119
|
+
| Sintaxis | Estado | Alternativa |
|
|
120
|
+
|----------|--------|-------------|
|
|
121
|
+
| Template literals `` `${var}` `` | Parse error (*illegal character*) sobre el backtick | Concatenación con `+` |
|
|
122
|
+
| `async` / `await` | Parse error (reservadas) | Callbacks o `Promise` |
|
|
123
|
+
| Spread/rest `...args`, default params `function f(x=1)` | Parse error | `arguments` / chequeo `=== undefined` |
|
|
124
|
+
| Computed keys en object literals `{[k]: v}` | Parse error (sí en class body) | `var o = {}; o[k] = v;` |
|
|
125
|
+
| Optional chaining `?.` / nullish coalescing `??` | Parse error | Chequeos manuales |
|
|
126
|
+
| Private fields `#name` en class | Parse error (requiere runtime) | Convención `_name` |
|
|
127
|
+
| Static blocks `static { ... }` en class | Parse error | Sentencias `ClassName.x = ...;` tras la clase |
|
|
128
|
+
|
|
129
|
+
**SÍ funciona:** `let`, `const`, arrow functions `() => {}`, destructuring (`var {a, b} = o`), `for...of` sobre arrays/strings, generadores con `yield` (runtime SpiderMonkey legacy — usar `try { while (true) v = iter.next(); } catch (e) {}`), Symbol, typed arrays, **`class` ES6+ con `extends`/`super`/`static`/getters/setters/computed keys/field declarations/generator methods (`*method()`)**, **`Promise` ES2024 completo** (`.then`/`.catch`/`.finally`/`Promise.all`/`allSettled`/`race`/`any`/`withResolvers`), `fetch`, `setTimeout`/`setInterval`, `URL`, `AbortController`, `TextEncoder`/`TextDecoder`, `console.{log,info,warn,error,debug,trace,...}`, `performance.now()`, métodos modernos de `String` (`padStart`, `replaceAll`, `at`, `matchAll`...) y de `Array` (`map`, `filter`, `reduce`, `find`...), `JSON`. Detalle completo en 01-xone-fundamentals.md §6.7.
|
|
130
|
+
|
|
131
|
+
### 1.5 Archivos JavaScript en el Proyecto
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
MiProyecto/
|
|
135
|
+
functions.js <- Funciones globales (siempre presente, carga automatica)
|
|
136
|
+
scripts/ <- Scripts adicionales organizados (opcional)
|
|
137
|
+
ubicacion.js
|
|
138
|
+
viajes.js
|
|
139
|
+
mensajeria.js
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
El archivo `functions.js` es el punto de entrada global. Se carga automáticamente al iniciar la aplicación y sus funciones están disponibles en todos los scripts `.xne` del proyecto. Para proyectos grandes, se recomienda organizar la lógica en archivos adicionales dentro de `scripts/` y cargarlos con `appData.loadIncludeFile()`.
|
|
143
|
+
|
|
144
|
+
### 1.6 Alcance de Variables
|
|
145
|
+
|
|
146
|
+
```javascript
|
|
147
|
+
// Variables globales: accesibles desde cualquier script del proyecto
|
|
148
|
+
// Se definen en functions.js
|
|
149
|
+
var MI_CONSTANTE = "valor";
|
|
150
|
+
var ESTADOS = { ACTIVO: 1, INACTIVO: 0 };
|
|
151
|
+
|
|
152
|
+
// Variables locales: solo dentro de la funcion
|
|
153
|
+
function miFuncion() {
|
|
154
|
+
let variableLocal = "solo existe aquí";
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// IMPORTANTE: 'self' puede cambiar de contexto en callbacks asincronos
|
|
158
|
+
// Guardar referencia ANTES de cualquier operación asincrona
|
|
159
|
+
function operacionAsincrona() {
|
|
160
|
+
let contexto = self; // Guardar referencia
|
|
161
|
+
$http.get(url, request,
|
|
162
|
+
function(sData) {
|
|
163
|
+
// Aquí 'self' puede NO ser el mismo objeto
|
|
164
|
+
// Usar 'contexto' en su lugar
|
|
165
|
+
contexto.MAP_RESULTADO = sData;
|
|
166
|
+
},
|
|
167
|
+
function(nError, sDesc) {
|
|
168
|
+
ui.showToast("Error: " + sDesc);
|
|
169
|
+
}
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### 1.7 Ambitos de ejecución y persistencia de variables
|
|
175
|
+
|
|
176
|
+
Entender en que **ambito** se ejecuta cada script es fundamental para usar correctamente `This` / `self`, `ThisDataColl` y las variables globales.
|
|
177
|
+
|
|
178
|
+
**Ambitos posibles:**
|
|
179
|
+
|
|
180
|
+
| Ambito | Cuando | `This` / `self` | `ThisDataColl` |
|
|
181
|
+
| --- | --- | --- | --- |
|
|
182
|
+
| **Objeto** | Acción disparada desde un objeto (p.ej. `<create>`, `<onchange>` de un prop) | El objeto en cuestion | `Nothing` / `null` |
|
|
183
|
+
| **Coleccion** | Acción en nodo `<coll-action>` (p.ej. `<onlogon>`) | `Nothing` / `null` | La coleccion que dispara el script |
|
|
184
|
+
| **Local** | Dentro de una `function()` | — | — |
|
|
185
|
+
|
|
186
|
+
**Reglas de visibilidad:**
|
|
187
|
+
|
|
188
|
+
- `appData` es siempre visible desde cualquier ambito.
|
|
189
|
+
- `self` / `This` y `ThisDataColl` son visibles durante la ejecución del script y de todas las funciones que se llamen desde el. **No** son visibles dentro de acciones anidadas (p.ej. un `Save` que dispara otro script tiene su propio ambito).
|
|
190
|
+
- `user` es visible cuando hay usuario logueado.
|
|
191
|
+
- Una variable declarada en el bloque principal del script es visible para todas las funciones llamadas desde ese mismo script, pero **no** para acciones anidadas.
|
|
192
|
+
|
|
193
|
+
**Intercambio de datos entre scripts anidados:** como las variables locales no sobreviven al anidamiento, hay que usar mecanismos persistentes:
|
|
194
|
+
|
|
195
|
+
- Propiedades del objeto (`self.MAP_FLAG = 1`)
|
|
196
|
+
- Variables de la coleccion (`coll.setVariable(...)`)
|
|
197
|
+
- Colecciones globales (`appData.getCollection("...")`)
|
|
198
|
+
- Macros globales (`appData.setGlobalMacro("##KEY##", valor)` / `appData.getGlobalMacro("##KEY##")`)
|
|
199
|
+
- Objeto `user` (persiste durante la sesión)
|
|
200
|
+
|
|
201
|
+
### 1.8 Buenas prácticas al programar en XOne
|
|
202
|
+
|
|
203
|
+
Patrones que evitan bugs sutiles y problemas de rendimiento:
|
|
204
|
+
|
|
205
|
+
**1. No uses `LoadAll()` sin motivo.** Cargar todos los objetos en memoria es caro. Si solo necesitas recorrer una coleccion, usa `startBrowse()` / `endBrowse()`. Para contar, `startBrowse(true)`.
|
|
206
|
+
|
|
207
|
+
**2. No filtres colecciones globales sin restaurar.** Si haces `coll.setFilter(...)` sobre una coleccion global y no la restauras, afectara a todas las vistas que usen esa coleccion.
|
|
208
|
+
|
|
209
|
+
```javascript
|
|
210
|
+
// MAL: filtra la coleccion global y afecta la UI
|
|
211
|
+
let coll = appData.getCollection("Clientes");
|
|
212
|
+
coll.setFilter("CODIGO=1");
|
|
213
|
+
coll.startBrowse();
|
|
214
|
+
// ... al salir, la lista de clientes solo muestra el cliente 1
|
|
215
|
+
|
|
216
|
+
// BIEN: trabajar sobre una copia
|
|
217
|
+
let coll = appData.getCollection("Clientes").createClone();
|
|
218
|
+
coll.setFilter("CODIGO=1");
|
|
219
|
+
// ... usar coll ...
|
|
220
|
+
coll = null; // liberar
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**3. Anula las referencias en orden inverso.** Si creas una coleccion y sacas objetos de ella, anula primero los objetos y luego la coleccion. Nunca al reves (la coleccion puede destruir los objetos antes).
|
|
224
|
+
|
|
225
|
+
**4. Guarda una marca para evitar reentradas.** Si un `Save` puede dispararse desde dentro de un `<onchange>` que a su vez puede llamarse otra vez al modificar el mismo campo, usa una propiedad centinela:
|
|
226
|
+
|
|
227
|
+
```xml
|
|
228
|
+
<onchange field="MAP_IMPORTE">
|
|
229
|
+
<action name="runscript">
|
|
230
|
+
<script language="javascript">
|
|
231
|
+
if (self.MAP_SAVING == 0) {
|
|
232
|
+
self.MAP_SAVING = 1;
|
|
233
|
+
// ... calcular cosas ...
|
|
234
|
+
self.save(); // dispara este mismo evento
|
|
235
|
+
self.MAP_SAVING = 0;
|
|
236
|
+
}
|
|
237
|
+
</script>
|
|
238
|
+
</action>
|
|
239
|
+
</onchange>
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
**5. No modifiques `CurrentItem` con cursores abiertos.** Algunas bases de datos no permiten modificar una tabla con cursores activos. Si tienes que modificar muchos objetos, mejor hazlo con `executeSql` o carga los IDs, cierra el cursor, y modifica uno a uno.
|
|
243
|
+
|
|
244
|
+
**6. En callbacks asíncronos, captura `self` antes.** Ver sección 1.6.
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
### 1.9 JavaScript dentro de XNE: escape XML o CDATA
|
|
249
|
+
|
|
250
|
+
Cuando el JavaScript va embebido dentro de un fichero `.xne` (en `<script language="javascript">` o en atributos como `onclick`, `disablevisible`, `value`, etc.), el bloque JS forma parte del XML y **debe respetar las reglas de XML**.
|
|
251
|
+
|
|
252
|
+
**Regla preferida — JS no trivial debe vivir fuera del `.xne`:** declarar una función en `functions.js` (o un fichero `.js` incluido) y llamarla desde el XML con `miFuncion();`. Así el JS se escribe normal (sin entidades, sin CDATA) y el XML solo invoca. Es lo más mantenible, lo más legible y evita por completo el problema del escape.
|
|
253
|
+
|
|
254
|
+
Cuando aun así necesitas escribir JS inline (snippets cortos), hay dos formas válidas de evitar que los caracteres especiales rompan el parseo XML:
|
|
255
|
+
|
|
256
|
+
1. **Entidades XML** dentro del JavaScript — funciona en cualquier sitio (nodo y atributo).
|
|
257
|
+
2. **`<![CDATA[...]]>` envolviendo el bloque** — funciona solo dentro de nodos `<script>`. NO es válido dentro de atributos XML (`onclick="..."`, `disablevisible="..."`).
|
|
258
|
+
|
|
259
|
+
Las dos son equivalentes en cuanto al resultado: el motor JS recibe el mismo código.
|
|
260
|
+
|
|
261
|
+
**Tabla de entidades:**
|
|
262
|
+
|
|
263
|
+
| Carácter JS | Entidad XML | Cuando aparece |
|
|
264
|
+
|-------------|-------------|----------------|
|
|
265
|
+
| `&` | `&` | Operador `&&` se escribe `&&` |
|
|
266
|
+
| `<` | `<` | Comparación `<` se escribe `<` |
|
|
267
|
+
| `>` | `>` | Comparación `>` se escribe `>` |
|
|
268
|
+
| `"` | `"` | Solo si el JS está dentro de un atributo XML con delimitador `"` |
|
|
269
|
+
| `'` | `'` | Solo si el JS está dentro de un atributo XML con delimitador `'` |
|
|
270
|
+
|
|
271
|
+
**Ejemplo comparativo — el mismo JS escrito de las dos formas:**
|
|
272
|
+
|
|
273
|
+
(fence sin lenguaje para que las entidades se rendericen literales y se vean como tendrías que teclearlas en el `.xne`)
|
|
274
|
+
|
|
275
|
+
```
|
|
276
|
+
<!-- OPCIÓN A: entidades XML dentro del JS (válido en nodo o atributo) -->
|
|
277
|
+
<before-edit>
|
|
278
|
+
<action name="runscript">
|
|
279
|
+
<script language="javascript">
|
|
280
|
+
if (a > 0 && b < 10) {
|
|
281
|
+
self.MAP_RES = a + b;
|
|
282
|
+
}
|
|
283
|
+
</script>
|
|
284
|
+
</action>
|
|
285
|
+
</before-edit>
|
|
286
|
+
|
|
287
|
+
<!-- OPCIÓN B: envolver en CDATA (solo válido en nodo <script>) -->
|
|
288
|
+
<before-edit>
|
|
289
|
+
<action name="runscript">
|
|
290
|
+
<script language="javascript"><![CDATA[
|
|
291
|
+
if (a > 0 && b < 10) {
|
|
292
|
+
self.MAP_RES = a + b;
|
|
293
|
+
}
|
|
294
|
+
]]></script>
|
|
295
|
+
</action>
|
|
296
|
+
</before-edit>
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
**JS dentro de un atributo XML** (CDATA no aplica — solo entidades):
|
|
300
|
+
|
|
301
|
+
```
|
|
302
|
+
<!-- Atributo onclick (delimitador "): comillas internas con " o usa '.
|
|
303
|
+
onclick es un script JS inline en modo estricto: cada sentencia debe acabar en ';'. -->
|
|
304
|
+
<prop name="MAP_BTN" type="B" title="Buscar"
|
|
305
|
+
onclick="if (self.MAP_TEXTO && self.MAP_TEXTO.length > 0) { hacerBusqueda(self.MAP_TEXTO); };" />
|
|
306
|
+
|
|
307
|
+
<!-- disablevisible con comparaciones también usa entidades -->
|
|
308
|
+
<prop name="MAP_AVISO" type="L"
|
|
309
|
+
disablevisible="MAP_TOTAL >= 100 && MAP_ACTIVO=1" />
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
**Regla de oro:**
|
|
313
|
+
|
|
314
|
+
| Donde vive el JS | Cómo se escribe |
|
|
315
|
+
|-------------------|-----------------|
|
|
316
|
+
| Fichero `.js` separado (`functions.js`, `scripts/*.js`) — **forma preferida para JS no trivial** | JavaScript puro, sin entidades, sin CDATA. |
|
|
317
|
+
| Atributo XML (`onclick=`/`disablevisible=`/…) | Con entidades XML (`&`, `<`, `>`, etc.). CDATA no es válido dentro de atributos. |
|
|
318
|
+
| Nodo `<script>` dentro de un `.xne` (snippets cortos) | Entidades XML o `<![CDATA[…]]>`. Ambas formas funcionan. |
|
|
319
|
+
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
# XOne JavaScript — Objeto ai: LLM generativo en el dispositivo
|
|
2
|
+
|
|
3
|
+
> Fuente: `xone/v2/xone-help-docs/topics/08-objeto-ai.md` §1–§13. Referencia de la skill; el índice está en [../../SKILL.md](../../SKILL.md).
|
|
4
|
+
|
|
5
|
+
Contenido: §1 flujo típico · §2 downloadModel · §3 canLoadModel · §4 getModelInfo · §5 loadModel/unload · §6 generate · §7 chat con streaming · §8 cancelar y reiniciar · §9 herramientas y function calling · §10 loadSkills · §11 formatos de imagen y audio · §12 parámetros recomendados Gemma 4 y decodificación especulativa · §13 buenas prácticas y problemas comunes
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. Flujo típico
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
// 1) Descargar el modelo (una vez; queda guardado en el dispositivo)
|
|
13
|
+
ai.downloadModel({
|
|
14
|
+
repository: "litert-community/gemma-4-E2B-it-litert-lm",
|
|
15
|
+
file: "gemma-4-E2B-it.litertlm",
|
|
16
|
+
onProgress: function (percent) { ui.showToast("Descargando: " + percent + "%"); },
|
|
17
|
+
onComplete: function (file) { cargarModelo(file); },
|
|
18
|
+
onError: function (msg) { ui.msgBox(msg); }
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
function cargarModelo(file) {
|
|
22
|
+
// 2) Cargar en memoria
|
|
23
|
+
ai.loadModel({ path: file, backend: "GPU" });
|
|
24
|
+
// 3) Generar
|
|
25
|
+
var respuesta = ai.generate({ prompt: "Resume en una frase qué es XOne." });
|
|
26
|
+
ui.msgBox(respuesta);
|
|
27
|
+
// 4) Liberar memoria cuando se termine
|
|
28
|
+
ai.unload();
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Todos los ficheros (modelo, imágenes, audio) se referencian por su nombre simple**, no por ruta. El modelo vive en una carpeta interna `models/` que gestiona el framework: `downloadModel` lo coloca ahí y `loadModel`/`getModelInfo` lo encuentran con el mismo nombre. No se aceptan rutas con `/`, `\`, `.` ni `..`.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 2. Descargar un modelo (`downloadModel`)
|
|
37
|
+
|
|
38
|
+
Descarga **asíncrona** de un fichero de modelo desde HuggingFace. No bloquea la pantalla.
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
ai.downloadModel({
|
|
42
|
+
repository: "litert-community/gemma-4-E2B-it-litert-lm", // requerido: <usuario>/<repo>
|
|
43
|
+
file: "gemma-4-E2B-it.litertlm", // requerido: nombre de fichero
|
|
44
|
+
revision: "main", // opcional: rama/tag/commit (def. "main")
|
|
45
|
+
token: "hf_xxx", // opcional: solo para repos protegidos
|
|
46
|
+
resume: true, // opcional: reanudar descarga parcial (def. true)
|
|
47
|
+
onProgress: function (percent) { }, // porcentaje entero 0-100
|
|
48
|
+
onComplete: function (file) { }, // nombre del fichero descargado
|
|
49
|
+
onError: function (msg) { }
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
- **`onProgress`** recibe un porcentaje entero y solo se llama cuando ese porcentaje cambia (no en cada bloque). Si el servidor no informa del tamaño total, no se emite progreso.
|
|
54
|
+
- **`onComplete`** recibe el **nombre del fichero**, listo para pasar tal cual a `loadModel({ path: file })`.
|
|
55
|
+
- **`resume: true`** reanuda una descarga cortada (útil para modelos de varios GB y redes inestables).
|
|
56
|
+
- **`token`**: algunos repos exigen aceptar una licencia y un token de acceso de HuggingFace. Sin él, el servidor responde con error (llega a `onError`). Los repos de la comunidad `litert-community` suelen ser abiertos y no necesitan token.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 3. Comprobar el dispositivo (`canLoadModel`)
|
|
61
|
+
|
|
62
|
+
Indica si el dispositivo tiene recursos para cargar un modelo. Devuelve `true`/`false`.
|
|
63
|
+
|
|
64
|
+
```js
|
|
65
|
+
// Comprobación básica (arquitectura del dispositivo + memoria no crítica)
|
|
66
|
+
if (ai.canLoadModel()) { /* ... */ }
|
|
67
|
+
|
|
68
|
+
// Comprobación con el modelo concreto: además valida que el fichero existe
|
|
69
|
+
// y que hay RAM total suficiente (tamaño del fichero × factor)
|
|
70
|
+
if (ai.canLoadModel({ path: "gemma-4-E2B-it.litertlm", memoryFactor: 1.5 })) {
|
|
71
|
+
ai.loadModel({ path: "gemma-4-E2B-it.litertlm" });
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`memoryFactor` (por defecto `1.5`) se compara contra la RAM **total** del dispositivo, no la libre: si el dispositivo no tiene RAM total suficiente, no podrá cargar el modelo aunque ahora haya algo libre.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## 4. Inspeccionar el modelo (`getModelInfo`)
|
|
80
|
+
|
|
81
|
+
Lee los **parámetros de inferencia** del modelo **sin cargarlo** (es instantáneo, solo lee la cabecera del fichero). Acepta el nombre del fichero o un objeto `{ file }`.
|
|
82
|
+
|
|
83
|
+
```js
|
|
84
|
+
var info = ai.getModelInfo("gemma-4-E2B-it.litertlm");
|
|
85
|
+
// info = {
|
|
86
|
+
// formatVersion: "1.5.0",
|
|
87
|
+
// modelType: "gemma4", // gemma4 | gemma3 | gemma3n | qwen3 | qwen2p5 | fastvlm | generic | unknown
|
|
88
|
+
// fileSize: 2598123456, // bytes
|
|
89
|
+
// supportsVision: true, // admite imágenes
|
|
90
|
+
// supportsAudio: true, // admite audio
|
|
91
|
+
// supportsSpeculativeDecoding: true, // trae acelerador MTP (ver §12)
|
|
92
|
+
// maxNumTokens: 4096 // OPCIONAL: solo si el modelo lo declara
|
|
93
|
+
// }
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Útil para decidir antes de cargar. Por ejemplo, no intentar pasar imágenes a un modelo de solo texto:
|
|
97
|
+
|
|
98
|
+
```js
|
|
99
|
+
var info = ai.getModelInfo(file);
|
|
100
|
+
if (info.supportsVision) {
|
|
101
|
+
ai.generate({ prompt: "¿Qué hay en la foto?", images: ["foto.jpg"] });
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
- Todos los campos están **siempre presentes** salvo `maxNumTokens`, que se omite cuando el modelo no lo declara. Muchos modelos (incluido Gemma 4 E2B) no fijan el tamaño de contexto en la cabecera; en ese caso lo controla el parámetro `maxTokens` de `loadModel`.
|
|
106
|
+
- `supportsSpeculativeDecoding` indica si tiene sentido activar `enableSpeculativeDecoding` al cargar (ver §12).
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## 5. Cargar y descargar de memoria
|
|
111
|
+
|
|
112
|
+
### `loadModel`
|
|
113
|
+
|
|
114
|
+
```js
|
|
115
|
+
ai.loadModel({
|
|
116
|
+
path, // requerido: nombre del fichero (en models/)
|
|
117
|
+
backend: "GPU", // "GPU" (def.) | "CPU"
|
|
118
|
+
visionBackend: "GPU", // requerido para usar imágenes; omitir en modelos de solo texto
|
|
119
|
+
audioBackend: "GPU", // requerido para usar audio; omitir en modelos de solo texto
|
|
120
|
+
maxImages: 1, // nº máximo de imágenes (junto con visionBackend en multimodales)
|
|
121
|
+
maxTokens: 4096, // tamaño de contexto (prompt + respuesta). Ver nota
|
|
122
|
+
topK: 64, // recomendado Gemma 4
|
|
123
|
+
topP: 0.95, // recomendado Gemma 4
|
|
124
|
+
temperature: 1.0, // recomendado Gemma 4 (creatividad; 0 = determinista)
|
|
125
|
+
randomSeed: 0,
|
|
126
|
+
enableSpeculativeDecoding: false, // activa MTP si el modelo lo soporta (ver §12)
|
|
127
|
+
onModelLoaded: function () { }, // opcional: si se pasa, la carga es asíncrona (ver abajo)
|
|
128
|
+
onModelLoadError: function (msg) { } // opcional: OBLIGATORIO junto con onModelLoaded
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
- **`backend`**: `"GPU"` por defecto (más rápido). Si un modelo no arranca o falla en GPU en cierto dispositivo, probar `"CPU"`.
|
|
133
|
+
- **`visionBackend` / `audioBackend`**: son los **activadores** de imagen/audio. Si no se indican, pasar `images`/`audio` a `generate`/`chat` da error. En modelos de solo texto, omitirlos.
|
|
134
|
+
- **`maxTokens`**: es el contexto **total** (entrada + salida). En móvil conviene mantenerlo moderado (p. ej. `4096`); valores muy altos consumen mucha memoria y pueden desestabilizar la carga.
|
|
135
|
+
- **`onModelLoaded` / `onModelLoadError` (carga asíncrona)**: dos callbacks opcionales que van **juntos** — o los dos o ninguno; pasar solo uno da error. Si se pasan, `loadModel` **no bloquea**: vuelve enseguida y carga el modelo en segundo plano, y al terminar llama a `onModelLoaded()` si fue bien o a `onModelLoadError(mensaje)` si falló. Es la forma recomendada de cargar el modelo **al arrancar** sin congelar la pantalla. Sin estos callbacks `loadModel` es **síncrono** (bloquea hasta cargar), por lo que en ese caso debe ejecutarse desde un nodo de acción/script en segundo plano.
|
|
136
|
+
|
|
137
|
+
```js
|
|
138
|
+
// Cargar al arrancar sin bloquear la interfaz (p. ej. si el modelo ya está descargado):
|
|
139
|
+
ai.loadModel({
|
|
140
|
+
path: "gemma-4-E2B-it.litertlm",
|
|
141
|
+
backend: "GPU",
|
|
142
|
+
onModelLoaded: function () { ui.showToast("IA lista."); },
|
|
143
|
+
onModelLoadError: function (msg) { ui.showToast("No se pudo cargar la IA: " + msg); }
|
|
144
|
+
});
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### `unload` / `isLoaded`
|
|
148
|
+
|
|
149
|
+
```js
|
|
150
|
+
ai.isLoaded(); // true si hay un modelo cargado e inicializado
|
|
151
|
+
ai.unload(); // libera el modelo de memoria (libéralo cuando termines: ocupa mucha RAM)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Cargar un modelo es **costoso** (varios segundos y mucha RAM). Cárgalo una vez y reutilízalo; no lo cargues y descargues en cada interacción.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## 6. Generación sin historial (`generate`)
|
|
159
|
+
|
|
160
|
+
Genera una respuesta **de una sola vez**, sin recordar mensajes anteriores. Es **síncrona** (bloquea hasta terminar) y devuelve el texto como `String`.
|
|
161
|
+
|
|
162
|
+
```js
|
|
163
|
+
var texto = ai.generate({
|
|
164
|
+
prompt: "Traduce al inglés: Buenos días",
|
|
165
|
+
system: "Eres un traductor profesional.", // opcional: instrucción de sistema
|
|
166
|
+
images: ["recibo.jpg"], // opcional: array de imágenes
|
|
167
|
+
audio: "nota.wav", // opcional: un audio
|
|
168
|
+
tools: [] // opcional: herramientas (function calling) — ver §9
|
|
169
|
+
});
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
> **No la llames desde el hilo de interfaz** (p. ej. directamente en un `onclick` que bloquee la pantalla): lanza error si detecta que corre en el hilo de UI. Ejecútala desde un nodo de acción/script en segundo plano, o usa `chat` (asíncrono) si quieres no bloquear.
|
|
173
|
+
|
|
174
|
+
Para imágenes hace falta `visionBackend` en `loadModel`; para audio, `audioBackend`.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## 7. Chat multi-turno con streaming (`chat`)
|
|
179
|
+
|
|
180
|
+
Mantiene **historial** entre llamadas y entrega la respuesta **token a token** mediante callbacks (no bloquea la pantalla).
|
|
181
|
+
|
|
182
|
+
```js
|
|
183
|
+
ai.chat({
|
|
184
|
+
prompt: "¿Y cuál es su capital?",
|
|
185
|
+
system: "Responde de forma breve.", // opcional
|
|
186
|
+
images: ["mapa.png"], // opcional
|
|
187
|
+
audio: "pregunta.wav", // opcional
|
|
188
|
+
tools: [], // opcional: herramientas (function calling) — ver §9
|
|
189
|
+
onToken: function (token) { // se llama por cada fragmento de texto
|
|
190
|
+
// acumular y refrescar la vista
|
|
191
|
+
},
|
|
192
|
+
onComplete: function (full) { // texto completo al terminar
|
|
193
|
+
ui.refresh();
|
|
194
|
+
},
|
|
195
|
+
onError: function (msg) {
|
|
196
|
+
ui.msgBox(msg);
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
- La conversación se conserva entre llamadas sucesivas a `chat`. Para empezar de cero, usar `clearChat()`.
|
|
202
|
+
- Si cambias el `system` —o el conjunto de herramientas (`tools`)— entre llamadas, la conversación se reinicia automáticamente.
|
|
203
|
+
- Los callbacks se ejecutan preservando el contexto del objeto activo (`self`), igual que cualquier callback asíncrono del framework — pero si vas a usar `self` dentro, guárdalo en una variable antes de llamar a `chat`.
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## 8. Cancelar y reiniciar
|
|
208
|
+
|
|
209
|
+
```js
|
|
210
|
+
ai.cancel(); // cancela la generación de chat en curso (si la hay)
|
|
211
|
+
ai.clearChat(); // borra el historial de la conversación y la instrucción de sistema
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## 9. Herramientas / function calling (`tools`)
|
|
217
|
+
|
|
218
|
+
Permite que el modelo **invoque funciones JavaScript** cuando lo considere necesario (function calling). Cada herramienta se describe en un fichero JSON (estilo OpenAI) y se asocia a una función JS que recibe los parámetros. Las herramientas se pasan en el parámetro `tools` de `chat` o `generate` — un array de objetos `{ jsonDescriptorPath, callback }`:
|
|
219
|
+
|
|
220
|
+
```js
|
|
221
|
+
// "tools/clima.json" describe la herramienta (nombre, descripción, parámetros)
|
|
222
|
+
ai.chat({
|
|
223
|
+
prompt: "¿Qué tiempo hace en Madrid?",
|
|
224
|
+
tools: [
|
|
225
|
+
{
|
|
226
|
+
jsonDescriptorPath: "tools/clima.json",
|
|
227
|
+
callback: function (params) {
|
|
228
|
+
var ciudad = params.ciudad;
|
|
229
|
+
// ... obtener el dato ...
|
|
230
|
+
return JSON.stringify({ temperatura: 21, ciudad: ciudad });
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
],
|
|
234
|
+
onComplete: function (full) { /* ... */ }
|
|
235
|
+
});
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
La función recibe los parámetros como un mapa `clave → valor` y debe devolver una cadena (normalmente JSON). Las herramientas funcionan tanto en `chat` como en `generate`. En `chat`, si cambias el conjunto de herramientas entre llamadas, la conversación se reinicia automáticamente.
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## 10. Skills automáticas (`loadSkills`)
|
|
243
|
+
|
|
244
|
+
Las **skills** son comportamientos especializados que el modelo **detecta y aplica automáticamente** según el mensaje del usuario. No son function calling: son instrucciones de comportamiento que se inyectan en el contexto. Cada skill es un fichero Markdown.
|
|
245
|
+
|
|
246
|
+
```js
|
|
247
|
+
ai.loadSkills("skills/"); // carga todos los .md de la carpeta (cada fichero = una skill)
|
|
248
|
+
ai.removeSkill("traductor"); // elimina una skill por nombre
|
|
249
|
+
ai.clearSkills(); // elimina todas las skills
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
**Formato del fichero `.md` — encabezado obligatorio:**
|
|
253
|
+
|
|
254
|
+
```
|
|
255
|
+
---
|
|
256
|
+
name: traductor
|
|
257
|
+
description: Traduce texto entre idiomas
|
|
258
|
+
---
|
|
259
|
+
Instrucciones de comportamiento de la skill...
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
- `name` y `description` son obligatorios; el cuerpo (las instrucciones) no puede quedar vacío. Los ficheros que no cumplan el formato se ignoran.
|
|
263
|
+
- Con **2 o más skills**, el modelo elige automáticamente la más adecuada a cada mensaje antes de responder (añade algo de latencia). Con **una sola**, se aplica siempre. Con **ninguna**, comportamiento normal.
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## 11. Multimedia: formatos soportados
|
|
268
|
+
|
|
269
|
+
> **No hay soporte de vídeo.** Las entradas multimedia son **imagen** y **audio**. Si necesitas analizar un vídeo, extrae fotogramas como imágenes.
|
|
270
|
+
|
|
271
|
+
### Imagen
|
|
272
|
+
|
|
273
|
+
| Formato | Soporte |
|
|
274
|
+
|---|---|
|
|
275
|
+
| JPEG / JPG | ✅ |
|
|
276
|
+
| PNG | ✅ |
|
|
277
|
+
| BMP | ✅ |
|
|
278
|
+
| GIF | ✅ (solo primer fotograma) |
|
|
279
|
+
| TGA, HDR, PSD, PNM/PPM/PGM | ✅ |
|
|
280
|
+
| WebP | ❌ |
|
|
281
|
+
| HEIC / AVIF | ❌ |
|
|
282
|
+
|
|
283
|
+
**Recomendado:** JPEG para fotos, PNG para capturas. Atención: **HEIC** es el formato por defecto de muchas cámaras de móvil modernas y **no se admite** — convertir a JPEG/PNG antes.
|
|
284
|
+
|
|
285
|
+
### Audio
|
|
286
|
+
|
|
287
|
+
| Formato | Soporte |
|
|
288
|
+
|---|---|
|
|
289
|
+
| WAV | ✅ |
|
|
290
|
+
| MP3 | ✅ |
|
|
291
|
+
| FLAC | ✅ |
|
|
292
|
+
| Ogg / Opus | ❌ |
|
|
293
|
+
| AAC / M4A | ❌ |
|
|
294
|
+
|
|
295
|
+
**Recomendado:** WAV mono 16 kHz 16-bit. Atención: **AAC/M4A** (grabaciones de voz típicas del móvil) **no se admiten**.
|
|
296
|
+
|
|
297
|
+
> **Para grabar ese WAV desde el micrófono** usa `ui.startAudioRecord({ outputFormat: "wav", timeout: 30, onComplete: ... })`: produce justo WAV PCM 16 bits / 16 kHz / mono y respeta el máximo de 30 s de audio del modelo. **Ojo:** su formato por defecto es `mp4` (AAC) y **no sirve** para la IA; hay que pedir `outputFormat: "wav"` explícitamente. Pasa la ruta del `onComplete` directamente como `audio`. Ver `topics/03b-js-ui.md` §3.10.
|
|
298
|
+
|
|
299
|
+
### Validar la extensión antes de enviar
|
|
300
|
+
|
|
301
|
+
```js
|
|
302
|
+
function getMediaType(fileName) {
|
|
303
|
+
if (!fileName) { return null; }
|
|
304
|
+
var nDot = fileName.lastIndexOf(".");
|
|
305
|
+
if (nDot < 0) { return null; }
|
|
306
|
+
switch (fileName.substring(nDot + 1).toLowerCase()) {
|
|
307
|
+
case "jpg": case "jpeg": case "png": case "bmp":
|
|
308
|
+
case "gif": case "tga": case "hdr": case "psd":
|
|
309
|
+
case "pnm": case "ppm": case "pgm":
|
|
310
|
+
return "image";
|
|
311
|
+
case "wav": case "mp3": case "flac":
|
|
312
|
+
return "audio";
|
|
313
|
+
default:
|
|
314
|
+
return null; // webp, heic, m4a, aac, vídeo, etc.
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
## 12. Parámetros recomendados (Gemma 4)
|
|
322
|
+
|
|
323
|
+
Google publica una configuración de muestreo estándar para Gemma 4, que son **los valores por defecto** del objeto `ai`:
|
|
324
|
+
|
|
325
|
+
| Parámetro | Gemma 4 (por defecto) |
|
|
326
|
+
|---|---|
|
|
327
|
+
| `temperature` | `1.0` |
|
|
328
|
+
| `topP` | `0.95` |
|
|
329
|
+
| `topK` | `64` |
|
|
330
|
+
|
|
331
|
+
**MTP (Multi-Token Prediction / decodificación especulativa):** algunos modelos Gemma 4 traen un "acelerador" que puede **duplicar la velocidad** de generación sin perder calidad. Se activa con `enableSpeculativeDecoding: true` en `loadModel`, y solo tiene efecto si:
|
|
332
|
+
|
|
333
|
+
- El modelo lo soporta — compruébalo con `getModelInfo(file).supportsSpeculativeDecoding`.
|
|
334
|
+
- Se usa `backend: "GPU"` (es donde está validado).
|
|
335
|
+
|
|
336
|
+
```js
|
|
337
|
+
var info = ai.getModelInfo(file);
|
|
338
|
+
ai.loadModel({
|
|
339
|
+
path: file,
|
|
340
|
+
backend: "GPU",
|
|
341
|
+
enableSpeculativeDecoding: info.supportsSpeculativeDecoding
|
|
342
|
+
});
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
## 13. Buenas prácticas y problemas comunes
|
|
348
|
+
|
|
349
|
+
| Situación | Recomendación |
|
|
350
|
+
|---|---|
|
|
351
|
+
| `generate` lanza error de "hilo de UI" | No la llames en un `onclick` que bloquee; usa un nodo de acción en segundo plano o `chat` (asíncrono). |
|
|
352
|
+
| El modelo tarda mucho o no arranca en GPU | Probar `backend: "CPU"`. La primera carga siempre es lenta (compila en el dispositivo). |
|
|
353
|
+
| Falta de memoria al cargar | Bajar `maxTokens`; comprobar antes con `canLoadModel({ path, memoryFactor })`; llamar a `unload()` cuando termines. |
|
|
354
|
+
| Pasar imagen/audio da error | Falta `visionBackend`/`audioBackend` en `loadModel`, o el modelo no es multimodal (`getModelInfo`). |
|
|
355
|
+
| Imagen/audio no reconocidos | Formato no soportado (HEIC, M4A, WebP, vídeo). Convertir a JPEG/PNG o WAV. |
|
|
356
|
+
| Recargas el mismo modelo una y otra vez | Cárgalo una vez y reutilízalo; cargar es costoso en tiempo y RAM. |
|
|
357
|
+
| `maxNumTokens` no aparece en `getModelInfo` | Normal: el modelo no lo declara. El contexto lo fija tú con `maxTokens` en `loadModel`. |
|
|
358
|
+
|