fix: trim configs

This commit is contained in:
Ian Chua
2026-07-15 21:43:13 +08:00
parent 9cdd399fdd
commit e21123d8e9
6 changed files with 105 additions and 132 deletions

View File

@@ -619,10 +619,8 @@ set(SLIC3R_GUI_SOURCES
plugin/CloudPluginService.hpp
plugin/PluginFsUtils.cpp
plugin/PluginFsUtils.hpp
plugin/CapabilityConfigDocument.hpp
plugin/PluginConfig.cpp
plugin/PluginConfig.hpp
plugin/PresetPluginConfig.hpp
plugin/PythonJsonUtils.hpp
plugin/PluginLoader.cpp
plugin/PluginLoader.hpp

View File

@@ -2,7 +2,7 @@
#include <slic3r/GUI/Widgets/WebViewHostDialog.hpp>
#include <libslic3r/Preset.hpp>
#include <slic3r/plugin/PresetPluginConfig.hpp>
#include <slic3r/plugin/PluginConfig.hpp>
#include <string>

View File

@@ -1,57 +0,0 @@
#pragma once
#include <nlohmann/json.hpp>
#include <map>
#include <optional>
#include <string>
#include <vector>
namespace Slic3r {
struct CapabilityConfigId
{
std::string plugin_key;
std::string capability;
friend bool operator<(const CapabilityConfigId& lhs, const CapabilityConfigId& rhs)
{
return lhs.plugin_key < rhs.plugin_key ||
(lhs.plugin_key == rhs.plugin_key && lhs.capability < rhs.capability);
}
friend bool operator==(const CapabilityConfigId& lhs, const CapabilityConfigId& rhs)
{
return lhs.plugin_key == rhs.plugin_key && lhs.capability == rhs.capability;
}
};
struct CapabilityConfigEntry
{
CapabilityConfigId id;
std::string plugin_version;
nlohmann::json cap_config = nlohmann::json::object();
};
class CapabilityConfigDocument
{
public:
static constexpr const char* KeyEntries = "config";
static CapabilityConfigDocument from_root_json(const nlohmann::json& root);
static CapabilityConfigDocument from_entries(const nlohmann::json& entries);
std::optional<CapabilityConfigEntry> find(const CapabilityConfigId& id) const;
bool contains(const CapabilityConfigId& id) const;
bool upsert(CapabilityConfigEntry entry);
bool erase(const CapabilityConfigId& id);
bool empty() const;
nlohmann::json serialize_entries() const;
nlohmann::json root_json() const;
private:
std::map<CapabilityConfigId, nlohmann::json> m_entries;
std::vector<nlohmann::json> m_opaque_entries;
};
} // namespace Slic3r

View File

@@ -13,7 +13,6 @@
#include <slic3r/GUI/format.hpp>
#include <slic3r/plugin/PluginLoader.hpp>
#include <slic3r/plugin/PluginManager.hpp>
#include <slic3r/plugin/PresetPluginConfig.hpp>
#include <slic3r/plugin/PythonInterpreter.hpp>
#include <slic3r/plugin/PythonPluginInterface.hpp>
#include <stdexcept>

View File

@@ -3,11 +3,11 @@
#include <libslic3r/Utils.hpp>
#include <boost/filesystem.hpp>
#include <nlohmann/json.hpp>
#include <slic3r/plugin/CapabilityConfigDocument.hpp>
#include <slic3r/plugin/PluginFsUtils.hpp>
#include <slic3r/plugin/PluginLoader.hpp>
#include <map>
#include <mutex>
#include <optional>
#include <string>
#include <utility>
#include <vector>
@@ -17,8 +17,54 @@
namespace Slic3r {
class PluginCapabilityInterface;
class Preset;
struct PluginCapabilityId;
struct CapabilityConfigId
{
std::string plugin_key;
std::string capability;
friend bool operator<(const CapabilityConfigId& lhs, const CapabilityConfigId& rhs)
{
return lhs.plugin_key < rhs.plugin_key ||
(lhs.plugin_key == rhs.plugin_key && lhs.capability < rhs.capability);
}
friend bool operator==(const CapabilityConfigId& lhs, const CapabilityConfigId& rhs)
{
return lhs.plugin_key == rhs.plugin_key && lhs.capability == rhs.capability;
}
};
struct CapabilityConfigEntry
{
CapabilityConfigId id;
std::string plugin_version;
nlohmann::json cap_config = nlohmann::json::object();
};
class CapabilityConfigDocument
{
public:
static constexpr const char* KeyEntries = "config";
static CapabilityConfigDocument from_root_json(const nlohmann::json& root);
static CapabilityConfigDocument from_entries(const nlohmann::json& entries);
std::optional<CapabilityConfigEntry> find(const CapabilityConfigId& id) const;
bool contains(const CapabilityConfigId& id) const;
bool upsert(CapabilityConfigEntry entry);
bool erase(const CapabilityConfigId& id);
bool empty() const;
nlohmann::json serialize_entries() const;
nlohmann::json root_json() const;
private:
std::map<CapabilityConfigId, nlohmann::json> m_entries;
std::vector<nlohmann::json> m_opaque_entries;
};
/*
Example config.json shape
{
@@ -44,6 +90,63 @@ struct BaseConfig {
bool empty() const { return plugin_key.empty() || capability_name.empty(); }
};
// A preset keeps its plugin capability overrides as one raw JSON string in this ordinary
// ConfigOptionString, so the whole preset lifecycle — load, save, diff/dirty, inheritance, 3MF,
// sync — carries it for free. The plugin layer is the only thing that gives that string meaning.
inline constexpr const char* PLUGIN_OVERRIDES_OPTION_KEY = "plugin_preference_overrides";
// The preset's raw override text, or "" when it stores none.
std::string plugin_overrides_of(const Preset& preset);
// An empty string is a valid, empty document. Returns false and fills `error` when the text is
// present but is not a JSON array of entries; the caller then shows it and edits nothing.
bool parse_plugin_overrides(const std::string& raw, CapabilityConfigDocument& document, std::string& error);
// The document as compact JSON text, and "" once it holds no entries. Empty text — rather than a
// removed option — records "cleared here" against an inheriting parent that has overrides.
std::string serialize_plugin_overrides(const CapabilityConfigDocument& document);
struct EffectiveCapabilityConfig
{
CapabilityConfigId id;
nlohmann::json config = nlohmann::json::object();
bool has_preset_override = false;
bool has_base_config = false;
std::string stored_plugin_version;
std::string running_plugin_version;
};
struct MutationResult
{
bool ok = false;
bool changed = false;
std::string error;
EffectiveCapabilityConfig effective;
};
// Resolves a capability's effective config as `preset override -> base config -> none`, and mutates
// the override layer. It works on a CapabilityConfigDocument the caller owns, never on a Preset and
// never on the base config file, which keeps the two layers from writing to each other:
// PluginConfigField holds the document and feeds the edited text back through the normal field/dirty
// pipeline, so the preset is written the way every other setting is.
class PresetPluginConfigService
{
public:
EffectiveCapabilityConfig get_effective_config(const CapabilityConfigDocument& overrides,
const PluginCapabilityId& id) const;
MutationResult set_preset_override(CapabilityConfigDocument& overrides,
const PluginCapabilityId& id,
const nlohmann::json& value) const;
MutationResult remove_preset_override(CapabilityConfigDocument& overrides,
const PluginCapabilityId& id) const;
};
// The same resolution against the preset that is active right now, rather than a document the
// caller holds: this is what a running capability reads through the Python config API. It falls
// back to the base config when no active preset bundle is available.
EffectiveCapabilityConfig active_capability_config(const PluginCapabilityId& id);
// Store for every plugin capability's configuration, persisted as a single config.json alongside the
// installed plugins. The shape of `cap_config` belongs to the plugin; this class only round-trips it.
//

View File

@@ -1,70 +0,0 @@
#pragma once
#include "CapabilityConfigDocument.hpp"
#include "PluginConfig.hpp"
#include <libslic3r/Preset.hpp>
#include <nlohmann/json.hpp>
#include <string>
namespace Slic3r {
// A preset keeps its plugin capability overrides as one raw JSON string in this ordinary
// ConfigOptionString, so the whole preset lifecycle — load, save, diff/dirty, inheritance, 3MF,
// sync — carries it for free. The plugin layer is the only thing that gives that string meaning.
inline constexpr const char* PLUGIN_OVERRIDES_OPTION_KEY = "plugin_preference_overrides";
// The preset's raw override text, or "" when it stores none.
std::string plugin_overrides_of(const Preset& preset);
// An empty string is a valid, empty document. Returns false and fills `error` when the text is
// present but is not a JSON array of entries; the caller then shows it and edits nothing.
bool parse_plugin_overrides(const std::string& raw, CapabilityConfigDocument& document, std::string& error);
// The document as compact JSON text, and "" once it holds no entries. Empty text — rather than a
// removed option — records "cleared here" against an inheriting parent that has overrides.
std::string serialize_plugin_overrides(const CapabilityConfigDocument& document);
struct EffectiveCapabilityConfig
{
CapabilityConfigId id;
nlohmann::json config = nlohmann::json::object();
bool has_preset_override = false;
bool has_base_config = false;
std::string stored_plugin_version;
std::string running_plugin_version;
};
struct MutationResult
{
bool ok = false;
bool changed = false;
std::string error;
EffectiveCapabilityConfig effective;
};
// Resolves a capability's effective config as `preset override -> base config -> none`, and mutates
// the override layer. It works on a CapabilityConfigDocument the caller owns, never on a Preset and
// never on the base config file, which keeps the two layers from writing to each other:
// PluginConfigField holds the document and feeds the edited text back through the normal field/dirty
// pipeline, so the preset is written the way every other setting is.
class PresetPluginConfigService
{
public:
EffectiveCapabilityConfig get_effective_config(const CapabilityConfigDocument& overrides,
const PluginCapabilityId& id) const;
MutationResult set_preset_override(CapabilityConfigDocument& overrides,
const PluginCapabilityId& id,
const nlohmann::json& value) const;
MutationResult remove_preset_override(CapabilityConfigDocument& overrides,
const PluginCapabilityId& id) const;
};
// The same resolution against the preset that is active right now, rather than a document the caller
// holds: this is what a running capability reads through the Python config API. It falls back to the
// base config when no active preset bundle is available.
EffectiveCapabilityConfig active_capability_config(const PluginCapabilityId& id);
} // namespace Slic3r