feat(framework): assets/ subfolder para distribuibles read-only
Refina la convencion de layout: el top de cada app distribuible solo lleva el .exe + DLLs nativas; todo lo demas (TTFs, enrichers, runtime Python, MCP servers) vive en <exe_dir>/assets/. Cambios: - cpp/CMakeLists.txt::add_imgui_app — copia las 5 TTFs (Karla, Roboto, DroidSans, Cousine, tabler-icons) a $<TARGET_FILE_DIR>/assets/ en lugar de junto al exe. - framework/app_base: nuevas funciones fn::asset_dir() y fn::asset_path(name) que resuelven a <exe_dir>/assets/<name>. - functions/core/icon_font.cpp::find_asset — anade fn::asset_path(filename) como PRIMERA ruta de busqueda, antes de las legacy ./<file> y ./assets/<file>. Mantiene los fallbacks para dev (FN_ASSETS_DIR, FN_CPP_ROOT). - .claude/commands/compile.md — el deploy a Desktop pone TTFs + enrichers/ + runtime/ + gx-cli en <DEST>/assets/. Solo .exe y DLLs nativas (duckdb.dll) quedan en el top. local_files/ se preserva si existe. Layout final: Desktop/apps/<APP>/ ├── <APP>.exe + *.dll (binario + DLLs Windows) ├── assets/ (read-only distribuible) │ ├── *.ttf, enrichers/, runtime/, gx-cli, ... └── local_files/ (per-PC, creado al primer arranque) Esto cierra la separacion conceptual de la convencion: la carpeta es trivial de zippear (solo .exe + assets/), el reset/sync es trivial (local_files/), y todas las apps del registry adoptan el mismo layout via fn_framework. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+33
-20
@@ -101,22 +101,26 @@ Si el target no existe en CMake (porque la app no esta registrada en `cpp/CMakeL
|
|||||||
|
|
||||||
### 4. Copiar a `/mnt/c/Users/lucas/Desktop/apps/<APP>/`
|
### 4. Copiar a `/mnt/c/Users/lucas/Desktop/apps/<APP>/`
|
||||||
|
|
||||||
Layout estandar (convencion `local_files/`, ver `cpp_apps.md` §7):
|
Layout estandar (convencion `assets/` + `local_files/`, ver `cpp_apps.md` §7):
|
||||||
|
|
||||||
```
|
```
|
||||||
Desktop/apps/<APP>/
|
Desktop/apps/<APP>/
|
||||||
├── <APP>.exe ← binario
|
├── <APP>.exe ← binario (top level por convencion Windows DLL)
|
||||||
├── *.ttf, *.dll ← fuentes + DLLs (junto al exe)
|
├── *.dll ← DLLs nativas (Windows las busca junto al exe)
|
||||||
├── assets/ ← opcional, copiada del build
|
├── assets/ ← read-only, ships con el zip
|
||||||
├── enrichers/ ← opcional, si <app_dir>/enrichers existe
|
│ ├── *.ttf ← fuentes (vienen de add_imgui_app)
|
||||||
├── runtime/ ← opcional, si app.md tiene python_runtime: true
|
│ ├── enrichers/ ← si <app_dir>/enrichers existe
|
||||||
└── local_files/ ← creado por la app al primer arranque
|
│ ├── runtime/ ← Python embed si app.md tiene python_runtime: true
|
||||||
NUNCA borrar al recompilar
|
│ ├── gx-cli, gx-cli.exe ← si la app necesita un MCP server
|
||||||
|
│ └── ... ← cualquier otro asset distribuible
|
||||||
|
└── local_files/ ← writable, per-PC, creado por la app al
|
||||||
|
primer arranque. NUNCA borrar al recompilar.
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
DEST="/mnt/c/Users/lucas/Desktop/apps/$APP_ARG"
|
DEST="/mnt/c/Users/lucas/Desktop/apps/$APP_ARG"
|
||||||
mkdir -p "$DEST"
|
ASSETS="$DEST/assets"
|
||||||
|
mkdir -p "$DEST" "$ASSETS"
|
||||||
|
|
||||||
EXE_SRC="$BUILD_WIN/apps/$APP_ARG/$APP_ARG.exe"
|
EXE_SRC="$BUILD_WIN/apps/$APP_ARG/$APP_ARG.exe"
|
||||||
if [ ! -f "$EXE_SRC" ]; then
|
if [ ! -f "$EXE_SRC" ]; then
|
||||||
@@ -124,24 +128,24 @@ if [ ! -f "$EXE_SRC" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 1. Binario + TTFs + DLLs (junto al exe del build, copiados por add_imgui_app).
|
# 1. Binario + DLLs en el top level (Windows DLL search convention).
|
||||||
cp -v "$EXE_SRC" "$DEST/"
|
cp -v "$EXE_SRC" "$DEST/"
|
||||||
find "$BUILD_WIN/apps/$APP_ARG" -maxdepth 1 -type f \
|
find "$BUILD_WIN/apps/$APP_ARG" -maxdepth 1 -type f -name '*.dll' \
|
||||||
\( -name '*.ttf' -o -name '*.dll' \) -exec cp -v {} "$DEST/" \;
|
-exec cp -v {} "$DEST/" \;
|
||||||
|
|
||||||
# 2. assets/ del build (opcional).
|
# 2. assets/ — TTFs (las copia add_imgui_app a build/<app>/assets/) y
|
||||||
|
# cualquier asset extra del build (build/<app>/assets/*).
|
||||||
if [ -d "$BUILD_WIN/apps/$APP_ARG/assets" ]; then
|
if [ -d "$BUILD_WIN/apps/$APP_ARG/assets" ]; then
|
||||||
rsync -a --delete "$BUILD_WIN/apps/$APP_ARG/assets/" "$DEST/assets/"
|
rsync -a --delete "$BUILD_WIN/apps/$APP_ARG/assets/" "$ASSETS/"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 3. enrichers/ del app_dir (opcional). Excluye __pycache__ + .pyc.
|
# 3. enrichers/ del app_dir -> assets/enrichers/.
|
||||||
if [ -d "$APP_DIR/enrichers" ]; then
|
if [ -d "$APP_DIR/enrichers" ]; then
|
||||||
rsync -a --delete --exclude '__pycache__' --exclude '*.pyc' \
|
rsync -a --delete --exclude '__pycache__' --exclude '*.pyc' \
|
||||||
"$APP_DIR/enrichers/" "$DEST/enrichers/"
|
"$APP_DIR/enrichers/" "$ASSETS/enrichers/"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 4. runtime/ Python embebido (si la app lo declara).
|
# 4. runtime/ Python embebido -> assets/runtime/ (si la app lo declara).
|
||||||
# Lee `python_runtime: true` del frontmatter de app.md.
|
|
||||||
if grep -q '^python_runtime:[[:space:]]*true' "$APP_DIR/app.md" 2>/dev/null; then
|
if grep -q '^python_runtime:[[:space:]]*true' "$APP_DIR/app.md" 2>/dev/null; then
|
||||||
if [ ! -d "$APP_DIR/runtime/python" ] || \
|
if [ ! -d "$APP_DIR/runtime/python" ] || \
|
||||||
[ "$APP_DIR/app.md" -nt "$APP_DIR/runtime/.lock" ]; then
|
[ "$APP_DIR/app.md" -nt "$APP_DIR/runtime/.lock" ]; then
|
||||||
@@ -149,10 +153,19 @@ if grep -q '^python_runtime:[[:space:]]*true' "$APP_DIR/app.md" 2>/dev/null; the
|
|||||||
"$APP_DIR/tools/freeze_python_runtime.sh" "$APP_DIR" windows
|
"$APP_DIR/tools/freeze_python_runtime.sh" "$APP_DIR" windows
|
||||||
fi
|
fi
|
||||||
rsync -a --delete --exclude '__pycache__' --exclude '*.pyc' \
|
rsync -a --delete --exclude '__pycache__' --exclude '*.pyc' \
|
||||||
"$APP_DIR/runtime/" "$DEST/runtime/"
|
"$APP_DIR/runtime/" "$ASSETS/runtime/"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 5. NO TOCAR local_files/. Si existe en $DEST, preservar — contiene
|
# 5. Otros assets sueltos del app_dir (gx-cli, scripts varios). El
|
||||||
|
# convention es: si vive en <app_dir>/ y no es codigo fuente, va a
|
||||||
|
# assets/. Ahora mismo la unica excepcion es gx-cli (graph_explorer).
|
||||||
|
for extra in gx-cli gx-cli.exe; do
|
||||||
|
if [ -f "$APP_DIR/$extra" ]; then
|
||||||
|
cp -v "$APP_DIR/$extra" "$ASSETS/"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# 6. NO TOCAR local_files/. Si existe en $DEST, preservar — contiene
|
||||||
# estado del usuario (DBs, settings, layouts ImGui, proyectos).
|
# estado del usuario (DBs, settings, layouts ImGui, proyectos).
|
||||||
echo "OK: $APP_ARG -> $DEST"
|
echo "OK: $APP_ARG -> $DEST"
|
||||||
[ -d "$DEST/local_files" ] && echo " local_files/ preservado: $(du -sh "$DEST/local_files" | cut -f1)"
|
[ -d "$DEST/local_files" ] && echo " local_files/ preservado: $(du -sh "$DEST/local_files" | cut -f1)"
|
||||||
|
|||||||
+17
-8
@@ -154,8 +154,11 @@ add_library(fn_framework STATIC
|
|||||||
functions/core/panel_menu.cpp
|
functions/core/panel_menu.cpp
|
||||||
functions/core/layouts_menu.cpp
|
functions/core/layouts_menu.cpp
|
||||||
functions/core/app_menubar.cpp
|
functions/core/app_menubar.cpp
|
||||||
|
functions/core/logger.cpp
|
||||||
|
functions/core/log_window.cpp
|
||||||
functions/gfx/gl_loader.cpp
|
functions/gfx/gl_loader.cpp
|
||||||
functions/core/layout_storage.cpp
|
functions/core/layout_storage.cpp
|
||||||
|
functions/core/selectable_text.cpp
|
||||||
)
|
)
|
||||||
target_include_directories(fn_framework PUBLIC
|
target_include_directories(fn_framework PUBLIC
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/framework
|
${CMAKE_CURRENT_SOURCE_DIR}/framework
|
||||||
@@ -194,25 +197,31 @@ function(add_imgui_app target)
|
|||||||
target_include_directories(${target} PRIVATE
|
target_include_directories(${target} PRIVATE
|
||||||
${FN_CPP_ROOT_DIR}/functions
|
${FN_CPP_ROOT_DIR}/functions
|
||||||
)
|
)
|
||||||
# Copia las fuentes junto al ejecutable para deploys autonomos (sin
|
# Convencion de layout (cpp_apps.md §7):
|
||||||
# FN_CPP_ROOT en runtime). 4 TTFs vectoriales para el menu Settings + Tabler
|
# <exe_dir>/<app>.exe + <app>.dll (binario + DLLs Windows convention)
|
||||||
# para los iconos TI_*.
|
# <exe_dir>/assets/ (read-only: ttfs, enrichers, runtime, etc.)
|
||||||
|
# <exe_dir>/local_files/ (creado en runtime: ini, db, projects)
|
||||||
|
#
|
||||||
|
# add_imgui_app copia las TTFs a <exe_dir>/assets/. La app las
|
||||||
|
# encuentra en runtime via fn::asset_path() (icon_font.cpp).
|
||||||
|
set(_ASSETS_DIR $<TARGET_FILE_DIR:${target}>/assets)
|
||||||
add_custom_command(TARGET ${target} POST_BUILD
|
add_custom_command(TARGET ${target} POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E make_directory ${_ASSETS_DIR}
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
${FN_CPP_ROOT_DIR}/vendor/imgui/misc/fonts/Karla-Regular.ttf
|
${FN_CPP_ROOT_DIR}/vendor/imgui/misc/fonts/Karla-Regular.ttf
|
||||||
$<TARGET_FILE_DIR:${target}>/Karla-Regular.ttf
|
${_ASSETS_DIR}/Karla-Regular.ttf
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
${FN_CPP_ROOT_DIR}/vendor/imgui/misc/fonts/Roboto-Medium.ttf
|
${FN_CPP_ROOT_DIR}/vendor/imgui/misc/fonts/Roboto-Medium.ttf
|
||||||
$<TARGET_FILE_DIR:${target}>/Roboto-Medium.ttf
|
${_ASSETS_DIR}/Roboto-Medium.ttf
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
${FN_CPP_ROOT_DIR}/vendor/imgui/misc/fonts/DroidSans.ttf
|
${FN_CPP_ROOT_DIR}/vendor/imgui/misc/fonts/DroidSans.ttf
|
||||||
$<TARGET_FILE_DIR:${target}>/DroidSans.ttf
|
${_ASSETS_DIR}/DroidSans.ttf
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
${FN_CPP_ROOT_DIR}/vendor/imgui/misc/fonts/Cousine-Regular.ttf
|
${FN_CPP_ROOT_DIR}/vendor/imgui/misc/fonts/Cousine-Regular.ttf
|
||||||
$<TARGET_FILE_DIR:${target}>/Cousine-Regular.ttf
|
${_ASSETS_DIR}/Cousine-Regular.ttf
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
${FN_CPP_ROOT_DIR}/vendor/tabler-icons/tabler-icons.ttf
|
${FN_CPP_ROOT_DIR}/vendor/tabler-icons/tabler-icons.ttf
|
||||||
$<TARGET_FILE_DIR:${target}>/tabler-icons.ttf
|
${_ASSETS_DIR}/tabler-icons.ttf
|
||||||
VERBATIM
|
VERBATIM
|
||||||
)
|
)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|||||||
@@ -106,6 +106,30 @@ const char* local_path(const char* name) {
|
|||||||
return buf.c_str();
|
return buf.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const std::string& asset_dir_ref() {
|
||||||
|
static std::string cached;
|
||||||
|
static bool inited = false;
|
||||||
|
if (inited) return cached;
|
||||||
|
const std::string& edir = exe_dir_ref();
|
||||||
|
cached = edir.empty() ? std::string("assets") : edir + "/assets";
|
||||||
|
inited = true;
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
const char* asset_dir() { return asset_dir_ref().c_str(); }
|
||||||
|
|
||||||
|
const char* asset_path(const char* name) {
|
||||||
|
static thread_local std::string buf;
|
||||||
|
buf = asset_dir_ref();
|
||||||
|
if (name && *name) {
|
||||||
|
if (!buf.empty() && buf.back() != '/' && buf.back() != '\\') buf += '/';
|
||||||
|
buf += name;
|
||||||
|
}
|
||||||
|
return buf.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
void migrate_to_local_files(const char* const* names, std::size_t n) {
|
void migrate_to_local_files(const char* const* names, std::size_t n) {
|
||||||
if (!names || n == 0) return;
|
if (!names || n == 0) return;
|
||||||
const std::string& ldir = local_dir_ref();
|
const std::string& ldir = local_dir_ref();
|
||||||
|
|||||||
@@ -68,6 +68,15 @@ const char* local_dir();
|
|||||||
// proxima llamada — copia el valor si vas a guardarlo.
|
// proxima llamada — copia el valor si vas a guardarlo.
|
||||||
const char* local_path(const char* name);
|
const char* local_path(const char* name);
|
||||||
|
|
||||||
|
// Devuelve `<exe_dir>/assets/` — read-only sidecar shipped con la
|
||||||
|
// app (ttfs, enrichers, runtime Python, etc.). NO crea la carpeta;
|
||||||
|
// si no existe, la app debe fallback a buscar los assets en
|
||||||
|
// FN_CPP_ROOT u otras rutas. Sin trailing slash.
|
||||||
|
const char* asset_dir();
|
||||||
|
|
||||||
|
// Construye `<asset_dir>/<name>`. Mismo lifetime que local_path.
|
||||||
|
const char* asset_path(const char* name);
|
||||||
|
|
||||||
// Mueve los archivos listados de cwd o exe_dir a local_files/ si
|
// Mueve los archivos listados de cwd o exe_dir a local_files/ si
|
||||||
// existen ahi pero NO existen ya en local_files/. Idempotente. Las
|
// existen ahi pero NO existen ya en local_files/. Idempotente. Las
|
||||||
// apps lo llaman al iniciar para migrar instalaciones viejas.
|
// apps lo llaman al iniciar para migrar instalaciones viejas.
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "icon_font.h"
|
#include "icon_font.h"
|
||||||
|
|
||||||
#include "app_settings.h"
|
#include "app_settings.h"
|
||||||
|
#include "../../framework/app_base.h"
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
@@ -25,13 +26,18 @@ bool file_exists(const char* path) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Busca un asset (TTF) en las rutas estandar del registry. Devuelve la primera
|
// Busca un asset (TTF) en las rutas estandar. Devuelve la primera
|
||||||
// ruta valida o vacio.
|
// ruta valida o vacio.
|
||||||
//
|
//
|
||||||
// Orden: ./<filename> → ./assets/<filename> → $FN_ASSETS_DIR/<filename>
|
// Orden:
|
||||||
// → ${FN_CPP_ROOT}/<repo_subpath>
|
// 1. fn::asset_path(filename) <exe_dir>/assets/<filename>
|
||||||
|
// 2. ./<filename> cwd directo (legacy)
|
||||||
|
// 3. ./assets/<filename> cwd subdir (legacy / dev)
|
||||||
|
// 4. $FN_ASSETS_DIR/<filename> override env
|
||||||
|
// 5. ${FN_CPP_ROOT}/<repo_subpath> fuente del repo (dev sin build)
|
||||||
std::string find_asset(const char* filename, const char* repo_subpath) {
|
std::string find_asset(const char* filename, const char* repo_subpath) {
|
||||||
std::string p;
|
std::string p;
|
||||||
|
p = fn::asset_path(filename); if (file_exists(p.c_str())) return p;
|
||||||
p = std::string("./") + filename; if (file_exists(p.c_str())) return p;
|
p = std::string("./") + filename; if (file_exists(p.c_str())) return p;
|
||||||
p = std::string("./assets/") + filename; if (file_exists(p.c_str())) return p;
|
p = std::string("./assets/") + filename; if (file_exists(p.c_str())) return p;
|
||||||
if (const char* env = std::getenv("FN_ASSETS_DIR")) {
|
if (const char* env = std::getenv("FN_ASSETS_DIR")) {
|
||||||
|
|||||||
Reference in New Issue
Block a user