agents-city 0.1.0 → 0.2.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.
@@ -9,7 +9,7 @@
9
9
  {
10
10
  "name": "city",
11
11
  "source": "./plugin",
12
- "version": "0.1.0",
12
+ "version": "0.2.0",
13
13
  "description": "Operate one autonomous city seat, its domain, role, repo support agents, goal, recognised skills and explicit roads to other cities."
14
14
  }
15
15
  ]
package/bin/hall.html CHANGED
@@ -16,62 +16,66 @@
16
16
  the local tool and the public site are visibly the same product: its paper,
17
17
  its ink, its violet. Nothing below this block names a colour directly. */
18
18
  :root{
19
- /* Night, the city's own light: an isometric map at 3am. The default. */
20
- --suelo:#131a24; --suelo2:#1b2431; --suelo3:#222d3c; --linea:#26313f; --linea2:#38495f;
21
- --tinta:#e8e2d4; --tinta2:#9aa7b8; --tinta3:#6b7889;
22
- --lampara:#f5c451; --carta:#e2604f; --verde:#4ec98a;
23
- --panel:#101720; --burbuja:#171f2a; --burbuja-mia:#27291f;
24
- --borde-acento:#65552d; --sobre-lampara:#1a1408; --lampara-viva:#ffd469;
25
- --chip-luz:36% 30%; --chip-tinta:#f3eee4;
26
- --tono-palabra:#aa93ff; --tono-evidencia:#61ace9; --tono-verificacion:#50ccb1;
19
+ /* Day is the default: the palette of agentscity.net, so the tool on your
20
+ machine and the public site are one product from the first second. Night
21
+ is one click away and remembered — it is the map's own light, not the
22
+ app's. */
23
+ --suelo:#f3f0e8; --suelo2:#fbfaf6; --suelo3:#ffffff;
24
+ --linea:#e2ddd0; --linea2:#cdc6b6;
25
+ --tinta:#1d2229; --tinta2:#515861; --tinta3:#7b8187;
26
+ --lampara:#7048e8; --carta:#dd5d3e; --verde:#5f8f2b;
27
+ --panel:#fbfaf6; --burbuja:#ffffff; --burbuja-mia:#f4efff;
28
+ --borde-acento:#c9b8ff; --sobre-lampara:#ffffff; --lampara-viva:#5331bd;
29
+ --chip-luz:52% 86%; --chip-tinta:#1d2229;
30
+ --tono-palabra:#6b46f0; --tono-evidencia:#2b7ab8; --tono-verificacion:#1c8d78;
27
31
  --hud:"Chakra Petch",system-ui,sans-serif; --texto:"Newsreader",Georgia,serif;
28
32
  --mono:"IBM Plex Mono",ui-monospace,monospace;
29
- --sombra:none;
33
+ --sombra:0 18px 40px -30px #1d222945;
30
34
 
31
- /* Day, declared once: the tokens of agentscity.net its paper, its ink, its
32
- violet so the tool on your machine and the public site are one product.
33
- The two rules below only MAP these; a palette written twice drifts twice. */
34
- --dia-suelo:#f3f0e8; --dia-suelo2:#fbfaf6; --dia-suelo3:#ffffff;
35
- --dia-linea:#e2ddd0; --dia-linea2:#cdc6b6;
36
- --dia-tinta:#1d2229; --dia-tinta2:#515861; --dia-tinta3:#7b8187;
37
- --dia-lampara:#7048e8; --dia-carta:#dd5d3e; --dia-verde:#5f8f2b;
38
- --dia-panel:#fbfaf6; --dia-burbuja:#ffffff; --dia-burbuja-mia:#f4efff;
39
- --dia-borde-acento:#c9b8ff; --dia-sobre-lampara:#ffffff; --dia-lampara-viva:#5331bd;
40
- --dia-chip-luz:52% 86%; --dia-chip-tinta:#1d2229;
41
- --dia-tono-palabra:#6b46f0; --dia-tono-evidencia:#2b7ab8;
42
- --dia-tono-verificacion:#1c8d78;
43
- --dia-sombra:0 18px 40px -30px #1d222945;
35
+ /* Night, declared once: the city's own light, an isometric map at 3am. The
36
+ two rules below only MAP these; a palette written twice drifts twice. */
37
+ --noche-suelo:#131a24; --noche-suelo2:#1b2431; --noche-suelo3:#222d3c;
38
+ --noche-linea:#26313f; --noche-linea2:#38495f;
39
+ --noche-tinta:#e8e2d4; --noche-tinta2:#9aa7b8; --noche-tinta3:#6b7889;
40
+ --noche-lampara:#f5c451; --noche-carta:#e2604f; --noche-verde:#4ec98a;
41
+ --noche-panel:#101720; --noche-burbuja:#171f2a; --noche-burbuja-mia:#27291f;
42
+ --noche-borde-acento:#65552d; --noche-sobre-lampara:#1a1408;
43
+ --noche-lampara-viva:#ffd469;
44
+ --noche-chip-luz:36% 30%; --noche-chip-tinta:#f3eee4;
45
+ --noche-tono-palabra:#aa93ff; --noche-tono-evidencia:#61ace9;
46
+ --noche-tono-verificacion:#50ccb1;
47
+ --noche-sombra:none;
44
48
  }
45
49
  /* One skin's worth of mapping, used by the explicit choice and by the system
46
50
  preference alike. An explicit choice always outranks the machine's. */
47
- :root[data-tema="claro"]{
48
- --suelo:var(--dia-suelo); --suelo2:var(--dia-suelo2); --suelo3:var(--dia-suelo3);
49
- --linea:var(--dia-linea); --linea2:var(--dia-linea2);
50
- --tinta:var(--dia-tinta); --tinta2:var(--dia-tinta2); --tinta3:var(--dia-tinta3);
51
- --lampara:var(--dia-lampara); --carta:var(--dia-carta); --verde:var(--dia-verde);
52
- --panel:var(--dia-panel); --burbuja:var(--dia-burbuja); --burbuja-mia:var(--dia-burbuja-mia);
53
- --borde-acento:var(--dia-borde-acento); --sobre-lampara:var(--dia-sobre-lampara);
54
- --lampara-viva:var(--dia-lampara-viva);
55
- --chip-luz:var(--dia-chip-luz); --chip-tinta:var(--dia-chip-tinta);
56
- --tono-palabra:var(--dia-tono-palabra); --tono-evidencia:var(--dia-tono-evidencia);
57
- --tono-verificacion:var(--dia-tono-verificacion);
58
- --sombra:var(--dia-sombra);
51
+ :root[data-tema="oscuro"]{
52
+ --suelo:var(--noche-suelo); --suelo2:var(--noche-suelo2); --suelo3:var(--noche-suelo3);
53
+ --linea:var(--noche-linea); --linea2:var(--noche-linea2);
54
+ --tinta:var(--noche-tinta); --tinta2:var(--noche-tinta2); --tinta3:var(--noche-tinta3);
55
+ --lampara:var(--noche-lampara); --carta:var(--noche-carta); --verde:var(--noche-verde);
56
+ --panel:var(--noche-panel); --burbuja:var(--noche-burbuja);
57
+ --burbuja-mia:var(--noche-burbuja-mia);
58
+ --borde-acento:var(--noche-borde-acento); --sobre-lampara:var(--noche-sobre-lampara);
59
+ --lampara-viva:var(--noche-lampara-viva);
60
+ --chip-luz:var(--noche-chip-luz); --chip-tinta:var(--noche-chip-tinta);
61
+ --tono-palabra:var(--noche-tono-palabra); --tono-evidencia:var(--noche-tono-evidencia);
62
+ --tono-verificacion:var(--noche-tono-verificacion);
63
+ --sombra:var(--noche-sombra);
59
64
  }
60
- @media(prefers-color-scheme:light){
61
- :root:not([data-tema="oscuro"]){
62
- --suelo:var(--dia-suelo); --suelo2:var(--dia-suelo2); --suelo3:var(--dia-suelo3);
63
- --linea:var(--dia-linea); --linea2:var(--dia-linea2);
64
- --tinta:var(--dia-tinta); --tinta2:var(--dia-tinta2); --tinta3:var(--dia-tinta3);
65
- --lampara:var(--dia-lampara); --carta:var(--dia-carta); --verde:var(--dia-verde);
66
- --panel:var(--dia-panel); --burbuja:var(--dia-burbuja); --burbuja-mia:var(--dia-burbuja-mia);
67
- --borde-acento:var(--dia-borde-acento); --sobre-lampara:var(--dia-sobre-lampara);
68
- --lampara-viva:var(--dia-lampara-viva);
69
- --chip-luz:var(--dia-chip-luz); --chip-tinta:var(--dia-chip-tinta);
70
- --tono-palabra:var(--dia-tono-palabra); --tono-evidencia:var(--dia-tono-evidencia);
71
- --tono-verificacion:var(--dia-tono-verificacion);
72
- --tono-palabra:var(--dia-tono-palabra); --tono-evidencia:var(--dia-tono-evidencia);
73
- --tono-verificacion:var(--dia-tono-verificacion);
74
- --sombra:var(--dia-sombra);
65
+ @media(prefers-color-scheme:dark){
66
+ :root:not([data-tema="claro"]){
67
+ --suelo:var(--noche-suelo); --suelo2:var(--noche-suelo2); --suelo3:var(--noche-suelo3);
68
+ --linea:var(--noche-linea); --linea2:var(--noche-linea2);
69
+ --tinta:var(--noche-tinta); --tinta2:var(--noche-tinta2); --tinta3:var(--noche-tinta3);
70
+ --lampara:var(--noche-lampara); --carta:var(--noche-carta); --verde:var(--noche-verde);
71
+ --panel:var(--noche-panel); --burbuja:var(--noche-burbuja);
72
+ --burbuja-mia:var(--noche-burbuja-mia);
73
+ --borde-acento:var(--noche-borde-acento); --sobre-lampara:var(--noche-sobre-lampara);
74
+ --lampara-viva:var(--noche-lampara-viva);
75
+ --chip-luz:var(--noche-chip-luz); --chip-tinta:var(--noche-chip-tinta);
76
+ --tono-palabra:var(--noche-tono-palabra); --tono-evidencia:var(--noche-tono-evidencia);
77
+ --tono-verificacion:var(--noche-tono-verificacion);
78
+ --sombra:var(--noche-sombra);
75
79
  }
76
80
  }
77
81
  *{box-sizing:border-box}
@@ -114,8 +118,9 @@ nav .pie a{font-family:var(--hud);font-size:12px;color:var(--tinta3);text-decora
114
118
  border:1px solid var(--borde-acento);background:var(--burbuja-mia);
115
119
  padding:7px 9px;border-radius:4px}
116
120
  .avisoVersion[hidden]{display:none}
117
- .temaBoton{align-self:flex-start;border:1px solid var(--linea2);background:var(--suelo3);
118
- color:var(--tinta2);font:11px var(--mono);padding:4px 9px;border-radius:4px;cursor:pointer}
121
+ .temaBoton{margin-top:12px;border:1px solid var(--linea2);background:var(--suelo3);
122
+ color:var(--tinta2);font:11px var(--mono);padding:5px 11px;border-radius:999px;
123
+ cursor:pointer;box-shadow:var(--sombra)}
119
124
  .temaBoton:hover{color:var(--lampara);border-color:var(--lampara)}
120
125
  nav .pie a:hover{color:var(--lampara)}
121
126
  #ciudades select{width:100%;background:var(--suelo);border:1px solid var(--linea2);
@@ -305,6 +310,51 @@ input[type=text]:focus,select:focus{outline:none;border-color:var(--lampara)}
305
310
  color:var(--lampara);font:12px var(--mono);padding:7px 14px;border-radius:5px;
306
311
  cursor:pointer}
307
312
  .rpgAlta:hover{background:var(--suelo);border-color:var(--lampara)}
313
+
314
+ /* ── the guided first run ────────────────────────────────────────────────── */
315
+ .bv{display:flex;flex-direction:column;gap:26px;max-width:min(760px,100%)}
316
+ .bv .prosa{max-width:100%}
317
+ .bv code.mono{word-break:break-all}
318
+ /* The live rail has nothing to show before a city exists, and it costs half
319
+ the screen exactly when the guide needs it. */
320
+ body.enGuia #livePanel,body.enGuia #liveResize{display:none}
321
+ body.enGuia #app{grid-template-columns:250px minmax(0,1fr)}
322
+ .bvPasos{display:flex;gap:6px;flex-wrap:wrap;align-items:center}
323
+ .bvPunto{display:flex;align-items:center;gap:6px;font:10px var(--hud);letter-spacing:.1em;
324
+ text-transform:uppercase;color:var(--tinta3)}
325
+ .bvPunto i{width:8px;height:8px;border-radius:50%;background:var(--linea2);display:block}
326
+ .bvPunto+.bvPunto{margin-left:10px}
327
+ .bvPunto.aqui{color:var(--lampara)}.bvPunto.aqui i{background:var(--lampara)}
328
+ .bvPunto.hecho i{background:var(--verde)}
329
+ .bvHola{display:grid;grid-template-columns:auto 1fr;gap:26px;align-items:start}
330
+ @media(max-width:820px){.bvHola{grid-template-columns:1fr}}
331
+ .bvArte{display:flex;flex-direction:column;gap:2px;padding-top:6px}
332
+ .bvCasa{width:92px;height:83px;margin-bottom:-26px;filter:drop-shadow(var(--sombra))}
333
+ .bvPaso{display:flex;flex-direction:column;gap:14px}
334
+ .bvPaso h1{font-size:26px;line-height:1.15}
335
+ .bvEtiqueta{font:600 10px var(--hud);letter-spacing:.14em;text-transform:uppercase;
336
+ color:var(--tinta3)}
337
+ .bvRejilla{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:8px}
338
+ .bvRejilla.bvTres{grid-template-columns:repeat(auto-fit,minmax(210px,1fr))}
339
+ .bvOpcion{text-align:left;display:flex;flex-direction:column;gap:4px;padding:12px 13px;
340
+ border:1px solid var(--linea2);border-radius:7px;background:var(--suelo2);cursor:pointer}
341
+ .bvOpcion b{font-family:var(--hud);font-size:13px;color:var(--tinta)}
342
+ .bvOpcion span{font-size:12px;line-height:1.45;color:var(--tinta3)}
343
+ .bvOpcion:hover{border-color:var(--lampara)}
344
+ .bvOpcion.elegida{border-color:var(--lampara);background:var(--burbuja-mia);
345
+ box-shadow:inset 3px 0 0 var(--lampara)}
346
+ .bvChips{display:flex;flex-wrap:wrap;gap:6px}
347
+ .bvChip{border:1px solid var(--linea2);background:var(--suelo2);color:var(--tinta2);
348
+ font:11px var(--mono);padding:5px 10px;border-radius:999px;cursor:pointer}
349
+ .bvChip:hover{border-color:var(--lampara)}
350
+ .bvChip.elegida{border-color:var(--lampara);background:var(--burbuja-mia);color:var(--tinta)}
351
+ .bvFichas{display:flex;flex-direction:column;gap:6px}
352
+ .bvFicha{display:flex;gap:10px;align-items:baseline;padding:9px 12px;border-radius:6px;
353
+ border:1px solid var(--linea);background:var(--suelo2)}
354
+ .bvFicha b{font-family:var(--hud);font-size:13px}
355
+ .bvFicha span{font:11px var(--mono);color:var(--tinta3)}
356
+ .bvBotones{display:flex;gap:9px;flex-wrap:wrap;margin-top:4px}
357
+ .bvSiguientes{display:flex;gap:9px;flex-wrap:wrap}
308
358
  /* The instruction editor: one file, one reader, saved where the agent lives. */
309
359
  #editorIns{position:fixed;inset:0;background:rgba(6,10,15,.72);z-index:60;
310
360
  display:flex;align-items:center;justify-content:center}
@@ -442,15 +492,14 @@ input[type=text]:focus,select:focus{outline:none;border-color:var(--lampara)}
442
492
  <span class="badge">town hall</span>
443
493
  <h2>Agents City</h2>
444
494
  <p>Manage one autonomous city: its seat, repo agents, roads and map. Everything here is a plain file you can also inspect by hand.</p>
495
+ <button type="button" id="temaBoton" class="temaBoton"
496
+ title="Daylight is the default; night is the map's own light">☾ Night</button>
445
497
  </div>
446
498
  <ol id="rail"></ol>
447
499
  <div id="ciudades" style="padding:0 18px"></div>
448
500
  <div class="pie">
449
501
  <p id="avisoVersion" class="avisoVersion" hidden></p>
450
502
  <a href="#" id="alWizard">Create another city →</a>
451
- <button type="button" id="temaBoton" class="temaBoton"
452
- title="Switch between the night city and the daylight palette of agentscity.net">
453
- ☾ Night</button>
454
503
  <span class="dato" id="dondeDatos"></span>
455
504
  </div>
456
505
  </nav>
package/bin/navegador.mjs CHANGED
@@ -108,6 +108,7 @@ function comprueba(texto, bien, detalle = '') {
108
108
 
109
109
  async function main() {
110
110
  const url = process.argv[2];
111
+ const ciudadVacia = process.argv[3] || '';
111
112
  if (!url) {
112
113
  console.error('usage: navegador.mjs <hall url>');
113
114
  process.exit(2);
@@ -228,12 +229,69 @@ async function main() {
228
229
  JSON.stringify(modal),
229
230
  );
230
231
 
232
+ // The guided first run, checked against a city with nobody in it: that is
233
+ // the only state where it appears, and it is the screen that used to say
234
+ // "Not drawn yet" to somebody arriving for the first time.
235
+ if (ciudadVacia) {
236
+ await cdp.manda('Page.navigate', {
237
+ url: url + '&city=' + encodeURIComponent(ciudadVacia),
238
+ });
239
+ for (let i = 0; i < 60; i += 1) {
240
+ const listo = await cdp.evalua('!!document.getElementById("rail") && !!window.PASE');
241
+ if (listo) break;
242
+ await dormir(250);
243
+ }
244
+ }
245
+ const guia = await cdp.evalua(`(async () => {
246
+ for (let i = 0; i < 40; i++) {
247
+ if (document.querySelector('.bvPasos')) break;
248
+ await new Promise(r => setTimeout(r, 250));
249
+ }
250
+ const pasos = [...document.querySelectorAll('.bvPunto')].map(p => p.textContent.trim());
251
+ const b = document.querySelector('[data-bv="siguiente"]');
252
+ if (b) b.click();
253
+ await new Promise(r => setTimeout(r, 900));
254
+ return {
255
+ pasos,
256
+ titulo: document.querySelector('.bvPaso h1')?.textContent?.trim() ?? '',
257
+ opciones: document.querySelectorAll('.bvOpcion').length,
258
+ };
259
+ })()`);
260
+ comprueba(
261
+ 'a city with no agents opens the guide, not an empty map',
262
+ (guia?.pasos?.length ?? 0) >= 4,
263
+ JSON.stringify(guia),
264
+ );
265
+ comprueba(
266
+ 'and its first question offers the work domains to choose from',
267
+ /work happens here/i.test(guia?.titulo ?? '') && (guia?.opciones ?? 0) > 3,
268
+ JSON.stringify(guia),
269
+ );
270
+
271
+ // Cities: a person must be able to start and retire one without a terminal.
272
+ const ciudades = await cdp.evalua(`(async () => {
273
+ const ir = [...document.querySelectorAll('nav li')].find(l => /cities/i.test(l.textContent));
274
+ if (!ir) return { falta: true };
275
+ ir.click();
276
+ await new Promise(r => setTimeout(r, 700));
277
+ return {
278
+ crear: !!document.getElementById('creaCiudad'),
279
+ campo: !!document.getElementById('nuevaCiudad'),
280
+ filas: document.querySelectorAll('.lista .fila').length,
281
+ };
282
+ })()`);
283
+ comprueba(
284
+ 'the Cities section can start a new city',
285
+ ciudades?.crear === true && ciudades?.campo === true,
286
+ JSON.stringify(ciudades),
287
+ );
288
+
231
289
  const errores = await cdp.evalua('window.__erroresDePagina ? window.__erroresDePagina.length : 0');
232
290
  comprueba('the page raised no uncaught errors while we drove it', errores === 0, String(errores));
233
291
  } finally {
234
292
  proceso.kill('SIGKILL');
235
293
  }
236
- console.log(`\n ${fallos ? `${fallos} failed` : 'browser ok'} — 11 checks\n`);
294
+ console.log(`\n ${fallos ? `${fallos} failed` : 'browser ok'} — 14 checks\n`);
237
295
  process.exit(fallos ? 1 : 0);
238
296
  }
239
297
 
package/bin/serve.py CHANGED
@@ -679,6 +679,7 @@ class Manejador(http.server.BaseHTTPRequestHandler):
679
679
  "/api/mapa": "p_mapa",
680
680
  "/api/sesion": "p_sesion",
681
681
  "/api/ciudades": "p_ciudades",
682
+ "/api/ciudad-archiva": "p_archiva_ciudad",
682
683
  "/api/roads": "p_roads",
683
684
  "/api/agente": "p_agente",
684
685
  "/api/agentes": "p_agentes",
@@ -1049,6 +1050,26 @@ class Manejador(http.server.BaseHTTPRequestHandler):
1049
1050
  {"ok": True, "city": datos, "address": cities.direccion(usuario, datos)}
1050
1051
  )
1051
1052
 
1053
+ def p_archiva_ciudad(self, q, cuerpo):
1054
+ """Take one city out of use — recoverably, and never the last one.
1055
+
1056
+ Not a delete: `cities.archiva` MOVES the folder into the owner's
1057
+ backups. A city is somebody's cards, deliberations and map, and a
1058
+ product that erases that on one click will erase the wrong one.
1059
+ """
1060
+ pedida = str(cuerpo.get("city") or "")
1061
+ usuario = seat.quien_soy()
1062
+ datos = cities.resuelve(pedida, usuario) if pedida else ""
1063
+ if not datos:
1064
+ return self.responde({"error": f"no city called {pedida!r}"}, codigo=404)
1065
+ try:
1066
+ copia = cities.archiva(datos, usuario)
1067
+ except (OSError, ValueError) as e:
1068
+ return self.responde({"error": str(e)}, codigo=409)
1069
+ return self.responde(
1070
+ {"ok": True, "backup": copia, "ciudades": lista_con(cities.actual(usuario))}
1071
+ )
1072
+
1052
1073
  def p_roads(self, q, cuerpo):
1053
1074
  origen = self.ciudad(q)
1054
1075
  accion = str(cuerpo.get("action") or "")
@@ -95,13 +95,20 @@ def main():
95
95
  )
96
96
  ciudad_de_prueba(datos)
97
97
 
98
+ # A SECOND city, deliberately empty: the guide only appears where there is
99
+ # nothing yet, and the first fixture has an agent so the sheets can render.
100
+ try:
101
+ vacia = serve.cities.crea("navtest", "sin-nadie", usar=False)
102
+ except (OSError, ValueError):
103
+ vacia = ""
104
+
98
105
  servidor = serve.Servidor(("127.0.0.1", 0), serve.Manejador)
99
106
  hilo = threading.Thread(target=servidor.serve_forever, daemon=True)
100
107
  hilo.start()
101
108
  url = f"http://127.0.0.1:{servidor.server_port}/?PASE={serve.PASE}"
102
109
  try:
103
110
  salida = subprocess.run(
104
- ["node", os.path.join(AQUI, "navegador.mjs"), url],
111
+ ["node", os.path.join(AQUI, "navegador.mjs"), url, vacia],
105
112
  capture_output=True,
106
113
  text=True,
107
114
  timeout=300,
package/bin/test-seat.py CHANGED
@@ -518,7 +518,7 @@ def maquinas_hostiles():
518
518
  "· an old cached Claude plugin is updated through Claude's own CLI",
519
519
  llamadas
520
520
  and llamadas[0][:4] == ["claude", "plugin", "update", "city@agents-city"]
521
- and "0.1.0" in salida.getvalue(),
521
+ and "0.2.0" in salida.getvalue(),
522
522
  f"calls={llamadas!r} output={salida.getvalue()!r}",
523
523
  )
524
524
 
package/bin/test-serve.py CHANGED
@@ -176,18 +176,24 @@ def main():
176
176
  cabecera = pagina.split("*{box-sizing", 1)[0]
177
177
  cuerpo_css = pagina.split("*{box-sizing", 1)[1].split("</style>", 1)[0]
178
178
  afirma(
179
- "· the day palette carries the tokens of agentscity.net",
180
- "--dia-suelo:#f3f0e8" in cabecera
181
- and "--dia-tinta:#1d2229" in cabecera
182
- and "--dia-lampara:#7048e8" in cabecera,
183
- cabecera[-400:],
179
+ daylight is the default skin, in the tokens of agentscity.net",
180
+ "--suelo:#f3f0e8" in cabecera
181
+ and "--tinta:#1d2229" in cabecera
182
+ and "--lampara:#7048e8" in cabecera,
183
+ cabecera[-500:],
184
+ )
185
+ afirma(
186
+ "· night is declared once and only mapped, never written twice",
187
+ "--noche-suelo:#131a24" in cabecera
188
+ and cabecera.count("--noche-suelo:#131a24") == 1,
189
+ cabecera[-500:],
184
190
  )
185
191
  afirma(
186
192
  "· an explicit choice outranks the machine's preference",
187
- '[data-tema="claro"]' in cabecera
188
- and "prefers-color-scheme:light" in cabecera
189
- and ':root:not([data-tema="oscuro"])' in cabecera,
190
- cabecera[-600:],
193
+ '[data-tema="oscuro"]' in cabecera
194
+ and "prefers-color-scheme:dark" in cabecera
195
+ and ':root:not([data-tema="claro"])' in cabecera,
196
+ cabecera[-700:],
191
197
  )
192
198
  import re as _re
193
199