feat(infra): auto-commit con 10 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -21,7 +21,7 @@ Cualquier decision tecnica que choque con estos objetivos esta mal priorizada. E
|
|||||||
|
|
||||||
**Sync entre PCs:** `fn sync` sincroniza datos no regenerables (proposals, apps, projects, analysis, vaults, pc_locations) contra `registry_api` en `https://registry.organic-machine.com`. Config: `~/.fn_pc` (identidad del PC), `FN_REGISTRY_API` (URL con basicAuth), `REGISTRY_API_TOKEN` (token).
|
**Sync entre PCs:** `fn sync` sincroniza datos no regenerables (proposals, apps, projects, analysis, vaults, pc_locations) contra `registry_api` en `https://registry.organic-machine.com`. Config: `~/.fn_pc` (identidad del PC), `FN_REGISTRY_API` (URL con basicAuth), `REGISTRY_API_TOKEN` (token).
|
||||||
|
|
||||||
**Sub-repos:** cada app y cada analysis es su propio repo Gitea en `dataforge/<basename>` con branch `master` (ver ADR 0002). `apps/*` y `analysis/*` estan en el `.gitignore` del repo padre — el codigo de cada app vive en `apps/<name>/.git/`. Los slash commands `/full-git-push` y `/full-git-pull` orquestan push/pull/clone de fn_registry + todos los sub-repos + `fn sync`. `/full-git-push` auto-inicializa apps/analyses sin `.git` via `ensure_repo_synced_bash_infra`. Los `vaults/` y `subrepos/` NO entran en este flujo. **Gotcha worktrees**: si creas una app nueva dentro de un git worktree del repo padre, haz `git init` dentro de `apps/<name>/` ANTES de limpiar el worktree, sino el codigo se pierde (apps/* gitignored). Ver `.claude/rules/apps_subrepo.md`.
|
**Sub-repos:** cada app y cada analysis es su propio repo Gitea en `dataforge/<basename>` con branch `master` (ver ADR 0002). `apps/*` y `analysis/*` estan en el `.gitignore` del repo padre — el codigo de cada app vive en `apps/<name>/.git/`. Los slash commands `/full-git-push` y `/full-git-pull` orquestan push/pull/clone de fn_registry + todos los sub-repos + `fn sync`. `/full-git-push` auto-inicializa apps/analyses sin `.git` via `ensure_repo_synced_bash_infra`. Los `vaults/` y `subrepos/` NO entran en este flujo. **Gotcha worktrees**: si creas una app nueva dentro de un git worktree del repo padre, haz `git init` dentro de `apps/<name>/` ANTES de limpiar el worktree, sino el codigo se pierde (apps/* gitignored). **REGLA DURA**: el repo padre NUNCA trackea contenido de artefactos hijos (apps/analysis/projects) — solo `.gitkeep`. Nada de `git add -f` sobre esos paths: deja el padre permanentemente dirty (doble-tracking). Auditoria + fix en `.claude/rules/apps_subrepo.md`. Ver `.claude/rules/apps_subrepo.md`.
|
||||||
|
|
||||||
**Artefactos:** termino paraguas para apps, analysis, vaults, projects y playgrounds — todo lo que NO es codigo reutilizable. Usa "artefacto" cuando una afirmacion aplica a varios tipos a la vez para no repetir la lista. Ver `.claude/rules/artefactos.md` y `.claude/rules/playgrounds.md`.
|
**Artefactos:** termino paraguas para apps, analysis, vaults, projects y playgrounds — todo lo que NO es codigo reutilizable. Usa "artefacto" cuando una afirmacion aplica a varios tipos a la vez para no repetir la lista. Ver `.claude/rules/artefactos.md` y `.claude/rules/playgrounds.md`.
|
||||||
|
|
||||||
|
|||||||
@@ -173,23 +173,39 @@ Si el build falla:
|
|||||||
- "undefined reference to render" → falta quitar `static` o falta el `#ifndef FN_TEST_BUILD` en main.cpp.
|
- "undefined reference to render" → falta quitar `static` o falta el `#ifndef FN_TEST_BUILD` en main.cpp.
|
||||||
- "multiple definition of main" → falta el `target_compile_definitions(... FN_TEST_BUILD)` en CMakeLists.
|
- "multiple definition of main" → falta el `target_compile_definitions(... FN_TEST_BUILD)` en CMakeLists.
|
||||||
|
|
||||||
### 8. Ejecutar (headless en WSL)
|
### 8. Ejecutar (headless preferente — sin parpadeo)
|
||||||
|
|
||||||
WSL no tiene GLX 4.3 nativo — los tests corren bajo `xvfb` con software renderer Mesa. Wrapper canonico:
|
`fn::run_app_test` crea la ventana GLFW **oculta por defecto** (`GLFW_VISIBLE=FALSE`, ver `cpp/framework/app_base.cpp`). El contexto GL real se crea igual, así que el render que ejercita el Test Engine es fiel, pero la ventana nunca se mapea en pantalla: cero parpadeo, no roba foco. Por eso los tests de frontend C++ corren headless por defecto, sin tocar el código de cada app.
|
||||||
|
|
||||||
|
Dos formas de lanzar, según el entorno:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd "$ROOT/cpp/build/linux_tests"
|
cd "$ROOT/cpp/build/linux_tests"
|
||||||
TEST_BIN="$(find . -name "${APP_ARG}_tests" -type f -executable | head -1)"
|
TEST_BIN="$(find . -name "${APP_ARG}_tests" -type f -executable | head -1)"
|
||||||
[ -z "$TEST_BIN" ] && { echo "no encuentro el binario de tests"; exit 1; }
|
[ -z "$TEST_BIN" ] && { echo "no encuentro el binario de tests"; exit 1; }
|
||||||
|
|
||||||
timeout 90 xvfb-run -a -s "-screen 0 1280x800x24" \
|
if [ -n "$DISPLAY" ] && command -v glxinfo >/dev/null 2>&1 \
|
||||||
env LIBGL_ALWAYS_SOFTWARE=1 GALLIUM_DRIVER=llvmpipe \
|
&& glxinfo 2>/dev/null | grep -q "OpenGL core profile version"; then
|
||||||
"$TEST_BIN" 2>&1
|
# Host con GL nativo (PC enmanuel, X11 + GPU): binario directo.
|
||||||
|
# La ventana ya nace oculta -> sin parpadeo, y usa la GPU real (rapido).
|
||||||
|
timeout 90 "$TEST_BIN" 2>&1
|
||||||
|
else
|
||||||
|
# CI / WSL sin GLX 4.3 nativo: display virtual en RAM + software Mesa.
|
||||||
|
timeout 90 xvfb-run -a -s "-screen 0 1280x800x24" \
|
||||||
|
env LIBGL_ALWAYS_SOFTWARE=1 GALLIUM_DRIVER=llvmpipe \
|
||||||
|
"$TEST_BIN" 2>&1
|
||||||
|
fi
|
||||||
EXIT=$?
|
EXIT=$?
|
||||||
echo "EXIT: $EXIT"
|
echo "EXIT: $EXIT"
|
||||||
```
|
```
|
||||||
|
|
||||||
Si en el host el usuario tiene GL nativo y `DISPLAY` funciona, el wrapper xvfb-run sigue siendo seguro (ejecuta dentro de su propio display).
|
Ambas vías son headless. `xvfb-run` sigue siendo seguro en host con display (corre en su propio display virtual), así que si el sniff de GL falla puedes usar siempre la rama xvfb.
|
||||||
|
|
||||||
|
**Para depurar un test a ojo** (ver la UI mientras el engine la maneja), desactiva el headless con `FN_HEADLESS=0`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
FN_HEADLESS=0 timeout 90 "$TEST_BIN" 2>&1
|
||||||
|
```
|
||||||
|
|
||||||
### 9. Reportar
|
### 9. Reportar
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ Reglas operativas del proyecto. Cada archivo es una regla independiente.
|
|||||||
| 15 | [projects.md](projects.md) | Projects: agrupar apps, analysis y vaults bajo un tema |
|
| 15 | [projects.md](projects.md) | Projects: agrupar apps, analysis y vaults bajo un tema |
|
||||||
| 16 | [kiss.md](kiss.md) | KISS en proyectos y apps: cuestionar herramientas externas, sin abstracciones especulativas |
|
| 16 | [kiss.md](kiss.md) | KISS en proyectos y apps: cuestionar herramientas externas, sin abstracciones especulativas |
|
||||||
| 17 | [apps_tbd.md](apps_tbd.md) | Trunk-based development obligatorio en apps generadas con `fn` (registry exento) |
|
| 17 | [apps_tbd.md](apps_tbd.md) | Trunk-based development obligatorio en apps generadas con `fn` (registry exento) |
|
||||||
| 17b | [apps_subrepo.md](apps_subrepo.md) | Apps son sub-repos Gitea (apps/* gitignored). `git init` dentro de cada app nueva ANTES de limpiar worktree, sino se pierde el codigo |
|
| 17b | [apps_subrepo.md](apps_subrepo.md) | Apps son sub-repos Gitea (apps/* gitignored). El padre NUNCA trackea contenido de artefactos hijos (solo `.gitkeep`); nada de `git add -f` sobre apps/analysis/projects o deja el padre dirty. `git init` dentro de cada app nueva ANTES de limpiar worktree, sino se pierde el codigo |
|
||||||
| 18 | [uses_functions.md](uses_functions.md) | Convencion de uses_functions para C++: el .md del consumidor declara las dependencias |
|
| 18 | [uses_functions.md](uses_functions.md) | Convencion de uses_functions para C++: el .md del consumidor declara las dependencias |
|
||||||
| 19 | [cpp_apps.md](cpp_apps.md) | Estandarizacion de apps C++: estructura, CMake, app.md, sub-repo, runtime — apunta a cpp/PATTERNS.md y cpp/DESIGN_SYSTEM.md como autoritativas |
|
| 19 | [cpp_apps.md](cpp_apps.md) | Estandarizacion de apps C++: estructura, CMake, app.md, sub-repo, runtime — apunta a cpp/PATTERNS.md y cpp/DESIGN_SYSTEM.md como autoritativas |
|
||||||
| 20 | [artefactos.md](artefactos.md) | Termino paraguas para apps, analysis, vaults, projects y playgrounds (todo lo que no es codigo reutilizable) |
|
| 20 | [artefactos.md](artefactos.md) | Termino paraguas para apps, analysis, vaults, projects y playgrounds (todo lo que no es codigo reutilizable) |
|
||||||
|
|||||||
@@ -45,6 +45,36 @@ Cuando el humano corre `/full-git-push` despues del merge, el script `ensure_rep
|
|||||||
|
|
||||||
Todo lo demas (codigo de la app + app.md + appicon + service unit + tests propios de la app) vive en `apps/<name>/.git` independiente.
|
Todo lo demas (codigo de la app + app.md + appicon + service unit + tests propios de la app) vive en `apps/<name>/.git` independiente.
|
||||||
|
|
||||||
|
### REGLA DURA: el repo padre NUNCA trackea contenido de artefactos hijos
|
||||||
|
|
||||||
|
El repo padre `fn_registry` solo versiona codigo del registry (`functions/`, `types/`, `registry/`, `cmd/`, `docs/`, `.claude/`, `dev/`, `migrations/`, y el framework/functions/vendor de `cpp/`). NUNCA debe trackear el contenido de un artefacto hijo:
|
||||||
|
|
||||||
|
- apps: `apps/*`, `cpp/apps/*`, `projects/*/apps/*`
|
||||||
|
- analyses: `analysis/*`, `projects/*/analysis/*`
|
||||||
|
- projects: `projects/*`
|
||||||
|
|
||||||
|
Cada artefacto es un sub-repo Gitea independiente con su propio `.git`; su contenido completo (codigo, `app.md`, `analysis.md`, `appicon.*`, binarios, frontend, `local_files/`, tests propios) vive SOLO en ese sub-repo. `fn index` lee los `.md` de registro directamente del disco — no necesitan estar en el git del padre. Lo unico que el padre versiona dentro de esos arboles son los marcadores `.gitkeep` (mantienen `apps/` y `analysis/` presentes cuando estan vacios) y, en `projects/`, los `project.md` template si los hubiera.
|
||||||
|
|
||||||
|
**Como se rompe (sintoma = repo padre permanentemente dirty):** un `git add -f apps/<x>/...` (forzado, saltandose el `.gitignore`) o un commit que mete contenido del hijo al padre. Como el archivo ya queda en el indice, el `.gitignore` NO lo vuelve a ignorar y aparece para siempre en `git status` del padre como modificado cada vez que el sub-repo cambia (doble-tracking). Caso real (2026-06-03): `apps/dag_engine/` (31 archivos: Go + frontend + app.md) y `apps/shaders_lab/` (app.md + un binario `.exe` de 23 MB) quedaron forzados al indice del padre y lo dejaban dirty en cada cambio del sub-repo.
|
||||||
|
|
||||||
|
**Auditoria (cero salida = sano):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git ls-files 'apps/*' 'analysis/*' 'projects/*/apps/*' 'projects/*/analysis/*' 'cpp/apps/*' \
|
||||||
|
| grep -vE '(^|/)\.gitkeep$'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fix si aparece contenido trackeado:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# --cached SIEMPRE: saca del indice del padre sin borrar el working tree.
|
||||||
|
# El codigo sigue a salvo en el .git del sub-repo.
|
||||||
|
git rm -r --cached apps/<x>
|
||||||
|
git commit -m "chore: untrack contenido del artefacto <x> (es sub-repo Gitea)"
|
||||||
|
```
|
||||||
|
|
||||||
|
NUNCA `git rm` sin `--cached` (borraria el working tree del sub-repo). **Prevencion:** jamas usar `git add -f` sobre paths de artefactos; las reglas `apps/*/`, `analysis/*/`, `projects/*/` del `.gitignore` ya cubren el caso por defecto y solo un force las salta.
|
||||||
|
|
||||||
### Sintomas de la perdida
|
### Sintomas de la perdida
|
||||||
|
|
||||||
Si limpias el worktree y luego corres `ls apps/<name>/`, devuelve "No such file or directory" pese a que el issue aparece cerrado en `dev/issues/completed/`. **Patron** = scaffold sin sub-repo init = trabajo perdido.
|
Si limpias el worktree y luego corres `ls apps/<name>/`, devuelve "No such file or directory" pese a que el issue aparece cerrado en `dev/issues/completed/`. **Patron** = scaffold sin sub-repo init = trabajo perdido.
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ El `if(EXISTS ...)` hace el registro tolerante a apps no clonadas (cada app es s
|
|||||||
### 6. Sub-repo Gitea (TBD obligatorio)
|
### 6. Sub-repo Gitea (TBD obligatorio)
|
||||||
|
|
||||||
Cada app C++ es su propio repo en `dataforge/<name>` con branch `master`. Esto significa:
|
Cada app C++ es su propio repo en `dataforge/<name>` con branch `master`. Esto significa:
|
||||||
- El directorio `<app_dir>/` esta en el `.gitignore` de `fn_registry` (excepto `app.md`).
|
- TODO el directorio `<app_dir>/` (incluido `app.md`, `appicon.*`, binarios y `local_files/`) esta en el `.gitignore` de `fn_registry`: el repo padre NUNCA versiona contenido del artefacto. `fn index` lee `app.md` directo del disco, no del git. NO forzar con `git add -f` — deja el padre dirty. Ver la regla dura en `apps_subrepo.md`.
|
||||||
- El propio directorio tiene `.git/` apuntando al sub-repo.
|
- El propio directorio tiene `.git/` apuntando al sub-repo.
|
||||||
- TBD obligatorio mientras se desarrolla la app: ver `apps_tbd.md`. Trabajar en `issue/<NNNN>-<slug>` o `quick/<slug>`, mergear a `master` con `--no-ff`.
|
- TBD obligatorio mientras se desarrolla la app: ver `apps_tbd.md`. Trabajar en `issue/<NNNN>-<slug>` o `quick/<slug>`, mergear a `master` con `--no-ff`.
|
||||||
- Sync entre PCs y push/pull se gestionan con `/full-git-push` y `/full-git-pull`.
|
- Sync entre PCs y push/pull se gestionan con `/full-git-push` y `/full-git-pull`.
|
||||||
|
|||||||
+2
-2
@@ -67,8 +67,8 @@ worktrees/
|
|||||||
# Temp — workspace efimero para pruebas rapidas (APIs, scripts, analisis)
|
# Temp — workspace efimero para pruebas rapidas (APIs, scripts, analisis)
|
||||||
temp/
|
temp/
|
||||||
|
|
||||||
# C++ build artifacts
|
# C++ build artifacts (build/, build-tests/, build-windows/, etc.)
|
||||||
cpp/build/
|
cpp/build*/
|
||||||
/build/
|
/build/
|
||||||
|
|
||||||
# OS
|
# OS
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ discover_git_repos() {
|
|||||||
-not -path "*/node_modules/*" \
|
-not -path "*/node_modules/*" \
|
||||||
-not -path "*/.venv/*" \
|
-not -path "*/.venv/*" \
|
||||||
-not -path "*/cpp/vendor/*" \
|
-not -path "*/cpp/vendor/*" \
|
||||||
-not -path "*/cpp/build/*" \
|
-not -path "*/cpp/build*/*" \
|
||||||
-not -path "*/sources/*" \
|
-not -path "*/sources/*" \
|
||||||
-not -path "*/temp/*" \
|
-not -path "*/temp/*" \
|
||||||
-not -path "*/subrepos/*" \
|
-not -path "*/subrepos/*" \
|
||||||
|
|||||||
@@ -169,6 +169,39 @@ Para diagnosticar un diff: revisar el PNG actual en
|
|||||||
`cpp/build/tests/visual_actual/<demo>.png` vs el golden en
|
`cpp/build/tests/visual_actual/<demo>.png` vs el golden en
|
||||||
`cpp/tests/golden/<demo>.png`.
|
`cpp/tests/golden/<demo>.png`.
|
||||||
|
|
||||||
|
### Tests de UI headless (Dear ImGui Test Engine)
|
||||||
|
|
||||||
|
`fn::run_app_test` (el harness del Test Engine usado por `/e2e-cpp`) crea la
|
||||||
|
ventana GLFW **oculta por defecto** (`GLFW_VISIBLE=FALSE`). El contexto OpenGL
|
||||||
|
real se crea igual, así que el render que el Test Engine ejercita sigue siendo
|
||||||
|
fiel, pero la ventana nunca se mapea en pantalla: cero parpadeo y no roba foco
|
||||||
|
mientras corre la suite. Es el comportamiento preferente para tests de
|
||||||
|
frontend en C++.
|
||||||
|
|
||||||
|
Control del modo (en orden de prioridad):
|
||||||
|
|
||||||
|
| Mecanismo | Efecto |
|
||||||
|
|---|---|
|
||||||
|
| `FN_HEADLESS=0` (env) | Fuerza ventana **visible** — para depurar un test a ojo. |
|
||||||
|
| `FN_HEADLESS=1` (env) | Fuerza oculta (es el default del path de test). |
|
||||||
|
| `cfg.headless = true` | Oculta también `fn::run_app` (apps reales, p.ej. smoke/capture). |
|
||||||
|
| sin nada | `run_app_test` → oculta; `run_app` → visible. |
|
||||||
|
|
||||||
|
Cómo correr la suite sin parpadeo:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Host con GL nativo (GPU real): binario directo, ventana oculta, sin parpadeo.
|
||||||
|
./build/linux_tests/apps/<app>/<app>_tests
|
||||||
|
|
||||||
|
# CI / WSL sin display: display virtual en RAM (también headless).
|
||||||
|
xvfb-run -a -s "-screen 0 1280x800x24" \
|
||||||
|
env LIBGL_ALWAYS_SOFTWARE=1 GALLIUM_DRIVER=llvmpipe \
|
||||||
|
./build/linux_tests/apps/<app>/<app>_tests
|
||||||
|
|
||||||
|
# Ver un test a ojo (desactiva headless):
|
||||||
|
FN_HEADLESS=0 ./build/linux_tests/apps/<app>/<app>_tests
|
||||||
|
```
|
||||||
|
|
||||||
### CI gate `check_tested.sh`
|
### CI gate `check_tested.sh`
|
||||||
|
|
||||||
`cpp/scripts/check_tested.sh [days]` (default `30`) consulta `registry.db` y
|
`cpp/scripts/check_tested.sh [days]` (default `30`) consulta `registry.db` y
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -647,6 +648,25 @@ static void draw_header_badge_on_floating_panels(const AppConfig& cfg) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resuelve si la ventana GLFW debe crearse oculta (GLFW_VISIBLE=FALSE).
|
||||||
|
// default_hidden : politica base del path de entrada (apps reales = false,
|
||||||
|
// tests de UI = true).
|
||||||
|
// config_headless: AppConfig.headless explicito de la app.
|
||||||
|
// El entorno FN_HEADLESS gana sobre ambos: "0"/"false" fuerza visible,
|
||||||
|
// cualquier otro valor no vacio fuerza oculta. Sin la variable, se respeta
|
||||||
|
// default_hidden || config_headless.
|
||||||
|
static bool resolve_headless(bool default_hidden, bool config_headless) {
|
||||||
|
bool hidden = default_hidden || config_headless;
|
||||||
|
if (const char* e = std::getenv("FN_HEADLESS")) {
|
||||||
|
if (std::strcmp(e, "0") == 0 || std::strcmp(e, "false") == 0) {
|
||||||
|
hidden = false;
|
||||||
|
} else if (e[0] != '\0') {
|
||||||
|
hidden = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hidden;
|
||||||
|
}
|
||||||
|
|
||||||
int run_app(AppConfig config, std::function<void()> render_fn) {
|
int run_app(AppConfig config, std::function<void()> render_fn) {
|
||||||
// Logger primero para capturar fallos del propio init (GLFW, ventana, GL).
|
// Logger primero para capturar fallos del propio init (GLFW, ventana, GL).
|
||||||
if (config.log.file_path != nullptr) {
|
if (config.log.file_path != nullptr) {
|
||||||
@@ -672,6 +692,11 @@ int run_app(AppConfig config, std::function<void()> render_fn) {
|
|||||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
||||||
|
|
||||||
|
// Apps reales: ventana visible por defecto. Solo se oculta si la app pide
|
||||||
|
// headless o el entorno FN_HEADLESS lo fuerza (smoke/capture sin parpadeo).
|
||||||
|
const bool hidden = resolve_headless(/*default_hidden=*/false, config.headless);
|
||||||
|
glfwWindowHint(GLFW_VISIBLE, hidden ? GLFW_FALSE : GLFW_TRUE);
|
||||||
|
|
||||||
GLFWwindow* window = glfwCreateWindow(config.width, config.height, config.title, nullptr, nullptr);
|
GLFWwindow* window = glfwCreateWindow(config.width, config.height, config.title, nullptr, nullptr);
|
||||||
if (!window) {
|
if (!window) {
|
||||||
fprintf(stderr, "Failed to create GLFW window\n");
|
fprintf(stderr, "Failed to create GLFW window\n");
|
||||||
@@ -1178,6 +1203,13 @@ int run_app_test(AppConfig config,
|
|||||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
||||||
|
|
||||||
|
// Tests de frontend: ventana OCULTA por defecto (headless) para no parpadear
|
||||||
|
// en la pantalla del desarrollador ni robar foco mientras el Test Engine
|
||||||
|
// ejercita la UI. El contexto GL real se crea igual, asi que el render sigue
|
||||||
|
// siendo fiel. Opt-out para depurar visualmente: FN_HEADLESS=0.
|
||||||
|
const bool hidden = resolve_headless(/*default_hidden=*/true, config.headless);
|
||||||
|
glfwWindowHint(GLFW_VISIBLE, hidden ? GLFW_FALSE : GLFW_TRUE);
|
||||||
|
|
||||||
GLFWwindow* window = glfwCreateWindow(
|
GLFWwindow* window = glfwCreateWindow(
|
||||||
config.width, config.height,
|
config.width, config.height,
|
||||||
config.title ? config.title : "fn_test", nullptr, nullptr);
|
config.title ? config.title : "fn_test", nullptr, nullptr);
|
||||||
|
|||||||
@@ -101,6 +101,21 @@ struct AppConfig {
|
|||||||
int height = 720;
|
int height = 720;
|
||||||
bool vsync = true;
|
bool vsync = true;
|
||||||
bool viewports = true; // Multi-viewport ON por defecto: ventanas ImGui arrastrables fuera del main window
|
bool viewports = true; // Multi-viewport ON por defecto: ventanas ImGui arrastrables fuera del main window
|
||||||
|
|
||||||
|
// Headless: si true, la ventana GLFW se crea oculta (GLFW_VISIBLE=FALSE).
|
||||||
|
// El contexto OpenGL real se sigue creando y el render ocurre offscreen,
|
||||||
|
// por lo que las pruebas visuales y de UI siguen siendo fieles, pero la
|
||||||
|
// ventana nunca se mapea en pantalla (cero parpadeo, no roba foco).
|
||||||
|
//
|
||||||
|
// Politica por path:
|
||||||
|
// - run_app (apps reales): default visible (headless = false).
|
||||||
|
// - run_app_test (Dear ImGui Test Engine): default OCULTA. Los tests de
|
||||||
|
// frontend corren headless salvo opt-out explicito para debug visual.
|
||||||
|
//
|
||||||
|
// Override por entorno (gana sobre el default del path y sobre este flag):
|
||||||
|
// FN_HEADLESS=1 / true -> fuerza ventana oculta.
|
||||||
|
// FN_HEADLESS=0 / false -> fuerza ventana visible (ej. ver un test).
|
||||||
|
bool headless = false;
|
||||||
ThemeMode theme = ThemeMode::FnDark; // Identidad visual unificada por defecto
|
ThemeMode theme = ThemeMode::FnDark; // Identidad visual unificada por defecto
|
||||||
float bg_r = 0.102f; // fn_tokens::colors::bg (dark.7 #1A1B1E)
|
float bg_r = 0.102f; // fn_tokens::colors::bg (dark.7 #1A1B1E)
|
||||||
float bg_g = 0.106f;
|
float bg_g = 0.106f;
|
||||||
|
|||||||
Reference in New Issue
Block a user