variables: fix binding lifetime, dataset editing and the missing UI to define

Review of #761 turned up three real defects and a feature that was visible
but not usable. All of it is fixed here, each defect with a regression test
that fails without the fix.

- A binding outlived the object it named. Deleting art left its entry in
  `variables.bindings` forever: it counted as a skip in every dataset
  application, `variable.list` counted it, and the saved file grew with every
  delete. `Document::prune_variable_bindings` now lets them go after each
  edit, as `prune_assets` does for Asset Export's.
- `Variables::prune` cleared `active_dataset` by comparing it against the
  *variable* names being deleted. A dataset named like a variable lost its
  active state when the variable went. The two are separate namespaces; the
  block is gone (dataset.delete already did it right).
- `variable.delete` reported the number of names asked for, not the number
  deleted, so deleting a name that was not there still said `deleted: 1`.

- `dataset.set` (new): a dataset's values could only be set when it was
  created, so changing one meant deleting and rebuilding the row — losing its
  place in the list and the active one. This replaces the row's values and
  reports what it dropped.
- Window › Variables, and dialogs for the "…" commands. Every command had an
  empty menu path and the command palette runs those with `{}`, so nothing
  could be defined from the UI at all. The commands now carry their menu
  path, the "…" ones open a Variables dialog (a name and a kind; a name and a
  field per variable, its kind's editor), and `dataset.set` opens with the
  row's own values.
- Locked art is neither bound nor rewritten by a dataset, and says so.
- `set_plain_text` told a node that existed but was not type that it was
  missing.
- The panel had Document Info's icon, so the two were indistinguishable in
  the collapsed column; it gets its own, plus New/Delete buttons, and its
  rows say what their count is.

Format round trips now cover variables (the testkit fixture defines both
kinds, binds them and applies a row), and ROADMAP/README no longer list the
feature as missing.

cargo xtask ci: all 7 steps pass.
This commit is contained in:
Fahri Meral
2026-10-10 23:34:21 +07:00
committed by bflatastic
parent c89906a13a
commit 202894372a
24 changed files with 894 additions and 106 deletions
+9
View File
@@ -154,6 +154,15 @@ pub fn rich_session() -> Session {
exec(&mut s, "symbol.new", json!({"name": "Dot"}));
let t = id_of(&exec(&mut s, "text.create", json!({"x": 250, "y": 370, "text": "Tap to add", "size": 18})));
exec(&mut s, "text.insertInline", json!({"id": t.0, "at": 4, "symbol": "Dot"}));
// Variables (data merge): both kinds, bound to art, with two dataset rows and one applied,
// so format round trips cover the definitions, the bindings and the rows.
exec(&mut s, "variable.define", json!({"name": "Title", "kind": "text"}));
exec(&mut s, "variable.define", json!({"name": "Badge", "kind": "visibility"}));
exec(&mut s, "variable.bind", json!({"variable": "Title", "ids": [tx.0]}));
exec(&mut s, "variable.bind", json!({"variable": "Badge", "ids": [a]}));
exec(&mut s, "dataset.new", json!({"name": "Row 1", "values": {"Title": "Merged", "Badge": true}}));
exec(&mut s, "dataset.new", json!({"name": "Row 2", "values": {"Title": "Other"}}));
exec(&mut s, "dataset.select", json!({"name": "Row 1"}));
let _ = (a, b, c, l);
exec(&mut s, "select.none", json!({}));
s