readme: capturas con el tema Acampada

Las ocho capturas, la portada y el esquema de la interfaz rehechos sobre la
1.3.3 con el tema Acampada. generar_portada.py compone la portada a partir
de las capturas y dibuja el esquema; el texto vive en el script. Novedades:
tema, fotos que no se pueden limpiar y DNS.
This commit is contained in:
oasis-bot 2026-10-19 02:06:05 +02:00
parent 9a4ca6b2b3
commit ac921c9d56
11 changed files with 145 additions and 1 deletions

View file

@ -26,12 +26,19 @@ Android 7 o superior, 64 bits · se instala junto al Oasis oficial sin pisarlo
- **Al día con Oasis 1.3.3**: karma en el perfil, salas con directo, fotos selladas en
chats privados y tribus, visibilidad de menciones y las traducciones nuevas.
- **Tema Acampada por defecto**: blanco, negro y cian, títulos en Anton y botones que
se hunden al pulsar, con los colores de acampada26s.net. Los otros temas siguen en
*Ajustes → Tema*.
- **Arranca en modo activistas**: chats, Karvan, pads, foro, imágenes, vídeos, wiki y
blogs. Todo cabe en una pantalla; el resto se activa en *Ajustes → Workflows*.
- **Filtros en fichas**: en chats, feed, foros o pads los filtros ya no son botones
gigantes que tapan el contenido, sino fichas pequeñas en fila.
- **Tarjetas más limpias**: borrar, fijar, enlazar y abrir van en una fila de iconos.
- **Karvan**: tus mensajes salen a la derecha también al volver a entrar en la sala.
- **Fotos más seguras**: si una foto no se puede limpiar de metadatos (ubicación,
cámara…), no se sube y se avisa. Antes se subía tal cual.
- **DNS**: primero el resolutor del sistema, que respeta la VPN y el DNS privado de
Android; los públicos quedan de respaldo.
---
@ -41,7 +48,7 @@ Android 7 o superior, 64 bits · se instala junto al Oasis oficial sin pisarlo
|---|---|---|
| **Salas efímeras** | — | Karvan: mensajes que solo viven en memoria y se autodestruyen |
| **Videollamadas** | — | Audio y vídeo por WebRTC, sin servidores de terceros |
| **Interfaz de móvil** | El menú de escritorio en una pantalla pequeña | Lanzador de módulos, filtros en fichas y barra de cuatro atajos |
| **Interfaz de móvil** | El menú de escritorio en una pantalla pequeña | Tema Acampada, lanzador de módulos, filtros en fichas y barra de cuatro atajos |
| **Primer arranque** | Casi sesenta módulos encendidos | Modo activistas: lo justo para organizarse |
| **Varias identidades** | Una por instalación | Varias en el mismo móvil, con aviso si dos comparten clave |
| **Respuesta citada** | — | Responder a un mensaje concreto dentro de un chat |

Binary file not shown.

Before

Width:  |  Height:  |  Size: 242 KiB

After

Width:  |  Height:  |  Size: 238 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 96 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

After

Width:  |  Height:  |  Size: 133 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 110 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 115 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 94 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 116 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 65 KiB

Before After
Before After

View file

@ -0,0 +1,137 @@
#!/usr/bin/env python3
"""Portada y esquema de la interfaz del README, con la paleta del tema Acampada.
Las capturas de movil (01..07) se hacen aparte, con la app arrancada; este script
solo compone 00-portada.png a partir de ellas y dibuja esquema-interfaz.png.
El texto vive aqui: se cambia y se relanza, nunca se retocan los PNG a mano.
python3 docs/capturas/generar_portada.py
"""
import math
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont
HERE = Path(__file__).resolve().parent
ANTON = HERE.parents[1] / "src/client/assets/fonts/Anton-Regular.ttf"
SANS = "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
BG, FG, NEON, MUTED = "#ffffff", "#000000", "#00e5ff", "#555555"
def font(path, size):
return ImageFont.truetype(str(path), size)
def phone(shot, width):
"""Una captura dentro de un marco negro grueso con la sombra cian de los botones."""
im = Image.open(HERE / shot).convert("RGB")
h = round(im.height * width / im.width)
im = im.resize((width, h), Image.LANCZOS)
pad, radius, shadow = 10, 34, 10
frame = Image.new("RGBA", (width + 2 * pad, h + 2 * pad + shadow), (0, 0, 0, 0))
d = ImageDraw.Draw(frame)
d.rounded_rectangle((0, shadow, width + 2 * pad - 1, h + 2 * pad + shadow - 1), radius, fill=NEON)
d.rounded_rectangle((0, 0, width + 2 * pad - 1, h + 2 * pad - 1), radius, fill=FG)
mask = Image.new("L", im.size, 0)
ImageDraw.Draw(mask).rounded_rectangle((0, 0, width - 1, h - 1), radius - pad, fill=255)
frame.paste(im, (pad, pad), mask)
return frame
def portada():
W, H = 1600, 1100
out = Image.new("RGB", (W, H), BG)
d = ImageDraw.Draw(out)
d.text((80, 40), "OASIS MOBILE", font=font(ANTON, 92), fill=FG)
bottom = d.textbbox((80, 40), "OASIS MOBILE", font=font(ANTON, 92))[3]
d.rectangle((80, bottom + 12, 420, bottom + 20), fill=FG)
d.text((80, 196), "Rama PRUEBAS del hacklab · base Oasis 1.3.3 · tema Acampada, pensado para el pulgar",
font=font(SANS, 27), fill=MUTED)
shots = [("01-portada.png", "Lanzador de módulos"), ("03-karvan.png", "Karvan: salas efímeras"),
("04-foros.png", "Filtros en fichas"), ("06-barra.png", "Tu barra de 4 atajos")]
pw, gap, top = 320, 50, 270
x = (W - (4 * (pw + 20) + 3 * gap)) // 2
cap = font(ANTON, 34)
for shot, label in shots:
fr = phone(shot, pw)
out.paste(fr, (x, top), fr)
tw = d.textlength(label.upper(), font=cap)
d.text((x + (fr.width - tw) / 2, top + fr.height + 18), label.upper(), font=cap, fill=FG)
x += fr.width + gap
out.save(HERE / "00-portada.png", optimize=True)
def caja(d, box, titulo, lineas, relleno=BG):
x0, y0, x1, y1 = box
d.rounded_rectangle((x0, y0 + 6, x1, y1 + 6), 18, fill=FG)
d.rounded_rectangle(box, 18, fill=relleno, outline=FG, width=4)
t = font(ANTON, 30)
d.text(((x0 + x1 - d.textlength(titulo, font=t)) / 2, y0 + 18), titulo, font=t, fill=FG)
s = font(SANS, 18)
for i, l in enumerate(lineas):
d.text(((x0 + x1 - d.textlength(l, font=s)) / 2, y0 + 70 + i * 26), l, font=s, fill=FG)
def esquema():
W, H = 1400, 840
out = Image.new("RGB", (W, H), BG)
d = ImageDraw.Draw(out)
d.text((46, 26), "LA INTERFAZ DE MÓVIL, POR DENTRO", font=font(ANTON, 46), fill=FG)
bottom = d.textbbox((46, 26), "LA INTERFAZ DE MÓVIL, POR DENTRO", font=font(ANTON, 46))[3]
d.rectangle((46, bottom + 10, 260, bottom + 16), fill=FG)
d.text((46, bottom + 30), "Casi sesenta módulos no caben en una pantalla de teléfono. Se reparten en tres piezas.",
font=font(SANS, 18), fill=MUTED)
# el movil esquematico
px0, py0, px1, py1 = 110, 170, 470, 790
d.rounded_rectangle((px0, py0, px1, py1), 30, outline=FG, width=5)
d.rounded_rectangle((px0 + 16, py0 + 20, px1 - 16, py0 + 84), 14, fill=BG, outline=FG, width=3)
s = font(SANS, 15)
d.text((px0 + 46, py0 + 42), "logo · (Personal | Social) · avatar", font=s, fill=FG)
secciones = [("COMUNIDAD", 8), ("BIBLIOTECA", 2), ("HERRAMIENTAS", 5)]
y = py0 + 110
for nombre, n in secciones:
d.text((px0 + 40, y), nombre, font=font(ANTON, 17), fill=FG)
d.line((px0 + 40, y + 26, px1 - 40, y + 26), fill=FG, width=3)
y += 38
for i in range(n):
cx = px0 + 48 + (i % 4) * 76
if i and i % 4 == 0:
y += 62
d.rounded_rectangle((cx, y + 4, cx + 48, y + 52), 12, fill=FG)
d.rounded_rectangle((cx, y, cx + 48, y + 48), 12, fill=BG, outline=FG, width=3)
y += 72
d.rounded_rectangle((px0 + 16, py1 - 84, px1 - 16, py1 - 20), 14, fill=NEON, outline=FG, width=3)
d.text((px0 + 92, py1 - 62), "barra: 4 atajos que eliges tú", font=s, fill=FG)
bx0, bx1 = 560, 1320
cajas = [
((bx0, 190, bx1, 340), "ARRIBA — LA BARRA SUPERIOR",
["El logo vuelve al inicio y el avatar abre tu perfil.",
"Con muchos módulos activos salen los filtros Personal y Social;",
"en modo activistas todo cabe en una pantalla y no hacen falta."], BG),
((bx0, 375, bx1, 565), "EN MEDIO — EL LANZADOR",
["Los módulos activos en una rejilla de iconos, por secciones,",
"como el cajón de apps del móvil. Los nombres son los del Oasis",
"oficial: Comunidad, Biblioteca, Herramientas…",
"Solo en la pantalla de inicio; dentro de cada módulo, filtros en fichas."], BG),
((bx0, 600, bx1, 750), "ABAJO — LOS ATAJOS",
["Cuatro módulos a tu elección: ni uno más, que es para el pulgar.",
"Se cambian en Herramientas → Tu barra. La elección se guarda",
"en tu nodo, así que sigue ahí después de cerrar la aplicación."], NEON),
]
flechas = [((px1 + 10, py0 + 52), (bx0 - 12, 262)), ((px1 + 10, 440), (bx0 - 12, 470)),
((px1 + 10, py1 - 52), (bx0 - 12, 675))]
for (a, b) in flechas:
ang = math.atan2(b[1] - a[1], b[0] - a[0])
tip = lambda da, r: (b[0] - r * math.cos(ang + da), b[1] - r * math.sin(ang + da))
d.line((a, tip(0, 12)), fill=FG, width=4)
d.polygon([b, tip(0.45, 20), tip(-0.45, 20)], fill=FG)
for box, t, l, r in cajas:
caja(d, box, t, l, r)
out.save(HERE / "esquema-interfaz.png", optimize=True)
if __name__ == "__main__":
portada()
esquema()