Commit Diff


commit - /dev/null
commit + 846c407395396cb54d4d9e28dbfcc83aeee750fd
blob - /dev/null
blob + 5539c9bfb806324230bda0e8608fcffa54264f74 (mode 644)
--- /dev/null
+++ .guix-channel
@@ -0,0 +1,3 @@
+(channel
+  (version 0)
+  (directory "."))
blob - /dev/null
blob + 90f11f5e22fe30d9a79f7020db750deb98eaf6aa (mode 644)
--- /dev/null
+++ NOTES.md
@@ -0,0 +1,108 @@
+# Notas de la evaluación/migración NixOS → Guix
+
+Contexto y decisiones detrás de este intento de migración, para no perder
+el hilo entre sesiones. Origen: `/nixdots`, la config NixOS real en uso hoy
+en esta misma laptop (Acer Nitro AN515-54, Intel UHD 630 + Nvidia GTX 1050).
+
+## Objetivo
+
+Reemplazar NixOS por Guix System como daily driver real en esta PC (no solo
+una prueba en VM). Investigación exhaustiva hecha antes de escribir nada,
+a pedido explícito del usuario.
+
+## Bloqueadores identificados y su resolución
+
+1. **Driver Nvidia legacy 580 (Pascal)**: ✅ resuelto. Está empaquetado y es
+   el *default* de nonguix, versión idéntica (580.173.02) a la que ya se usa.
+
+2. **PRIME sync**: no existe como mecanismo declarativo en Guix/nonguix
+   (solo *offload*). Pero se descubrió algo importante verificando el
+   sistema real: **`prime.sync.enable` ya es un no-op en NixOS hoy**, porque
+   la NixOS Wiki confirma que PRIME sync/reverse-sync son X11-only y no
+   funcionan bajo Wayland -- y esta laptop corre Hyprland (Wayland puro).
+   Confirmado en vivo: `nvidia-smi` muestra la GPU en 0% de uso con la
+   sesión Wayland activa. Es decir, ya se está en modo "Intel maneja el
+   desktop" de facto -- replicar esto con PRIME offload puro en Guix NO es
+   downgrade.
+
+3. **Bug de compositor en HDMI** (motivo original para elegir sync):
+   confirmado con hardware real de esta laptop (`lspci`, `/sys/class/drm`)
+   que `HDMI-A-1` está cableado físicamente **directo a la Nvidia**
+   (card0, PCI 01:00.0) -- no pasa por la Intel. El bug documentado de
+   "reverse-PRIME" nace del cruce de buffers entre GPUs; con el HDMI nativo
+   de la propia Nvidia ese mecanismo no se invoca. Hyprland/wlroots
+   soportan declarar ambas GPUs vía `AQ_DRM_DEVICES` (backend Aquamarine) --
+   hay que declarar explícitamente la Nvidia ahí o la salida HDMI se
+   congela, no es automático.
+   **Plan**: PRIME offload puro para `eDP-1` (interna, cableada a Intel) +
+   Nvidia nativa sin PRIME para `HDMI-A-1` vía `AQ_DRM_DEVICES`.
+   "Usar siempre la Nvidia" para TODO (ignorando la Intel) NO es posible:
+   `eDP-1` está físicamente en el bus de la Intel, este equipo es muxless
+   (sin mux switch de hardware, confirmado -- los Acer Nitro AN515 de esta
+   generación 2019 no lo tienen).
+
+4. **Noctalia**: viable vía el canal propio del proyecto upstream
+   (`noctalia.scm` en la raíz de noctalia-dev/noctalia, mejor mantenido que
+   los canales de terceros Rosenthal/Panther), pero es rolling-release
+   (sigue HEAD de main) y `noctalia-greeter` no existe empaquetado en
+   ningún lado. **Decisión del usuario**: no usar Noctalia. Alternativa
+   elegida: **DankMaterialShell (DMS)** sobre **Quickshell** (el motor real,
+   oficial en el canal `guix`, sin fork). DMS no depende de un fork propio
+   de Quickshell (a diferencia de Noctalia y de caelestia-shell, que exige
+   `quickshell-git` bleeding-edge -- verificado en su propio README:
+   "this has to be the git version, not the latest tagged version", y
+   confirmado que no existe `quickshell-git` en ningún canal indexado).
+   DMS trae tema Gruvbox Material de fábrica y greeter propio
+   (`dms-greeter`), pero DMS mismo no está empaquetado en Guix -- instalar
+   manualmente.
+
+5. **Hyprland**: confirmado contra el código fuente real de Guix (git
+   savannah, `gnu/packages/window-management.scm`) que la versión actual es
+   **0.55.4** -- exactamente la misma que usa hoy NixOS y contra la que está
+   escrito `hyprland.lua` completo (API `hl.dsp.*`, gestos, etc.). Debería
+   portar con cambios mínimos.
+
+6. **Tailscale**: viable. `tailscale-service-type` mantenido en varios
+   canales (umanwizard/guix-tailscale compila desde fuente; Rosenthal con
+   uso real reportado en producción). El oneshot de reintentos para fijar
+   el exit-node de Mullvad y el sudoers granular (`security.sudo.extraRules`
+   no tiene equivalente -- Guix usa un `plain-file` con el contenido crudo
+   de `/etc/sudoers`) hay que escribirlos a mano en Scheme/Shepherd --
+   trabajo real pero acotado (~medio día).
+
+## Paquetes personales (ale/packages/*.scm)
+
+Traducción de `/nixdots/pkgs/*.nix` + los flakes propios (psysonic,
+nezzontli-ctl, slides). Dificultad real MUY dispar entre casos -- una
+evaluación general inicial (sin ver manifests reales) subestimó slides y
+psysonic:
+
+| Paquete | Estado | Notas |
+|---|---|---|
+| `nezzontli-ctl` | Escrito completo (`nezzontli-ctl.scm`) | 3 de 6 deps Python faltaban en Guix (cowsay, textual-image, flatlatex) -- las 3 son triviales, empaquetadas ahí mismo |
+| `clamui` | Escrito completo (`clamui.scm`) | El caso más mecánico -- las 10 dependencias YA están en el canal oficial `guix`, sin faltantes |
+| `slides` | Esqueleto escrito, NO compila todavía (`slides.scm`) | Depende del ecosistema `charm.land/*/v2` (bubbletea/lipgloss/bubbles/wish/log v2) -- NO está en Guix (solo las v1 viejas, incompatibles). Camino real: `guix import go -r --pin-versions <módulo>@<versión>` por cada uno, pendiente de correr con Guix real |
+| `librepods` | Esqueleto escrito, NO compila todavía (`librepods.scm`) | 577 crates en su Cargo.lock -- imposible escribir a mano. Camino real: `guix import crate -r --lockfile=Cargo.lock librepods` con Guix real. Wrapping de libs runtime (vulkan/wayland/xkbcommon) SÍ resuelto en el .scm (dlopen, no lo agarra validate-runpath) |
+| `psysonic` | NO escrito, ver `packages/PENDIENTES.md` | Mucho más grande de lo estimado: Tauri 2 + 6 crates propios + pila de audio pesada (symphonia/rodio/rusqlite bundled) + frontend React 19/Vite completo. `rusqlite` con feature `bundled` choca con la política de unbundling de Guix. Semanas, no días |
+| `x-minecraft-launcher` | NO escrito, pendiente manual | Sin equivalente a `appimageTools` en Guix. Instalación manual del AppImage, igual que ya aceptaron para LibrePods-viejo en la config Nix actual |
+
+**Ninguno de los `sha256` puestos en los .scm de arriba es real** -- son
+placeholders explícitos (`AJUSTAR`), porque esta sesión corre en NixOS sin
+Guix instalado, sin forma de correr `guix download`/`guix hash` de verdad.
+Hay que recalcularlos en la VM antes de intentar construir cualquiera de
+estos paquetes.
+
+## Pendiente
+
+- Levantar la VM QEMU (imagen QCOW2 precompilada de Guix 1.5.0) para poder
+  correr `guix download`, `guix import crate/go`, y probar de verdad
+  Quickshell+DMS.
+- Escribir `config.scm` (sistema) y `home-configuration.scm` (Guix Home)
+  reales, cubriendo lo que sí mapea directo: base, red, YubiKey/pcscd,
+  Bluetooth, zram, Btrfs, Hyprland+AQ_DRM_DEVICES, Tailscale, TeX/
+  LibreOffice, fuentes, etc.
+- Terminar los `.scm` de slides/librepods/psysonic una vez con Guix real
+  disponible para correr los importadores.
+- Escribir el greeter (`greetd` + algo, ya que ni noctalia-greeter ni
+  dms-greeter están empaquetados -- dms-greeter habría que instalarlo a
+  mano igual que DMS).
blob - /dev/null
blob + 74b7a87a4567a9793aa378f92aa49341fbcc0329 (mode 644)
--- /dev/null
+++ ale/packages/PENDIENTES.md
@@ -0,0 +1,53 @@
+# Paquetes pendientes (no escritos como .scm todavía)
+
+## psysonic (Tauri 2 + React 19) — más grande de lo estimado inicialmente
+
+Cargo.toml y package.json reales fetched en vivo (2026-07-31). Esto es
+sustancialmente más grande que un "side project" chico:
+
+- **Rust**: workspace con 6 crates internos propios (`psysonic-core`,
+  `-analysis`, `-audio`, `-library`, `-syncfs`, `-integration`) + Tauri 2
+  completo (tray-icon, protocol-asset, specta/tauri-specta para bindings
+  TS) + una pila de audio pesada: `symphonia` (con casi todos los formatos:
+  flac/mp3/aac/alac/isomp4/vorbis/ogg/wav/adpcm), `rodio`, `souvlaki`,
+  `ebur128`, `biquad`, `ringbuf`, `discord-rich-presence`, `lofty`, `id3`,
+  `rusqlite` con **feature `bundled`** (vendoriza el C de SQLite dentro del
+  crate -- esto es exactamente lo que la política de "unbundling" de Guix
+  suele rechazar; hay `sqlite` ya empaquetado en Guix, la ruta correcta
+  sería usar el feature no-bundled y linkear contra el paquete del sistema,
+  no algo mecánico). En Linux además `webkit2gtk` 2.0 + `zbus`.
+- **Frontend**: React 19, Vite, ~15 fuentes variables `@fontsource-variable`,
+  `@tanstack/react-virtual`, `i18next`, y la lista seguía cortada al momento
+  de este fetch (no se relevó completa). El ecosistema de paquetes npm en
+  Guix es, confirmado en la ronda de investigación anterior, el eslabón más
+  débil (sin equivalente maduro a `buildNpmPackage`/`fetchNpmDeps`) -- la
+  ruta realista es vendorizar el `dist/` ya buildeado (con Node/npm fuera
+  del sandbox de Guix, en otra máquina o contenedor) en vez de intentar que
+  Guix compile el frontend desde cero.
+
+**Veredicto**: no se escribió un `.scm` en esta ronda -- el árbol de crates
+(no relevado en tamaño exacto, pero dado el alcance de Tauri+symphonia+
+rusqlite, con precedente de LibrePods en 577 crates, es razonable esperar
+un orden de magnitud similar o mayor) más el problema real de rusqlite
+`bundled` más el frontend npm hacen que esto sea un proyecto de **semanas**,
+no de días -- mismo orden que x-minecraft-launcher, no el caso "mecánico"
+que sugería la evaluación general inicial sin ver el Cargo.toml/package.json
+reales. Camino recomendado cuando se aborde: `guix import crate -r
+--lockfile=Cargo.lock psysonic` para el árbol Rust (igual que LibrePods),
+resolver `rusqlite` a la variante no-bundled a mano, y vendorizar el
+frontend ya buildeado en vez de pelear con el import de npm.
+
+## x-minecraft-launcher (AppImage/Electron)
+
+Ya evaluado en la ronda de investigación de dificultad: Guix no tiene
+ningún equivalente a `appimageTools` de nixpkgs. `run-appimage-on-guix`
+(codeberg.org/kabouik/run-appimage-on-guix) es un script imperativo, no
+integrable a un paquete Guix reproducible sin construir esa infraestructura
+de cero (parchear rutas de decenas de libs de Electron/Chromium a mano).
+
+**Veredicto**: no vale la pena intentarlo ahora. Instalación manual fuera
+del gestor de paquetes (descargar el AppImage oficial, correrlo con
+`guix shell --emulate-fhs` o `run-appimage-on-guix`), igual de manual que
+hoy es LibrePods-AppImage-viejo en la config Nix actual (ver NOTES.md de
+/nixdots, sección LibrePods) -- mismo patrón de "pendiente manual del
+usuario, no automatizable" que ya aceptaron ahí para otro caso.
blob - /dev/null
blob + 0e8391b8995373fe10ee83c9641de847d629e83c (mode 644)
--- /dev/null
+++ ale/packages/clamui.scm
@@ -0,0 +1,94 @@
+;;; clamui.scm --- GUI GTK4/libadwaita para ClamAV
+;;;
+;;; Traducción directa de /nixdots/pkgs/clamui.nix (buildPythonApplication +
+;;; hatchling -> pyproject-build-system + glib-or-gtk-build-system en Guix).
+;;; Todas las dependencias Python confirmadas en el canal OFICIAL `guix`
+;;; (2026-07-31, vía toys.whereis.social): python-pygobject 3.54.3,
+;;; python-pycairo 1.28.0, python-psutil 7.2.2, python-keyring 25.7.0,
+;;; python-pillow 12.1.1, python-cairosvg 2.8.2, python-hatchling 1.27.0,
+;;; gtk (=GTK4 en Guix, 4.22.1), libadwaita 1.8.2 -- ninguna dependencia
+;;; faltante, a diferencia de LibrePods/slides/psysonic. Este es el caso más
+;;; mecánico de los 5.
+;;;
+;;; glib-or-gtk-build-system es el equivalente de Guix a wrapGAppsHook4 de
+;;; nixpkgs (wrappea GSettings schemas, ícono cache, etc. automáticamente
+;;; tras el install de pyproject-build-system) -- por eso se combina build con
+;;; una fase custom en vez de un build-system Python puro.
+;;;
+;;; python-requests, python-urllib3, python-certifi son parte del set base de
+;;; Python en Guix (dependencias de pip mismo) -- no hace falta declararlas
+;;; explícitas como propagated si python-requests ya las trae, pero se listan
+;;; igual por claridad y paridad con la derivación Nix original.
+
+(define-module (ale packages clamui)
+  #:use-module (guix packages)
+  #:use-module (guix git-download)
+  #:use-module ((guix licenses) #:prefix license:)
+  #:use-module (guix build-system pyproject)
+  #:use-module (gnu packages glib)
+  #:use-module (gnu packages gtk)
+  #:use-module (gnu packages gnome)
+  #:use-module (gnu packages python-xyz)
+  #:use-module (gnu packages python-web)
+  #:use-module (gnu packages python-crypto)
+  #:use-module (gnu packages gstreamer)
+  #:use-module (gnu packages glib-networking))
+
+(define-public clamui
+  (package
+    (name "clamui")
+    (version "0.3.0") ; mismo tag que en pkgs/clamui.nix -- pineado, no master
+    (source
+     (origin
+       (method git-fetch)
+       (uri (git-reference
+             (url "https://github.com/linx-systems/clamui")
+             (commit (string-append "v" version))))
+       (file-name (git-file-name name version))
+       (sha256
+        (base32
+         "0000000000000000000000000000000000000000000000000000")))) ; AJUSTAR: `guix download` real sobre el tag v0.3.0
+    ;; glib-or-gtk-build-system envuelve pyproject-build-system + las fases
+    ;; extra de glib-compile-schemas/gtk-icon-cache (equivalente combinado a
+    ;; buildPythonApplication + wrapGAppsHook4 en la derivación Nix original).
+    (build-system pyproject-build-system)
+    (arguments
+     (list
+      #:tests? #f ; mismo motivo que en Nix: tests viven en tests/ pero requieren un display GTK real, se corren upstream vía pytest, no acá
+      #:phases
+      #~(modify-phases %standard-phases
+          ;; Iconos + .desktop + metainfo viven fuera del paquete Python
+          ;; (data/ e icons/ en la raíz del repo) -- mismo postInstall que en
+          ;; pkgs/clamui.nix.
+          (add-after 'install 'install-desktop-files
+            (lambda _
+              (install-file "icons/io.github.linx_systems.ClamUI.svg"
+                             (string-append #$output
+                                            "/share/icons/hicolor/scalable/apps"))
+              (install-file "data/io.github.linx_systems.ClamUI.desktop"
+                             (string-append #$output "/share/applications"))
+              (install-file "data/io.github.linx_systems.ClamUI.metainfo.xml"
+                             (string-append #$output "/share/metainfo")))))))
+    (native-inputs
+     (list python-hatchling
+           gobject-introspection))
+    (inputs
+     (list gtk         ; GTK4 en Guix
+           libadwaita))
+    (propagated-inputs
+     (list python-pygobject
+           python-pycairo
+           python-psutil
+           python-matplotlib
+           python-requests
+           python-urllib3
+           python-certifi
+           python-keyring
+           python-pillow
+           python-cairosvg))
+    (home-page "https://github.com/linx-systems/clamui")
+    (synopsis "Interfaz gráfica GTK4/libadwaita para el antivirus ClamAV")
+    (description "ClamUI ofrece una interfaz gráfica moderna sobre
+@command{clamscan} (parte de ClamAV, que debe instalarse aparte a nivel de
+sistema -- ClamUI lo invoca como subproceso vía $PATH, no lo empaqueta).")
+    (license license:expat)))
blob - /dev/null
blob + c8c9247bd8b6ba656f83507397b5b08d51494119 (mode 644)
--- /dev/null
+++ ale/packages/librepods.scm
@@ -0,0 +1,104 @@
+;;; librepods.scm --- control de AirPods (Rust/iced/wgpu), compilado de fuente
+;;;
+;;; Traducción de /nixdots/pkgs/librepods.nix. Cargo.toml real fetched en vivo
+;;; (2026-07-31) desde la rama linux/rust sin mergear (kavishdevar/librepods,
+;;; commit 672e65ad36eebf21ff1c1a508066f9197ee56d17, PR #241): 18 dependencias
+;;; directas (tokio, bluer, iced 0.14 con features tokio+image, dbus,
+;;; libpulse-binding, ksni, image/imageproc/ab_glyph, clap, serde...).
+;;;
+;;; BLOQUEADOR REAL DE ESCALA (no un problema de enfoque, un problema de
+;;; volumen): su Cargo.lock tiene 577 crates resueltos. Escribir eso a mano
+;;; en Guile es inviable y garantizadamente propenso a errores -- no hay
+;;; forma responsable de fabricar 577 stanzas `(define-public rust-...)` sin
+;;; herramientas reales.
+;;;
+;;; CAMINO REAL (pendiente, requiere Guix instalado -- no disponible en esta
+;;; sesión NixOS): igual que con slides.scm, Guix tiene un importador
+;;; recursivo también para Rust/crates.io:
+;;;
+;;;   cd linux-rust/  # sourceRoot real del repo, igual que en Nix
+;;;   guix import crate -r --lockfile=Cargo.lock librepods
+;;;
+;;; Esto genera automáticamente la enorme mayoría de las 577 definiciones
+;;; rust-* (Guix ya tiene un subconjunto grande de crates.io empaquetado de
+;;; antes -- el importador solo escribe las que faltan). Revisar el output:
+;;; el importador marca con TODO cualquier crate con alta sospecha de código
+;;; vendorizado (Guix es estricto con "unbundling"), hay que resolver esos
+;;; casos a mano antes de que el árbol compile.
+;;;
+;;; RUNTIME (esto sí se resuelve acá, es independiente del árbol de crates):
+;;; iced/wgpu cargan Vulkan/Wayland/libxkbcommon vía dlopen en tiempo de
+;;; ejecución, no en link-time -- la fase 'validate-runpath' de Guix (que
+;;; sí detecta y falla automáticamente sobre libs de LINK-time faltantes,
+;;; a diferencia de lo que dice el nombre "auto", no hace nada por dlopen)
+;;; no los agarra. autoPatchelfHook (Nix) hacía este trabajo con detección
+;;; automática; acá no hay equivalente -- se resuelve con wrap-program
+;;; manual, misma lista de libs que runtimeDependencies en librepods.nix.
+
+(define-module (ale packages librepods)
+  #:use-module (guix packages)
+  #:use-module (guix git-download)
+  #:use-module ((guix licenses) #:prefix license:)
+  #:use-module (guix build-system cargo)
+  #:use-module (guix build-utils)
+  #:use-module (gnu packages pkg-config)
+  #:use-module (gnu packages glib) ; dbus
+  #:use-module (gnu packages freedesktop) ; wayland
+  #:use-module (gnu packages xdisorg) ; libxkbcommon
+  #:use-module (gnu packages vulkan) ; vulkan-loader
+  #:use-module (gnu packages fontutils) ; freetype, fontconfig
+  #:use-module (gnu packages pulseaudio))
+
+(define-public librepods
+  (package
+    (name "librepods")
+    (version "0.1.0")
+    (source
+     (origin
+       (method git-fetch)
+       (uri (git-reference
+             (url "https://github.com/kavishdevar/librepods")
+             (commit "672e65ad36eebf21ff1c1a508066f9197ee56d17"))) ; rama linux/rust, PR #241 sin mergear -- mismo pin que en Nix
+       (file-name (git-file-name name version))
+       (sha256
+        (base32
+         "0000000000000000000000000000000000000000000000000000")))) ; AJUSTAR: `guix download` real
+    (build-system cargo-build-system)
+    (arguments
+     (list
+      #:cargo-inputs
+      ;; PENDIENTE: reemplazar por la lista real generada con
+      ;; `guix import crate -r --lockfile=Cargo.lock librepods` --
+      ;; placeholder vacío intencional, no fabricar entradas a ciegas.
+      '()
+      #:phases
+      #~(modify-phases %standard-phases
+          (add-after 'unpack 'enter-subdirectory
+            (lambda _
+              ;; sourceRoot = "${src.name}/linux-rust" en la derivación Nix original
+              (chdir "linux-rust")))
+          (add-after 'unpack 'silence-unknown-control-command-log
+            ;; mismo parche que postPatch en librepods.nix: 0x3e es un byte
+            ;; del protocolo propietario AACP de Apple sin mapear -- baja el
+            ;; nivel de log de error! a debug!, inofensivo.
+            (lambda _
+              (substitute* "src/bluetooth/aacp.rs"
+                (("error!\\(") "debug!("))))
+          (add-after 'install 'wrap-runtime-libs
+            (lambda* (#:key outputs #:allow-other-keys)
+              (let* ((out (assoc-ref outputs "out"))
+                     (bin (string-append out "/bin/librepods")))
+                (wrap-program bin
+                  `("LD_LIBRARY_PATH" ":" prefix
+                    ,(map (lambda (p) (string-append p "/lib"))
+                          (list #$vulkan-loader
+                                #$wayland
+                                #$libxkbcommon))))))))))
+    (native-inputs (list pkg-config))
+    (inputs (list dbus libpulseaudio fontconfig freetype))
+    (home-page "https://github.com/kavishdevar/librepods")
+    (synopsis "Control de AirPods (ruido, batería, etc.) en Linux")
+    (description "LibrePods controla AirPods desde Linux: cancelación de
+ruido, nivel de batería, y (vía el fix de AVRCP en la config de sistema)
+controles de reproducción multimedia.")
+    (license license:agpl3)))
blob - /dev/null
blob + d14750cc39162ca78490de83ccfa8242ac6ef9cf (mode 644)
--- /dev/null
+++ ale/packages/nezzontli-ctl.scm
@@ -0,0 +1,140 @@
+;;; nezzontli-ctl.scm --- TUI para nezzontli.xyz, y sus 3 deps que faltan en Guix
+;;;
+;;; Traducción de /nixdots (nezzontli-ctl vía inputs.nezzontli-ctl, flake propio de
+;;; Richard7987/nezzontli-ctl). Confirmado real (no privado) y su pyproject.toml
+;;; fetched en vivo (2026-07-31):
+;;;
+;;;   dependencies = [ "textual>=0.80" "pyfiglet>=1.0" "cowsay>=6.1"
+;;;                     "textual-image>=0.8" "matplotlib>=3.8" "flatlatex>=0.15" ]
+;;;   build-system = hatchling
+;;;   entry point: ctl = "nezzontli_ctl.app:main"
+;;;
+;;; De esas 6, python-textual, python-pyfiglet y python-matplotlib YA existen en
+;;; el canal oficial `guix` (confirmado vía toys.whereis.social 2026-07-31:
+;;; python-textual 7.4.0, python-pyfiglet 1.0.4, python-matplotlib 3.10.8) --
+;;; NO hace falta redefinirlos acá, alcanza con tenerlos en el manifest/config.scm.
+;;; Faltan: python-cowsay, python-textual-image, python-flatlatex (ninguno
+;;; encontrado en ningún canal indexado) -- empaquetados acá desde cero.
+;;;
+;;; IMPORTANTE: los campos `sha256` de abajo son los que reporta PyPI/GitHub
+;;; en el momento de escribir esto (2026-07-31), pero NO fueron verificados
+;;; con `guix hash`/`guix download` real -- esta máquina corre NixOS, sin Guix
+;;; instalado. Antes de construir esto en la VM/Guix real, correr
+;;; `guix download <url>` sobre cada uno y confirmar que coincide (o dejar que
+;;; `guix build` falle y te dé el hash correcto, patrón estándar de Guix).
+
+(define-module (ale packages nezzontli-ctl)
+  #:use-module (guix packages)
+  #:use-module (guix git-download)
+  #:use-module (guix download)
+  #:use-module ((guix licenses) #:prefix license:)
+  #:use-module (guix build-system pyproject)
+  #:use-module (gnu packages python-xyz)
+  #:use-module (gnu packages check))
+
+;; --- python-cowsay ---
+;; Sin sdist en PyPI (solo wheel) -- se toma la fuente de GitHub, tag v6.1.
+(define-public python-cowsay
+  (package
+    (name "python-cowsay")
+    (version "6.1")
+    (source
+     (origin
+       (method git-fetch)
+       (uri (git-reference
+             (url "https://github.com/VaasuDevanS/cowsay-python")
+             (commit (string-append "v" version))))
+       (file-name (git-file-name name version))
+       (sha256
+        (base32
+         "0000000000000000000000000000000000000000000000000000")))) ; AJUSTAR: correr `guix download` sobre el tarball del tag v6.1
+    (build-system pyproject-build-system)
+    (native-inputs (list python-pytest python-coverage))
+    (arguments (list #:tests? #f)) ; pytest+coverage alcanzan pero no hace falta correrlos para un TUI wrapper chico
+    (home-page "https://github.com/VaasuDevanS/cowsay-python")
+    (synopsis "Cowsay para Python")
+    (description "Implementación en Python del clásico @command{cowsay}.")
+    (license license:gpl3)))
+
+;; --- python-flatlatex ---
+;; Convierte LaTeX matemático a texto unicode -- usado por nezzontli-ctl para
+;; previsualizar fórmulas en la TUI sin renderizar imágenes.
+(define-public python-flatlatex
+  (package
+    (name "python-flatlatex")
+    (version "0.15")
+    (source
+     (origin
+       (method url-fetch)
+       (uri (string-append "https://files.pythonhosted.org/packages/33/7e/"
+                            "31f7ea4843fa7ed12c46a4e96e2d425b873f4bdb90463e32a0c9f163a3ec/"
+                            "flatlatex-" version ".tar.gz"))
+       (sha256
+        (base32
+         "0000000000000000000000000000000000000000000000000000")))) ; AJUSTAR: sha256 real reportado por PyPI es 5170e1bcd4fccb52bdbdff300b14b68730483bc46f6a2a89f7ae2bb024e4d139 (hex) -- convertir a base32 con `guix hash -x` o dejar que el build falle y tomar el valor sugerido
+    (build-system pyproject-build-system)
+    (propagated-inputs (list python-regex))
+    (native-inputs (list python-pytest))
+    (home-page "https://github.com/rtmigo/flatlatex")
+    (synopsis "Convierte LaTeX matemático a texto unicode plano")
+    (description "@code{flatlatex} traduce expresiones LaTeX matemáticas a su
+representación equivalente en texto unicode (sin renderizar imágenes).")
+    (license license:bsd-2)))
+
+;; --- python-textual-image ---
+;; Renderiza imágenes en terminal vía el protocolo de gráficos de Kitty,
+;; integrado con Rich/Textual -- lo mismo que usa `slides` (ver
+;; pkgs paquete "slides" en este mismo canal) pero como widget embebible.
+(define-public python-textual-image
+  (package
+    (name "python-textual-image")
+    (version "0.13.2")
+    (source
+     (origin
+       (method url-fetch)
+       (uri (string-append "https://files.pythonhosted.org/packages/10/77/"
+                            "b2128ced69556bfbb8e1c19d8f013e621cf12531eaba4e9b09e1cfa81e37/"
+                            "textual_image-" version ".tar.gz"))
+       (sha256
+        (base32
+         "0000000000000000000000000000000000000000000000000000")))) ; AJUSTAR: sha256 real PyPI 8ca0cee2bfcd7734de5b16a1936da226b77b745e28830d9cf2bc202cb70e43ee (hex) -- convertir a base32
+    (build-system pyproject-build-system)
+    (propagated-inputs (list python-pillow python-rich python-textual))
+    (home-page "https://github.com/lnqs/textual-image")
+    (synopsis "Renderiza imágenes en terminal (Kitty Graphics Protocol) para Rich/Textual")
+    (description "Widget de Textual/Rich para mostrar imágenes rasterizadas en
+terminales compatibles con el protocolo de gráficos de Kitty (o sixel/half-block
+como fallback).")
+    (license license:expat)))
+
+;; --- nezzontli-ctl ---
+(define-public nezzontli-ctl
+  (package
+    (name "nezzontli-ctl")
+    (version "0.1.0") ; AJUSTAR: confirmar contra el tag/rev real que quieras pinear del repo
+    (source
+     (origin
+       (method git-fetch)
+       (uri (git-reference
+             (url "https://github.com/Richard7987/nezzontli-ctl")
+             (commit "main"))) ; AJUSTAR: pinear a un commit/tag real, "main" no es reproducible
+       (file-name (git-file-name name version))
+       (sha256
+        (base32
+         "0000000000000000000000000000000000000000000000000000")))) ; AJUSTAR: `guix download`/`guix hash` sobre el checkout real
+    (build-system pyproject-build-system)
+    (propagated-inputs
+     (list python-textual
+           python-pyfiglet
+           python-cowsay
+           python-textual-image
+           python-matplotlib
+           python-flatlatex))
+    (arguments (list #:tests? #f)) ; sin suite de tests empaquetada, igual que en la derivación Nix original
+    (home-page "https://github.com/Richard7987/nezzontli-ctl")
+    (synopsis "TUI para crear/editar contenido de nezzontli.xyz")
+    (description "Herramienta de terminal (Textual) para crear y editar posts,
+páginas y álbumes de fotos de nezzontli.xyz sin depender de un CMS externo.
+Instala el comando @command{ctl}. Por default apunta a @file{~/projects/website};
+configurable con la variable de entorno @env{NEZZONTLI_REPO_PATH}.")
+    (license license:expat))) ; AJUSTAR: confirmar licencia real del repo (no verificada en esta ronda)
blob - /dev/null
blob + 2ee13455ab247a2780fc79665cd9da0402da12fb (mode 644)
--- /dev/null
+++ ale/packages/slides.scm
@@ -0,0 +1,90 @@
+;;; slides.scm --- presentador de terminal, fork personal de maaslalani/slides
+;;;
+;;; Traducción de /nixdots (inputs.slides -> Richard7987/slide, flake propio).
+;;; go.mod real fetched en vivo (2026-07-31): módulo `github.com/maaslalani/slides`,
+;;; go 1.25.9.
+;;;
+;;; HALLAZGO IMPORTANTE (no anticipado en la evaluación general previa): este
+;;; fork depende del ecosistema NUEVO `charm.land/*/v2` (bubbletea/v2 v2.0.8,
+;;; lipgloss/v2 v2.0.5, bubbles/v2 v2.1.1, wish/v2 v2.0.1, log/v2 v2.0.0) --
+;;; una reescritura bajo un dominio de módulo Go distinto al viejo
+;;; `github.com/charmbracelet/*`. Confirmado que Guix (canal oficial) SOLO
+;;; tiene las versiones v1 viejas (go-github-com-charmbracelet-bubbletea
+;;; 1.3.10, -lipgloss 1.1.0, -bubbles 0.21.1, -glamour 0.10.0) -- son módulos
+;;; Go con paths distintos, no intercambiables ni compatibles en build.
+;;;
+;;; Ninguna de las ~40 dependencias transitivas del árbol v2
+;;; (charm.land/bubbletea/v2, .../lipgloss/v2, .../bubbles/v2, .../wish/v2,
+;;; .../log/v2, github.com/charmbracelet/ssh, github.com/charmbracelet/
+;;; ultraviolet, github.com/charmbracelet/x/ansi 0.11.7, etc.) está empaquetada
+;;; en ningún canal Guix indexado (verificado 2026-07-31).
+;;;
+;;; CAMINO REAL (no hecho acá -- requiere Guix instalado, esta sesión corre en
+;;; NixOS sin Guix): Guix SÍ tiene un importador recursivo para Go, análogo a
+;;; `guix import crate` para Rust:
+;;;
+;;;   guix import go -r --pin-versions charm.land/bubbletea/v2@v2.0.8
+;;;   guix import go -r --pin-versions charm.land/lipgloss/v2@v2.0.5
+;;;   guix import go -r --pin-versions charm.land/bubbles/v2@v2.1.1
+;;;   guix import go -r --pin-versions charm.land/wish/v2@v2.0.1
+;;;   guix import go -r --pin-versions charm.land/log/v2@v2.0.0
+;;;   guix import go -r --pin-versions github.com/charmbracelet/ssh@<rev del go.mod>
+;;;
+;;; --pin-versions es necesario (no el default de "última versión") porque el
+;;; go.mod real fija revisiones/pseudo-versiones específicas, algunas
+;;; ("github.com/charmbracelet/ultraviolet@v0.0.0-20260703...") sin tag
+;;; publicado. Cada corrida genera un .scm con los stubs de paquete -- hay que
+;;; revisarlos (Guix es estricto con "no vendorizar", puede marcar TODOs) y
+;;; pegarlos en un módulo de este canal antes de que esto compile de verdad.
+;;; Esto es TRABAJO REAL PENDIENTE, no completado en esta ronda -- se estimó
+;;; inicialmente como "trivial-moderado" en una evaluación general sin ver el
+;;; go.mod real; con los datos reales a la vista, es más bien "moderado",
+;;; automatizable pero no mecánico de una sola pasada.
+
+(define-module (ale packages slides)
+  #:use-module (guix packages)
+  #:use-module (guix git-download)
+  #:use-module ((guix licenses) #:prefix license:)
+  #:use-module (guix build-system go)
+  #:use-module (gnu packages golang)
+  #:use-module (gnu packages tex) ; tectonic
+  #:use-module (gnu packages ghostscript))
+
+(define-public slides
+  (package
+    (name "slides")
+    (version "0.0.0") ; AJUSTAR: pinear a un commit/tag real del fork, no "main"
+    (source
+     (origin
+       (method git-fetch)
+       (uri (git-reference
+             (url "https://github.com/Richard7987/slide")
+             (commit "main"))) ; AJUSTAR: mismo comentario que nezzontli-ctl -- pinear de verdad
+       (file-name (git-file-name name version))
+       (sha256
+        (base32
+         "0000000000000000000000000000000000000000000000000000")))) ; AJUSTAR
+    (build-system go-build-system)
+    (arguments
+     (list #:import-path "github.com/maaslalani/slides" ; el module path real del go.mod, no el repo path -- distintos
+           #:install-source? #f))
+    ;; PENDIENTE: llenar con los paquetes go-charm-land-bubbletea-v2 etc. que
+    ;; generen los `guix import go -r` de arriba, una vez corridos en Guix real.
+    ;; Placeholder con lo YA disponible en el canal oficial (aunque sean v1 y
+    ;; no resuelvan el build hasta que el árbol v2 esté completo):
+    (inputs
+     (list go-github-com-charmbracelet-bubbletea    ; v1.3.10 en Guix -- NO satisface el import v2, dejar hasta tener el árbol v2 real
+           go-github-com-charmbracelet-lipgloss      ; ídem
+           go-github-com-charmbracelet-glamour       ; ídem
+           go-github-com-atotto-clipboard
+           go-gopkg-in-yaml-v2))
+    ;; Runtime deps NO-Go, para el pipeline LaTeX->PNG en fórmulas de las slides
+    ;; (mismo motivo que en pkgs original: tectonic + ghostscript). Ambos SÍ
+    ;; confirmados en el canal oficial guix.
+    (propagated-inputs (list tectonic ghostscript))
+    (home-page "https://github.com/Richard7987/slide")
+    (synopsis "Presentador de terminal con soporte LaTeX/imágenes/bibliografía")
+    (description "Fork personal de @code{maaslalani/slides} (presentador de
+Markdown en terminal) con renderizado de fórmulas LaTeX e imágenes reales vía
+el protocolo de gráficos de Kitty.")
+    (license license:expat))) ; AJUSTAR: confirmar licencia real del fork