xonecode 0.34.2 → 0.36.3

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 (107) hide show
  1. package/THIRD_PARTY_NOTICES.md +30 -0
  2. package/apps/web/dist/assets/{EditorDeFichero-Cj0knNmP.js → EditorDeFichero-B9EyiuM6.js} +1 -1
  3. package/apps/web/dist/assets/index-C6MsC7A5.js +506 -0
  4. package/apps/web/dist/assets/index-DRNhZyYF.css +1 -0
  5. package/apps/web/dist/assets/{php-MVUx_K96.js → php-DCP5mtTp.js} +1 -1
  6. package/apps/web/dist/assets/{ruby-n-r-AXdi.js → ruby-B41ayseU.js} +1 -1
  7. package/apps/web/dist/index.html +2 -2
  8. package/dist/agent/config/settingsEnDisco.js +30 -0
  9. package/dist/agent/config/settingsEnDisco.js.map +1 -1
  10. package/dist/agent/dispositivos/instalacionEnMaquina.js +13 -0
  11. package/dist/agent/dispositivos/instalacionEnMaquina.js.map +1 -1
  12. package/dist/agent/motores/trueforge/sesionTrueforge.js +94 -5
  13. package/dist/agent/motores/trueforge/sesionTrueforge.js.map +1 -1
  14. package/dist/agent/remoto/ngrokEnMaquina.js +113 -0
  15. package/dist/agent/remoto/ngrokEnMaquina.js.map +1 -0
  16. package/dist/agent/remoto/puenteWebSocket.js +175 -0
  17. package/dist/agent/remoto/puenteWebSocket.js.map +1 -0
  18. package/dist/agent/remoto/tunelNgrok.js +245 -0
  19. package/dist/agent/remoto/tunelNgrok.js.map +1 -0
  20. package/dist/agent/tareas/juezDeTarea.js +53 -1
  21. package/dist/agent/tareas/juezDeTarea.js.map +1 -1
  22. package/dist/cli/main.js +2 -0
  23. package/dist/cli/main.js.map +1 -1
  24. package/dist/core/dispositivos.js +53 -0
  25. package/dist/core/dispositivos.js.map +1 -1
  26. package/dist/core/entrega.js +31 -2
  27. package/dist/core/entrega.js.map +1 -1
  28. package/dist/core/idiomaDeLaRespuesta.js +38 -0
  29. package/dist/core/idiomaDeLaRespuesta.js.map +1 -0
  30. package/dist/core/modoDeEscritura.js +4 -1
  31. package/dist/core/modoDeEscritura.js.map +1 -1
  32. package/dist/core/panorama.js +93 -0
  33. package/dist/core/panorama.js.map +1 -0
  34. package/dist/core/ports.js +100 -0
  35. package/dist/core/ports.js.map +1 -1
  36. package/dist/core/remoto.js +117 -0
  37. package/dist/core/remoto.js.map +1 -0
  38. package/dist/core/repartoDeEncargo.js +23 -0
  39. package/dist/core/repartoDeEncargo.js.map +1 -1
  40. package/dist/core/settings.js +83 -0
  41. package/dist/core/settings.js.map +1 -1
  42. package/dist/core/tareas.js +55 -1
  43. package/dist/core/tareas.js.map +1 -1
  44. package/dist/core/tunel.js +92 -0
  45. package/dist/core/tunel.js.map +1 -0
  46. package/dist/web/servidor/arranque.js +644 -66
  47. package/dist/web/servidor/arranque.js.map +1 -1
  48. package/dist/web/servidor/consolaWeb.js +52 -2
  49. package/dist/web/servidor/consolaWeb.js.map +1 -1
  50. package/dist/web/servidor/corredorDeTareas.js +15 -2
  51. package/dist/web/servidor/corredorDeTareas.js.map +1 -1
  52. package/dist/web/servidor/puenteRemoto.js +272 -0
  53. package/dist/web/servidor/puenteRemoto.js.map +1 -0
  54. package/dist/web/servidor/transporte.js.map +1 -1
  55. package/dist/web/servidor/vestibulo.js +52 -1
  56. package/dist/web/servidor/vestibulo.js.map +1 -1
  57. package/node_modules/@xone/xonecode-remoto/dist/base64url.d.ts +2 -0
  58. package/node_modules/@xone/xonecode-remoto/dist/base64url.js +19 -0
  59. package/node_modules/@xone/xonecode-remoto/dist/cifrado.d.ts +25 -0
  60. package/node_modules/@xone/xonecode-remoto/dist/cifrado.js +75 -0
  61. package/node_modules/@xone/xonecode-remoto/dist/contenido.d.ts +121 -0
  62. package/node_modules/@xone/xonecode-remoto/dist/contenido.js +191 -0
  63. package/node_modules/@xone/xonecode-remoto/dist/index.d.ts +6 -0
  64. package/node_modules/@xone/xonecode-remoto/dist/index.js +6 -0
  65. package/node_modules/@xone/xonecode-remoto/dist/servidor/salas.d.ts +46 -0
  66. package/node_modules/@xone/xonecode-remoto/dist/servidor/salas.js +128 -0
  67. package/node_modules/@xone/xonecode-remoto/dist/servidor/servidor.d.ts +31 -0
  68. package/node_modules/@xone/xonecode-remoto/dist/servidor/servidor.js +198 -0
  69. package/node_modules/@xone/xonecode-remoto/dist/sobre.d.ts +53 -0
  70. package/node_modules/@xone/xonecode-remoto/dist/sobre.js +69 -0
  71. package/node_modules/@xone/xonecode-remoto/dist/url.d.ts +12 -0
  72. package/node_modules/@xone/xonecode-remoto/dist/url.js +35 -0
  73. package/node_modules/@xone/xonecode-remoto/dist/version.d.ts +2 -0
  74. package/node_modules/@xone/xonecode-remoto/dist/version.js +2 -0
  75. package/node_modules/@xone/xonecode-remoto/package.json +41 -0
  76. package/node_modules/@xone/xonecode-remoto/web/assets/index-Bfa2gK3J.css +1 -0
  77. package/node_modules/@xone/xonecode-remoto/web/assets/index-qBo9eRDN.js +77 -0
  78. package/node_modules/@xone/xonecode-remoto/web/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  79. package/node_modules/@xone/xonecode-remoto/web/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  80. package/node_modules/@xone/xonecode-remoto/web/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  81. package/node_modules/@xone/xonecode-remoto/web/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  82. package/node_modules/@xone/xonecode-remoto/web/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  83. package/node_modules/@xone/xonecode-remoto/web/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  84. package/node_modules/@xone/xonecode-remoto/web/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  85. package/node_modules/@xone/xonecode-remoto/web/index.html +14 -0
  86. package/node_modules/ws/LICENSE +20 -0
  87. package/node_modules/ws/README.md +548 -0
  88. package/node_modules/ws/browser.js +8 -0
  89. package/node_modules/ws/index.js +22 -0
  90. package/node_modules/ws/lib/buffer-util.js +131 -0
  91. package/node_modules/ws/lib/constants.js +19 -0
  92. package/node_modules/ws/lib/event-target.js +292 -0
  93. package/node_modules/ws/lib/extension.js +203 -0
  94. package/node_modules/ws/lib/limiter.js +55 -0
  95. package/node_modules/ws/lib/permessage-deflate.js +530 -0
  96. package/node_modules/ws/lib/receiver.js +743 -0
  97. package/node_modules/ws/lib/sender.js +607 -0
  98. package/node_modules/ws/lib/stream.js +161 -0
  99. package/node_modules/ws/lib/subprotocol.js +62 -0
  100. package/node_modules/ws/lib/validation.js +152 -0
  101. package/node_modules/ws/lib/websocket-server.js +562 -0
  102. package/node_modules/ws/lib/websocket.js +1407 -0
  103. package/node_modules/ws/package.json +74 -0
  104. package/node_modules/ws/wrapper.mjs +21 -0
  105. package/package.json +6 -1
  106. package/apps/web/dist/assets/index-Ga8fTlWp.js +0 -499
  107. package/apps/web/dist/assets/index-R3kXj69E.css +0 -1
@@ -0,0 +1,69 @@
1
+ /**
2
+ * El tope de un sobre en BYTES, tal como viaja (el JSON entero del sobre). Quien manda tiene que
3
+ * mantener cada sobre por debajo: la carga va cifrada (AES-GCM) y en base64url, que suman en torno a
4
+ * un 33 % al contenido en claro. Por encima, el relé no contesta con un `error`: cierra el socket con
5
+ * el código 1009 de WebSocket (mensaje demasiado grande).
6
+ */
7
+ export const TOPE_DE_SOBRE = 1_000_000;
8
+ const MOTIVOS = [
9
+ "version",
10
+ "sala-inexistente",
11
+ "sala-ocupada",
12
+ "tope-de-moviles",
13
+ "ritmo",
14
+ "mensaje-ilegible",
15
+ ];
16
+ const esTexto = (x) => typeof x === "string" && x.length > 0;
17
+ /** Lee un sobre de un lado. Lo que no tiene exactamente una forma permitida es `undefined`. */
18
+ export function interpretarSobre(texto, lado) {
19
+ let o;
20
+ try {
21
+ const crudo = JSON.parse(texto);
22
+ if (typeof crudo !== "object" || crudo === null || Array.isArray(crudo))
23
+ return undefined;
24
+ o = crudo;
25
+ }
26
+ catch {
27
+ return undefined;
28
+ }
29
+ const r = sobreDe(o, lado);
30
+ return r;
31
+ }
32
+ function sobreDe(o, lado) {
33
+ if (lado === "anfitrion") {
34
+ if (o.t === "abrir" && esTexto(o.sala) && typeof o.v === "number")
35
+ return { t: "abrir", sala: o.sala, v: o.v };
36
+ if (o.t === "dato" && esTexto(o.carga)) {
37
+ if (o.para === undefined)
38
+ return { t: "dato", carga: o.carga };
39
+ return esTexto(o.para) ? { t: "dato", para: o.para, carga: o.carga } : undefined;
40
+ }
41
+ if (o.t === "cerrar")
42
+ return { t: "cerrar" };
43
+ return undefined;
44
+ }
45
+ if (lado === "movil") {
46
+ if (o.t === "entrar" && esTexto(o.sala) && typeof o.v === "number")
47
+ return { t: "entrar", sala: o.sala, v: o.v };
48
+ if (o.t === "dato" && esTexto(o.carga) && o.para === undefined)
49
+ return { t: "dato", carga: o.carga };
50
+ return undefined;
51
+ }
52
+ if (o.t === "abierta" || o.t === "cerrada")
53
+ return { t: o.t };
54
+ if (o.t === "entrada" && esTexto(o.id))
55
+ return { t: "entrada", id: o.id };
56
+ if (o.t === "presencia" && Array.isArray(o.moviles) && o.moviles.every(esTexto))
57
+ return { t: "presencia", moviles: o.moviles };
58
+ if (o.t === "anfitrion" && typeof o.conectado === "boolean")
59
+ return { t: "anfitrion", conectado: o.conectado };
60
+ if (o.t === "dato" && esTexto(o.carga)) {
61
+ if (o.de === undefined)
62
+ return { t: "dato", carga: o.carga };
63
+ return esTexto(o.de) ? { t: "dato", de: o.de, carga: o.carga } : undefined;
64
+ }
65
+ if (o.t === "error" && typeof o.motivo === "string" && MOTIVOS.includes(o.motivo)) {
66
+ return { t: "error", motivo: o.motivo };
67
+ }
68
+ return undefined;
69
+ }
@@ -0,0 +1,12 @@
1
+ /** La forma de un id de sala: 16 bytes aleatorios en base64url. La comprueban la URL y el relé. */
2
+ export declare const FORMA_DE_SALA: RegExp;
3
+ export interface Credenciales {
4
+ sala: string;
5
+ secreto: string;
6
+ }
7
+ export declare function generarCredenciales(): Credenciales;
8
+ /** `https://<host>/r/<sala>#<secreto>`: el secreto en el FRAGMENTO, que el navegador nunca envía. */
9
+ export declare function urlDeSesion(baseHttp: string, sala: string, secreto: string): string;
10
+ export declare function leerUrlDeSesion(href: string): Credenciales | undefined;
11
+ /** La base http(s) que sirve la web, a partir de la dirección ws(s) del puente. */
12
+ export declare function baseHttpDe(servidorWs: string): string;
@@ -0,0 +1,35 @@
1
+ import { aBase64url } from "./base64url.js";
2
+ const BYTES_DE_SALA = 16;
3
+ const BYTES_DE_SECRETO = 32;
4
+ /** La forma de un id de sala: 16 bytes aleatorios en base64url. La comprueban la URL y el relé. */
5
+ export const FORMA_DE_SALA = /^[A-Za-z0-9_-]{22}$/;
6
+ const SECRETO = /^[A-Za-z0-9_-]{43}$/;
7
+ export function generarCredenciales() {
8
+ const sala = crypto.getRandomValues(new Uint8Array(BYTES_DE_SALA));
9
+ const secreto = crypto.getRandomValues(new Uint8Array(BYTES_DE_SECRETO));
10
+ return { sala: aBase64url(sala), secreto: aBase64url(secreto) };
11
+ }
12
+ /** `https://<host>/r/<sala>#<secreto>`: el secreto en el FRAGMENTO, que el navegador nunca envía. */
13
+ export function urlDeSesion(baseHttp, sala, secreto) {
14
+ return `${baseHttp.replace(/\/+$/, "")}/r/${sala}#${secreto}`;
15
+ }
16
+ export function leerUrlDeSesion(href) {
17
+ let url;
18
+ try {
19
+ url = new URL(href);
20
+ }
21
+ catch {
22
+ return undefined;
23
+ }
24
+ const sala = /^\/r\/([^/]+)\/?$/.exec(url.pathname)?.[1];
25
+ const secreto = url.hash.replace(/^#/, "");
26
+ if (sala === undefined || !FORMA_DE_SALA.test(sala) || !SECRETO.test(secreto))
27
+ return undefined;
28
+ return { sala, secreto };
29
+ }
30
+ /** La base http(s) que sirve la web, a partir de la dirección ws(s) del puente. */
31
+ export function baseHttpDe(servidorWs) {
32
+ const url = new URL(servidorWs);
33
+ const protocolo = url.protocol === "wss:" ? "https:" : "http:";
34
+ return `${protocolo}//${url.host}`;
35
+ }
@@ -0,0 +1,2 @@
1
+ /** La versión del PROTOCOLO (lo que viaja), no la del paquete. Solo sube cuando cambia el sobre o el contenido. */
2
+ export declare const VERSION_DEL_PROTOCOLO = 1;
@@ -0,0 +1,2 @@
1
+ /** La versión del PROTOCOLO (lo que viaja), no la del paquete. Solo sube cuando cambia el sobre o el contenido. */
2
+ export const VERSION_DEL_PROTOCOLO = 1;
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@xone/xonecode-remoto",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ },
12
+ "./servidor": {
13
+ "types": "./dist/servidor/servidor.d.ts",
14
+ "import": "./dist/servidor/servidor.js"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "web"
20
+ ],
21
+ "dependencies": {
22
+ "ws": "^8.18.0"
23
+ },
24
+ "devDependencies": {
25
+ "@types/ws": "^8.5.12"
26
+ },
27
+ "scripts": {
28
+ "build": "tsc -p tsconfig.json",
29
+ "copiar-web": "node scripts/copiar-web.mjs",
30
+ "test": "vitest run --passWithNoTests",
31
+ "typecheck": "tsc -p tsconfig.json --noEmit"
32
+ },
33
+ "publishConfig": {
34
+ "access": "restricted"
35
+ },
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "git+https://github.com/sleiva/xonecode-server.git",
39
+ "directory": "packages/remoto"
40
+ }
41
+ }
@@ -0,0 +1 @@
1
+ @font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2) format("woff2-variations");unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2) format("woff2-variations");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2) format("woff2-variations");unicode-range:U+1F00-1FFF}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/assets/inter-greek-wght-normal-CkhJZR-_.woff2) format("woff2-variations");unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2) format("woff2-variations");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2) format("woff2-variations");unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/assets/inter-latin-wght-normal-Dx4kXJAl.woff2) format("woff2-variations");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}:root{--azul: #0c3c60;--azul-hondo: #05263a;--sobre-azul: #ffffff;--sobre-azul-suave: rgba(224, 242, 254, .75);--cian: #00a3e0;--cian-oscuro: #0284c7;--verde: #10b981;--ambar: #f59e0b;--rojo: #ef4444;--fondo: #ffffff;--superficie: #f4f6f9;--texto: #1b1f24;--tenue: #5b6470;--borde: #dfe3e8;--codigo: #eef1f5;--anadido: #e7f6ec;--quitado: #fdecec;--sombra: 0 1px 2px rgba(5, 38, 58, .06), 0 4px 16px rgba(5, 38, 58, .08);color-scheme:light}@media(prefers-color-scheme:dark){:root{--verde: #34d399;--rojo: #f87171;--fondo: #111418;--superficie: #1a1f25;--texto: #e6e9ee;--tenue: #9aa3ad;--borde: #2a3038;--codigo: #1f252c;--anadido: #12301d;--quitado: #3a1616;--sombra: 0 1px 2px rgba(0, 0, 0, .4), 0 4px 16px rgba(0, 0, 0, .35);color-scheme:dark}}*{box-sizing:border-box}html,body{margin:0}body{background:var(--fondo);color:var(--texto);font:15px/1.5 Inter Variable,system-ui,-apple-system,sans-serif;-webkit-font-smoothing:antialiased;-webkit-tap-highlight-color:transparent}button{font:inherit;cursor:pointer}button:disabled{opacity:.5;cursor:default}a{color:var(--cian-oscuro)}.pantalla{display:flex;flex-direction:column;min-height:100dvh;padding:0 16px env(safe-area-inset-bottom)}.cabecera{position:sticky;top:0;z-index:2;display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin:0 -16px;padding:calc(10px + env(safe-area-inset-top)) 16px 10px;background:var(--azul);color:var(--sobre-azul);border-bottom:1px solid var(--azul-hondo)}.marca{font-weight:700;letter-spacing:-.01em}.punto{width:9px;height:9px;border-radius:50%;flex:none;box-shadow:0 0 0 2px #ffffff2e}.punto.verde{background:var(--verde)}.punto.ambar{background:var(--ambar)}.punto.rojo{background:var(--rojo)}.donde{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--sobre-azul-suave);font-size:14px}.trabajando{color:var(--sobre-azul-suave);font-size:13px}.parar{background:var(--rojo);color:#fff;border:0;border-radius:8px;padding:5px 12px;font-size:13px;font-weight:600}.aviso-de-conexion{width:100%;margin:0;color:var(--sobre-azul-suave);font-size:13px}.chat{list-style:none;margin:0;padding:16px 0;flex:1;display:flex;flex-direction:column;gap:12px}.chat .usuario{align-self:flex-end;max-width:85%;background:var(--superficie);border:1px solid var(--borde);border-radius:16px 16px 4px;padding:8px 12px;white-space:pre-wrap;overflow-wrap:anywhere}.chat .asistente{min-width:0;max-width:100%;overflow-wrap:anywhere}.chat .sistema,.chat .herramientas{color:var(--tenue);font-size:13px}.chat .herramientas{padding-left:10px;border-left:2px solid var(--borde)}.consulta p{margin:0 0 4px}.consulta button,.pregunta button{margin:4px 6px 0 0;border:1px solid var(--borde);border-radius:999px;padding:7px 14px;background:var(--fondo);color:var(--texto)}.markdown>:first-child{margin-top:0}.markdown>:last-child{margin-bottom:0}.markdown p,.markdown ul,.markdown ol,.markdown pre,.markdown blockquote,.markdown .tabla{margin:0 0 10px}.markdown h1,.markdown h2,.markdown h3,.markdown h4{line-height:1.3;margin:16px 0 6px}.markdown h1{font-size:20px}.markdown h2{font-size:18px}.markdown h3{font-size:16px}.markdown h4{font-size:15px}.markdown ul,.markdown ol{padding-left:22px}.markdown ul{list-style:disc}.markdown li{margin:2px 0}.markdown li>p{margin:0}.markdown code{font:13px/1.45 ui-monospace,SFMono-Regular,Menlo,monospace;background:var(--codigo);border-radius:4px;padding:1px 4px}.markdown pre{background:var(--codigo);border-radius:10px;padding:10px 12px;overflow-x:auto}.markdown pre code{background:none;padding:0}.markdown blockquote{border-left:3px solid var(--cian);padding-left:10px;color:var(--tenue)}.markdown hr{border:0;border-top:1px solid var(--borde);margin:14px 0}.markdown .tabla{overflow-x:auto}.markdown table{border-collapse:collapse;font-size:13px}.markdown th,.markdown td{border:1px solid var(--borde);padding:5px 8px;text-align:left;vertical-align:top}.markdown th{background:var(--superficie)}.imagen-omitida{color:var(--tenue)}.aprobacion,.tarea-que-espera{border:1px solid var(--borde);border-radius:14px;padding:12px;margin:12px 0;background:var(--fondo);box-shadow:var(--sombra)}.aprobacion h2{font-size:16px;margin:0 0 8px}.aprobacion h3{font-size:13px;margin:10px 0 0;word-break:break-all;font-family:ui-monospace,Menlo,monospace}.origen{color:var(--tenue);font-size:13px;margin:0}.diff{overflow-x:auto;font:12px/1.45 ui-monospace,Menlo,monospace;margin:6px 0;background:var(--superficie);border-radius:8px;padding:6px 0}.diff .anadido{background:var(--anadido)}.diff .quitado{background:var(--quitado)}.acciones{display:flex;gap:8px;justify-content:flex-end;margin-top:8px}.acciones button{border-radius:10px;padding:9px 16px;border:1px solid var(--borde);background:var(--fondo);color:var(--texto);font-weight:500}.acciones .primario{background:var(--azul);color:var(--sobre-azul);border-color:var(--azul)}.fallo{color:var(--rojo);font-size:13px;margin:0}.aviso-de-pregunta{border:1px solid var(--ambar);border-radius:14px;padding:10px 12px;margin:12px 0}.tarea-que-espera h3{font-size:16px;margin:0}.tarea-que-espera .motivo{margin:6px 0;overflow-wrap:anywhere}.tarea-que-espera textarea{width:100%;resize:vertical;border:1px solid var(--borde);border-radius:10px;padding:8px 10px;font:inherit;background:var(--fondo);color:var(--texto);margin-bottom:8px}.compositor{position:sticky;bottom:0;z-index:1;display:flex;flex-wrap:wrap;align-items:flex-end;gap:8px;margin:0 -8px;padding:8px 8px 12px;background:linear-gradient(to bottom,transparent,var(--fondo) 12px)}.compositor .fallo{flex:1 1 100%;padding:0 4px}.compositor textarea{flex:1;min-width:0;resize:none;max-height:40dvh;field-sizing:content;min-height:44px;border:1px solid var(--borde);border-radius:22px;padding:11px 16px;font:inherit;background:var(--fondo);color:var(--texto);box-shadow:var(--sombra)}.compositor textarea:focus{outline:none;border-color:var(--cian)}.compositor textarea::placeholder{color:var(--tenue)}.compositor .enviar{flex:none;width:44px;height:44px;border:0;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;background:var(--azul);color:var(--sobre-azul)}.compositor .enviar:active{background:var(--cian)}.pantalla.con-pestanas{--alto-pestanas: 56px}.pantalla.con-pestanas .compositor{bottom:var(--alto-pestanas)}.pestanas{position:sticky;bottom:0;z-index:2;display:flex;margin:0 -16px;background:var(--fondo);border-top:1px solid var(--borde)}.pestanas button{flex:1;height:var(--alto-pestanas, 56px);border:0;background:transparent;color:var(--tenue);display:flex;align-items:center;justify-content:center;gap:6px;font-weight:500}.pestanas button.activa{color:var(--azul);font-weight:600;box-shadow:inset 0 2px 0 var(--azul)}@media(prefers-color-scheme:dark){.pestanas button.activa{color:var(--cian);box-shadow:inset 0 2px 0 var(--cian)}}.insignia{min-width:20px;padding:0 6px;border-radius:10px;background:var(--rojo);color:#fff;font-size:12px;line-height:20px;text-align:center}.panel{flex:1;padding:4px 0 12px}.panel h2{font-size:12px;text-transform:uppercase;letter-spacing:.06em;color:var(--tenue);margin:18px 0 6px}.panel .aprobacion h2{font-size:16px;text-transform:none;letter-spacing:0;color:var(--texto);margin:0 0 8px}.tenue{color:var(--tenue);font-size:14px;margin:4px 0}.filas{list-style:none;margin:0;padding:0;border:1px solid var(--borde);border-radius:14px;overflow:hidden}.filas li{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 10px;padding:10px 12px;border-bottom:1px solid var(--borde)}.filas li:last-child{border-bottom:0}.fila-titulo{font-weight:600;flex:1 1 100%;overflow-wrap:anywhere}.estado{font-size:12px;font-weight:500;border-radius:10px;padding:0 8px;border:1px solid currentColor}.estado.rojo{color:var(--rojo)}.estado.ambar{color:var(--ambar)}.estado.verde{color:var(--verde)}.estado.gris{color:var(--tenue)}