studio: improve data-backed graphs
This commit is contained in:
@@ -113,6 +113,7 @@ void App::RefreshData(const char* reason) {
|
|||||||
bool ok = loader_.Refresh();
|
bool ok = loader_.Refresh();
|
||||||
state_.last_refresh_time = glfwGetTime();
|
state_.last_refresh_time = glfwGetTime();
|
||||||
SyncDataBackedState();
|
SyncDataBackedState();
|
||||||
|
EnsureActiveGraph();
|
||||||
|
|
||||||
const auto& status = loader_.GetLastStatus();
|
const auto& status = loader_.GetLastStatus();
|
||||||
std::string msg;
|
std::string msg;
|
||||||
@@ -230,6 +231,35 @@ void App::SyncDataBackedState() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void App::EnsureActiveGraph() {
|
||||||
|
if (state_.active_graph != PlotKind::None &&
|
||||||
|
graph_browser_.IsGraphAvailable(state_.active_graph, state_, loader_)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PlotKind preferred[] = {
|
||||||
|
PlotKind::KnowledgeGraph,
|
||||||
|
PlotKind::DatasetInventory,
|
||||||
|
PlotKind::MountsStatus,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto kind : preferred) {
|
||||||
|
if (graph_browser_.IsGraphAvailable(kind, state_, loader_)) {
|
||||||
|
graph_navigator_.NavigateToGraph(state_, kind);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& graph : graph_browser_.GetAllGraphs()) {
|
||||||
|
if (graph_browser_.IsGraphAvailable(graph.kind, state_, loader_)) {
|
||||||
|
graph_navigator_.NavigateToGraph(state_, graph.kind);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state_.active_graph = PlotKind::None;
|
||||||
|
}
|
||||||
|
|
||||||
void App::SeedDefaultState() {
|
void App::SeedDefaultState() {
|
||||||
ui::AppendLog(state_, "system", "AFS Studio environment ready.", "system");
|
ui::AppendLog(state_, "system", "AFS Studio environment ready.", "system");
|
||||||
state_.sparkline_data.resize(30, 0.0f);
|
state_.sparkline_data.resize(30, 0.0f);
|
||||||
@@ -367,7 +397,7 @@ void App::RenderLayout() {
|
|||||||
if (state_.show_graph_browser) {
|
if (state_.show_graph_browser) {
|
||||||
ImGui::Begin("GraphBrowser", &state_.show_graph_browser,
|
ImGui::Begin("GraphBrowser", &state_.show_graph_browser,
|
||||||
ImGuiWindowFlags_NoCollapse);
|
ImGuiWindowFlags_NoCollapse);
|
||||||
graph_browser_.Render(state_);
|
graph_browser_.Render(state_, loader_);
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,10 +38,11 @@ class App {
|
|||||||
const DataLoader& loader() const { return loader_; }
|
const DataLoader& loader() const { return loader_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void RefreshData(const char* reason);
|
void RefreshData(const char* reason);
|
||||||
void SeedDefaultState();
|
void SeedDefaultState();
|
||||||
void SyncDataBackedState();
|
void SyncDataBackedState();
|
||||||
void TickSimulatedMetrics(float dt);
|
void TickSimulatedMetrics(float dt);
|
||||||
|
void EnsureActiveGraph();
|
||||||
|
|
||||||
void RenderFrame();
|
void RenderFrame();
|
||||||
void RenderLayout();
|
void RenderLayout();
|
||||||
|
|||||||
@@ -40,7 +40,17 @@ RegistryReader::RegistryReader(const std::filesystem::path& registry_path)
|
|||||||
: registry_path_(registry_path) {}
|
: registry_path_(registry_path) {}
|
||||||
|
|
||||||
std::filesystem::path RegistryReader::ResolveDefaultPath() const {
|
std::filesystem::path RegistryReader::ResolveDefaultPath() const {
|
||||||
return ResolveContextRoot() / "models" / "registry.json";
|
auto preferred = ResolveContextRoot() / "models" / "registry.json";
|
||||||
|
if (core::FileSystem::Exists(preferred)) {
|
||||||
|
return preferred;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto fallback = core::FileSystem::ResolvePath("~/.context/models/registry.json");
|
||||||
|
if (core::FileSystem::Exists(fallback)) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
return preferred;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RegistryReader::Exists() const {
|
bool RegistryReader::Exists() const {
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ using json = nlohmann::json;
|
|||||||
constexpr size_t kTrendWindow = 5;
|
constexpr size_t kTrendWindow = 5;
|
||||||
constexpr float kPi = 3.14159265f;
|
constexpr float kPi = 3.14159265f;
|
||||||
|
|
||||||
|
std::optional<std::filesystem::path> ResolveTrunkRoot();
|
||||||
|
|
||||||
std::optional<std::filesystem::path> ResolveHafsScawfulRoot() {
|
std::optional<std::filesystem::path> ResolveHafsScawfulRoot() {
|
||||||
const char* env_root = std::getenv("AFS_SCAWFUL_ROOT");
|
const char* env_root = std::getenv("AFS_SCAWFUL_ROOT");
|
||||||
if (env_root && env_root[0] != '\0') {
|
if (env_root && env_root[0] != '\0') {
|
||||||
@@ -318,6 +320,15 @@ bool DataLoader::Refresh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LoadResult resource = LoadResourceIndex(&next_resource_index);
|
LoadResult resource = LoadResourceIndex(&next_resource_index);
|
||||||
|
last_status_.resource_index_found = resource.found;
|
||||||
|
last_status_.resource_index_ok = resource.ok;
|
||||||
|
if (resource.found && !resource.ok) {
|
||||||
|
last_status_.error_count += 1;
|
||||||
|
if (last_status_.last_error.empty()) {
|
||||||
|
last_status_.last_error = resource.error;
|
||||||
|
last_status_.last_error_source = "resource_index.json";
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!resource.found) {
|
if (!resource.found) {
|
||||||
resource_index_ = ResourceIndexData{};
|
resource_index_ = ResourceIndexData{};
|
||||||
resource_index_error_ = "resource_index.json not found";
|
resource_index_error_ = "resource_index.json not found";
|
||||||
@@ -329,6 +340,15 @@ bool DataLoader::Refresh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LoadResult registry = LoadDatasetRegistry(&next_dataset_registry);
|
LoadResult registry = LoadDatasetRegistry(&next_dataset_registry);
|
||||||
|
last_status_.dataset_registry_found = registry.found;
|
||||||
|
last_status_.dataset_registry_ok = registry.ok;
|
||||||
|
if (registry.found && !registry.ok) {
|
||||||
|
last_status_.error_count += 1;
|
||||||
|
if (last_status_.last_error.empty()) {
|
||||||
|
last_status_.last_error = registry.error;
|
||||||
|
last_status_.last_error_source = "dataset_registry.json";
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!registry.found) {
|
if (!registry.found) {
|
||||||
dataset_registry_ = DatasetRegistryData{};
|
dataset_registry_ = DatasetRegistryData{};
|
||||||
dataset_registry_error_ = "dataset_registry.json not found";
|
dataset_registry_error_ = "dataset_registry.json not found";
|
||||||
@@ -340,6 +360,15 @@ bool DataLoader::Refresh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LoadResult context_graph = LoadContextGraph(&next_context_graph);
|
LoadResult context_graph = LoadContextGraph(&next_context_graph);
|
||||||
|
last_status_.context_graph_found = context_graph.found;
|
||||||
|
last_status_.context_graph_ok = context_graph.ok;
|
||||||
|
if (context_graph.found && !context_graph.ok) {
|
||||||
|
last_status_.error_count += 1;
|
||||||
|
if (last_status_.last_error.empty()) {
|
||||||
|
last_status_.last_error = context_graph.error;
|
||||||
|
last_status_.last_error_source = "afs_graph.json";
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!context_graph.found) {
|
if (!context_graph.found) {
|
||||||
context_graph_ = ContextGraphData{};
|
context_graph_ = ContextGraphData{};
|
||||||
context_graph_error_ = "afs_graph.json not found";
|
context_graph_error_ = "afs_graph.json not found";
|
||||||
@@ -386,7 +415,10 @@ bool DataLoader::Refresh() {
|
|||||||
has_data_ = !quality_trends_.empty() || !generator_stats_.empty() ||
|
has_data_ = !quality_trends_.empty() || !generator_stats_.empty() ||
|
||||||
!embedding_regions_.empty() || !training_runs_.empty() ||
|
!embedding_regions_.empty() || !training_runs_.empty() ||
|
||||||
!optimization_data_.domain_effectiveness.empty() ||
|
!optimization_data_.domain_effectiveness.empty() ||
|
||||||
!optimization_data_.threshold_sensitivity.empty();
|
!optimization_data_.threshold_sensitivity.empty() ||
|
||||||
|
!dataset_registry_.datasets.empty() ||
|
||||||
|
resource_index_.total_files > 0 ||
|
||||||
|
!context_graph_.labels.empty();
|
||||||
last_error_ = last_status_.last_error;
|
last_error_ = last_status_.last_error;
|
||||||
|
|
||||||
return last_status_.AnyOk() || (!(last_status_.FoundCount() > 0) && has_data_);
|
return last_status_.AnyOk() || (!(last_status_.FoundCount() > 0) && has_data_);
|
||||||
|
|||||||
@@ -150,19 +150,30 @@ struct LoadStatus {
|
|||||||
bool active_ok = false;
|
bool active_ok = false;
|
||||||
bool training_found = false;
|
bool training_found = false;
|
||||||
bool training_ok = false;
|
bool training_ok = false;
|
||||||
|
bool resource_index_found = false;
|
||||||
|
bool resource_index_ok = false;
|
||||||
|
bool dataset_registry_found = false;
|
||||||
|
bool dataset_registry_ok = false;
|
||||||
|
bool context_graph_found = false;
|
||||||
|
bool context_graph_ok = false;
|
||||||
int error_count = 0;
|
int error_count = 0;
|
||||||
std::string last_error;
|
std::string last_error;
|
||||||
std::string last_error_source;
|
std::string last_error_source;
|
||||||
|
|
||||||
int FoundCount() const {
|
int FoundCount() const {
|
||||||
return static_cast<int>(quality_found) + static_cast<int>(active_found) +
|
return static_cast<int>(quality_found) + static_cast<int>(active_found) +
|
||||||
static_cast<int>(training_found);
|
static_cast<int>(training_found) + static_cast<int>(resource_index_found) +
|
||||||
|
static_cast<int>(dataset_registry_found) + static_cast<int>(context_graph_found);
|
||||||
}
|
}
|
||||||
int OkCount() const {
|
int OkCount() const {
|
||||||
return static_cast<int>(quality_ok) + static_cast<int>(active_ok) +
|
return static_cast<int>(quality_ok) + static_cast<int>(active_ok) +
|
||||||
static_cast<int>(training_ok);
|
static_cast<int>(training_ok) + static_cast<int>(resource_index_ok) +
|
||||||
|
static_cast<int>(dataset_registry_ok) + static_cast<int>(context_graph_ok);
|
||||||
|
}
|
||||||
|
bool AnyOk() const {
|
||||||
|
return quality_ok || active_ok || training_ok || resource_index_ok ||
|
||||||
|
dataset_registry_ok || context_graph_ok;
|
||||||
}
|
}
|
||||||
bool AnyOk() const { return quality_ok || active_ok || training_ok; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Loads training data from JSON files.
|
/// Loads training data from JSON files.
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ enum class PlotKind {
|
|||||||
Effectiveness,
|
Effectiveness,
|
||||||
Thresholds,
|
Thresholds,
|
||||||
MountsStatus,
|
MountsStatus,
|
||||||
|
DatasetInventory,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class GraphViewMode { Single, Compare, Overview };
|
enum class GraphViewMode { Single, Compare, Overview };
|
||||||
@@ -238,7 +239,7 @@ struct AppState {
|
|||||||
bool show_plot_legends = true;
|
bool show_plot_legends = true;
|
||||||
bool show_plot_markers = true;
|
bool show_plot_markers = true;
|
||||||
bool data_scientist_mode = false;
|
bool data_scientist_mode = false;
|
||||||
bool show_all_charts = true;
|
bool show_all_charts = false;
|
||||||
float pulse_timer = 0.0f;
|
float pulse_timer = 0.0f;
|
||||||
int custom_grid_rows = 2;
|
int custom_grid_rows = 2;
|
||||||
int custom_grid_columns = 2;
|
int custom_grid_columns = 2;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ namespace viz {
|
|||||||
namespace ui {
|
namespace ui {
|
||||||
void RenderMountsChart(AppState& state, const DataLoader& loader);
|
void RenderMountsChart(AppState& state, const DataLoader& loader);
|
||||||
void RenderKnowledgeGraphChart(AppState& state, const DataLoader& loader);
|
void RenderKnowledgeGraphChart(AppState& state, const DataLoader& loader);
|
||||||
|
void RenderDatasetInventoryChart(AppState& state, const DataLoader& loader);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -265,6 +266,65 @@ void RenderTrainingLossChart(AppState& state, const DataLoader& loader) {
|
|||||||
ImPlot::PopStyleVar(6);
|
ImPlot::PopStyleVar(6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RenderDatasetInventoryChart(AppState& state, const DataLoader& loader) {
|
||||||
|
RenderChartHeader(PlotKind::DatasetInventory,
|
||||||
|
"DATASET INVENTORY",
|
||||||
|
"Dataset sizes and file counts from the registry index.",
|
||||||
|
state);
|
||||||
|
|
||||||
|
const auto& registry = loader.GetDatasetRegistry();
|
||||||
|
if (registry.datasets.empty()) {
|
||||||
|
ImGui::TextDisabled("No dataset registry data available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<const char*> labels;
|
||||||
|
std::vector<float> sizes_mb;
|
||||||
|
std::vector<float> file_counts;
|
||||||
|
std::vector<std::string> label_storage;
|
||||||
|
labels.reserve(registry.datasets.size());
|
||||||
|
sizes_mb.reserve(registry.datasets.size());
|
||||||
|
file_counts.reserve(registry.datasets.size());
|
||||||
|
label_storage.reserve(registry.datasets.size());
|
||||||
|
|
||||||
|
for (const auto& dataset : registry.datasets) {
|
||||||
|
label_storage.push_back(dataset.name);
|
||||||
|
sizes_mb.push_back(static_cast<float>(dataset.size_bytes) / (1024.0f * 1024.0f));
|
||||||
|
file_counts.push_back(static_cast<float>(dataset.files.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& label : label_storage) labels.push_back(label.c_str());
|
||||||
|
|
||||||
|
ImPlotFlags plot_flags = BasePlotFlags(state, true);
|
||||||
|
ApplyPremiumPlotStyles("##DatasetInventory", state);
|
||||||
|
if (ImPlot::BeginPlot("##DatasetInventory", ImGui::GetContentRegionAvail(), plot_flags)) {
|
||||||
|
ImPlotAxisFlags axis_flags = static_cast<ImPlotAxisFlags>(GetPlotAxisFlags(state));
|
||||||
|
ImPlot::SetupAxes("Dataset", "Size (MB)", axis_flags, axis_flags);
|
||||||
|
ImPlot::SetupAxis(ImAxis_Y2, "Files", axis_flags);
|
||||||
|
if (!labels.empty()) {
|
||||||
|
ImPlot::SetupAxisTicks(ImAxis_X1, 0, static_cast<double>(labels.size() - 1),
|
||||||
|
static_cast<int>(labels.size()), labels.data());
|
||||||
|
}
|
||||||
|
HandlePlotContextMenu(PlotKind::DatasetInventory, state);
|
||||||
|
|
||||||
|
ImPlot::SetNextFillStyle(GetSeriesColor(3), 0.75f);
|
||||||
|
ImPlot::PlotBars("Size (MB)", sizes_mb.data(),
|
||||||
|
static_cast<int>(sizes_mb.size()), 0.6f);
|
||||||
|
|
||||||
|
ImPlot::SetAxis(ImAxis_Y2);
|
||||||
|
ImPlot::SetNextLineStyle(GetSeriesColor(6), 2.0f);
|
||||||
|
if (state.show_plot_markers) {
|
||||||
|
ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle, 4.0f, GetSeriesColor(6));
|
||||||
|
}
|
||||||
|
ImPlot::PlotLine("Files", file_counts.data(),
|
||||||
|
static_cast<int>(file_counts.size()));
|
||||||
|
|
||||||
|
ImPlot::EndPlot();
|
||||||
|
}
|
||||||
|
ImPlot::PopStyleColor(2);
|
||||||
|
ImPlot::PopStyleVar(6);
|
||||||
|
}
|
||||||
|
|
||||||
void RenderRejectionChart(AppState& state, const DataLoader& loader) {
|
void RenderRejectionChart(AppState& state, const DataLoader& loader) {
|
||||||
RenderChartHeader(PlotKind::Rejections,
|
RenderChartHeader(PlotKind::Rejections,
|
||||||
"REJECTION REASONS",
|
"REJECTION REASONS",
|
||||||
@@ -1075,6 +1135,9 @@ void RenderPlotByKind(PlotKind kind, AppState& state, const DataLoader& loader)
|
|||||||
case PlotKind::MountsStatus:
|
case PlotKind::MountsStatus:
|
||||||
RenderMountsChart(state, loader);
|
RenderMountsChart(state, loader);
|
||||||
break;
|
break;
|
||||||
|
case PlotKind::DatasetInventory:
|
||||||
|
RenderDatasetInventoryChart(state, loader);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "graph_browser.h"
|
#include "graph_browser.h"
|
||||||
#include "../core.h"
|
#include "../core.h"
|
||||||
#include "../../icons.h"
|
#include "../../icons.h"
|
||||||
|
#include "../../data_loader.h"
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
@@ -15,6 +16,7 @@ void GraphBrowser::InitializeGraphRegistry() {
|
|||||||
// Training Category
|
// Training Category
|
||||||
{PlotKind::TrainingLoss, "Training Loss", "Loss curves over training steps", GraphCategory::Training, true, true, true, true},
|
{PlotKind::TrainingLoss, "Training Loss", "Loss curves over training steps", GraphCategory::Training, true, true, true, true},
|
||||||
{PlotKind::LossVsSamples, "Loss vs Samples", "Training loss progression by sample count", GraphCategory::Training, false, true, true, false},
|
{PlotKind::LossVsSamples, "Loss vs Samples", "Training loss progression by sample count", GraphCategory::Training, false, true, true, false},
|
||||||
|
{PlotKind::DatasetInventory, "Dataset Inventory", "Dataset sizes and file counts", GraphCategory::Training, false, false, true, false},
|
||||||
|
|
||||||
// Quality Category
|
// Quality Category
|
||||||
{PlotKind::QualityTrends, "Quality Trends", "Data quality trends by domain", GraphCategory::Quality, true, true, true, true},
|
{PlotKind::QualityTrends, "Quality Trends", "Data quality trends by domain", GraphCategory::Quality, true, true, true, true},
|
||||||
@@ -93,7 +95,57 @@ const GraphInfo* GraphBrowser::GetGraphInfo(PlotKind kind) const {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphBrowser::Render(AppState& state) {
|
bool GraphBrowser::IsGraphAvailable(PlotKind kind,
|
||||||
|
const AppState& state,
|
||||||
|
const DataLoader& loader) const {
|
||||||
|
switch (kind) {
|
||||||
|
case PlotKind::QualityTrends:
|
||||||
|
case PlotKind::QualityDirection:
|
||||||
|
return !loader.GetQualityTrends().empty();
|
||||||
|
case PlotKind::GeneratorEfficiency:
|
||||||
|
case PlotKind::GeneratorMix:
|
||||||
|
return !loader.GetGeneratorStats().empty();
|
||||||
|
case PlotKind::CoverageDensity:
|
||||||
|
case PlotKind::EmbeddingDensity:
|
||||||
|
case PlotKind::EmbeddingQuality:
|
||||||
|
case PlotKind::LatentSpace:
|
||||||
|
return !loader.GetEmbeddingRegions().empty();
|
||||||
|
case PlotKind::TrainingLoss:
|
||||||
|
case PlotKind::LossVsSamples:
|
||||||
|
return !loader.GetTrainingRuns().empty();
|
||||||
|
case PlotKind::DomainCoverage:
|
||||||
|
return !loader.GetCoverage().domain_coverage.empty();
|
||||||
|
case PlotKind::Rejections:
|
||||||
|
return !loader.GetRejectionSummary().reasons.empty();
|
||||||
|
case PlotKind::EvalMetrics: {
|
||||||
|
const auto& runs = loader.GetTrainingRuns();
|
||||||
|
for (const auto& run : runs) {
|
||||||
|
if (!run.eval_metrics.empty()) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
case PlotKind::Effectiveness:
|
||||||
|
return !loader.GetOptimizationData().domain_effectiveness.empty() ||
|
||||||
|
!loader.GetCoverage().domain_coverage.empty();
|
||||||
|
case PlotKind::Thresholds:
|
||||||
|
return !loader.GetOptimizationData().threshold_sensitivity.empty();
|
||||||
|
case PlotKind::AgentThroughput:
|
||||||
|
return !state.agents.empty();
|
||||||
|
case PlotKind::MissionQueue:
|
||||||
|
case PlotKind::MissionProgress:
|
||||||
|
return !state.missions.empty();
|
||||||
|
case PlotKind::MountsStatus:
|
||||||
|
return !loader.GetMounts().empty();
|
||||||
|
case PlotKind::KnowledgeGraph:
|
||||||
|
return !loader.GetContextGraph().labels.empty();
|
||||||
|
case PlotKind::DatasetInventory:
|
||||||
|
return !loader.GetDatasetRegistry().datasets.empty();
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphBrowser::Render(AppState& state, const DataLoader& loader) {
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8, 8));
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8, 8));
|
||||||
|
|
||||||
// Search bar
|
// Search bar
|
||||||
@@ -136,7 +188,17 @@ void GraphBrowser::Render(AppState& state) {
|
|||||||
for (auto kind : state.graph_bookmarks) {
|
for (auto kind : state.graph_bookmarks) {
|
||||||
const GraphInfo* info = GetGraphInfo(kind);
|
const GraphInfo* info = GetGraphInfo(kind);
|
||||||
if (info) {
|
if (info) {
|
||||||
RenderGraphItem(*info, state);
|
bool available = IsGraphAvailable(info->kind, state, loader);
|
||||||
|
if (!available && !state.show_all_charts) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!available && state.show_all_charts) {
|
||||||
|
ImGui::BeginDisabled();
|
||||||
|
RenderGraphItem(*info, state);
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
} else {
|
||||||
|
RenderGraphItem(*info, state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -158,7 +220,17 @@ void GraphBrowser::Render(AppState& state) {
|
|||||||
for (auto kind : recent_unique) {
|
for (auto kind : recent_unique) {
|
||||||
const GraphInfo* info = GetGraphInfo(kind);
|
const GraphInfo* info = GetGraphInfo(kind);
|
||||||
if (info) {
|
if (info) {
|
||||||
RenderGraphItem(*info, state);
|
bool available = IsGraphAvailable(info->kind, state, loader);
|
||||||
|
if (!available && !state.show_all_charts) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!available && state.show_all_charts) {
|
||||||
|
ImGui::BeginDisabled();
|
||||||
|
RenderGraphItem(*info, state);
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
} else {
|
||||||
|
RenderGraphItem(*info, state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -170,13 +242,31 @@ void GraphBrowser::Render(AppState& state) {
|
|||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|
||||||
auto filtered = GetFilteredGraphs(state.browser_filter, state.graph_search_query);
|
auto filtered = GetFilteredGraphs(state.browser_filter, state.graph_search_query);
|
||||||
|
if (!state.show_all_charts) {
|
||||||
|
filtered.erase(std::remove_if(filtered.begin(), filtered.end(),
|
||||||
|
[&](const GraphInfo& graph) {
|
||||||
|
return !IsGraphAvailable(graph.kind, state, loader);
|
||||||
|
}),
|
||||||
|
filtered.end());
|
||||||
|
}
|
||||||
|
|
||||||
if (filtered.empty()) {
|
if (filtered.empty()) {
|
||||||
ImGui::TextDisabled("No graphs found");
|
if (state.show_all_charts) {
|
||||||
|
ImGui::TextDisabled("No graphs found");
|
||||||
|
} else {
|
||||||
|
ImGui::TextDisabled("No graphs with data (toggle Show All Chart Windows to view empty graphs)");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ImGui::BeginChild("GraphList", ImVec2(0, 0), false);
|
ImGui::BeginChild("GraphList", ImVec2(0, 0), false);
|
||||||
for (const auto& graph : filtered) {
|
for (const auto& graph : filtered) {
|
||||||
RenderGraphItem(graph, state);
|
bool available = IsGraphAvailable(graph.kind, state, loader);
|
||||||
|
if (!available && state.show_all_charts) {
|
||||||
|
ImGui::BeginDisabled();
|
||||||
|
RenderGraphItem(graph, state);
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
} else {
|
||||||
|
RenderGraphItem(graph, state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,10 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include "../../models/state.h"
|
#include "../../models/state.h"
|
||||||
|
|
||||||
|
namespace afs::viz {
|
||||||
|
class DataLoader;
|
||||||
|
}
|
||||||
|
|
||||||
namespace afs::viz::ui {
|
namespace afs::viz::ui {
|
||||||
|
|
||||||
// Graph metadata for browser display
|
// Graph metadata for browser display
|
||||||
@@ -23,16 +27,21 @@ public:
|
|||||||
GraphBrowser();
|
GraphBrowser();
|
||||||
|
|
||||||
// Render the graph browser sidebar
|
// Render the graph browser sidebar
|
||||||
void Render(AppState& state);
|
void Render(AppState& state, const DataLoader& loader);
|
||||||
|
|
||||||
// Get all available graphs
|
// Get all available graphs
|
||||||
const std::vector<GraphInfo>& GetAllGraphs() const { return all_graphs_; }
|
const std::vector<GraphInfo>& GetAllGraphs() const { return all_graphs_; }
|
||||||
|
|
||||||
// Get filtered graphs based on category and search
|
// Get filtered graphs based on category and search
|
||||||
std::vector<GraphInfo> GetFilteredGraphs(GraphCategory category, const std::string& search) const;
|
std::vector<GraphInfo> GetFilteredGraphs(GraphCategory category,
|
||||||
|
const std::string& search) const;
|
||||||
|
|
||||||
// Get graph info by kind
|
// Get graph info by kind
|
||||||
const GraphInfo* GetGraphInfo(PlotKind kind) const;
|
const GraphInfo* GetGraphInfo(PlotKind kind) const;
|
||||||
|
|
||||||
|
bool IsGraphAvailable(PlotKind kind,
|
||||||
|
const AppState& state,
|
||||||
|
const DataLoader& loader) const;
|
||||||
|
|
||||||
// Get category name
|
// Get category name
|
||||||
static const char* GetCategoryName(GraphCategory category);
|
static const char* GetCategoryName(GraphCategory category);
|
||||||
|
|||||||
@@ -245,6 +245,7 @@ const std::vector<PlotOption>& PlotOptions() {
|
|||||||
{PlotKind::Effectiveness, "Domain Effectiveness"},
|
{PlotKind::Effectiveness, "Domain Effectiveness"},
|
||||||
{PlotKind::Thresholds, "Threshold Sensitivity"},
|
{PlotKind::Thresholds, "Threshold Sensitivity"},
|
||||||
{PlotKind::MountsStatus, "Local Mounts Status"},
|
{PlotKind::MountsStatus, "Local Mounts Status"},
|
||||||
|
{PlotKind::DatasetInventory, "Dataset Inventory"},
|
||||||
};
|
};
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user