feat(projects): integrate project switcher in app shell + Inspector menu
main.cpp: - Forward decl + switch_to_project: cierra layout_store, libera grafo, aplica nuevos paths, vuelve a cargar. - apply_project_paths: deriva operations.db/types.yaml/graph_explorer.db del slug y los expone a g_app.active_project. - main: arg --project <slug>; modo legacy si --input/positional dado; modo proyecto si no — migra layout legacy, decide target via arg/last_active/'default', crea si no existe, abre BDs y carga. - render(): handler want_switch_project + monta views_new_project_modal. views.h: AppState gana active_project, want_switch_project, switch_project_target, show_new_project_modal, new_project_buf, new_project_error, project_list_cache, project_recent_cache. views.cpp: - Toolbar: boton 'Project: <slug>' con popup (New/Recent/Open/Reveal). Refresca caches al abrir el menu. - views_new_project_modal: input slug + validacion + creacion + switch. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
#include "views.h"
|
||||
#include "entity_ops.h"
|
||||
#include "project_manager.h"
|
||||
|
||||
#include "viz/graph_types.h"
|
||||
#include "viz/graph_viewport.h"
|
||||
@@ -91,6 +92,74 @@ void views_apply_visibility(AppState& app) {
|
||||
void views_toolbar(AppState& app) {
|
||||
using namespace fn_ui;
|
||||
toolbar_begin();
|
||||
// Project switcher — etiqueta = proyecto activo. Click abre popup con
|
||||
// New / Open / Recent / Reveal. Si no hay proyecto activo (modo
|
||||
// legacy con --input directo), muestra "(no project)".
|
||||
{
|
||||
char btn_label[128];
|
||||
const char* p = app.active_project.empty()
|
||||
? "(no project)" : app.active_project.c_str();
|
||||
std::snprintf(btn_label, sizeof(btn_label), TI_FOLDER " Project: %s", p);
|
||||
if (button(btn_label, ButtonVariant::Secondary)) {
|
||||
// Refresca caches al abrir
|
||||
project_list(&app.project_list_cache);
|
||||
ProjectSettings ps;
|
||||
project_settings_load(&ps);
|
||||
app.project_recent_cache = ps.recent;
|
||||
ImGui::OpenPopup("##project_menu");
|
||||
}
|
||||
if (ImGui::BeginPopup("##project_menu")) {
|
||||
if (ImGui::MenuItem(TI_PLUS " New project...")) {
|
||||
app.show_new_project_modal = true;
|
||||
app.new_project_buf[0] = 0;
|
||||
app.new_project_error.clear();
|
||||
}
|
||||
ImGui::Separator();
|
||||
|
||||
// Recent submenu
|
||||
if (!app.project_recent_cache.empty()) {
|
||||
if (ImGui::BeginMenu("Recent")) {
|
||||
for (const auto& slug : app.project_recent_cache) {
|
||||
bool is_active = (slug == app.active_project);
|
||||
if (ImGui::MenuItem(slug.c_str(), nullptr, is_active)
|
||||
&& !is_active) {
|
||||
app.want_switch_project = true;
|
||||
app.switch_project_target = slug;
|
||||
}
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
// Open submenu — todos los detectados
|
||||
if (ImGui::BeginMenu("Open")) {
|
||||
if (app.project_list_cache.empty()) {
|
||||
ImGui::TextDisabled("(no projects yet)");
|
||||
} else {
|
||||
for (const auto& slug : app.project_list_cache) {
|
||||
bool is_active = (slug == app.active_project);
|
||||
if (ImGui::MenuItem(slug.c_str(), nullptr, is_active)
|
||||
&& !is_active) {
|
||||
app.want_switch_project = true;
|
||||
app.switch_project_target = slug;
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
bool can_reveal = !app.active_project.empty();
|
||||
if (ImGui::MenuItem(TI_FOLDER_OPEN " Reveal in explorer",
|
||||
nullptr, false, can_reveal)) {
|
||||
project_reveal_in_explorer(app.active_project.c_str());
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
toolbar_separator();
|
||||
|
||||
if (button(TI_FOLDER " Open file...", ButtonVariant::Secondary)) {
|
||||
app.show_open_modal = true;
|
||||
}
|
||||
@@ -490,6 +559,58 @@ bool views_filters_modal(AppState& app) {
|
||||
return changed;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Modal: New project
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
bool views_new_project_modal(AppState& app) {
|
||||
if (!app.show_new_project_modal) return false;
|
||||
bool created = false;
|
||||
if (fn_ui::modal_dialog_begin("New project", &app.show_new_project_modal,
|
||||
ImVec2(480, 0))) {
|
||||
ImGui::TextWrapped(
|
||||
"Crea una subcarpeta en projects/ con su propio operations.db,"
|
||||
" types.yaml y graph_explorer.db.");
|
||||
ImGui::Spacing();
|
||||
fn_ui::text_input("Slug", app.new_project_buf, sizeof(app.new_project_buf),
|
||||
"caso_aurgi");
|
||||
ImGui::TextDisabled("a-z, 0-9, '_' y '-' (max 64)");
|
||||
|
||||
if (!app.new_project_error.empty()) {
|
||||
ImGui::Spacing();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.5f, 0.4f, 1.0f));
|
||||
ImGui::TextWrapped("%s", app.new_project_error.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
if (fn_ui::button("Create", fn_ui::ButtonVariant::Primary)) {
|
||||
std::string err;
|
||||
if (!project_validate_slug(app.new_project_buf, &err)) {
|
||||
app.new_project_error = err;
|
||||
} else if (project_exists(app.new_project_buf)) {
|
||||
app.new_project_error = "ya existe un proyecto con ese slug";
|
||||
} else if (!project_create(app.new_project_buf, &err)) {
|
||||
app.new_project_error = err;
|
||||
} else {
|
||||
// Switch al recien creado
|
||||
app.want_switch_project = true;
|
||||
app.switch_project_target = app.new_project_buf;
|
||||
app.show_new_project_modal = false;
|
||||
app.new_project_error.clear();
|
||||
created = true;
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (fn_ui::button("Cancel", fn_ui::ButtonVariant::Subtle)) {
|
||||
app.show_new_project_modal = false;
|
||||
app.new_project_error.clear();
|
||||
}
|
||||
}
|
||||
fn_ui::modal_dialog_end();
|
||||
return created;
|
||||
}
|
||||
|
||||
bool views_open_modal(AppState& app) {
|
||||
if (!app.show_open_modal) return false;
|
||||
bool opened = false;
|
||||
|
||||
Reference in New Issue
Block a user