pi-revit 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,40 @@ Format follows [Keep a Changelog](https://keepachangelog.com/); version headers
|
|
|
7
7
|
Every published version gets an entry with **Added** / **Changed** / **Fixed** sections
|
|
8
8
|
describing what the user will notice — not internal refactors.
|
|
9
9
|
|
|
10
|
+
## [0.2.6] - 2026-07-20
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- Write transactions (`set_parameters`, `execute_csharp`, and the temporary-isolate
|
|
14
|
+
branch of `manage_selection`) now register a failures preprocessor. Previously Revit
|
|
15
|
+
handled commit failures interactively: warnings popped the transient toast and spammed
|
|
16
|
+
the journal, and an error-severity failure showed the modal resolution dialog, blocking
|
|
17
|
+
the bridge until a human clicked. Now warnings are auto-dismissed and reported back
|
|
18
|
+
(`commitWarnings` in the result, e.g. duplicate Mark values), and errors roll the
|
|
19
|
+
transaction back with the actual Revit failure text in the error message.
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- `set_parameters` tool description tells the model to relay `commitWarnings` to the user.
|
|
23
|
+
|
|
24
|
+
Requires redeploying the Revit add-in (`scripts\deploy.ps1` + Revit restart).
|
|
25
|
+
|
|
26
|
+
## [0.2.5] - 2026-07-20
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
- `execute_csharp` dialog guard no longer answers every Revit popup with OK. On some
|
|
30
|
+
dialogs OK is the destructive choice (e.g. "Delete Element(s)"), so a script could
|
|
31
|
+
silently delete dimensions or constraints and still report success. Unrecognized
|
|
32
|
+
dialogs are now answered dismissively (Cancel, then Close, then No; OK only as the
|
|
33
|
+
last resort so Revit can never hang behind a popup), a small allowlist keeps OK for
|
|
34
|
+
dialogs that are safe to confirm, and `suppressedDialogs` now reports which answer
|
|
35
|
+
was given (e.g. `TaskDialog_… (answered Cancel)`).
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
- The `execute_csharp` tool description tells the model that confirmation prompts may be
|
|
39
|
+
cancelled and to check `suppressedDialogs` when a result looks incomplete.
|
|
40
|
+
|
|
41
|
+
Requires redeploying the Revit add-in (`scripts\deploy.ps1` + Revit restart) — `ping`
|
|
42
|
+
warns on a version mismatch until then.
|
|
43
|
+
|
|
10
44
|
## [0.2.4] - 2026-07-20
|
|
11
45
|
|
|
12
46
|
### Added
|
package/package.json
CHANGED
|
@@ -59,7 +59,7 @@ namespace RevitBridge.Tools
|
|
|
59
59
|
|
|
60
60
|
public string Name => "execute_csharp";
|
|
61
61
|
public string Label => "Execute C#";
|
|
62
|
-
public string Description => "Compile and run a C# script on the Revit API thread against the open model — the escape hatch for anything without a dedicated tool: element creation, deletion, geometry edits (move/copy/rotate), views, sheets, schedules, tagging, families, links, worksets. Globals: doc (Document), uidoc (UIDocument), uiapp (UIApplication), and Dump(value) to record intermediate values into the result's dumps[]. Default imports: System, System.Linq, System.Collections.Generic, Autodesk.Revit.DB, Autodesk.Revit.UI — add using directives at the top for sub-namespaces (e.g. using Autodesk.Revit.DB.Architecture;). The entire run is wrapped in ONE transaction named 'execute_csharp': committed on success, rolled back on any exception, so a failed script never changes the model (do not open your own Transaction; sub-transactions are fine). The script's final expression or return statement becomes returnValue; return primitives, strings, or anonymous objects/lists — Revit API values are projected to safe shapes (Element -> {id,name,category,typeName,levelId}, ElementId -> number, XYZ -> {x,y,z}, Parameter -> {name,value,displayValue}; other API objects become strings) with depth and item caps, so never rely on raw API objects round-tripping. Lengths are in internal units (decimal feet) — convert with UnitUtils. Prefer collector-level filtering (FilteredElementCollector .OfCategory/.OfClass/.WhereElementIsNotElementType) and bounded loops: the call budget is 120s and Revit cannot be interrupted mid-script. Scripts must be fully synchronous — await/async is rejected at compile time, and blocking on tasks (Task.Result/.Wait()) can freeze Revit. Modal dialogs raised while running are auto-dismissed and reported in suppressedDialogs. Verify unfamiliar signatures with search_api_docs first.";
|
|
62
|
+
public string Description => "Compile and run a C# script on the Revit API thread against the open model — the escape hatch for anything without a dedicated tool: element creation, deletion, geometry edits (move/copy/rotate), views, sheets, schedules, tagging, families, links, worksets. Globals: doc (Document), uidoc (UIDocument), uiapp (UIApplication), and Dump(value) to record intermediate values into the result's dumps[]. Default imports: System, System.Linq, System.Collections.Generic, Autodesk.Revit.DB, Autodesk.Revit.UI — add using directives at the top for sub-namespaces (e.g. using Autodesk.Revit.DB.Architecture;). The entire run is wrapped in ONE transaction named 'execute_csharp': committed on success, rolled back on any exception, so a failed script never changes the model (do not open your own Transaction; sub-transactions are fine). The script's final expression or return statement becomes returnValue; return primitives, strings, or anonymous objects/lists — Revit API values are projected to safe shapes (Element -> {id,name,category,typeName,levelId}, ElementId -> number, XYZ -> {x,y,z}, Parameter -> {name,value,displayValue}; other API objects become strings) with depth and item caps, so never rely on raw API objects round-tripping. Lengths are in internal units (decimal feet) — convert with UnitUtils. Prefer collector-level filtering (FilteredElementCollector .OfCategory/.OfClass/.WhereElementIsNotElementType) and bounded loops: the call budget is 120s and Revit cannot be interrupted mid-script. Scripts must be fully synchronous — await/async is rejected at compile time, and blocking on tasks (Task.Result/.Wait()) can freeze Revit. Modal dialogs raised while running are auto-dismissed and reported in suppressedDialogs — unrecognized dialogs are answered dismissively (Cancel/Close/No) rather than confirmed, so an operation that raises a confirmation prompt may be cancelled; check suppressedDialogs when a result looks incomplete. Verify unfamiliar signatures with search_api_docs first.";
|
|
63
63
|
public bool Write => true;
|
|
64
64
|
|
|
65
65
|
public object ParametersSchema => new
|
|
@@ -113,6 +113,7 @@ namespace RevitBridge.Tools
|
|
|
113
113
|
|
|
114
114
|
using var dialogGuard = new DialogGuard(uiapp);
|
|
115
115
|
using var transaction = new Transaction(doc, "execute_csharp");
|
|
116
|
+
var failureGuard = FailureGuard.Attach(transaction);
|
|
116
117
|
if (transaction.Start() != TransactionStatus.Started)
|
|
117
118
|
throw new InvalidOperationException("Unable to start the execute_csharp transaction.");
|
|
118
119
|
|
|
@@ -142,6 +143,7 @@ namespace RevitBridge.Tools
|
|
|
142
143
|
if (transaction.Commit() != TransactionStatus.Committed)
|
|
143
144
|
throw new InvalidOperationException(
|
|
144
145
|
"Revit rolled back the execute_csharp transaction during commit (failure processing rejected the changes); no model changes were saved."
|
|
146
|
+
+ failureGuard.DescribeErrors()
|
|
145
147
|
+ DescribeDialogs(dialogGuard.Suppressed));
|
|
146
148
|
|
|
147
149
|
stopwatch.Stop();
|
|
@@ -154,6 +156,8 @@ namespace RevitBridge.Tools
|
|
|
154
156
|
};
|
|
155
157
|
if (dialogGuard.Suppressed.Count > 0)
|
|
156
158
|
payload["suppressedDialogs"] = dialogGuard.Suppressed;
|
|
159
|
+
if (failureGuard.Warnings.Count > 0)
|
|
160
|
+
payload["commitWarnings"] = failureGuard.Warnings;
|
|
157
161
|
return payload;
|
|
158
162
|
}
|
|
159
163
|
|
|
@@ -234,9 +238,31 @@ namespace RevitBridge.Tools
|
|
|
234
238
|
}
|
|
235
239
|
|
|
236
240
|
/// <summary>Auto-dismisses any modal Revit dialog raised while the script runs, so a
|
|
237
|
-
/// popup cannot hang the Revit thread; dismissed dialog ids are
|
|
241
|
+
/// popup cannot hang the Revit thread; dismissed dialog ids and the answer given are
|
|
242
|
+
/// reported. Unrecognized dialogs get the dismissive answer (Cancel, then Close, then
|
|
243
|
+
/// No) — on several Revit dialogs OK is the destructive choice (e.g. "Delete
|
|
244
|
+
/// Element(s)"), so confirming blind can silently damage the model. OK leads only for
|
|
245
|
+
/// dialogs known to be safe to confirm, and is otherwise the last resort: every
|
|
246
|
+
/// override attempt failing would leave the dialog up and hang the Revit thread,
|
|
247
|
+
/// which is the one outcome this guard exists to prevent.</summary>
|
|
238
248
|
private sealed class DialogGuard : IDisposable
|
|
239
249
|
{
|
|
250
|
+
private const int IDOK = 1;
|
|
251
|
+
private const int IDCANCEL = 2;
|
|
252
|
+
private const int IDNO = 7;
|
|
253
|
+
private const int IDCLOSE = 8;
|
|
254
|
+
|
|
255
|
+
/// <summary>Dialogs where confirming is the benign answer and cancelling would
|
|
256
|
+
/// abort the operation the script deliberately started.</summary>
|
|
257
|
+
private static readonly HashSet<string> ConfirmSafeDialogIds = new(StringComparer.OrdinalIgnoreCase)
|
|
258
|
+
{
|
|
259
|
+
// "Export with temporary hide/isolate": OK exports the view as displayed.
|
|
260
|
+
"TaskDialog_Really_Print_Or_Export_Temp_View_Modes",
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
private static readonly int[] ConfirmFirst = { IDOK, IDCANCEL, IDCLOSE };
|
|
264
|
+
private static readonly int[] DismissFirst = { IDCANCEL, IDCLOSE, IDNO, IDOK };
|
|
265
|
+
|
|
240
266
|
private readonly UIApplication _uiapp;
|
|
241
267
|
public List<string> Suppressed { get; } = new();
|
|
242
268
|
|
|
@@ -248,17 +274,35 @@ namespace RevitBridge.Tools
|
|
|
248
274
|
|
|
249
275
|
private void OnDialogBoxShowing(object? sender, DialogBoxShowingEventArgs e)
|
|
250
276
|
{
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
e.OverrideResult(1); // IDOK
|
|
255
|
-
}
|
|
256
|
-
catch
|
|
277
|
+
string id = string.IsNullOrEmpty(e.DialogId) ? e.GetType().Name : e.DialogId;
|
|
278
|
+
int[] answers = ConfirmSafeDialogIds.Contains(id) ? ConfirmFirst : DismissFirst;
|
|
279
|
+
foreach (int answer in answers)
|
|
257
280
|
{
|
|
258
|
-
|
|
281
|
+
try
|
|
282
|
+
{
|
|
283
|
+
if (e.OverrideResult(answer))
|
|
284
|
+
{
|
|
285
|
+
Suppressed.Add($"{id} (answered {AnswerName(answer)})");
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
catch
|
|
290
|
+
{
|
|
291
|
+
// Some dialogs reject specific overrides; try the next answer.
|
|
292
|
+
}
|
|
259
293
|
}
|
|
294
|
+
Suppressed.Add($"{id} (override rejected — dialog may need manual dismissal)");
|
|
260
295
|
}
|
|
261
296
|
|
|
297
|
+
private static string AnswerName(int answer) => answer switch
|
|
298
|
+
{
|
|
299
|
+
IDOK => "OK",
|
|
300
|
+
IDCANCEL => "Cancel",
|
|
301
|
+
IDNO => "No",
|
|
302
|
+
IDCLOSE => "Close",
|
|
303
|
+
_ => answer.ToString(),
|
|
304
|
+
};
|
|
305
|
+
|
|
262
306
|
public void Dispose()
|
|
263
307
|
{
|
|
264
308
|
try
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
using Autodesk.Revit.DB;
|
|
2
|
+
|
|
3
|
+
namespace RevitBridge.Tools
|
|
4
|
+
{
|
|
5
|
+
/// <summary>
|
|
6
|
+
/// Deterministic commit-time failure handling for tool-owned transactions. Without a
|
|
7
|
+
/// preprocessor, Revit resolves commit failures interactively: warnings pop the
|
|
8
|
+
/// transient toast (and spam the journal), and error-severity failures block the
|
|
9
|
+
/// Revit API thread behind the modal resolution dialog until a human clicks — past
|
|
10
|
+
/// the bridge's call budget. Attach() deletes warnings (recorded for the tool
|
|
11
|
+
/// result) and turns errors into a rollback, so a headless caller always gets an
|
|
12
|
+
/// answer instead of a hang.
|
|
13
|
+
/// </summary>
|
|
14
|
+
internal sealed class FailureGuard : IFailuresPreprocessor
|
|
15
|
+
{
|
|
16
|
+
public List<string> Warnings { get; } = new();
|
|
17
|
+
public List<string> Errors { get; } = new();
|
|
18
|
+
|
|
19
|
+
public static FailureGuard Attach(Transaction transaction)
|
|
20
|
+
{
|
|
21
|
+
var guard = new FailureGuard();
|
|
22
|
+
var options = transaction.GetFailureHandlingOptions();
|
|
23
|
+
options.SetFailuresPreprocessor(guard);
|
|
24
|
+
options.SetClearAfterRollback(true);
|
|
25
|
+
transaction.SetFailureHandlingOptions(options);
|
|
26
|
+
return guard;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public FailureProcessingResult PreprocessFailures(FailuresAccessor accessor)
|
|
30
|
+
{
|
|
31
|
+
bool hasError = false;
|
|
32
|
+
foreach (FailureMessageAccessor failure in accessor.GetFailureMessages())
|
|
33
|
+
{
|
|
34
|
+
string description;
|
|
35
|
+
try
|
|
36
|
+
{
|
|
37
|
+
description = failure.GetDescriptionText();
|
|
38
|
+
}
|
|
39
|
+
catch
|
|
40
|
+
{
|
|
41
|
+
description = "(failure without description)";
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (failure.GetSeverity() == FailureSeverity.Warning)
|
|
45
|
+
{
|
|
46
|
+
Warnings.Add(description);
|
|
47
|
+
accessor.DeleteWarning(failure);
|
|
48
|
+
}
|
|
49
|
+
else
|
|
50
|
+
{
|
|
51
|
+
hasError = true;
|
|
52
|
+
Errors.Add(description);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return hasError ? FailureProcessingResult.ProceedWithRollBack : FailureProcessingResult.Continue;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/// <summary>Formats the recorded errors for a commit-failure message.</summary>
|
|
59
|
+
public string DescribeErrors()
|
|
60
|
+
=> Errors.Count > 0 ? $" Revit failure(s): {string.Join("; ", Errors)}." : string.Empty;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -192,6 +192,7 @@ namespace RevitBridge.Tools
|
|
|
192
192
|
private static void ApplyTemporaryIsolate(Document doc, View view, ICollection<ElementId> elementIds)
|
|
193
193
|
{
|
|
194
194
|
using var transaction = new Transaction(doc, "manage_selection: temporary isolate");
|
|
195
|
+
var failureGuard = FailureGuard.Attach(transaction);
|
|
195
196
|
if (transaction.Start() != TransactionStatus.Started)
|
|
196
197
|
throw new InvalidOperationException("Unable to start the temporary-isolate transaction.");
|
|
197
198
|
try
|
|
@@ -201,7 +202,7 @@ namespace RevitBridge.Tools
|
|
|
201
202
|
else
|
|
202
203
|
view.IsolateElementsTemporary(elementIds);
|
|
203
204
|
if (transaction.Commit() != TransactionStatus.Committed)
|
|
204
|
-
throw new InvalidOperationException("The temporary-isolate transaction failed to commit.");
|
|
205
|
+
throw new InvalidOperationException("The temporary-isolate transaction failed to commit." + failureGuard.DescribeErrors());
|
|
205
206
|
}
|
|
206
207
|
catch (Autodesk.Revit.Exceptions.InvalidOperationException ex)
|
|
207
208
|
{
|
|
@@ -19,7 +19,7 @@ namespace RevitBridge.Tools
|
|
|
19
19
|
|
|
20
20
|
public string Name => "set_parameters";
|
|
21
21
|
public string Label => "Set Parameters";
|
|
22
|
-
public string Description => "Write parameter values on elements — also the home for rename: parameter 'Name' covers levels, views, sheets, types, etc. (falls back to the element's Name property when the Name parameter is read-only). updates apply in ONE transaction: partial success commits and lists the failures; if every update fails the transaction is rolled back and nothing changes. parameter accepts a display name (Comments, Mark, Name), a BuiltInParameter enum name (e.g. ALL_MODEL_MARK), or guid:<GUID> for a shared parameter; type parameters live on the element type, so pass the type's id. Values are validated against the parameter's storage type; numeric values are interpreted in the document's display units for that parameter unless 'unit' (e.g. millimeters, feet, squareMeters) is given.";
|
|
22
|
+
public string Description => "Write parameter values on elements — also the home for rename: parameter 'Name' covers levels, views, sheets, types, etc. (falls back to the element's Name property when the Name parameter is read-only). updates apply in ONE transaction: partial success commits and lists the failures; if every update fails the transaction is rolled back and nothing changes. parameter accepts a display name (Comments, Mark, Name), a BuiltInParameter enum name (e.g. ALL_MODEL_MARK), or guid:<GUID> for a shared parameter; type parameters live on the element type, so pass the type's id. Values are validated against the parameter's storage type; numeric values are interpreted in the document's display units for that parameter unless 'unit' (e.g. millimeters, feet, squareMeters) is given. Revit warnings raised at commit (e.g. duplicate Mark values) are auto-dismissed and listed in commitWarnings — mention them to the user; error-severity failures roll the whole transaction back.";
|
|
23
23
|
public bool Write => true;
|
|
24
24
|
|
|
25
25
|
public object ParametersSchema => new
|
|
@@ -64,6 +64,7 @@ namespace RevitBridge.Tools
|
|
|
64
64
|
var failed = new List<Dictionary<string, object?>>();
|
|
65
65
|
|
|
66
66
|
using var transaction = new Transaction(doc, "set_parameters");
|
|
67
|
+
var failureGuard = FailureGuard.Attach(transaction);
|
|
67
68
|
if (transaction.Start() != TransactionStatus.Started)
|
|
68
69
|
throw new InvalidOperationException("Unable to start the set_parameters transaction.");
|
|
69
70
|
|
|
@@ -95,7 +96,9 @@ namespace RevitBridge.Tools
|
|
|
95
96
|
if (succeeded.Count > 0)
|
|
96
97
|
{
|
|
97
98
|
if (transaction.Commit() != TransactionStatus.Committed)
|
|
98
|
-
throw new InvalidOperationException(
|
|
99
|
+
throw new InvalidOperationException(
|
|
100
|
+
"Revit rejected the set_parameters commit and the transaction was rolled back; no changes were saved."
|
|
101
|
+
+ failureGuard.DescribeErrors());
|
|
99
102
|
}
|
|
100
103
|
else
|
|
101
104
|
{
|
|
@@ -118,6 +121,8 @@ namespace RevitBridge.Tools
|
|
|
118
121
|
: succeeded.Count == 0
|
|
119
122
|
? $"No updates applied; all {failed.Count} failed — transaction rolled back.{failureSample}"
|
|
120
123
|
: $"Updated {succeeded.Count} parameter value(s) on {elementCount} element(s); {failed.Count} failed.{failureSample}";
|
|
124
|
+
if (failureGuard.Warnings.Count > 0)
|
|
125
|
+
compact += $" {failureGuard.Warnings.Count} Revit warning(s) auto-dismissed at commit (see commitWarnings), e.g.: {failureGuard.Warnings[0]}";
|
|
121
126
|
|
|
122
127
|
return new ToolOutput(new
|
|
123
128
|
{
|
|
@@ -125,6 +130,7 @@ namespace RevitBridge.Tools
|
|
|
125
130
|
committed = succeeded.Count > 0,
|
|
126
131
|
succeeded,
|
|
127
132
|
failed,
|
|
133
|
+
commitWarnings = failureGuard.Warnings,
|
|
128
134
|
}, compact);
|
|
129
135
|
}
|
|
130
136
|
|