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.
Files changed (187) hide show
  1. package/README.md +167 -0
  2. package/dist/agent/authEnDisco.js +56 -0
  3. package/dist/agent/authEnDisco.js.map +1 -0
  4. package/dist/agent/configEnDisco.js +109 -0
  5. package/dist/agent/configEnDisco.js.map +1 -0
  6. package/dist/agent/crearProyecto.js +34 -0
  7. package/dist/agent/crearProyecto.js.map +1 -0
  8. package/dist/agent/entorno.js +124 -0
  9. package/dist/agent/entorno.js.map +1 -0
  10. package/dist/agent/guionizado.js +53 -0
  11. package/dist/agent/guionizado.js.map +1 -0
  12. package/dist/agent/instantanea.js +129 -0
  13. package/dist/agent/instantanea.js.map +1 -0
  14. package/dist/agent/interrupts.js +83 -0
  15. package/dist/agent/interrupts.js.map +1 -0
  16. package/dist/agent/mensajes.js +24 -0
  17. package/dist/agent/mensajes.js.map +1 -0
  18. package/dist/agent/modelos.js +42 -0
  19. package/dist/agent/modelos.js.map +1 -0
  20. package/dist/agent/normalizar.js +20 -0
  21. package/dist/agent/normalizar.js.map +1 -0
  22. package/dist/agent/perfiles.js +103 -0
  23. package/dist/agent/perfiles.js.map +1 -0
  24. package/dist/agent/proyecto.js +107 -0
  25. package/dist/agent/proyecto.js.map +1 -0
  26. package/dist/agent/puente.js +142 -0
  27. package/dist/agent/puente.js.map +1 -0
  28. package/dist/agent/resumenDeTool.js +48 -0
  29. package/dist/agent/resumenDeTool.js.map +1 -0
  30. package/dist/agent/skills.js +65 -0
  31. package/dist/agent/skills.js.map +1 -0
  32. package/dist/agent/textoDeTool.js +113 -0
  33. package/dist/agent/textoDeTool.js.map +1 -0
  34. package/dist/agent/turnoReal.js +173 -0
  35. package/dist/agent/turnoReal.js.map +1 -0
  36. package/dist/agent/verificador.js +65 -0
  37. package/dist/agent/verificador.js.map +1 -0
  38. package/dist/agent/xoneAgent.js +106 -0
  39. package/dist/agent/xoneAgent.js.map +1 -0
  40. package/dist/bin.js +9 -0
  41. package/dist/bin.js.map +1 -0
  42. package/dist/cli/aprobar.js +82 -0
  43. package/dist/cli/aprobar.js.map +1 -0
  44. package/dist/cli/config.js +133 -0
  45. package/dist/cli/config.js.map +1 -0
  46. package/dist/cli/consola.js +346 -0
  47. package/dist/cli/consola.js.map +1 -0
  48. package/dist/cli/describe.js +53 -0
  49. package/dist/cli/describe.js.map +1 -0
  50. package/dist/cli/doctor.js +45 -0
  51. package/dist/cli/doctor.js.map +1 -0
  52. package/dist/cli/main.js +520 -0
  53. package/dist/cli/main.js.map +1 -0
  54. package/dist/cli/markdown.js +90 -0
  55. package/dist/cli/markdown.js.map +1 -0
  56. package/dist/cli/run.js +164 -0
  57. package/dist/cli/run.js.map +1 -0
  58. package/dist/cli/spinner.js +89 -0
  59. package/dist/cli/spinner.js.map +1 -0
  60. package/dist/cli/stdio.js +193 -0
  61. package/dist/cli/stdio.js.map +1 -0
  62. package/dist/cli/tema.js +40 -0
  63. package/dist/cli/tema.js.map +1 -0
  64. package/dist/cli/verify.js +62 -0
  65. package/dist/cli/verify.js.map +1 -0
  66. package/dist/core/bitacora.js +30 -0
  67. package/dist/core/bitacora.js.map +1 -0
  68. package/dist/core/config.js +236 -0
  69. package/dist/core/config.js.map +1 -0
  70. package/dist/core/contextos.js +56 -0
  71. package/dist/core/contextos.js.map +1 -0
  72. package/dist/core/deps.js +51 -0
  73. package/dist/core/deps.js.map +1 -0
  74. package/dist/core/diff.js +79 -0
  75. package/dist/core/diff.js.map +1 -0
  76. package/dist/core/esqueleto.js +366 -0
  77. package/dist/core/esqueleto.js.map +1 -0
  78. package/dist/core/events.js +2 -0
  79. package/dist/core/events.js.map +1 -0
  80. package/dist/core/modelos.js +85 -0
  81. package/dist/core/modelos.js.map +1 -0
  82. package/dist/core/notify.js +106 -0
  83. package/dist/core/notify.js.map +1 -0
  84. package/dist/core/ports.js +114 -0
  85. package/dist/core/ports.js.map +1 -0
  86. package/dist/core/turno.js +127 -0
  87. package/dist/core/turno.js.map +1 -0
  88. package/dist/vendor/hitl.js +103 -0
  89. package/dist/vendor/hitl.js.map +1 -0
  90. package/dist/vendor/skillLoaders/catalog.js +74 -0
  91. package/dist/vendor/skillLoaders/catalog.js.map +1 -0
  92. package/dist/vendor/tokenTracking.js +58 -0
  93. package/dist/vendor/tokenTracking.js.map +1 -0
  94. package/package.json +41 -0
  95. package/skills/xone-debugging/SKILL.md +52 -0
  96. package/skills/xone-debugging/references/faq.md +561 -0
  97. package/skills/xone-debugging/references/troubleshooting-y-glosario.md +429 -0
  98. package/skills/xone-development/SKILL.md +105 -0
  99. package/skills/xone-development/references/anti-patrones.md +94 -0
  100. package/skills/xone-development/references/css/atributos-por-categoria.md +509 -0
  101. package/skills/xone-development/references/css/buenas-practicas-y-parser.md +419 -0
  102. package/skills/xone-development/references/css/dinamicos-cascada-y-componentes.md +550 -0
  103. package/skills/xone-development/references/css/patrones-material-y-temas.md +1140 -0
  104. package/skills/xone-development/references/css/propiedades-y-herencia.md +885 -0
  105. package/skills/xone-development/references/css/selectores-unidades-colores.md +840 -0
  106. package/skills/xone-development/references/datos/appdata-referencia-ampliada.md +457 -0
  107. package/skills/xone-development/references/datos/appdata.md +611 -0
  108. package/skills/xone-development/references/datos/http-sqlmanager-y-crypto.md +720 -0
  109. package/skills/xone-development/references/datos/http.md +366 -0
  110. package/skills/xone-development/references/datos/oauth2-y-replica.md +156 -0
  111. package/skills/xone-development/references/device/biometria-imagedrawing-y-otros.md +544 -0
  112. package/skills/xone-development/references/device/objetos-de-dispositivo.md +583 -0
  113. package/skills/xone-development/references/device/systemsettings-referencia-ampliada.md +396 -0
  114. package/skills/xone-development/references/device/systemsettings-y-permisos.md +342 -0
  115. package/skills/xone-development/references/fundamentos/conceptos-clave.md +695 -0
  116. package/skills/xone-development/references/fundamentos/configuracion-app-xml-ini-mappings.md +668 -0
  117. package/skills/xone-development/references/fundamentos/errores-comunes.md +312 -0
  118. package/skills/xone-development/references/fundamentos/navegacion-convenciones-y-primer-proyecto.md +576 -0
  119. package/skills/xone-development/references/fundamentos/plataforma-y-anatomia-de-proyecto.md +387 -0
  120. package/skills/xone-development/references/indice-completo.md +77 -0
  121. package/skills/xone-development/references/javascript/coleccion-error-y-usuario.md +260 -0
  122. package/skills/xone-development/references/javascript/debugging-y-best-practices.md +245 -0
  123. package/skills/xone-development/references/javascript/metodos-de-los-controles.md +556 -0
  124. package/skills/xone-development/references/javascript/metodos-nativos-de-la-vista.md +114 -0
  125. package/skills/xone-development/references/javascript/motor-js-y-contexto-de-ejecucion.md +319 -0
  126. package/skills/xone-development/references/javascript/objeto-ai-llm-en-dispositivo.md +358 -0
  127. package/skills/xone-development/references/javascript/objetos-creables-a-m.md +527 -0
  128. package/skills/xone-development/references/javascript/objetos-creables-n-z.md +385 -0
  129. package/skills/xone-development/references/javascript/patrones-criticos-seguridad-y-rendimiento.md +595 -0
  130. package/skills/xone-development/references/javascript/patrones-de-navegacion-datos-y-codigo.md +751 -0
  131. package/skills/xone-development/references/javascript/patrones-de-ui-voz-integracion-y-seguridad.md +701 -0
  132. package/skills/xone-development/references/javascript/plantillas-y-funciones-utilitarias.md +647 -0
  133. package/skills/xone-development/references/javascript/self-y-dataobject.md +449 -0
  134. package/skills/xone-development/references/javascript/singletons-globales.md +361 -0
  135. package/skills/xone-development/references/javascript/ui-catalogo-de-metodos.md +464 -0
  136. package/skills/xone-development/references/javascript/ui-gps-camara-y-multimedia.md +506 -0
  137. package/skills/xone-development/references/javascript/ui-navegacion-mensajes-y-vista.md +422 -0
  138. package/skills/xone-development/references/resumen-js-datos-dispositivo.md +80 -0
  139. package/skills/xone-development/references/resumen-xml-y-css.md +74 -0
  140. package/skills/xone-development/references/tipos-de-prop.md +28 -0
  141. package/skills/xone-development/references/xml-ui/asfilter-visibilidad-eventos-y-macros.md +576 -0
  142. package/skills/xone-development/references/xml-ui/atributos-coll-group-frame.md +235 -0
  143. package/skills/xone-development/references/xml-ui/atributos-method-macro-script-event-app.md +244 -0
  144. package/skills/xone-development/references/xml-ui/atributos-prop.md +439 -0
  145. package/skills/xone-development/references/xml-ui/contents-y-macros.md +392 -0
  146. package/skills/xone-development/references/xml-ui/errores-comunes-xml.md +245 -0
  147. package/skills/xone-development/references/xml-ui/estructura-y-nodo-coll.md +326 -0
  148. package/skills/xone-development/references/xml-ui/eventos-ciclo-de-vida-e-interaccion.md +704 -0
  149. package/skills/xone-development/references/xml-ui/eventos-sistema-login-y-personalizados.md +944 -0
  150. package/skills/xone-development/references/xml-ui/layouts-herencia-y-buenas-practicas.md +618 -0
  151. package/skills/xone-development/references/xml-ui/mapas.md +685 -0
  152. package/skills/xone-development/references/xml-ui/mappings-y-colecciones-separadas.md +148 -0
  153. package/skills/xone-development/references/xml-ui/nodos-group-y-frame.md +666 -0
  154. package/skills/xone-development/references/xml-ui/patrones-de-pantalla.md +643 -0
  155. package/skills/xone-development/references/xml-ui/prop-atributos-y-condiciones.md +298 -0
  156. package/skills/xone-development/references/xml-ui/prop-tipos-basicos.md +493 -0
  157. package/skills/xone-development/references/xml-ui/prop-tipos-combos-y-controles.md +664 -0
  158. package/skills/xone-development/references/xml-ui/prop-tipos-listas-y-mapas.md +412 -0
  159. package/skills/xone-plan-builder/SKILL.md +183 -0
  160. package/skills/xone-plan-builder/agents/openai.yaml +5 -0
  161. package/skills/xone-plan-builder/references/TASKS-FORMAT.md +132 -0
  162. package/skills/xone-project-generator/SKILL.md +111 -0
  163. package/skills/xone-project-generator/references/canonical-sizes.md +292 -0
  164. package/skills/xone-project-generator/references/colecciones-base.md +16 -0
  165. package/skills/xone-project-generator/references/convenciones-de-nombres.md +16 -0
  166. package/skills/xone-project-generator/references/ejemplos-por-sector-y-prohibiciones.md +140 -0
  167. package/skills/xone-project-generator/references/fase-3-estilos-css.md +597 -0
  168. package/skills/xone-project-generator/references/fase-6-colecciones.md +762 -0
  169. package/skills/xone-project-generator/references/fase-7-asfilter-e-integraciones.md +142 -0
  170. package/skills/xone-project-generator/references/fase-7-entidades-y-estructura-de-pantalla.md +494 -0
  171. package/skills/xone-project-generator/references/fase-7-plantilla-consola.md +477 -0
  172. package/skills/xone-project-generator/references/fase-7-plantillas-de-pantalla.md +302 -0
  173. package/skills/xone-project-generator/references/fase-7-viewmodes-graficos-y-listas.md +681 -0
  174. package/skills/xone-project-generator/references/fase-7-viewmodes-mapa-y-calendario.md +479 -0
  175. package/skills/xone-project-generator/references/fases-0-2-analisis-y-modelo-de-datos.md +350 -0
  176. package/skills/xone-project-generator/references/fases-10-12-readmes-y-validacion.md +194 -0
  177. package/skills/xone-project-generator/references/fases-4-5-estructura-y-configuracion.md +587 -0
  178. package/skills/xone-project-generator/references/fases-8-9-eventos-y-javascript.md +861 -0
  179. package/skills/xone-project-generator/references/flujo-de-generacion.md +67 -0
  180. package/skills/xone-project-generator/references/plantillas-estandar.md +98 -0
  181. package/skills/xone-project-generator/references/tamanos-canonicos.md +49 -0
  182. package/skills/xone-review/SKILL.md +189 -0
  183. package/skills/xone-spec-builder/SKILL.md +235 -0
  184. package/skills/xone-spec-builder/agents/openai.yaml +5 -0
  185. package/skills/xone-spec-builder/references/ADR-FORMAT.md +55 -0
  186. package/skills/xone-spec-builder/references/CONTEXT-FORMAT.md +42 -0
  187. package/skills/xone-spec-builder/references/PLAN-FORMAT.md +120 -0
@@ -0,0 +1,701 @@
1
+ # XOne — Patrones de UI, voz, integración y seguridad
2
+
3
+ > Fuente: `xone/v2/xone-help-docs/topics/05-events-patterns-faq.md` §11–§13. Referencia de la skill; el índice está en [../../SKILL.md](../../SKILL.md).
4
+
5
+ Contenido: §11 patrones de UI · §11A control por voz (TTS + STT) · §12 patrones de integración · §13 patrones de seguridad
6
+
7
+ ---
8
+
9
+ ## 11. Patrones de UI
10
+
11
+ ### 11.1 Formulario con validación
12
+
13
+ ```javascript
14
+ function validarFormulario() {
15
+ if (!self.MAP_NOMBRE || self.MAP_NOMBRE.length == 0) {
16
+ ui.showToast("El nombre es obligatorio");
17
+ return false;
18
+ }
19
+ if (!self.MAP_EMAIL || self.MAP_EMAIL.length == 0) {
20
+ ui.showToast("El email es obligatorio");
21
+ return false;
22
+ }
23
+ return true;
24
+ }
25
+
26
+ function guardarFormulario() {
27
+ if (!validarFormulario()) return;
28
+ self.save();
29
+ ui.showToast("Guardado correctamente");
30
+ ui.getView(self).exit();
31
+ }
32
+ ```
33
+
34
+ ### 11.2 Tarjeta de información (Card)
35
+
36
+ ```xml
37
+ <frame name="frmCard" width="96%" height="auto" lmargin="2%"
38
+ tmargin="10p" bgcolor="#FFFFFF" border-corner-radius="12"
39
+ framebox="true" forecolor="#E0E0E0" border-width="1">
40
+ <frame name="frmCardHeader" width="100%" height="48p" bgcolor="#F5F5F5">
41
+ <prop name="lblCardTitulo" type="L" visible="7"
42
+ width="100%" height="48p" lmargin="16p"
43
+ fontsize="16" fontbold="true" forecolor="#212121"
44
+ title="Título de la Tarjeta"/>
45
+ </frame>
46
+ <prop name="lblCardContenido" type="L" visible="7"
47
+ width="96%" lmargin="2%" tmargin="12p" bmargin="12p"
48
+ forecolor="#757575" fontsize="14" label-wrap="true"
49
+ title="Contenido de la tarjeta con descripción detallada"/>
50
+ </frame>
51
+ ```
52
+
53
+ ### 11.3 Dashboard con estadisticas
54
+
55
+ ```xml
56
+ <frame name="frmDashboard" width="100%" scroll="true">
57
+ <!-- Fila de estadisticas -->
58
+ <frame name="frmStats" width="96%" lmargin="2%" tmargin="10p">
59
+ <frame name="frmStat1" width="31%" height="100p"
60
+ bgcolor="#E3F2FD" border-corner-radius="8">
61
+ <prop name="lblStatNum1" type="L" visible="7"
62
+ width="100%" align="center" tmargin="15p"
63
+ fontsize="24" fontbold="true" forecolor="#1565C0"
64
+ title="##FLD_MAP_TOTAL_PENDIENTES##"/>
65
+ <prop name="lblStatLabel1" type="L" visible="7"
66
+ width="100%" align="center"
67
+ fontsize="12" forecolor="#1565C0"
68
+ title="Pendientes"/>
69
+ </frame>
70
+ <frame name="frmStat2" width="31%" height="100p"
71
+ lmargin="3%" newline="false"
72
+ bgcolor="#E8F5E9" border-corner-radius="8">
73
+ <prop name="lblStatNum2" type="L" visible="7"
74
+ width="100%" align="center" tmargin="15p"
75
+ fontsize="24" fontbold="true" forecolor="#2E7D32"
76
+ title="##FLD_MAP_TOTAL_COMPLETADOS##"/>
77
+ <prop name="lblStatLabel2" type="L" visible="7"
78
+ width="100%" align="center"
79
+ fontsize="12" forecolor="#2E7D32"
80
+ title="Completados"/>
81
+ </frame>
82
+ </frame>
83
+ </frame>
84
+ ```
85
+
86
+ ### 11.4 Item de lista con icono + textos
87
+
88
+ ```xml
89
+ <!-- Dentro de la coleccion del content (visible="4" para modo lista) -->
90
+ <group name="General" id="1">
91
+ <prop name="IMAGEN" type="IMG" width="115p" height="118p"
92
+ visible="4" tmargin="2p" lmargin="0"/>
93
+ <frame name="frm1" newline="false" width="600p" lmargin="5p" height="120p">
94
+ <prop name="MAP_NOMBRE_GRID" type="T" class="classgrid"/>
95
+ <prop name="MAP_DIRECCION_GRID" class="classgrid" type="T"
96
+ text-forecolor="#666666" textfont-size="5"
97
+ lines="2" fixed-lines="true"/>
98
+ </frame>
99
+ </group>
100
+ ```
101
+
102
+ ### 11.5 Botón flotante (FAB)
103
+
104
+ ```xml
105
+ <frame name="floatadd1" top="920p" left="510p"
106
+ width="290p" height="90p" floating="true">
107
+ <prop name="BTADD1" type="B" visible="1" labelwidth="0"
108
+ method="ExecuteNode(nuevo)" width="75p"
109
+ img="add.png" imgsel="add_click.png"/>
110
+ </frame>
111
+ ```
112
+
113
+ ### 11.6 Modal/Overlay
114
+
115
+ ```xml
116
+ <frame name="frmnuevochat"
117
+ animation-in-delay="250" animation-out-delay="250"
118
+ animation-in="##RIGHT_IN##" animation-out="##LEFT_OUT##"
119
+ disablevisible="MAP_VERFLOTANTE=0"
120
+ bgcolor="#ffffff" modal="true" floating="true"
121
+ top="0" left="0" width="100%" height="100%">
122
+ <!-- Contenido del modal -->
123
+ <prop name="MAP_BUSCAR_USUARIO"
124
+ ontextchanged="javascript:AccionesChatEspecial('textoU', e);"
125
+ type="T" visible="1"/>
126
+ <prop name="@nUsuarios" contents="nUsuarios" viewmode="recyclerview"
127
+ type="Z" visible="1" width="100%" height="1061p"/>
128
+ <contents name="nUsuarios" src="UsuariosChat"/>
129
+ </frame>
130
+ ```
131
+
132
+ ### 11.7 Barra de busqueda
133
+
134
+ ```xml
135
+ <frame name="frmBuscador" width="98%" lmargin="1%" height="150p"
136
+ tmargin="1%" framebox="true">
137
+ <prop name="MAP_BUSCAR_TEXT"
138
+ ontextchanged="javascript:FiltraMarcados(e);"
139
+ labelwidth="0" text-border="true" type="T"
140
+ lpadding="10p" rpadding="10p"
141
+ width="98%" tmargin="10p" lmargin="1%" height="60p"
142
+ tooltip="Texto a buscar"/>
143
+ </frame>
144
+ ```
145
+
146
+ ### 11.8 Badge de notificación
147
+
148
+ ```xml
149
+ <!-- Usar un L con fondo circular como badge -->
150
+ <frame name="frmBadge" floating="true" top="10p" left="80p"
151
+ width="24p" height="24p" disablevisible="MAP_NOTIF_COUNT=0">
152
+ <prop name="lblBadge" type="L" visible="7"
153
+ width="24p" height="24p" align="center"
154
+ bgcolor="#FF0000" forecolor="#FFFFFF" fontsize="10"
155
+ border-corner-radius="12"
156
+ title="##FLD_MAP_NOTIF_COUNT##"/>
157
+ </frame>
158
+ ```
159
+
160
+ ### 11.9 Slider de imágenes
161
+
162
+ ```xml
163
+ <prop name="ContentsDatosSlide" viewmode="slideview"
164
+ autoslide-delay="5" type="Z"
165
+ width="100%" lmargin="0" height="1160p"
166
+ contents="ContentsDatosSlide"
167
+ onchange="refresh255" forceonchange="true"/>
168
+ <contents name="ContentsDatosSlide" src="ContentsDatosSlide"/>
169
+ ```
170
+
171
+ ### 11.10 Vista de chat (burbujas)
172
+
173
+ **Ejemplo real** (del proyecto EspecialChat):
174
+
175
+ ```xml
176
+ <frame name="frmChatear" tmargin="0" width="100%" height="937p"
177
+ imgbk="##FLD_MAP_FOTO_FONDO##">
178
+ <frame name="frmContent" tmargin="0" width="100%" height="937p">
179
+ <prop name="Chatear" bgcolor="#00000000" contents="Chatear"
180
+ edit-inrow="true" type="Z" width="100%" height="100%"/>
181
+ <contents name="Chatear" src="Chatear"
182
+ filter="IDCHAT=##FLD_MAP_CHATSEL##"/>
183
+ </frame>
184
+ </frame>
185
+
186
+ <!-- Barra inferior con input y botones -->
187
+ <frame name="frmMenuW" bgcolor="#ffffff" align="center"
188
+ width="100%" height="100p">
189
+ <prop name="MAP_ADDOTHER" img="icon_more.png" type="B"
190
+ width="100p" height="100p"/>
191
+ <prop name="MAP_TITLE"
192
+ onfocuschanged="javascript:AccionesChatEspecial('foco', e);"
193
+ ontextchanged="javascript:AccionesChatEspecial('textoChange', e);"
194
+ type="T" visible="1" newline="false"
195
+ width="424p" height="80p" tmargin="10p"/>
196
+ <prop name="MAP_ADDTEXT"
197
+ method="executenode(AccionesChatEspecial('enviar'))"
198
+ img="icon_send.png" type="B" width="100p" height="100p"
199
+ newline="false" disablevisible="MAP_SHOWADDTEXT=0"/>
200
+ </frame>
201
+ ```
202
+
203
+ ---
204
+
205
+ ## 11A. Patron Control por Voz (TTS + STT)
206
+
207
+ XOne soporta síntesis de voz (Text-to-Speech) y reconocimiento de voz (Speech-to-Text) nativamente a traves del objeto global `ui`, combinando dos métodos:
208
+
209
+ - `ui.speak({...})` — el dispositivo "habla" un texto.
210
+ - `ui.recognizeSpeech({...})` — el dispositivo "escucha" y te entrega el texto reconocido.
211
+
212
+ El patron más potente es **encadenar ambos**: hablar primero (pregunta al usuario) y, cuando termine la síntesis, arrancar la escucha (respuesta del usuario). Así se evita que el reconocedor capte la propia voz sintetizada.
213
+
214
+ ### 11A.1 Anatomia de los parámetros
215
+
216
+ **`ui.speak(params)`**
217
+
218
+ | Parámetro | Descripción |
219
+ | --- | --- |
220
+ | `language` | Idioma: `"es"`, `"en"`, ... |
221
+ | `text` | Texto que se va a pronunciar. |
222
+ | `speechRate` | Ritmo de habla en milisegundos. |
223
+ | `onCompleted` | Callback `function()` al terminar de hablar. |
224
+
225
+ **`ui.recognizeSpeech(params)`**
226
+
227
+ | Parámetro | Descripción |
228
+ | --- | --- |
229
+ | `language` | Idioma del reconocedor. |
230
+ | `timeoutAfterSilence` | Milisegundos de silencio antes de cerrar la escucha. |
231
+ | `characterLimit` | *(Opcional)* Número máximo de caracteres a reconocer. |
232
+ | `onRecognize` | `function(sText)` con el texto reconocido. |
233
+ | `onError` | `function(nErrorCode, sError)` si hubo error. |
234
+ | `onPartialResults` | *(Opcional)* `function(extras)` con resultados parciales. |
235
+ | `onEndOfSpeech` | *(Opcional)* `function()` al terminar la locucion del usuario. |
236
+
237
+ ### 11A.2 Patron "preguntar y escuchar" (flujo completo)
238
+
239
+ ```xml
240
+ <!-- Botón que lanza la interacción por voz -->
241
+ <prop name="MAP_BT_VOZ" type="B" visible="1" title="Preguntar por voz"
242
+ onclick="doSpeakYRecoger('es', '¿Que opción quieres?', self, null);" />
243
+
244
+ <!-- Icono del micrófono (cambia según el estado) -->
245
+ <prop name="MAP_IMGLISTENING" type="IMG" visible="1" width="64p" height="64p"/>
246
+ ```
247
+
248
+ ```javascript
249
+ // Funcion 1: habla la pregunta y encadena la escucha cuando termina el TTS
250
+ function doSpeakYRecoger(sLanguage, strText, objSource, objAR) {
251
+ ui.speak({
252
+ language : sLanguage,
253
+ text : strText,
254
+ speechRate : 120,
255
+ onCompleted: function() {
256
+ // Cuando el dispositivo ha terminado de hablar, arrancamos el reconocedor.
257
+ // Así el microfono no capta la propia voz sintetizada.
258
+ objSource.MAP_IMGLISTENING = "microRojo.png"; // indicar "escuchando"
259
+ ui.refresh("MAP_IMGLISTENING");
260
+ doRecognize(sLanguage, objSource, objAR);
261
+ }
262
+ });
263
+ }
264
+
265
+ // Funcion 2: escucha, procesa, actualiza UI
266
+ function doRecognize(sLanguage, objSource, objAR) {
267
+ ui.recognizeSpeech({
268
+ language: sLanguage,
269
+ timeoutAfterSilence: 10000,
270
+
271
+ onRecognize: function(sText) {
272
+ sText = (sText || "").toUpperCase();
273
+
274
+ // Caso A: comparar contra opciones predefinidas
275
+ if (objSource.MAP_INITAR == 1 && objAR != null) {
276
+ let idx = 100;
277
+ if (objAR.MAP_TITLE0.toUpperCase() == sText) idx = 0;
278
+ else if (objAR.MAP_TITLE1.toUpperCase() == sText) idx = 1;
279
+ else if (objAR.MAP_TITLE2.toUpperCase() == sText) idx = 2;
280
+ // ...aquí harias algo con idx...
281
+ } else {
282
+ // Caso B: volcar el texto reconocido en una propiedad
283
+ objSource.MAP_TEXT = sText;
284
+ ui.refreshValue("MAP_TEXT");
285
+ }
286
+ },
287
+
288
+ onError: function(nErrorCode, sError) {
289
+ objSource.MAP_IMGLISTENING = "microGris.png"; // restaurar icono
290
+ ui.refresh("MAP_IMGLISTENING");
291
+ // ui.msgBox("Error " + nErrorCode + ": " + sError, "Voz", 0);
292
+ },
293
+
294
+ onEndOfSpeech: function() {
295
+ // Restaurar icono al terminar la locucion (aunque el reconocedor
296
+ // aún este procesando). onRecognize se llama después.
297
+ objSource.MAP_IMGLISTENING = "microGris.png";
298
+ ui.refresh("MAP_IMGLISTENING");
299
+ }
300
+ });
301
+ }
302
+ ```
303
+
304
+ ### 11A.3 Solo escuchar (sin TTS previo)
305
+
306
+ Si solo quieres dictado, se puede usar `recognizeSpeech` directamente, sin `speak`:
307
+
308
+ ```javascript
309
+ function dictarNota() {
310
+ ui.recognizeSpeech({
311
+ language: "es",
312
+ timeoutAfterSilence: 10000,
313
+ onRecognize: function(sText) {
314
+ self.MAP_NOTA = sText;
315
+ ui.refreshValue("MAP_NOTA");
316
+ },
317
+ onError: function(nErrorCode, sError) {
318
+ ui.showToast("Error de voz: " + sError);
319
+ }
320
+ });
321
+ }
322
+ ```
323
+
324
+ ### 11A.4 Buenas prácticas
325
+
326
+ - **Siempre** lanzar `recognizeSpeech` desde `onCompleted` de `speak`, nunca antes — evita que el reconocedor oiga la síntesis.
327
+ - Gestionar el icono del microfono con una propiedad tipo `MAP_IMGLISTENING` y refrescarla en cada transición de estado (hablando / escuchando / inactivo).
328
+ - Para comparar lo dictado con opciones predefinidas, normalizar siempre con `.toUpperCase()` (o `.toLowerCase()`) antes de comparar.
329
+ - `timeoutAfterSilence` en ms — valores típicos 5000-10000. Más bajo corta frases largas; más alto introduce latencia.
330
+ - El `onError` **siempre** debe restaurar el estado visual del microfono.
331
+
332
+ ### 11A.5 Referencias
333
+
334
+ - Doc del wiki: `2.-desarrollo-app/2.5.-controles-by-xone/control_por_voz/start.md`
335
+ - Referencia de los métodos (objetos complementarios): `ui.speak` y `ui.recognizeSpeech` en la documentación del objeto global `ui`.
336
+
337
+ ---
338
+
339
+ ## 12. Patrones de Integración
340
+
341
+ ### 12.1 Conexión con API REST
342
+
343
+ ```javascript
344
+ function consultarAPI(endpoint) {
345
+ var miObjeto = self; // Guardar contexto
346
+ ui.showWaitDialog("Consultando...");
347
+
348
+ var request = {
349
+ parameters: {
350
+ connectTimeout: 120000,
351
+ readTimeout: 120000
352
+ },
353
+ headers: {
354
+ "Authorization": "Bearer " + appData.getGlobalMacro("##TOKEN##"),
355
+ "Accept": "application/json"
356
+ }
357
+ };
358
+
359
+ $http.get("https://api.ejemplo.com/" + endpoint, request,
360
+ function(sData, headers, nHttpStatusCode) {
361
+ var json = JSON.parse(sData);
362
+ miObjeto.MAP_RESULTADO = json.resultado;
363
+ ui.refresh("MAP_RESULTADO");
364
+ ui.hideWaitDialog();
365
+ },
366
+ function(nError, sErrorDesc) {
367
+ ui.showToast("Error " + nError + ": " + sErrorDesc);
368
+ ui.hideWaitDialog();
369
+ }
370
+ );
371
+ }
372
+ ```
373
+
374
+ > **Referencia cruzada:** Para la API HTTP completa (`$http`), consultar el tópico 03 sobre la API JavaScript.
375
+
376
+ ### 12.2 GPS y seguimiento en tiempo real
377
+
378
+ **Iniciar GPS (ejemplo real de EspecialMapa):**
379
+
380
+ ```javascript
381
+ var ok = ui.msgBox("Desea acceder al Geoposicionamiento?", "Aviso", 4);
382
+ if (ok == 6) {
383
+ appData.getCurrentEnterprise().setVariable("MIUBICACION", 1);
384
+ }
385
+ if (appData.getCurrentEnterprise().getVariable("MIUBICACION") == 1) {
386
+ ui.startGps();
387
+ PosicionamientoGPS();
388
+ }
389
+ ```
390
+
391
+ **GPS con callback:**
392
+
393
+ ```javascript
394
+ ui.startGps({
395
+ nodeName: "callbackGPS",
396
+ timeBetweenUpdates: 10000,
397
+ minimumMetersDistanceRange: 10,
398
+ foreground: true,
399
+ title: "Mi App GPS",
400
+ text: "Rastreando ubicación..."
401
+ });
402
+ ```
403
+
404
+ ### 12.3 Camara: captura de fotos
405
+
406
+ ```xml
407
+ <prop name="MAP_FOTO" type="PH" img-width="48p" img-height="48p"
408
+ height="40%" title="Foto" lmargin="2%"/>
409
+ ```
410
+
411
+ La captura se lanza automáticamente al tocar el control `PH`. Para foto de solo lectura:
412
+
413
+ ```xml
414
+ <prop name="MAP_FOTOVER" type="PH" locked="true"
415
+ height="40%" title="foto" lmargin="2%"/>
416
+ ```
417
+
418
+ ### 12.4 Escaneo QR/Barcode
419
+
420
+ ```javascript
421
+ ui.scanBarCode({
422
+ onScanned: function(sCode) {
423
+ self.MAP_CODIGO_QR = sCode;
424
+ ui.refresh("MAP_CODIGO_QR");
425
+ ui.showToast("Código: " + sCode);
426
+ },
427
+ onCancelled: function() {
428
+ ui.showToast("Escaneo cancelado");
429
+ }
430
+ });
431
+ ```
432
+
433
+ ### 12.5 Firma digital
434
+
435
+ ```xml
436
+ <prop name="MAP_SIGNATURE" img-width="48p" img-height="48p"
437
+ type="DR" readonly="false" height="40%" title="Firma"
438
+ stroke-width="##FLD_MAP_TAMANO_TRAZO##"
439
+ stroke-color="##FLD_MAP_COLOR_TRAZO##"
440
+ bgcolor="##FLD_MAP_COLOR_FONDO##"
441
+ apply-format-to-file="true"
442
+ onchange="refresh(MAP_SIGNATURE)"/>
443
+ ```
444
+
445
+ ### 12.6 Calendario con eventos
446
+
447
+ **Ejemplo real** (del proyecto EspecialCalendario):
448
+
449
+ ```xml
450
+ <frame name="calendario" width="100%" height="350p">
451
+ <prop name="Calendario" type="Z" calendar-viewmode="week"
452
+ contents="calendario" width="100%" height="100%"
453
+ viewmode="calendarview"
454
+ onchange="refresh" postonchange="refresh"/>
455
+ <contents name="calendario" src="ContentdatosCalendario"/>
456
+ </frame>
457
+
458
+ <!-- Lista de eventos del mes -->
459
+ <frame name="calendario2" width="100%" height="360p">
460
+ <prop name="Calendariodatos" type="Z" contents="Calendariodatos"
461
+ width="100%" height="300p"/>
462
+ <contents name="Calendariodatos" src="ContentdatosCalendariolista"
463
+ filter="strftime('%m',##FLD_MAP_FECHA##)=strftime('%m',FECHA)
464
+ and strftime('%Y',##FLD_MAP_FECHA##)=strftime('%Y',FECHA)"/>
465
+ </frame>
466
+ ```
467
+
468
+ ### 12.7 Gráficos (pie, bar, line)
469
+
470
+ **Ejemplo real** (del proyecto EspecialGraficos):
471
+
472
+ ```xml
473
+ <!-- Gráfico de barras -->
474
+ <prop name="@GraficosBarrasDatos" classid="XOneCharts"
475
+ viewmode="barchart" type="Z" contents="GraficosBarrasDatos"
476
+ width="692p" height="500p"/>
477
+ <contents name="GraficosBarrasDatos" src="ContentGraficosBarrasDatos"/>
478
+
479
+ <!-- Gráfico circular -->
480
+ <prop name="@GraficosPastelDatos" classid="XOneCharts"
481
+ viewmode="piechart" type="Z" contents="GraficosPastelDatos"
482
+ width="692p" height="500p"/>
483
+
484
+ <!-- Gráfico de línea de tiempo -->
485
+ <prop name="@GraficosLineasTiempoDatos" classid="XOneCharts"
486
+ viewmode="timeserieschart" type="Z"
487
+ contents="GraficosLineasTiempoDatos"
488
+ width="692p" height="500p"/>
489
+
490
+ <!-- Gráfico de lineas -->
491
+ <prop name="@GraficosLineasDatos" classid="XOneCharts"
492
+ viewmode="linechart" type="Z" contents="GraficosLineasDatos"
493
+ width="692p" height="500p"/>
494
+ <contents name="GraficosLineasDatos" src="ContentGraficosLineasDatos"
495
+ sort="CATEGORIA,VALOR1,VALOR2,VALOR3"/>
496
+
497
+ <!-- Gráfico XY -->
498
+ <prop name="@GraficosLineasXYDatos" classid="XOneCharts"
499
+ viewmode="xylinechart" type="Z" contents="GraficosLineasXYDatos"
500
+ width="692p" height="500p"/>
501
+ ```
502
+
503
+ **ViewModes de gráficos disponibles:**
504
+
505
+ | viewmode | Descripción |
506
+ |----------|-------------|
507
+ | `barchart` | Gráfico de barras |
508
+ | `3dbarchart` | Gráfico de barras 3D |
509
+ | `piechart` | Gráfico circular tipo 1 |
510
+ | `piechart2` | Gráfico circular tipo 2 |
511
+ | `linechart` | Gráfico de lineas |
512
+ | `timeserieschart` | Gráfico de series temporales |
513
+ | `xylinechart` | Gráfico de lineas XY |
514
+
515
+ ### 12.8 Mapas con marcadores
516
+
517
+ **Ejemplo real** (del proyecto EspecialMapa):
518
+
519
+ ```xml
520
+ <prop name="MAP_mapa" width="100%" title="Mapa" height="80%"
521
+ type="Z" visible="1" viewmode="mapview"
522
+ mapview-embedded="true" contents="mapaDatos"/>
523
+ <contents name="mapaDatos" src="ContentmapaDatos"/>
524
+
525
+ <prop name="boton0it" type="B" title="showStreetView"
526
+ onclick="javascript:showStreetView('MAP_mapa');"/>
527
+ <prop name="boton1it" type="B" title="showMap"
528
+ onclick="javascript:showMap('MAP_mapa');" newline="false"/>
529
+ ```
530
+
531
+ ### 12.9 Notificaciones push
532
+
533
+ **Crear notificación local (ejemplo real de EspecialNotificaciones):**
534
+
535
+ ```javascript
536
+ ui.showNotification(1, "Titulo", "Esto es una notificación",
537
+ "Aviso de recepcion de datos");
538
+
539
+ // Eliminar notificación
540
+ ui.dismissNotification("1");
541
+ ```
542
+
543
+ **Notificación avanzada con botones:**
544
+
545
+ ```javascript
546
+ ui.showNotification({
547
+ id: 5000,
548
+ title: "Nueva tarea asignada",
549
+ text: "Tiene una nueva tarea pendiente",
550
+ icon: "app_icon1",
551
+ backgroundColor: "#1976D2",
552
+ sound: "notification.wav",
553
+ cancelable: true,
554
+ dataObject: self,
555
+ nodeName: "callbackNotificacion",
556
+ parameters: '{ "tareaId": "123" }',
557
+ buttons: [{
558
+ id: 5001,
559
+ title: "Responder",
560
+ directReply: true,
561
+ directReplyLabel: "Escriba su respuesta...",
562
+ dataObject: self,
563
+ nodeName: "respuestaCallback"
564
+ }]
565
+ });
566
+ ```
567
+
568
+ ### 12.10 Sincronización con servidor
569
+
570
+ La sincronización se configura en el nodo `maintenance` de `Empresas`:
571
+
572
+ ```xml
573
+ <maintenance>
574
+ <action name="Replica" type="replica"
575
+ frecuency="400" period="X" synchronize="true"/>
576
+ <action name="SincronizarDatos" type="runscript"
577
+ period="S" frecuency="600" auto="true" show="false">
578
+ <script language="javascript">
579
+ sincronizarConServidor();
580
+ </script>
581
+ </action>
582
+ </maintenance>
583
+ ```
584
+
585
+ ---
586
+
587
+ ## 13. Patrones de Seguridad
588
+
589
+ ### 13.1 Login seguro
590
+
591
+ ```javascript
592
+ function realizarLogin() {
593
+ if (!validarRequerido(self.MAP_EMAIL, "Email")) return;
594
+ if (!validarRequerido(self.MAP_PASSWORD, "Contraseña")) return;
595
+
596
+ ui.showWaitDialog("Iniciando sesion...");
597
+
598
+ var collUsuarios = appData.getCollection("Usuarios");
599
+ var usuario = collUsuarios.findObject(
600
+ "LOGIN = '" + self.MAP_EMAIL + "'"
601
+ );
602
+
603
+ ui.hideWaitDialog();
604
+
605
+ if (usuario) {
606
+ // Verificar contraseña (idealmente hasheada)
607
+ if (usuario.PWD == self.MAP_PASSWORD) {
608
+ appData.setGlobalMacro("##USERID##", usuario.ID);
609
+ appData.setGlobalMacro("##USERNAME##", usuario.NOMBRE);
610
+ ui.showToast("Bienvenido, " + usuario.NOMBRE);
611
+ ui.openEditView("MenuPrincipal");
612
+ } else {
613
+ ui.showToast("Credenciales incorrectas");
614
+ }
615
+ } else {
616
+ ui.showToast("Usuario no encontrado");
617
+ }
618
+ }
619
+ ```
620
+
621
+ ### 13.2 Validación de entrada
622
+
623
+ ```javascript
624
+ function validarRequerido(valor, nombre) {
625
+ if (!valor || valor.toString().length == 0) {
626
+ ui.showToast("El campo " + nombre + " es obligatorio");
627
+ return false;
628
+ }
629
+ return true;
630
+ }
631
+
632
+ function validarEmail(email) {
633
+ if (!email) return false;
634
+ return email.indexOf("@") > 0 && email.indexOf(".") > 0;
635
+ }
636
+
637
+ function validarNumeroPositivo(valor, nombre) {
638
+ if (isNaN(valor) || valor <= 0) {
639
+ ui.showToast(nombre + " debe ser un número positivo");
640
+ return false;
641
+ }
642
+ return true;
643
+ }
644
+ ```
645
+
646
+ ### 13.3 SQL parameterizado
647
+
648
+ Para evitar inyeccion SQL al construir filtros:
649
+
650
+ ```javascript
651
+ // INCORRECTO - vulnerable a inyeccion SQL
652
+ coll.setFilter("NOMBRE = '" + self.MAP_BUSCAR + "'");
653
+
654
+ // MEJOR - sanitizar la entrada
655
+ var busqueda = self.MAP_BUSCAR.toString().replace(/'/g, "''");
656
+ coll.setFilter("NOMBRE LIKE '%" + busqueda + "%'");
657
+ ```
658
+
659
+ ### 13.4 Encriptación de datos sensibles
660
+
661
+ ```javascript
662
+ // Hashear con SHA-256
663
+ var hash = crypto.sha256("texto a hashear");
664
+
665
+ // Cifrar con AES
666
+ var cifrado = crypto.encrypt("texto", "clave_secreta", "AES");
667
+
668
+ // Descifrar
669
+ var original = crypto.decrypt(cifrado, "clave_secreta", "AES");
670
+
671
+ // Base64
672
+ var encoded = crypto.encodeBase64("texto");
673
+ var decoded = crypto.decodeBase64(encoded);
674
+ ```
675
+
676
+ ### 13.5 Timeout de sesión
677
+
678
+ ```javascript
679
+ // Configurar timeout usando maintenance
680
+ // En Empresas (mappings.xne):
681
+ // <maintenance>
682
+ // <action name="VerificarSesion" type="runscript"
683
+ // period="S" frecuency="300" auto="true" show="false">
684
+ // <script language="javascript">
685
+ // verificarTimeoutSesion();
686
+ // </script>
687
+ // </action>
688
+ // </maintenance>
689
+
690
+ function verificarTimeoutSesion() {
691
+ var ultimaActividad = appData.getGlobalMacro("##ULTIMA_ACTIVIDAD##");
692
+ var ahora = new Date().getTime();
693
+ var diferencia = ahora - parseInt(ultimaActividad);
694
+ // 30 minutos = 1800000 ms
695
+ if (diferencia > 1800000) {
696
+ ui.showToast("Sesion expirada");
697
+ appData.logout();
698
+ }
699
+ }
700
+ ```
701
+