xonecode 0.29.0 → 0.33.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/apps/web/dist/assets/{EditorDeFichero-ChyJtiiz.js → EditorDeFichero-H_kGeZt2.js} +1 -1
- package/apps/web/dist/assets/{index-IptObD2z.css → index-CWBaotdZ.css} +1 -1
- package/apps/web/dist/assets/index-DnGuOvbb.js +499 -0
- package/apps/web/dist/assets/{php-B91ZcS-9.js → php-CMFQiZgk.js} +1 -1
- package/apps/web/dist/assets/{ruby-DrxrTSz0.js → ruby-Dj3BOBHc.js} +1 -1
- package/apps/web/dist/index.html +2 -2
- package/dist/agent/config/modelosDeMotor.js +4 -2
- package/dist/agent/config/modelosDeMotor.js.map +1 -1
- package/dist/agent/config/settingsEnDisco.js +26 -0
- package/dist/agent/config/settingsEnDisco.js.map +1 -1
- package/dist/agent/dispositivos/cajasDeMaqueta.js +130 -0
- package/dist/agent/dispositivos/cajasDeMaqueta.js.map +1 -0
- package/dist/agent/dispositivos/geometriaEnDisco.js +25 -0
- package/dist/agent/dispositivos/geometriaEnDisco.js.map +1 -0
- package/dist/agent/dispositivos/juezVisual.js +84 -3
- package/dist/agent/dispositivos/juezVisual.js.map +1 -1
- package/dist/agent/grafo/criticaVisual.js +240 -11
- package/dist/agent/grafo/criticaVisual.js.map +1 -1
- package/dist/agent/grafo/lectorDeReferencias.js +21 -0
- package/dist/agent/grafo/lectorDeReferencias.js.map +1 -1
- package/dist/agent/grafo/validarFicheroXone.js +114 -0
- package/dist/agent/grafo/validarFicheroXone.js.map +1 -0
- package/dist/agent/motores/motoresLocales.js +429 -0
- package/dist/agent/motores/motoresLocales.js.map +1 -0
- package/dist/agent/motores/trueforge/sesionTrueforge.js +79 -11
- package/dist/agent/motores/trueforge/sesionTrueforge.js.map +1 -1
- package/dist/agent/motores/trueforge/toolsDeFichero.js +24 -6
- package/dist/agent/motores/trueforge/toolsDeFichero.js.map +1 -1
- package/dist/agent/sesiones/adjuntosDeSesion.js +125 -5
- package/dist/agent/sesiones/adjuntosDeSesion.js.map +1 -1
- package/dist/agent/subagentes/ejecutableEnWindows.js +139 -0
- package/dist/agent/subagentes/ejecutableEnWindows.js.map +1 -0
- package/dist/agent/subagentes/subagenteCodex.js +7 -2
- package/dist/agent/subagentes/subagenteCodex.js.map +1 -1
- package/dist/agent/subagentes/subagenteOpencode.js +12 -5
- package/dist/agent/subagentes/subagenteOpencode.js.map +1 -1
- package/dist/agent/turno/resumenDeTool.js +2 -0
- package/dist/agent/turno/resumenDeTool.js.map +1 -1
- package/dist/core/adjuntos.js +10 -2
- package/dist/core/adjuntos.js.map +1 -1
- package/dist/core/contextos.js +20 -0
- package/dist/core/contextos.js.map +1 -1
- package/dist/core/cssBienFormado.js +97 -0
- package/dist/core/cssBienFormado.js.map +1 -0
- package/dist/core/descripcionDePantalla.js +354 -0
- package/dist/core/descripcionDePantalla.js.map +1 -0
- package/dist/core/estiloMedido.js +332 -0
- package/dist/core/estiloMedido.js.map +1 -0
- package/dist/core/geometriaDePantalla.js +437 -0
- package/dist/core/geometriaDePantalla.js.map +1 -0
- package/dist/core/jsParaRhino.js +93 -0
- package/dist/core/jsParaRhino.js.map +1 -0
- package/dist/core/motoresLocales.js +128 -0
- package/dist/core/motoresLocales.js.map +1 -0
- package/dist/core/notify.js +2 -0
- package/dist/core/notify.js.map +1 -1
- package/dist/core/nucleoXone.js +6 -6
- package/dist/core/nucleoXone.js.map +1 -1
- package/dist/core/settings.js +5 -0
- package/dist/core/settings.js.map +1 -1
- package/dist/core/shellDeAgente.js +13 -0
- package/dist/core/shellDeAgente.js.map +1 -1
- package/dist/core/xmlBienFormado.js +206 -0
- package/dist/core/xmlBienFormado.js.map +1 -0
- package/dist/core/zipDeAdjunto.js +79 -0
- package/dist/core/zipDeAdjunto.js.map +1 -0
- package/dist/web/servidor/arranque.js +84 -0
- package/dist/web/servidor/arranque.js.map +1 -1
- package/dist/web/servidor/transporte.js.map +1 -1
- package/package.json +2 -1
- package/skills/xone-development/SKILL.md +2 -1
- package/skills/xone-development/references/css/buenas-practicas-y-parser.md +2 -1
- package/skills/xone-development/references/css/de-una-maqueta-a-xone.md +81 -0
- package/skills/xone-development/references/css/patrones-material-y-temas.md +15 -13
- package/skills/xone-development/references/css/propiedades-y-herencia.md +2 -2
- package/skills/xone-development/references/css/selectores-unidades-colores.md +8 -3
- package/skills/xone-development/references/indice-completo.md +1 -0
- package/skills/xone-hotswap/lib/geometria.mjs +147 -0
- package/skills/xone-hotswap/scripts/xone-captura-android +6 -0
- package/skills/xone-hotswap/scripts/xone-hotswap +8 -0
- package/skills/xone-hotswap/scripts/xone-recargar-android +15 -0
- package/apps/web/dist/assets/index-CFIrUOmW.js +0 -499
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transporte.js","sourceRoot":"","sources":["../../../src/web/servidor/transporte.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"transporte.js","sourceRoot":"","sources":["../../../src/web/servidor/transporte.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xonecode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.33.0",
|
|
4
4
|
"description": "Consola CLI y harness de agente para desarrollar aplicaciones XOne",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"@langchain/openai": "1.5.5",
|
|
47
47
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
48
48
|
"@truefoundry/trueforge-core": "0.2.1",
|
|
49
|
+
"acorn": "^8.19.0",
|
|
49
50
|
"better-sqlite3": "13.0.3",
|
|
50
51
|
"deepagents": "^1.14.0",
|
|
51
52
|
"fflate": "^0.8.3",
|
|
@@ -12,7 +12,8 @@ Consulta estos ficheros según lo que busques:
|
|
|
12
12
|
|
|
13
13
|
| Qué buscas | Dónde |
|
|
14
14
|
|---|---|
|
|
15
|
-
| **El índice COMPLETO** de las
|
|
15
|
+
| **El índice COMPLETO** de las 56 referencias, por familia | [references/indice-completo.md](references/indice-completo.md) |
|
|
16
|
+
| **Traducir una maqueta HTML** (el `code.html` de Stitch, Tailwind) a XOne, clase a clase | [references/css/de-una-maqueta-a-xone.md](references/css/de-una-maqueta-a-xone.md) |
|
|
16
17
|
| **Tipos de `prop` válidos** (la tabla) | [references/tipos-de-prop.md](references/tipos-de-prop.md) |
|
|
17
18
|
| **Anti-patrones**: lo que NO hay que hacer, con el porqué | [references/anti-patrones.md](references/anti-patrones.md) |
|
|
18
19
|
| Resumen de la capa XML y de la capa CSS | [references/resumen-xml-y-css.md](references/resumen-xml-y-css.md) |
|
|
@@ -30,7 +30,7 @@ Contenido: §17 top 15 buenas prácticas, anti-patrones y checklist · §18 func
|
|
|
30
30
|
|
|
31
31
|
10. **Preferir `text-border-bottom: true` para inputs** - Es el patron Material Design más limpio y común.
|
|
32
32
|
|
|
33
|
-
11. **
|
|
33
|
+
11. **Para un botón pill, `border-corner-radius: 999`** - El radio va en **píxeles reales del aparato**, no en `p`, y se recorta a la mitad del alto. «La mitad del `height`» (`height: 56p; border-corner-radius: 28;`) solo da una píldora en el aparato de referencia: en uno de más resolución sale un rectángulo redondeado (medido en un emulador Android).
|
|
34
34
|
|
|
35
35
|
12. **Recordar que alpha va PRIMERO en ARGB** - `#80FFFFFF` = blanco 50%, no `#FFFFFF80`.
|
|
36
36
|
|
|
@@ -395,6 +395,7 @@ Recomendado durante el desarrollo para detectar typos y referencias rotas; opcio
|
|
|
395
395
|
| `padding-left: 20px` | `lpadding: 20p` |
|
|
396
396
|
| `border-radius: 8px` | `border-corner-radius: 8` |
|
|
397
397
|
| `border: 1px solid #ccc` | `border: true; border-width: 1; border-color: #CCCCCC;` |
|
|
398
|
+
| `border: none` en un botón | `border-width: 0;` — un `type="B"` pinta un contorno fino por omisión, y `border: false`, `border: 0`, `framebox: false` o `labelbox: false` NO lo quitan (medido en un emulador Android) |
|
|
398
399
|
| `text-align: center` | `text-align: center` |
|
|
399
400
|
| `height: 50px` | `height: 50p` |
|
|
400
401
|
| `width: 100%` | `width: 100%` |
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# De una maqueta HTML (Stitch, Tailwind) a XOne
|
|
2
|
+
|
|
3
|
+
Contenido: §1 cómo leer esta tabla · §2 medidas y unidades · §3 forma, bordes y fondos · §4 color · §5 texto y
|
|
4
|
+
tipografías · §6 composición · §7 lo que no existe
|
|
5
|
+
|
|
6
|
+
Una maqueta de Stitch trae un `code.html` con clases de Tailwind y una configuración propia (`tailwind.config`:
|
|
7
|
+
colores con nombre, `borderRadius`, `fontFamily`, `fontSize`). Esta tabla dice cómo se escribe cada cosa en XOne.
|
|
8
|
+
**XOne ignora en silencio lo que no conoce**: una clase traducida «a ojo» no da error, da una pantalla distinta.
|
|
9
|
+
|
|
10
|
+
## 1. Cómo leer esta tabla
|
|
11
|
+
|
|
12
|
+
Cada fila dice de dónde sale:
|
|
13
|
+
|
|
14
|
+
- **medido**: comprobado en un emulador Android con una pantalla de prueba. Fíate.
|
|
15
|
+
- **documentado**: lo dice otra referencia de esta skill, sin medida propia.
|
|
16
|
+
- **sin medir**: no se ha comprobado. Pruébalo en el aparato antes de dar nada por bueno.
|
|
17
|
+
|
|
18
|
+
Lo primero, siempre: lee la configuración de Tailwind del `code.html` (`tailwind.config = {…}`). Los nombres de las
|
|
19
|
+
clases (`rounded-lg`, `text-key-numeric`, `bg-surface-container-high`) son de ESA maqueta y valen lo que diga ella.
|
|
20
|
+
|
|
21
|
+
## 2. Medidas y unidades
|
|
22
|
+
|
|
23
|
+
| En la maqueta | En XOne | De dónde |
|
|
24
|
+
|---|---|---|
|
|
25
|
+
| un ancho en px | `p`: `px_maqueta × resolution-width / ancho_de_la_maqueta`; o en `%` del contenedor | medido |
|
|
26
|
+
| un alto en px | `p` con la misma cuenta. **Ojo**: un alto en `p` escala con el ALTO útil de la pantalla y un ancho con el ANCHO, así que en un teléfono alargado sale más alto (medido: ×1,72 en alto y ×1,5 en ancho, 15 % más alto de proporción) | medido |
|
|
27
|
+
| `w-full` | `width="100%"` | documentado |
|
|
28
|
+
| `p-N`, `px-N`, `py-N` | `lpadding`, `rpadding`, `tpadding`, `bpadding` (no hay abreviado) | documentado |
|
|
29
|
+
| `m-N`, `mt-N`… | `lmargin`, `tmargin`… (no hay abreviado; `rmargin` en `%` se ignora) | documentado / medido |
|
|
30
|
+
|
|
31
|
+
`p` no es el píxel y escala **por eje**: un ancho por `ancho_real / resolution-width`, un alto por
|
|
32
|
+
`alto_útil / resolution-height` (el alto útil es la pantalla sin las barras de estado y de navegación). Lee los
|
|
33
|
+
dos valores en el `app.xml` del proyecto.
|
|
34
|
+
|
|
35
|
+
## 3. Forma, bordes y fondos
|
|
36
|
+
|
|
37
|
+
| En la maqueta | En XOne | De dónde |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| `rounded-full` (píldora o círculo) | `border-corner-radius: 999` | medido |
|
|
40
|
+
| `rounded-lg`, `rounded-xl`, `rounded-[Npx]` | `border-corner-radius` en **píxeles del aparato**, no en `p`: `px_maqueta × ancho_real / ancho_de_la_maqueta` (32 px en una maqueta de 390 son unos 89 en un aparato de 1080). El radio se recorta a la mitad del alto | medido (la unidad) |
|
|
41
|
+
| un botón sin borde | `border-width: 0`. Un `type="B"` pinta un contorno fino por omisión; `border: false`, `border: 0`, `framebox: false` y `labelbox: false` NO lo quitan | medido |
|
|
42
|
+
| `border`, `border-COLOR` | `border-width: 1; border-color: #RRGGBB` | documentado |
|
|
43
|
+
| `bg-gradient-to-br from-A to-B` | un SVG con `generar_fondo_svg` (tipo degradado, ángulo 45 para `to-br`): en un BOTÓN va en `img`, en un frame en `imgbk` (en un botón `imgbk` se ignora) | medido |
|
|
44
|
+
| `shadow-[0_6px_14px_…]` (sombra exterior) | un SVG con `generar_fondo_svg` (tipo sombra). **No** `elevation` en algo redondeado: recorta el fondo por abajo | medido |
|
|
45
|
+
| `shadow-[inset_…]` (brillo interior) | no hay equivalente; si importa, dibújalo en el mismo SVG del fondo | sin medir |
|
|
46
|
+
| `border-corner-radius` sobre una imagen de fondo | no la recorta: la esquina se dibuja DENTRO del SVG | medido |
|
|
47
|
+
|
|
48
|
+
## 4. Color
|
|
49
|
+
|
|
50
|
+
| En la maqueta | En XOne | De dónde |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| `bg-<nombre>`, `text-<nombre>` | el `#RRGGBB` que diga `tailwind.config` para ese nombre, en `bgcolor` y `forecolor` / `text-forecolor` | documentado |
|
|
53
|
+
| `bg-<nombre>/80` (transparencia) | `#AARRGGBB` con el alfa **primero**: 80 % es `CC`, 60 % `99`, 5 % `0D` | documentado |
|
|
54
|
+
| un color en un SVG | `#RRGGBB` más `opacity` (HTML y SVG ponen el alfa al final; XOne al principio) | medido |
|
|
55
|
+
|
|
56
|
+
## 5. Texto y tipografías
|
|
57
|
+
|
|
58
|
+
| En la maqueta | En XOne | De dónde |
|
|
59
|
+
|---|---|---|
|
|
60
|
+
| una fuente de Google Fonts (`fontFamily` en la configuración, `fonts.googleapis.com` en el `<head>`) | su `.ttf` en `fonts/` y `fontname: Nombre.ttf`. El `.ttf` variable que da Google Fonts vale tal cual (sale con su peso por omisión). **Se sube desplegando entero**: la recarga en caliente no lleva `fonts/`. Un `fontname` que no está en el aparato saca un diálogo de error y el control no se pinta | medido |
|
|
61
|
+
| `text-[Npx]`, `fontSize` de la configuración | `fontsize` / `textfont-size`. La relación con los px de la maqueta **no está medida**: `fontsize: 40` salió de unos 100 px de alto en un aparato de 1080. Ajusta midiendo la captura | sin medir |
|
|
62
|
+
| `font-medium`, `font-semibold`, `font-bold` | la variante de la fuente con ese peso (`Roboto-Bold.ttf`) o `fontbold` | documentado |
|
|
63
|
+
| `uppercase` | escribe el texto ya en mayúsculas | sin medir |
|
|
64
|
+
| `tracking-*` (espaciado entre letras), `leading-*` (interlineado) | sin equivalente conocido | sin medir |
|
|
65
|
+
| un texto que no cabe | `auto-fontsize="true"`, o una caja más alta: un `height` menor que la letra la corta por abajo | medido |
|
|
66
|
+
| iconos `material-symbols-outlined` | `buscar_icono` → `icons/ic_<nombre>.svg`, en un `type="IMG"` (en el `img` de un botón se estira) | medido |
|
|
67
|
+
|
|
68
|
+
## 6. Composición
|
|
69
|
+
|
|
70
|
+
| En la maqueta | En XOne | De dónde |
|
|
71
|
+
|---|---|---|
|
|
72
|
+
| `grid grid-cols-4 gap-N` | una fila de controles con `width` en `%` que, con sus márgenes, sume como mucho el 100 %: el que se pasa desborda por la derecha | medido |
|
|
73
|
+
| `flex justify-between` | márgenes en `%` entre los elementos de la fila | documentado |
|
|
74
|
+
| `flex-col` | controles uno bajo otro (`newline`) dentro de un frame | documentado |
|
|
75
|
+
| una cabecera fija (`fixed top-0`) | un `group` con `fixed="true"` y `orientation="top"` | documentado |
|
|
76
|
+
|
|
77
|
+
## 7. Lo que no existe en XOne (no lo traduzcas, ni a medias)
|
|
78
|
+
|
|
79
|
+
`hover:`, `active:`, `focus:`, `transition-*`, `animate-*`, `translate-*`, `scale-*`, `blur-*`, `backdrop-blur-*`,
|
|
80
|
+
`opacity` animada, `pointer-events-*`, `select-none`, `overflow-x-auto` con barra oculta, `env(safe-area-inset-*)`.
|
|
81
|
+
Son estados o animaciones del navegador; en XOne no hacen nada, y escribirlos con otro nombre no los hace existir.
|
|
@@ -126,13 +126,15 @@ El footer es el frame inferior, normalmente con botones de acción.
|
|
|
126
126
|
Los botones siguen el patron de Material Design con esquinas redondeadas y colores por estado:
|
|
127
127
|
|
|
128
128
|
```css
|
|
129
|
+
/* Píldora: border-corner-radius: 999 (el radio va en PÍXELES del aparato y se recorta a la mitad del alto;
|
|
130
|
+
«28 = la mitad de 56p» solo vale en el aparato de referencia — medido en un emulador Android) */
|
|
129
131
|
/* Boton primario - Accion principal */
|
|
130
132
|
.btnPrimario {
|
|
131
133
|
width: 90%;
|
|
132
134
|
height: 56p;
|
|
133
135
|
bgcolor: #1565C0;
|
|
134
136
|
forecolor: #FFFFFF;
|
|
135
|
-
border-corner-radius:
|
|
137
|
+
border-corner-radius: 999;
|
|
136
138
|
text-align: center;
|
|
137
139
|
fontsize: 16;
|
|
138
140
|
fontname: Roboto-Bold.ttf;
|
|
@@ -146,7 +148,7 @@ Los botones siguen el patron de Material Design con esquinas redondeadas y color
|
|
|
146
148
|
forecolor: #1565C0;
|
|
147
149
|
border: true;
|
|
148
150
|
border-color: #1565C0;
|
|
149
|
-
border-corner-radius:
|
|
151
|
+
border-corner-radius: 999;
|
|
150
152
|
text-align: center;
|
|
151
153
|
fontsize: 16;
|
|
152
154
|
}
|
|
@@ -157,7 +159,7 @@ Los botones siguen el patron de Material Design con esquinas redondeadas y color
|
|
|
157
159
|
height: 56p;
|
|
158
160
|
bgcolor: #F44336;
|
|
159
161
|
forecolor: #FFFFFF;
|
|
160
|
-
border-corner-radius:
|
|
162
|
+
border-corner-radius: 999;
|
|
161
163
|
text-align: center;
|
|
162
164
|
fontsize: 16;
|
|
163
165
|
}
|
|
@@ -168,7 +170,7 @@ Los botones siguen el patron de Material Design con esquinas redondeadas y color
|
|
|
168
170
|
height: 56p;
|
|
169
171
|
bgcolor: #4CAF50;
|
|
170
172
|
forecolor: #FFFFFF;
|
|
171
|
-
border-corner-radius:
|
|
173
|
+
border-corner-radius: 999;
|
|
172
174
|
text-align: center;
|
|
173
175
|
fontsize: 16;
|
|
174
176
|
fontname: Roboto-Bold.ttf;
|
|
@@ -180,7 +182,7 @@ Los botones siguen el patron de Material Design con esquinas redondeadas y color
|
|
|
180
182
|
height: 56p;
|
|
181
183
|
bgcolor: #00BCD4;
|
|
182
184
|
forecolor: #FFFFFF;
|
|
183
|
-
border-corner-radius:
|
|
185
|
+
border-corner-radius: 999;
|
|
184
186
|
text-align: center;
|
|
185
187
|
fontsize: 16;
|
|
186
188
|
fontname: Roboto-Bold.ttf;
|
|
@@ -226,7 +228,7 @@ Los botones siguen el patron de Material Design con esquinas redondeadas y color
|
|
|
226
228
|
width: 95%;
|
|
227
229
|
height: 56p;
|
|
228
230
|
bgcolor: #FFFFFF;
|
|
229
|
-
border-corner-radius:
|
|
231
|
+
border-corner-radius: 999;
|
|
230
232
|
border: true;
|
|
231
233
|
border-color: #E0E0E0;
|
|
232
234
|
lmargin: 15p;
|
|
@@ -306,7 +308,7 @@ El FAB es un botón circular flotante que representa la acción principal de la
|
|
|
306
308
|
width: 56p;
|
|
307
309
|
height: 56p;
|
|
308
310
|
bgcolor: #1565C0;
|
|
309
|
-
border-corner-radius:
|
|
311
|
+
border-corner-radius: 999;
|
|
310
312
|
}
|
|
311
313
|
|
|
312
314
|
/* FAB grande (64p) */
|
|
@@ -699,7 +701,7 @@ prop {
|
|
|
699
701
|
height: 56p;
|
|
700
702
|
bgcolor: #1565C0;
|
|
701
703
|
forecolor: #FFFFFF;
|
|
702
|
-
border-corner-radius:
|
|
704
|
+
border-corner-radius: 999;
|
|
703
705
|
text-align: center;
|
|
704
706
|
fontsize: 16;
|
|
705
707
|
fontname: Roboto-Bold.ttf;
|
|
@@ -713,7 +715,7 @@ prop {
|
|
|
713
715
|
forecolor: #1565C0;
|
|
714
716
|
border: true;
|
|
715
717
|
border-color: #1565C0;
|
|
716
|
-
border-corner-radius:
|
|
718
|
+
border-corner-radius: 999;
|
|
717
719
|
text-align: center;
|
|
718
720
|
fontsize: 16;
|
|
719
721
|
}
|
|
@@ -724,7 +726,7 @@ prop {
|
|
|
724
726
|
height: 56p;
|
|
725
727
|
bgcolor: #F44336;
|
|
726
728
|
forecolor: #FFFFFF;
|
|
727
|
-
border-corner-radius:
|
|
729
|
+
border-corner-radius: 999;
|
|
728
730
|
text-align: center;
|
|
729
731
|
fontsize: 16;
|
|
730
732
|
}
|
|
@@ -735,7 +737,7 @@ prop {
|
|
|
735
737
|
height: 56p;
|
|
736
738
|
bgcolor: #4CAF50;
|
|
737
739
|
forecolor: #FFFFFF;
|
|
738
|
-
border-corner-radius:
|
|
740
|
+
border-corner-radius: 999;
|
|
739
741
|
text-align: center;
|
|
740
742
|
fontsize: 16;
|
|
741
743
|
fontname: Roboto-Bold.ttf;
|
|
@@ -746,7 +748,7 @@ prop {
|
|
|
746
748
|
width: 56p;
|
|
747
749
|
height: 56p;
|
|
748
750
|
bgcolor: #FFFFFF;
|
|
749
|
-
border-corner-radius:
|
|
751
|
+
border-corner-radius: 999;
|
|
750
752
|
}
|
|
751
753
|
|
|
752
754
|
/* FAB */
|
|
@@ -818,7 +820,7 @@ prop {
|
|
|
818
820
|
width: 95%;
|
|
819
821
|
height: 56p;
|
|
820
822
|
bgcolor: #FFFFFF;
|
|
821
|
-
border-corner-radius:
|
|
823
|
+
border-corner-radius: 999;
|
|
822
824
|
border: true;
|
|
823
825
|
border-color: #E0E0E0;
|
|
824
826
|
lmargin: 15p;
|
|
@@ -499,7 +499,7 @@ El atributo `visible` usa un sistema de mascara de bits que controla en que modo
|
|
|
499
499
|
height: 56p;
|
|
500
500
|
bgcolor: #1565C0;
|
|
501
501
|
forecolor: #FFFFFF;
|
|
502
|
-
border-corner-radius:
|
|
502
|
+
border-corner-radius: 999; /* píldora: el radio va en píxeles del aparato */
|
|
503
503
|
ripple-effect: true;
|
|
504
504
|
elevation: 4;
|
|
505
505
|
}
|
|
@@ -573,7 +573,7 @@ El caso más común: una clase hereda de otra y sobreescribe algunos atributos.
|
|
|
573
573
|
height: 56p;
|
|
574
574
|
bgcolor: #1565C0;
|
|
575
575
|
forecolor: #FFFFFF;
|
|
576
|
-
border-corner-radius:
|
|
576
|
+
border-corner-radius: 999; /* píldora: el radio va en píxeles del aparato */
|
|
577
577
|
text-align: center;
|
|
578
578
|
fontsize: 16;
|
|
579
579
|
fontname: Roboto-Bold.ttf;
|
|
@@ -492,7 +492,12 @@ frame {
|
|
|
492
492
|
|
|
493
493
|
### 3.1 `p` (puntos) - Unidad absoluta
|
|
494
494
|
|
|
495
|
-
La unidad `p` representa **pixels en el dispositivo de referencia** definido por `resolution-width` y `resolution-height` en `app.xml`. En el dispositivo de referencia, `1p = 1px` real. En cualquier otro dispositivo, XOne escala
|
|
495
|
+
La unidad `p` representa **pixels en el dispositivo de referencia** definido por `resolution-width` y `resolution-height` en `app.xml`. En el dispositivo de referencia, `1p = 1px` real. En cualquier otro dispositivo, XOne escala **cada eje por separado**:
|
|
496
|
+
|
|
497
|
+
- un **ancho** en `p`: `px_reales = valor_p × (ancho_real / resolution-width)`;
|
|
498
|
+
- un **alto** en `p`: `px_reales = valor_p × (alto_útil / resolution-height)`, donde el alto útil es la pantalla sin las barras de estado y de navegación.
|
|
499
|
+
|
|
500
|
+
> **Medido** en un emulador Android de 1080×2400 con una app de `resolution-width="720"` y `resolution-height="1280"`: `width: 421p` → 631 px (×1,5) y `height: 120p` → 206 px (×1,72, porque el alto útil son 2205 px). Así que **la proporción de un control declarado en `p` cambia de un aparato a otro**: un cuadrado de `100p × 100p` sale más alto que ancho en un teléfono alargado.
|
|
496
501
|
|
|
497
502
|
> **CRÍTICO: `p` ≠ Material `dp`.** Es un error común asumir que `p` equivale a `dp` (density-independent pixels) de Android. **NO lo es.** Para el dispositivo de referencia por defecto de XOne (1080×1920, xxhdpi, density 3×), Material `56dp` ≈ `168p`, NO `56p`. Aplicar valores Material directamente como `p` produce barras/botones ~3× más pequeños de lo necesario.
|
|
498
503
|
|
|
@@ -517,7 +522,7 @@ La unidad `p` representa **pixels en el dispositivo de referencia** definido por
|
|
|
517
522
|
.btnPrimario {
|
|
518
523
|
width: 90%;
|
|
519
524
|
height: 124p; /* Workflow "pill" CTA */
|
|
520
|
-
border-corner-radius:
|
|
525
|
+
border-corner-radius: 999; /* píldora en CUALQUIER aparato: el radio va en píxeles y se recorta a la mitad del alto */
|
|
521
526
|
}
|
|
522
527
|
```
|
|
523
528
|
|
|
@@ -526,7 +531,7 @@ La unidad `p` representa **pixels en el dispositivo de referencia** definido por
|
|
|
526
531
|
- Alturas fijas de headers, footers, botones
|
|
527
532
|
- Tamaños de iconos y avatares
|
|
528
533
|
- Margenes y paddings fijos
|
|
529
|
-
-
|
|
534
|
+
- **Los radios NO**: `border-corner-radius` va en **píxeles reales del aparato** y no escala con `p` (medido: un radio `50` en un botón de `100p` sale de ~48 px, un tercio de su alto real de 172 px). Para una píldora usa `border-corner-radius: 999`: el radio se recorta a la mitad del alto, sea cual sea el aparato
|
|
530
535
|
- Dimensiones de componentes que no deben cambiar con el tamaño de pantalla
|
|
531
536
|
|
|
532
537
|
### 3.2 `%` (porcentaje) - Unidad relativa
|
|
@@ -60,6 +60,7 @@ Lee el fichero que corresponda antes de responder sobre atributos concretos, val
|
|
|
60
60
|
- [css/dinamicos-cascada-y-componentes.md](references/css/dinamicos-cascada-y-componentes.md) — `##FLD_CAMPO##`, cascada de dispositivo, `strict-mode`, animaciones y tokens, gráficos, calendario y mapa
|
|
61
61
|
- [css/patrones-material-y-temas.md](references/css/patrones-material-y-temas.md) — Patrones Material (header/body/footer, botones, inputs, tarjetas, FAB, toolbar, item de lista), temas light/dark y un `default.css` + `colors.css` completos y comentados
|
|
62
62
|
- [css/buenas-practicas-y-parser.md](references/css/buenas-practicas-y-parser.md) — Buenas prácticas, anti-patrones, checklist de validación y detalle de las funciones del parser (`@import`, variables, `calc()`, `!important`, `!default`, `@extend`, modo estricto)
|
|
63
|
+
- [css/de-una-maqueta-a-xone.md](references/css/de-una-maqueta-a-xone.md) — De una maqueta HTML de Stitch (Tailwind) a XOne, clase a clase: forma, bordes, fondos, color, tipografías y composición, con lo MEDIDO en el aparato separado de lo documentado
|
|
63
64
|
|
|
64
65
|
### Datos e integración
|
|
65
66
|
- [datos/appdata.md](references/datos/appdata.md) — `appData` completo: colecciones, login/logout, paso de datos entre pantallas, macros globales, SQL directo, detección de dispositivo, `loadIncludeFile` y `loadCssFile`
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* La GEOMETRÍA de una captura: el árbol de controles y las barras del sistema EN EL MISMO MOMENTO,
|
|
3
|
+
* guardados al lado de la imagen con su nombre (`calc_08.jpg` → `calc_08.geometria.json`).
|
|
4
|
+
*
|
|
5
|
+
* POR QUÉ, y por qué AQUÍ. El crítico visual del host (`xone_critica_visual`) compara tecla a tecla
|
|
6
|
+
* —qué falta, en qué fila, qué tamaño, qué queda tapado— leyendo cajas, no píxeles. Para eso necesita
|
|
7
|
+
* el árbol de la MISMA pantalla que la captura: emparejarlo después por la fecha de los ficheros ya
|
|
8
|
+
* cruzó una vez el árbol de una ronda con la captura de otra, y una cifra «exacta» de la ronda
|
|
9
|
+
* equivocada es peor que ninguna. Sacarlo en el mismo comando que la captura lo hace imposible.
|
|
10
|
+
*
|
|
11
|
+
* Las barras salen de `dumpsys window` (`InsetsSource … type=navigationBars frame=[0,2337][1080,2400]`):
|
|
12
|
+
* con Android dibujando de borde a borde, un control puede quedar DEBAJO de la barra de navegación, y
|
|
13
|
+
* su alto cambia según el aparato y el modo de navegación (medido en un pixel8: 63 px con gestos), así
|
|
14
|
+
* que no se adivina.
|
|
15
|
+
*
|
|
16
|
+
* NUNCA hace fallar la captura: si no se puede, se dice por stderr y la captura queda sin geometría.
|
|
17
|
+
* Va a `$XONECODE_HOTSWAP` (andamio, como los volcados del canal), no a artefactos: no es algo que mirar.
|
|
18
|
+
*/
|
|
19
|
+
import { execFileSync } from "node:child_process";
|
|
20
|
+
import { mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
21
|
+
import { tmpdir } from "node:os";
|
|
22
|
+
import { join } from "node:path";
|
|
23
|
+
import { NODE, rutaDeAdb } from "./dispositivo.mjs";
|
|
24
|
+
|
|
25
|
+
/** El nombre del fichero de geometría de una captura: su nombre sin extensión + `.geometria.json`. */
|
|
26
|
+
export function nombreDeGeometria(nombreDeCaptura) {
|
|
27
|
+
return `${nombreDeCaptura.replace(/\.[A-Za-z0-9]{2,4}$/, "")}.geometria.json`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Las barras del sistema de la salida de `dumpsys window`, o `undefined` si no salen. Pura. */
|
|
31
|
+
export function barrasDeDumpsys(salida) {
|
|
32
|
+
const frame = (tipo) => {
|
|
33
|
+
const m = new RegExp(`type=${tipo} frame=\\[(-?\\d+),(-?\\d+)\\]\\[(-?\\d+),(-?\\d+)\\] visible=true`).exec(String(salida));
|
|
34
|
+
return m === null ? undefined : { izquierda: +m[1], arriba: +m[2], derecha: +m[3], abajo: +m[4] };
|
|
35
|
+
};
|
|
36
|
+
const estado = frame("statusBars");
|
|
37
|
+
const navegacion = frame("navigationBars");
|
|
38
|
+
if (estado === undefined && navegacion === undefined) return undefined;
|
|
39
|
+
return { ...(estado === undefined ? {} : { estado }), ...(navegacion === undefined ? {} : { navegacion }) };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Hasta dónde llegan los controles del árbol (el borde derecho y el inferior más lejanos). Pura. */
|
|
43
|
+
export function extensionDelArbol(arbol) {
|
|
44
|
+
let ancho = 0;
|
|
45
|
+
let alto = 0;
|
|
46
|
+
const visitar = (n) => {
|
|
47
|
+
if (Array.isArray(n)) return n.forEach(visitar);
|
|
48
|
+
if (n === null || typeof n !== "object") return;
|
|
49
|
+
const b = n.bounds;
|
|
50
|
+
if (b && typeof b.left === "number" && typeof b.width === "number" && typeof b.top === "number" && typeof b.height === "number") {
|
|
51
|
+
ancho = Math.max(ancho, b.left + b.width);
|
|
52
|
+
alto = Math.max(alto, b.top + b.height);
|
|
53
|
+
}
|
|
54
|
+
for (const v of Object.values(n)) if (v !== null && typeof v === "object") visitar(v);
|
|
55
|
+
};
|
|
56
|
+
visitar(arbol);
|
|
57
|
+
return { ancho, alto };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* La imagen es de UN CONTROL y no de la pantalla entera (`xone-hotswap shot name=…`): es claramente más pequeña que
|
|
62
|
+
* lo que ocupa el árbol. Su geometría no le corresponde —las cajas son de la pantalla— y el crítico mediría píxeles
|
|
63
|
+
* que no son (medido: una foto del visor salía con siete textos «recortados»). Pura.
|
|
64
|
+
*/
|
|
65
|
+
export function esCapturaDeUnControl(pantalla, arbol, barras) {
|
|
66
|
+
// La PANTALLA la dice la barra de navegación (llega al borde inferior y ocupa el ancho entero). Sin ella, lo que
|
|
67
|
+
// ocupa el árbol, que puede pasarse: medido, un pie flotante de la app llegaba a y=2542 en una pantalla de 2400, y
|
|
68
|
+
// la captura completa parecía la de un control.
|
|
69
|
+
const n = barras?.navegacion;
|
|
70
|
+
const e = n !== undefined ? { ancho: n.derecha, alto: n.abajo } : extensionDelArbol(arbol);
|
|
71
|
+
return pantalla !== undefined && e.alto > 0 && (pantalla.alto < e.alto * 0.95 || pantalla.ancho < e.ancho * 0.95);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** El ancho y el alto de un PNG o un JPEG, leídos de su cabecera. `undefined` si no se sabe. Pura. */
|
|
75
|
+
export function medidasDeImagen(bytes) {
|
|
76
|
+
if (bytes.length >= 24 && bytes[0] === 0x89 && bytes[1] === 0x50) return { ancho: bytes.readUInt32BE(16), alto: bytes.readUInt32BE(20) };
|
|
77
|
+
if (bytes[0] === 0xff && bytes[1] === 0xd8) {
|
|
78
|
+
let i = 2;
|
|
79
|
+
while (i + 9 < bytes.length) {
|
|
80
|
+
if (bytes[i] !== 0xff) return undefined;
|
|
81
|
+
const marca = bytes[i + 1];
|
|
82
|
+
const largo = bytes.readUInt16BE(i + 2);
|
|
83
|
+
if (marca >= 0xc0 && marca <= 0xc3) return { alto: bytes.readUInt16BE(i + 5), ancho: bytes.readUInt16BE(i + 7) };
|
|
84
|
+
i += 2 + largo;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Guarda `<captura>.geometria.json` en `$XONECODE_HOTSWAP`. Devuelve el nombre, o `undefined` y lo dice.
|
|
92
|
+
* `bytes` son los de la captura (para su tamaño); `serie`, el aparato.
|
|
93
|
+
*/
|
|
94
|
+
export function guardarGeometria({ nombreDeCaptura, bytes, serie, entorno = process.env }) {
|
|
95
|
+
const destino = entorno.XONECODE_HOTSWAP;
|
|
96
|
+
if (!destino) {
|
|
97
|
+
process.stderr.write("(sin XONECODE_HOTSWAP: la captura queda sin geometría para el crítico)\n");
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
let arbol;
|
|
101
|
+
const temporal = mkdtempSync(join(tmpdir(), "xonecode-geometria-"));
|
|
102
|
+
try {
|
|
103
|
+
// El cliente guarda un árbol grande en `$XONECODE_HOTSWAP`: se le da uno PROPIO para saber cuál es.
|
|
104
|
+
const cliente = join(import.meta.dirname, "..", "scripts", "xone-hotswap");
|
|
105
|
+
execFileSync(NODE, [cliente, "getAllElements", "format=xone", ...(serie ? ["--serie", serie] : [])], {
|
|
106
|
+
encoding: "utf8",
|
|
107
|
+
env: { ...entorno, XONECODE_HOTSWAP: temporal },
|
|
108
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
109
|
+
timeout: 30_000,
|
|
110
|
+
});
|
|
111
|
+
const fichero = readdirSync(temporal).find((n) => n.endsWith(".json"));
|
|
112
|
+
if (fichero !== undefined) arbol = JSON.parse(readFileSync(join(temporal, fichero), "utf8"));
|
|
113
|
+
} catch {
|
|
114
|
+
// Sin canal o sin app: se queda sin árbol, y se dice abajo.
|
|
115
|
+
} finally {
|
|
116
|
+
rmSync(temporal, { recursive: true, force: true });
|
|
117
|
+
}
|
|
118
|
+
if (arbol === undefined) {
|
|
119
|
+
process.stderr.write("(no pude leer el árbol de controles: la captura queda sin geometría para el crítico)\n");
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
let barras;
|
|
123
|
+
try {
|
|
124
|
+
barras = barrasDeDumpsys(
|
|
125
|
+
execFileSync(rutaDeAdb(entorno), [...(serie ? ["-s", serie] : []), "shell", "dumpsys", "window"], {
|
|
126
|
+
encoding: "utf8",
|
|
127
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
128
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
129
|
+
timeout: 15_000,
|
|
130
|
+
})
|
|
131
|
+
);
|
|
132
|
+
} catch {
|
|
133
|
+
// Sin barras, el crítico no puede decir qué queda tapado: lo dirá él.
|
|
134
|
+
}
|
|
135
|
+
const nombre = nombreDeGeometria(nombreDeCaptura);
|
|
136
|
+
const pantalla = medidasDeImagen(bytes);
|
|
137
|
+
if (esCapturaDeUnControl(pantalla, arbol, barras)) {
|
|
138
|
+
process.stderr.write("(captura de un control, no de la pantalla entera: sin geometría para el crítico)\n");
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
mkdirSync(destino, { recursive: true });
|
|
142
|
+
writeFileSync(
|
|
143
|
+
join(destino, nombre),
|
|
144
|
+
JSON.stringify({ v: 1, captura: nombreDeCaptura, en: new Date().toISOString(), ...(pantalla ? { pantalla } : {}), ...(barras ? { barras } : {}), arbol })
|
|
145
|
+
);
|
|
146
|
+
return nombre;
|
|
147
|
+
}
|
|
@@ -26,6 +26,7 @@ import { execFileSync } from "node:child_process";
|
|
|
26
26
|
import { writeFileSync } from "node:fs";
|
|
27
27
|
import { join, basename } from "node:path";
|
|
28
28
|
import { serieAndroid, rutaDeAdb, NODE } from "../lib/dispositivo.mjs";
|
|
29
|
+
import { guardarGeometria } from "../lib/geometria.mjs";
|
|
29
30
|
|
|
30
31
|
const args = process.argv.slice(2);
|
|
31
32
|
if (args.includes("--help") || args.includes("-h")) {
|
|
@@ -183,3 +184,8 @@ try {
|
|
|
183
184
|
}
|
|
184
185
|
|
|
185
186
|
console.log(`captura guardada como ${nombre} (${bytes.length} bytes). Es de la vía nativa: enseña lo que el SISTEMA pinta, no lo que la app host dice.`);
|
|
187
|
+
|
|
188
|
+
// Y su GEOMETRÍA, en el mismo momento: el árbol de controles y las barras del sistema, para que el
|
|
189
|
+
// crítico visual compare tecla a tecla con la pantalla que ESTA captura enseña (`../lib/geometria.mjs`).
|
|
190
|
+
const geometria = guardarGeometria({ nombreDeCaptura: nombre, bytes, serie });
|
|
191
|
+
if (geometria !== undefined) console.log(`(con su geometría para el crítico: /hotswap/${geometria})`);
|
|
@@ -55,6 +55,7 @@ process.on("warning", (aviso) => {
|
|
|
55
55
|
|
|
56
56
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
57
57
|
import { join } from "node:path";
|
|
58
|
+
import { guardarGeometria } from "../lib/geometria.mjs";
|
|
58
59
|
import { dispositivoDeLaSesion, puertoConMotivo, retirarOpciones, serieAndroid, PUERTO_DEL_HOTSWAP } from "../lib/dispositivo.mjs";
|
|
59
60
|
|
|
60
61
|
/**
|
|
@@ -320,6 +321,13 @@ function sinBinarios(respuesta) {
|
|
|
320
321
|
const bytes = imagen === undefined ? Buffer.from(valor, "utf8") : Buffer.from(valor, "base64");
|
|
321
322
|
mkdirSync(destino, { recursive: true });
|
|
322
323
|
writeFileSync(join(destino, nombre), bytes);
|
|
324
|
+
// Una captura se guarda con su GEOMETRÍA (árbol de controles y barras del sistema) del mismo
|
|
325
|
+
// momento, para el crítico visual (`../lib/geometria.mjs`). La llamada de dentro pide un árbol, no
|
|
326
|
+
// una imagen, así que no vuelve a entrar aquí.
|
|
327
|
+
if (imagen !== undefined) {
|
|
328
|
+
const geometria = guardarGeometria({ nombreDeCaptura: nombre, bytes, serie: seriePedida });
|
|
329
|
+
if (geometria !== undefined) process.stderr.write(`(con su geometría para el crítico: /hotswap/${geometria})\n`);
|
|
330
|
+
}
|
|
323
331
|
// Con la ruta VIRTUAL delante, que es la que se puede volver a abrir. Una captura sigue
|
|
324
332
|
// diciendo `/artefactos/`, que es donde está y donde la tarjeta del hilo la enseña.
|
|
325
333
|
const virtual = imagen === undefined ? RUTA_VIRTUAL : "/artefactos/";
|
|
@@ -198,6 +198,21 @@ if (cambiados.length === 0) {
|
|
|
198
198
|
console.log("nada que recargar: los ficheros del proyecto ya son los del aparato.");
|
|
199
199
|
if (otros.length > 0 && posicionales.length === 0) {
|
|
200
200
|
console.log(`(no se comparan ${otros.length} fichero(s) que no son xne/js/css/ini/xml: iconos, bd, files…)`);
|
|
201
|
+
// Una FUENTE no se queda en «no se compara»: un `fontname` que no está en el aparato saca un diálogo de error y el
|
|
202
|
+
// control no se pinta (medido). Se dice con el remedio.
|
|
203
|
+
// Solo las NUEVAS o cambiadas (git: cada turno commitea lo que dejó, así que una recién añadida está sin
|
|
204
|
+
// commitear); sin git no se dice nada, mejor que avisar de todas en cada recarga.
|
|
205
|
+
let fuentes = [];
|
|
206
|
+
try {
|
|
207
|
+
fuentes = execFileSync("git", ["status", "--porcelain", "--", "fonts"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] })
|
|
208
|
+
.split("\n")
|
|
209
|
+
.filter((l) => l.trim() !== "");
|
|
210
|
+
} catch {
|
|
211
|
+
fuentes = [];
|
|
212
|
+
}
|
|
213
|
+
if (fuentes.length > 0) {
|
|
214
|
+
console.log(`(hay ${fuentes.length} fuente(s) nueva(s) o cambiada(s) en fonts/ y la recarga NO las sube: despliega entero con xone-desplegar-android)`);
|
|
215
|
+
}
|
|
201
216
|
}
|
|
202
217
|
process.exit(0);
|
|
203
218
|
}
|