fix(user-data): keep ikvxx persistence in text format

This commit is contained in:
2026-06-19 16:57:18 -05:00
parent ce81922ebb
commit 069d4e341f

View File

@@ -2,10 +2,6 @@
#include <izo/Paths.hpp>
#include <ikvxx/ikvxx.hpp>
extern "C"
{
#include <ikv.h>
}
#include <algorithm>
#include <fstream>
@@ -133,6 +129,15 @@ namespace
}
}
std::optional<float> readFloat(const ikv::Value &value)
{
if (value.isDouble())
return static_cast<float>(value.asDouble());
if (value.isInt())
return static_cast<float>(value.asInt64());
return std::nullopt;
}
// Load settings from an ikv::Value "settings" object into UserData fields.
// Returns true if anything useful was read.
void loadSettings(const ikv::Value &settings, float &sidebar_width, float &details_width,
@@ -142,16 +147,20 @@ namespace
std::array<bool, 4> &sidebar_section_open,
std::array<float, 4> &commit_table_column_widths)
{
if (settings.isMember("sidebar_width") && settings["sidebar_width"].isDouble())
sidebar_width = static_cast<float>(settings["sidebar_width"].asDouble());
if (settings.isMember("details_width") && settings["details_width"].isDouble())
details_width = static_cast<float>(settings["details_width"].asDouble());
if (settings.isMember("sidebar_width"))
if (const auto value = readFloat(settings["sidebar_width"]); value)
sidebar_width = *value;
if (settings.isMember("details_width"))
if (const auto value = readFloat(settings["details_width"]); value)
details_width = *value;
if (settings.isMember("sidebar_collapsed") && settings["sidebar_collapsed"].isBool())
sidebar_collapsed = settings["sidebar_collapsed"].asBool();
if (settings.isMember("commit_message_height") && settings["commit_message_height"].isDouble())
commit_message_height = static_cast<float>(settings["commit_message_height"].asDouble());
if (settings.isMember("working_composer_height") && settings["working_composer_height"].isDouble())
working_composer_height = static_cast<float>(settings["working_composer_height"].asDouble());
if (settings.isMember("commit_message_height"))
if (const auto value = readFloat(settings["commit_message_height"]); value)
commit_message_height = *value;
if (settings.isMember("working_composer_height"))
if (const auto value = readFloat(settings["working_composer_height"]); value)
working_composer_height = *value;
if (settings.isMember("pull_mode") && settings["pull_mode"].isInt())
pull_mode = static_cast<int>(settings["pull_mode"].asInt64());
if (settings.isMember("zoom_percent") && settings["zoom_percent"].isInt())
@@ -162,7 +171,8 @@ namespace
const ikv::Value &arr = settings["sidebar_sections"];
const auto count = std::min(arr.size(), sidebar_section_heights.size());
for (std::size_t i = 0; i < count; ++i)
sidebar_section_heights[i] = static_cast<float>(arr[static_cast<ArrayIndex>(i)].asDouble());
if (const auto value = readFloat(arr[static_cast<ArrayIndex>(i)]); value)
sidebar_section_heights[i] = *value;
}
if (settings.isMember("sidebar_section_open") && settings["sidebar_section_open"].isArray())
{
@@ -176,7 +186,8 @@ namespace
const ikv::Value &arr = settings["commit_table_columns"];
const auto count = std::min(arr.size(), commit_table_column_widths.size());
for (std::size_t i = 0; i < count; ++i)
commit_table_column_widths[i] = static_cast<float>(arr[static_cast<ArrayIndex>(i)].asDouble());
if (const auto value = readFloat(arr[static_cast<ArrayIndex>(i)]); value)
commit_table_column_widths[i] = *value;
}
}
} // namespace
@@ -234,7 +245,17 @@ void UserData::load()
// ── Primary load: iKvxx binary (.ikv) ────────────────────────────────────
try
{
ikv::Value root = ikv::Value::loadBinary(binary_path.string());
const ikv::Value root = [&]() -> ikv::Value
{
try
{
return ikv::Value::load(binary_path.string());
}
catch (const ikv::Error &)
{
return ikv::Value::loadBinary(binary_path.string());
}
}();
if (root.isMember("settings") && root["settings"].isObject())
{
@@ -536,76 +557,66 @@ void UserData::setPendingCommitDescription(const std::string &repo_path, const s
void UserData::save() const
{
std::filesystem::create_directories(directory_);
ikv::Value root(ikv::objectValue, "gitree");
ikv_node_t *root = ikv_create_object("gitree");
if (!root)
return;
auto settings = root.makeObject("settings");
settings["sidebar_width"] = static_cast<double>(sidebar_width_);
settings["details_width"] = static_cast<double>(details_width_);
settings["sidebar_collapsed"] = sidebar_collapsed_;
settings["commit_message_height"] = static_cast<double>(commit_message_height_);
settings["working_composer_height"] = static_cast<double>(working_composer_height_);
settings["pull_mode"] = static_cast<int64_t>(pull_mode_);
settings["zoom_percent"] = static_cast<int64_t>(zoom_percent_);
// ── settings ─────────────────────────────────────────────────────────────
ikv_node_t *settings = ikv_object_add_object(root, "settings");
ikv_object_set_float(settings, "sidebar_width", sidebar_width_);
ikv_object_set_float(settings, "details_width", details_width_);
ikv_object_set_bool(settings, "sidebar_collapsed", sidebar_collapsed_);
ikv_object_set_float(settings, "commit_message_height", commit_message_height_);
ikv_object_set_float(settings, "working_composer_height", working_composer_height_);
ikv_object_set_int(settings, "pull_mode", pull_mode_);
ikv_object_set_int(settings, "zoom_percent", zoom_percent_);
ikv_node_t *heights = ikv_object_add_array(settings, "sidebar_sections", IKV_FLOAT);
auto heights = settings.makeArray("sidebar_sections", ikv::realValue);
for (const float h : sidebar_section_heights_)
ikv_array_add_float(heights, h);
heights.append(static_cast<double>(h));
ikv_node_t *open = ikv_object_add_array(settings, "sidebar_section_open", IKV_BOOL);
auto open = settings.makeArray("sidebar_section_open", ikv::booleanValue);
for (const bool b : sidebar_section_open_)
ikv_array_add_bool(open, b);
open.append(b);
ikv_node_t *col_widths = ikv_object_add_array(settings, "commit_table_columns", IKV_FLOAT);
auto col_widths = settings.makeArray("commit_table_columns", ikv::realValue);
for (const float w : commit_table_column_widths_)
ikv_array_add_float(col_widths, w);
col_widths.append(static_cast<double>(w));
// ── per-repository settings ───────────────────────────────────────────────
ikv_node_t *repos = ikv_object_add_array(root, "repository_settings", IKV_OBJECT);
auto repos = root.makeArray("repository_settings", ikv::objectValue);
for (const auto &[repo_path, rs] : repo_settings_)
{
ikv_node_t *entry = ikv_array_add_object(repos);
ikv_object_set_string(entry, "path", repo_path.c_str());
ikv_node_t *rw = ikv_object_add_array(entry, "commit_table_columns", IKV_FLOAT);
auto entry = repos.appendObject();
entry["path"] = repo_path;
auto rw = entry.makeArray("commit_table_columns", ikv::realValue);
for (const float w : rs.column_widths)
ikv_array_add_float(rw, w);
rw.append(static_cast<double>(w));
if (!rs.pending_commit_summary.empty())
ikv_object_set_string(entry, "commit_summary", rs.pending_commit_summary.c_str());
entry["commit_summary"] = rs.pending_commit_summary;
if (!rs.pending_commit_description.empty())
ikv_object_set_string(entry, "commit_description", rs.pending_commit_description.c_str());
entry["commit_description"] = rs.pending_commit_description;
}
// ── recently closed ───────────────────────────────────────────────────────
ikv_node_t *history = ikv_object_add_array(root, "recently_closed", IKV_STRING);
auto history = root.makeArray("recently_closed", ikv::stringValue);
for (const auto &p : recently_closed_)
ikv_array_add_string(history, p.c_str());
history.append(p);
// ── recent repositories ───────────────────────────────────────────────────
ikv_node_t *recent = ikv_object_add_array(root, "recent_repositories", IKV_STRING);
auto recent = root.makeArray("recent_repositories", ikv::stringValue);
for (const auto &p : recent_repositories_)
ikv_array_add_string(recent, p.c_str());
recent.append(p);
// ── session ───────────────────────────────────────────────────────────────
ikv_node_t *session = ikv_object_add_object(root, "session");
ikv_object_set_int(session, "active_tab", static_cast<int64_t>(active_repository_));
ikv_node_t *tabs = ikv_object_add_array(session, "tabs", IKV_STRING);
auto session = root.makeObject("session");
session["active_tab"] = static_cast<int64_t>(active_repository_);
auto tabs = session.makeArray("tabs", ikv::stringValue);
for (const auto &p : open_repositories_)
ikv_array_add_string(tabs, p.c_str());
tabs.append(p);
// ── credentials ───────────────────────────────────────────────────────────
ikv_node_t *credentials = ikv_object_add_array(root, "credentials", IKV_OBJECT);
auto credentials = root.makeArray("credentials", ikv::objectValue);
for (const auto &[scope, secret] : encrypted_credentials_)
{
ikv_node_t *entry = ikv_array_add_object(credentials);
ikv_object_set_string(entry, "scope", scope.c_str());
ikv_object_set_string(entry, "secret", secret.c_str());
auto entry = credentials.appendObject();
entry["scope"] = scope;
entry["secret"] = secret;
}
ikvb_write_file_version(dataPath().string().c_str(), root, IKV_VERSION_2);
ikv_free(root);
root.write(dataPath().string(), ikv::Version::v2);
removeLegacyFiles();
}