pi-revit 0.2.4 → 0.2.5

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,24 @@ 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.5] - 2026-07-20
11
+
12
+ ### Fixed
13
+ - `execute_csharp` dialog guard no longer answers every Revit popup with OK. On some
14
+ dialogs OK is the destructive choice (e.g. "Delete Element(s)"), so a script could
15
+ silently delete dimensions or constraints and still report success. Unrecognized
16
+ dialogs are now answered dismissively (Cancel, then Close, then No; OK only as the
17
+ last resort so Revit can never hang behind a popup), a small allowlist keeps OK for
18
+ dialogs that are safe to confirm, and `suppressedDialogs` now reports which answer
19
+ was given (e.g. `TaskDialog_… (answered Cancel)`).
20
+
21
+ ### Changed
22
+ - The `execute_csharp` tool description tells the model that confirmation prompts may be
23
+ cancelled and to check `suppressedDialogs` when a result looks incomplete.
24
+
25
+ Requires redeploying the Revit add-in (`scripts\deploy.ps1` + Revit restart) — `ping`
26
+ warns on a version mismatch until then.
27
+
10
28
  ## [0.2.4] - 2026-07-20
11
29
 
12
30
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-revit",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "Native Pi connector for Autodesk Revit. Run npx.cmd -y pi-revit for the full Windows install.",
5
5
  "author": "Ahmad Altahlawi",
6
6
  "license": "MIT",
@@ -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
@@ -234,9 +234,31 @@ namespace RevitBridge.Tools
234
234
  }
235
235
 
236
236
  /// <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 reported.</summary>
237
+ /// popup cannot hang the Revit thread; dismissed dialog ids and the answer given are
238
+ /// reported. Unrecognized dialogs get the dismissive answer (Cancel, then Close, then
239
+ /// No) — on several Revit dialogs OK is the destructive choice (e.g. "Delete
240
+ /// Element(s)"), so confirming blind can silently damage the model. OK leads only for
241
+ /// dialogs known to be safe to confirm, and is otherwise the last resort: every
242
+ /// override attempt failing would leave the dialog up and hang the Revit thread,
243
+ /// which is the one outcome this guard exists to prevent.</summary>
238
244
  private sealed class DialogGuard : IDisposable
239
245
  {
246
+ private const int IDOK = 1;
247
+ private const int IDCANCEL = 2;
248
+ private const int IDNO = 7;
249
+ private const int IDCLOSE = 8;
250
+
251
+ /// <summary>Dialogs where confirming is the benign answer and cancelling would
252
+ /// abort the operation the script deliberately started.</summary>
253
+ private static readonly HashSet<string> ConfirmSafeDialogIds = new(StringComparer.OrdinalIgnoreCase)
254
+ {
255
+ // "Export with temporary hide/isolate": OK exports the view as displayed.
256
+ "TaskDialog_Really_Print_Or_Export_Temp_View_Modes",
257
+ };
258
+
259
+ private static readonly int[] ConfirmFirst = { IDOK, IDCANCEL, IDCLOSE };
260
+ private static readonly int[] DismissFirst = { IDCANCEL, IDCLOSE, IDNO, IDOK };
261
+
240
262
  private readonly UIApplication _uiapp;
241
263
  public List<string> Suppressed { get; } = new();
242
264
 
@@ -248,17 +270,35 @@ namespace RevitBridge.Tools
248
270
 
249
271
  private void OnDialogBoxShowing(object? sender, DialogBoxShowingEventArgs e)
250
272
  {
251
- Suppressed.Add(string.IsNullOrEmpty(e.DialogId) ? e.GetType().Name : e.DialogId);
252
- try
253
- {
254
- e.OverrideResult(1); // IDOK
255
- }
256
- catch
273
+ string id = string.IsNullOrEmpty(e.DialogId) ? e.GetType().Name : e.DialogId;
274
+ int[] answers = ConfirmSafeDialogIds.Contains(id) ? ConfirmFirst : DismissFirst;
275
+ foreach (int answer in answers)
257
276
  {
258
- // Some dialogs reject overrides; never let the guard itself throw.
277
+ try
278
+ {
279
+ if (e.OverrideResult(answer))
280
+ {
281
+ Suppressed.Add($"{id} (answered {AnswerName(answer)})");
282
+ return;
283
+ }
284
+ }
285
+ catch
286
+ {
287
+ // Some dialogs reject specific overrides; try the next answer.
288
+ }
259
289
  }
290
+ Suppressed.Add($"{id} (override rejected — dialog may need manual dismissal)");
260
291
  }
261
292
 
293
+ private static string AnswerName(int answer) => answer switch
294
+ {
295
+ IDOK => "OK",
296
+ IDCANCEL => "Cancel",
297
+ IDNO => "No",
298
+ IDCLOSE => "Close",
299
+ _ => answer.ToString(),
300
+ };
301
+
262
302
  public void Dispose()
263
303
  {
264
304
  try