Marco — app nativa de hábitos
Seguimiento de hábitos diarios que funciona sin conexión y sin cuenta en ningún servidor: todo vive en un SQLite local del teléfono. App nativa de Android construida sobre Sparkling, el shell de TikTok que empaqueta una app Lynx en un instalable de verdad.




Capturas con datos de ejemplo, no reales: cuenta recién creada con los cinco hábitos que siembra la app y siete semanas de historial generado.
Qué hace
Un toque marca o desmarca un hábito. Dejar pulsada la fila abre el registro parcial —¿leíste 15 de 20 páginas?— mientras una línea del color del hábito recorre el borde de la tarjeta. Los hábitos se borran arrastrando.
El análisis filtra por 7, 14 o 30 días, por mes o por año, compara contra el periodo anterior con sus diferencias y dibuja ocho gráficas SVG con zoom a pantalla completa. Los ajustes cubren categorías, recordatorio diario por notificación, backups a Descargas y borrado de datos.
Cómo está construido
Toda la lógica de negocio vive en src/api/ contra SQLite, un archivo por área —perfil, hábitos, registros, calendario, análisis, categorías, semilla— con marco.ts como barril. El puente src/db/native.ts solo serializa y deserializa: no tiene lógica.
Del lado Android, MarcoDbHelper.kt define el esquema y NativeDatabaseModule.kt ejecuta las consultas. El recordatorio diario son cuatro clases sobre AlarmManager —NativeReminderModule, ReminderScheduler, ReminderAlarmReceiver y BootReceiver— porque sobrevivir a un reinicio del teléfono no es gratis. LynxInputComponent.kt es un <input> propio.
Al crear una cuenta aparecen cinco hábitos de ejemplo con sus categorías, para que ninguna pantalla arranque vacía.
Lo que costó dispositivo
Lynx es un runtime joven y su motor de JavaScript, PrimJS, no es el navegador. Estas siete no se deducen leyendo el código: cada una salió de un fallo real y quedó documentada en su archivo.
Escribir el módulo nativo no basta
Hay que registrarlo en
SparklingApplication.ktvíaSparklingLynxConfig.Builder.addLynxModules(...). Sin eso,NativeModules.<Nombre>simplemente no existe del lado JS.<svg>no es parte del core de LynxEs un X-Element aparte,
org.lynxsdk.lynx:xelement-svg, forzado a la versión 3.7.0 enbuild.gradle.ktsy registrado a mano. Las ocho gráficas dependen de ello.Sin code splitting
Un import perezoso se compila como bundle Lynx aparte que no hereda el CSS global: texto invisible y pantallas que no se reemplazaban. El router va sin lazy a propósito.
El hilo principal no ve las estructuras
Dentro de un handler
'main thread'solo llegan los números. Recorrer una tabla declarada arriba del archivo revienta en silencio, sin excepción ni log.touchstartllega tarde ytouchenda veces no llegaDentro de un
scroll-viewel evento se entrega cuando el gesto ya no puede ser scroll; y si se monta un modal encima, la fila que lo abrió nunca recibe el final del gesto. De ahí el componente de deslizamiento propio.El
<input>ignora elcolorde CSSEl color del texto va por la prop
text-color, no por la hoja de estilos. Centralizado ensrc/lib/colors.tspara no volver a tropezar.Sin
IntlPrimJS no garantiza soporte de internacionalización, así que el formateo de fechas es manual en
src/lib/date.ts. Nada detoLocaleDateString.
Lo que falta
- iOS no está configurado: es una decisión, solo Android por ahora.
- El identificador del paquete sigue siendo el del scaffold,
com.example.sparkling.go. - Los backups se exportan como JSON plano, sin cifrar.
Stack
¿Te interesa cómo está resuelto algo de aquí, o tienes un problema parecido?