In-memory record of inspector direct-manipulation edits ("tweaks"), keyed by stable_anchor_id + dotted property path (e.g. More...
#include <tweak_store.hpp>
Classes | |
| struct | Record |
| A single tweak record returned by list_tweaks(). More... | |
| struct | BatchEntry |
| One (property_path, value) pair within a batched apply. More... | |
| struct | DiskResult |
| Outcome of a disk operation. More... | |
| struct | DriftedTweak |
| One drifted tweak — a stored edit that no longer maps cleanly to the current design. More... | |
| struct | DriftReport |
| Three-way classification of every stored tweak against a design. More... | |
| struct | DesignSnapshot |
| A design snapshot the drift logic diffs tweaks against. More... | |
Public Types | |
| enum class | DriftReason { anchor_not_found , property_not_found } |
| Why a tweak failed to apply cleanly. More... | |
| using | BypassValue = std::variant<bool, std::vector<std::string>> |
| Bypass overlay value — matches the TS true | string[] shape. | |
Public Member Functions | |
| TweakStore ()=default | |
| std::size_t | apply_tweak (std::string_view anchor_id, std::string_view property_path, choc::value::Value value, std::string_view source={}) |
| Record an edit. | |
| std::size_t | apply_tweaks_batch (std::string_view anchor_id, std::vector< BatchEntry > entries, std::string_view source={}) |
| Apply several tweaks under one anchor as a SINGLE atomic unit. | |
| bool | remove_tweak (std::string_view anchor_id, std::string_view property_path) |
| Remove a single (anchor_id, property_path) entry. | |
| std::size_t | remove_anchor (std::string_view anchor_id) |
| Remove all entries for an anchor. | |
| void | clear () |
| Clear the entire table (tweaks + bypass overlay + lock set). | |
| void | set_bypass (std::string_view anchor_id, BypassValue value) |
| Set the bypass state for an anchor. | |
| void | clear_bypass (std::string_view anchor_id) |
| Convenience: clear the bypass for an anchor (equivalent to set_bypass(id, false)). | |
| void | set_locked (std::string_view anchor_id, bool locked) |
| Set the lock state for an anchor. | |
| void | clear_lock (std::string_view anchor_id) |
| Convenience: clear the lock for an anchor (equivalent to set_locked(id, false)). | |
| std::size_t | count () const |
| Total number of (anchor, property) entries. | |
| std::vector< Record > | list_tweaks () const |
| Return every record in the store's stable order. | |
| std::optional< choc::value::Value > | lookup (std::string_view anchor_id, std::string_view property_path) const |
| Look up a specific (anchor_id, property_path). | |
| bool | is_bypassed (std::string_view anchor_id, std::string_view property_path) const |
| Whether a specific (anchor_id, property_path) is currently bypassed (either whole-anchor or path-specific). | |
| std::optional< BypassValue > | bypass_for (std::string_view anchor_id) const |
| Current bypass state for an anchor. | |
| std::vector< std::string > | bypassed_anchors () const |
| Return every anchor id that currently has a bypass entry, regardless of whether it also has tweak records. | |
| bool | is_locked (std::string_view anchor_id) const |
| Whether anchor_id is currently locked. | |
| std::vector< std::string > | locked_anchors () const |
| Return every anchor id that currently carries a lock, in unspecified order. | |
| DiskResult | load_from_disk (std::string_view path={}) |
| Replace the in-memory state with the contents of path. | |
| DiskResult | save_to_disk (std::string_view path={}) const |
| Atomically write the current in-memory state to path. | |
| void | set_auto_save (bool enabled, std::string_view path={}) |
| Enable / disable post-mutation auto-save. | |
| bool | auto_save_enabled () const |
| Whether auto-save is currently armed. | |
| std::string | auto_save_path () const |
| The path auto-save flushes to (empty if auto-save is off and no explicit path has ever been set). | |
| std::string | to_json () const |
| Serialize the current state to a JSON string in the on-disk schema. | |
| DiskResult | from_json (std::string_view json) |
| Replace state from an in-memory JSON string. | |
| DriftReport | diff (const DesignSnapshot &design) const |
| Classify every stored tweak against design. | |
| DriftReport | diff (const std::vector< std::string > &live_anchors) const |
| Convenience: classify against a flat anchor list (anchor-only matching — no property-level drift). | |
| std::vector< DriftedTweak > | find_drifted (const DesignSnapshot &design) const |
| Return only the orphaned + drifted tweaks for design — the subset the inspector drift drawer renders. | |
| std::vector< DriftedTweak > | find_drifted (const std::vector< std::string > &live_anchors) const |
| Convenience overload taking a flat anchor list. | |
Static Public Member Functions | |
| static std::string | default_tweaks_path () |
| Resolve the default pulp-tweaks.json location: | |
| static const char * | drift_reason_str (DriftReason reason) |
| Stringify a DriftReason for JSON / human output. | |
| static std::string | drift_report_to_json (const DriftReport &report) |
| Render a DriftReport as a JSON string (used by pulp tweaks
diff --json and the protocol layer). | |
Static Public Attributes | |
| static constexpr int | kSchemaVersion = 1 |
| On-disk schema version. Bumped if/when we ever break the format. | |
In-memory record of inspector direct-manipulation edits ("tweaks"), keyed by stable_anchor_id + dotted property path (e.g.
"paint.backgroundColor", "layout.padding").
Thread-safety: external mutex; all public methods take the same internal mutex so the inspector can safely receive concurrent applyTweak / listTweaks calls from multiple connected clients.
| using pulp::inspect::TweakStore::BypassValue = std::variant<bool, std::vector<std::string>> |
Bypass overlay value — matches the TS true | string[] shape.
bool=true bypasses every tweak under an anchor; a non-empty vector bypasses only the listed dotted paths.
|
strong |
|
default |
| std::size_t pulp::inspect::TweakStore::apply_tweak | ( | std::string_view | anchor_id, |
| std::string_view | property_path, | ||
| choc::value::Value | value, | ||
| std::string_view | source = {} ) |
Record an edit.
Overwrites any prior value at the same (anchor_id, property_path). Returns the post-write tweak count.
| std::size_t pulp::inspect::TweakStore::apply_tweaks_batch | ( | std::string_view | anchor_id, |
| std::vector< BatchEntry > | entries, | ||
| std::string_view | source = {} ) |
Apply several tweaks under one anchor as a SINGLE atomic unit.
Motivation: the drag-to-move gesture writes three tweaks — layout.position, layout.left, layout.top — and apply_tweak() auto-saves to disk after EVERY call, so three separate calls flush the file three times and a crash mid-sequence can persist a partial move (e.g. left/top without position). Worse, that partial state is NOT a no-op: Yoga applies insets to relative nodes too, so a still-relative node with stray left/top shifts in flow.
apply_tweaks_batch takes the internal lock once, mutates every (anchor, path) key, and triggers at most ONE auto-save flush after all keys are written — so the on-disk state is all-or-nothing. Returns the post-write total tweak count. A no-op (returns the current count) when entries is empty.
| bool pulp::inspect::TweakStore::auto_save_enabled | ( | ) | const |
Whether auto-save is currently armed.
| std::string pulp::inspect::TweakStore::auto_save_path | ( | ) | const |
The path auto-save flushes to (empty if auto-save is off and no explicit path has ever been set).
| std::optional< BypassValue > pulp::inspect::TweakStore::bypass_for | ( | std::string_view | anchor_id | ) | const |
Current bypass state for an anchor.
Returns std::nullopt if the anchor has no bypass entry; otherwise the live BypassValue.
| std::vector< std::string > pulp::inspect::TweakStore::bypassed_anchors | ( | ) | const |
Return every anchor id that currently has a bypass entry, regardless of whether it also has tweak records.
Used by Inspector.listTweaks to surface bypass-only anchors. A setBypass call on an anchor with no active tweaks, or one whose tweaks were later cleared, must still show up in the protocol response's bypassed map so clients and disk persistence can round-trip the bypass state.
| void pulp::inspect::TweakStore::clear | ( | ) |
Clear the entire table (tweaks + bypass overlay + lock set).
| void pulp::inspect::TweakStore::clear_bypass | ( | std::string_view | anchor_id | ) |
Convenience: clear the bypass for an anchor (equivalent to set_bypass(id, false)).
| void pulp::inspect::TweakStore::clear_lock | ( | std::string_view | anchor_id | ) |
Convenience: clear the lock for an anchor (equivalent to set_locked(id, false)).
| std::size_t pulp::inspect::TweakStore::count | ( | ) | const |
Total number of (anchor, property) entries.
|
static |
Resolve the default pulp-tweaks.json location:
| DriftReport pulp::inspect::TweakStore::diff | ( | const DesignSnapshot & | design | ) | const |
Classify every stored tweak against design.
Bypass overlay is ignored — a bypassed tweak can still be orphaned, and the user should see that.
| DriftReport pulp::inspect::TweakStore::diff | ( | const std::vector< std::string > & | live_anchors | ) | const |
Convenience: classify against a flat anchor list (anchor-only matching — no property-level drift).
Equivalent to building a DesignSnapshot with just anchors populated.
|
static |
Stringify a DriftReason for JSON / human output.
|
static |
Render a DriftReport as a JSON string (used by pulp tweaks diff --json and the protocol layer).
Shape: { "clean": [...], "drifted": [...], "orphaned": [...], "summary": { "total", "clean", "drifted", "orphaned" } }
| std::vector< DriftedTweak > pulp::inspect::TweakStore::find_drifted | ( | const DesignSnapshot & | design | ) | const |
Return only the orphaned + drifted tweaks for design — the subset the inspector drift drawer renders.
| std::vector< DriftedTweak > pulp::inspect::TweakStore::find_drifted | ( | const std::vector< std::string > & | live_anchors | ) | const |
Convenience overload taking a flat anchor list.
| DiskResult pulp::inspect::TweakStore::from_json | ( | std::string_view | json | ) |
Replace state from an in-memory JSON string.
Same error semantics as load_from_disk(). Public so tests can round-trip without touching the filesystem.
| bool pulp::inspect::TweakStore::is_bypassed | ( | std::string_view | anchor_id, |
| std::string_view | property_path ) const |
Whether a specific (anchor_id, property_path) is currently bypassed (either whole-anchor or path-specific).
| bool pulp::inspect::TweakStore::is_locked | ( | std::string_view | anchor_id | ) | const |
Whether anchor_id is currently locked.
| std::vector< Record > pulp::inspect::TweakStore::list_tweaks | ( | ) | const |
Return every record in the store's stable order.
Overwriting an existing (anchor, property) pair keeps its original position; JSON loads rebuild the order from file order plus any preserved locked anchors.
| DiskResult pulp::inspect::TweakStore::load_from_disk | ( | std::string_view | path = {} | ) |
Replace the in-memory state with the contents of path.
Returns ok=false (without touching internal state) if the file is missing, malformed, or has an unsupported schema version.
If path is empty, resolves via default_tweaks_path().
| std::vector< std::string > pulp::inspect::TweakStore::locked_anchors | ( | ) | const |
Return every anchor id that currently carries a lock, in unspecified order.
Mirrors bypassed_anchors() — used by the management panel and the disk-persistence path so lock state round-trips even for anchors with no active tweaks.
| std::optional< choc::value::Value > pulp::inspect::TweakStore::lookup | ( | std::string_view | anchor_id, |
| std::string_view | property_path ) const |
Look up a specific (anchor_id, property_path).
Returns the value if set, std::nullopt otherwise.
| std::size_t pulp::inspect::TweakStore::remove_anchor | ( | std::string_view | anchor_id | ) |
Remove all entries for an anchor.
Returns the number of entries removed.
| bool pulp::inspect::TweakStore::remove_tweak | ( | std::string_view | anchor_id, |
| std::string_view | property_path ) |
Remove a single (anchor_id, property_path) entry.
Returns true if something was removed.
| DiskResult pulp::inspect::TweakStore::save_to_disk | ( | std::string_view | path = {} | ) | const |
Atomically write the current in-memory state to path.
Writes to <path>.tmp first then renames; on success the .tmp file no longer exists at the canonical path.
If path is empty, resolves via default_tweaks_path().
| void pulp::inspect::TweakStore::set_auto_save | ( | bool | enabled, |
| std::string_view | path = {} ) |
Enable / disable post-mutation auto-save.
When enabled, every successful apply_tweak / remove_tweak / remove_anchor / clear / set_bypass / clear_bypass / set_locked / clear_lock call flushes the table to disk at path (or default_tweaks_path() if empty). Default OFF so unit tests don't write to disk by accident.
Set enabled=false to disable. The path is ignored when disabling.
| void pulp::inspect::TweakStore::set_bypass | ( | std::string_view | anchor_id, |
| BypassValue | value ) |
Set the bypass state for an anchor.
| void pulp::inspect::TweakStore::set_locked | ( | std::string_view | anchor_id, |
| bool | locked ) |
Set the lock state for an anchor.
A locked anchor is protected from being cleared by a bulk operation or by re-import. locked=true adds the anchor to the lock set; locked=false removes it. Lock is a sibling overlay — it never affects whether a tweak is applied, only whether destructive bulk operations may remove it.
| std::string pulp::inspect::TweakStore::to_json | ( | ) | const |
Serialize the current state to a JSON string in the on-disk schema.
Public so callers (e.g. the protocol layer's saveTweaks response) can preview without writing.
|
staticconstexpr |
On-disk schema version. Bumped if/when we ever break the format.