pi-revit 0.2.11 → 0.2.12

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,19 @@ 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.12] - 2026-07-22
11
+
12
+ ### Added
13
+ - New tool `open_view`: activates a view or sheet in the Revit UI — the equivalent of
14
+ double-clicking it in the Project Browser. Identify the target by `view_id` or by
15
+ `name` (view name, sheet number like "A-101", or "number - name"). Uses Revit's
16
+ queued `RequestViewChange`, which is explicitly legal from the bridge's ExternalEvent
17
+ context; the activation completes the instant the call returns. Ends the
18
+ "please double-click the sheet yourself" gap after sheet/view creation.
19
+
20
+ Requires redeploying the Revit add-in (`scripts\deploy.ps1` with Revit closed, then
21
+ restart Revit).
22
+
10
23
  ## [0.2.11] - 2026-07-22
11
24
 
12
25
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-revit",
3
- "version": "0.2.11",
3
+ "version": "0.2.12",
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",
@@ -1,52 +1,53 @@
1
- ---
2
- name: pi-revit
3
- description: Work with the open Autodesk Revit model through the Revit bridge tools (ping, get_model_overview, get_elements, get_element_details, get_element_types, manage_selection, set_parameters, search_api_docs, execute_csharp, capture_view, export_documents, get_model_health). Use when the user asks about the Revit project, its elements, parameters, selection, or wants to change, script, capture, or export the model.
4
- ---
5
-
6
- # Revit
7
-
8
- Work with the live Revit model. The tools call a headless bridge add-in inside Revit (2025, 2026, or 2027); Revit must be running with a project open (only `ping` and `search_api_docs` work without a document).
9
-
10
- ## Tool selection
11
-
12
- | Task | Tool |
13
- |------|------|
14
- | Bridge alive? Which Revit version? | `ping` |
15
- | Orientation: project info, units, levels, grids, category counts | `get_model_overview` |
16
- | List or count elements of ANY category (walls, doors, rooms, sheets, views, ...) | `get_elements` |
17
- | Read parameter VALUES, location, bounding box, materials of specific elements | `get_element_details` |
18
- | List element types / family symbols; "used vs merely loaded" | `get_element_types` |
19
- | Read or change the user's selection; zoom; temporary isolate | `manage_selection` |
20
- | Write parameter values; rename anything (levels, views, sheets, types) | `set_parameters` |
21
- | Look up Revit API classes/members/signatures | `search_api_docs` |
22
- | Everything else (create, delete, move, views, sheets, tagging, ...) | `execute_csharp` |
23
- | PNG snapshot of a view (visual QA) | `capture_view` (advanced) |
24
- | PDF/DWG/PNG/IFC file export | `export_documents` (advanced) |
25
- | Warnings / model quality audit | `get_model_health` (advanced) |
26
-
27
- Workflow guidance:
28
-
29
- - Call `get_model_overview` first when starting work on an unfamiliar model — one call returns project metadata, units, levels, grids, and category counts.
30
- - `get_elements` is the listing/counting primitive (`count_only: true` for bare counts). It returns identity fields only (id, name, category, typeName, levelId); read parameter values with `get_element_details`. Prefer a `category` or `of_class` scope when filtering by a parameter's display name.
31
- - The selection pipeline is `get_elements` -> ids -> `manage_selection` (action `set`); there is no inline filter on selection.
32
- - `set_parameters` is the home for bulk parameter writes AND renames (the `Name` parameter covers levels, views, sheets, types). One transaction per batch; per-element failures are reported. Pass `expected_document` (the model title) when several models are open or the session is long — it makes the write fail cleanly instead of landing in a different active document.
33
- - Parameter display names are LOCALIZED: in a non-English Revit UI, `Mark` is `Kennzeichen` (German), `マーク` (Japanese), etc. When a display-name lookup or `parameter_names` filter finds nothing, or the document may be non-English, use the language-independent `BuiltInParameter` enum name instead (e.g. `ALL_MODEL_MARK` for Mark, `ALL_MODEL_INSTANCE_COMMENTS` for Comments) — `set_parameters`, `get_element_details.parameter_names`, and `get_elements` filter rules all accept them, and `get_element_details` reports each parameter's `builtInParameter` name for discovery.
34
- - Before writing `execute_csharp` code, verify unfamiliar classes/members with `search_api_docs` (works with no document open; first query builds the index and takes a few seconds). The top match carries its remarks, parameter docs, and returns inline, and every public API enum value is searchable — trust the result over guessing or web search; narrow the query to promote a different match into the top slot.
35
- - `export_documents` files its output under `Documents\pi-revit\Models\<model title>\exports` automatically when `output_dir` is omitted — keyed to the exported document, so it lands right even across many models. Pass `output_dir` only when the user names a different target.
36
-
37
- ## execute_csharp playbook
38
-
39
- - Globals: `doc` (Document), `uidoc` (UIDocument), `uiapp` (UIApplication), and `Dump(value)` to record intermediates into the result's `dumps[]`.
40
- - The transaction is automatic: the whole script runs inside ONE backend-owned transaction — committed on success, rolled back on any exception. Do not open your own `Transaction` (sub-transactions are fine).
41
- - Scripts must be fully synchronous: `await`/`async` is rejected at compile time; never block on `Task.Result`/`.Wait()`.
42
- - Return primitives, strings, or anonymous objects/lists; raw Revit API objects are projected to compact shapes (Element -> `{id,name,category,typeName,levelId}`, ElementId -> number, XYZ -> `{x,y,z}`).
43
- - Lengths are internal units (decimal feet) — convert with `UnitUtils.ConvertToInternalUnits`/`ConvertFromInternalUnits`.
44
- - Common pitfalls: call `FamilySymbol.Activate()` before `NewFamilyInstance`; use collector-level filtering (`OfCategory`/`OfClass`/`WhereElementIsNotElementType`) and bounded loops — the budget is 120s and Revit cannot be interrupted mid-script; modal dialogs are auto-dismissed and reported in `suppressedDialogs`.
45
- - `capture_view` returns a `filePath` to a temp PNG, never image data — open it with the read tool to actually see it.
46
-
47
- ## Failure modes
48
-
49
- - **Bridge not reachable** ("Revit bridge is not available" / "Could not reach the Revit bridge"): Revit is not running or the add-in did not load. Ask the user to start Revit, then retry `ping`.
50
- - **HTTP 409 / "No active Revit document is open."** (`hasActiveDocument: false`): Revit is running but no project is open. Ask the user to open a project, then retry. This fails immediately; do not wait or retry blindly.
51
- - **Timeout** ("Revit did not answer within Ns", 30s default / 120s for execute_csharp, capture_view, export_documents): Revit is busy or showing a modal dialog. An already-started tool still runs to completion in Revit — verify model state (e.g. `get_elements`) before re-issuing a write.
52
- - **Cancelled**: same caveat — the bridge cannot abort queued or running work, so verify model state before retrying a write tool.
1
+ ---
2
+ name: pi-revit
3
+ description: Work with the open Autodesk Revit model through the Revit bridge tools (ping, get_model_overview, get_elements, get_element_details, get_element_types, manage_selection, open_view, set_parameters, search_api_docs, execute_csharp, capture_view, export_documents, get_model_health). Use when the user asks about the Revit project, its elements, parameters, selection, or wants to change, script, capture, or export the model.
4
+ ---
5
+
6
+ # Revit
7
+
8
+ Work with the live Revit model. The tools call a headless bridge add-in inside Revit (2025, 2026, or 2027); Revit must be running with a project open (only `ping` and `search_api_docs` work without a document).
9
+
10
+ ## Tool selection
11
+
12
+ | Task | Tool |
13
+ |------|------|
14
+ | Bridge alive? Which Revit version? | `ping` |
15
+ | Orientation: project info, units, levels, grids, category counts | `get_model_overview` |
16
+ | List or count elements of ANY category (walls, doors, rooms, sheets, views, ...) | `get_elements` |
17
+ | Read parameter VALUES, location, bounding box, materials of specific elements | `get_element_details` |
18
+ | List element types / family symbols; "used vs merely loaded" | `get_element_types` |
19
+ | Read or change the user's selection; zoom; temporary isolate | `manage_selection` |
20
+ | Put a view or sheet on the user's screen (activate it) | `open_view` |
21
+ | Write parameter values; rename anything (levels, views, sheets, types) | `set_parameters` |
22
+ | Look up Revit API classes/members/signatures | `search_api_docs` |
23
+ | Everything else (create, delete, move, views, sheets, tagging, ...) | `execute_csharp` |
24
+ | PNG snapshot of a view (visual QA) | `capture_view` (advanced) |
25
+ | PDF/DWG/PNG/IFC file export | `export_documents` (advanced) |
26
+ | Warnings / model quality audit | `get_model_health` (advanced) |
27
+
28
+ Workflow guidance:
29
+
30
+ - Call `get_model_overview` first when starting work on an unfamiliar model — one call returns project metadata, units, levels, grids, and category counts.
31
+ - `get_elements` is the listing/counting primitive (`count_only: true` for bare counts). It returns identity fields only (id, name, category, typeName, levelId); read parameter values with `get_element_details`. Prefer a `category` or `of_class` scope when filtering by a parameter's display name.
32
+ - The selection pipeline is `get_elements` -> ids -> `manage_selection` (action `set`); there is no inline filter on selection.
33
+ - `set_parameters` is the home for bulk parameter writes AND renames (the `Name` parameter covers levels, views, sheets, types). One transaction per batch; per-element failures are reported. Pass `expected_document` (the model title) when several models are open or the session is long — it makes the write fail cleanly instead of landing in a different active document.
34
+ - Parameter display names are LOCALIZED: in a non-English Revit UI, `Mark` is `Kennzeichen` (German), `マーク` (Japanese), etc. When a display-name lookup or `parameter_names` filter finds nothing, or the document may be non-English, use the language-independent `BuiltInParameter` enum name instead (e.g. `ALL_MODEL_MARK` for Mark, `ALL_MODEL_INSTANCE_COMMENTS` for Comments) — `set_parameters`, `get_element_details.parameter_names`, and `get_elements` filter rules all accept them, and `get_element_details` reports each parameter's `builtInParameter` name for discovery.
35
+ - Before writing `execute_csharp` code, verify unfamiliar classes/members with `search_api_docs` (works with no document open; first query builds the index and takes a few seconds). The top match carries its remarks, parameter docs, and returns inline, and every public API enum value is searchable — trust the result over guessing or web search; narrow the query to promote a different match into the top slot.
36
+ - `export_documents` files its output under `Documents\pi-revit\Models\<model title>\exports` automatically when `output_dir` is omitted — keyed to the exported document, so it lands right even across many models. Pass `output_dir` only when the user names a different target.
37
+
38
+ ## execute_csharp playbook
39
+
40
+ - Globals: `doc` (Document), `uidoc` (UIDocument), `uiapp` (UIApplication), and `Dump(value)` to record intermediates into the result's `dumps[]`.
41
+ - The transaction is automatic: the whole script runs inside ONE backend-owned transaction — committed on success, rolled back on any exception. Do not open your own `Transaction` (sub-transactions are fine).
42
+ - Scripts must be fully synchronous: `await`/`async` is rejected at compile time; never block on `Task.Result`/`.Wait()`.
43
+ - Return primitives, strings, or anonymous objects/lists; raw Revit API objects are projected to compact shapes (Element -> `{id,name,category,typeName,levelId}`, ElementId -> number, XYZ -> `{x,y,z}`).
44
+ - Lengths are internal units (decimal feet) — convert with `UnitUtils.ConvertToInternalUnits`/`ConvertFromInternalUnits`.
45
+ - Common pitfalls: call `FamilySymbol.Activate()` before `NewFamilyInstance`; use collector-level filtering (`OfCategory`/`OfClass`/`WhereElementIsNotElementType`) and bounded loops — the budget is 120s and Revit cannot be interrupted mid-script; modal dialogs are auto-dismissed and reported in `suppressedDialogs`.
46
+ - `capture_view` returns a `filePath` to a temp PNG, never image data — open it with the read tool to actually see it.
47
+
48
+ ## Failure modes
49
+
50
+ - **Bridge not reachable** ("Revit bridge is not available" / "Could not reach the Revit bridge"): Revit is not running or the add-in did not load. Ask the user to start Revit, then retry `ping`.
51
+ - **HTTP 409 / "No active Revit document is open."** (`hasActiveDocument: false`): Revit is running but no project is open. Ask the user to open a project, then retry. This fails immediately; do not wait or retry blindly.
52
+ - **Timeout** ("Revit did not answer within Ns", 30s default / 120s for execute_csharp, capture_view, export_documents): Revit is busy or showing a modal dialog. An already-started tool still runs to completion in Revit — verify model state (e.g. `get_elements`) before re-issuing a write.
53
+ - **Cancelled**: same caveat — the bridge cannot abort queued or running work, so verify model state before retrying a write tool.
@@ -71,6 +71,7 @@ namespace RevitBridge
71
71
  registry.Add(new GetElementTypes());
72
72
  registry.Add(new GetElementDetails());
73
73
  registry.Add(new ManageSelection());
74
+ registry.Add(new OpenView());
74
75
  registry.Add(new SetParameters());
75
76
  registry.Add(new CaptureView());
76
77
  registry.Add(new ExportDocuments());
@@ -0,0 +1,103 @@
1
+ using System.Text.Json;
2
+ using Autodesk.Revit.DB;
3
+
4
+ namespace RevitBridge.Tools
5
+ {
6
+ /// <summary>
7
+ /// Activates a view or sheet in the Revit UI — the API equivalent of double-clicking
8
+ /// it in the Project Browser. Uses UIDocument.RequestViewChange, which is explicitly
9
+ /// permitted from an ExternalEvent callback (where all bridge tools run) as long as no
10
+ /// transaction is open; the tool opens none. The activation is asynchronous by design:
11
+ /// Revit performs it the moment control returns from the bridge call.
12
+ /// </summary>
13
+ internal sealed class OpenView : ITool
14
+ {
15
+ public string Name => "open_view";
16
+ public string Label => "Open View";
17
+ public string Description => "Activate a view or sheet in the Revit UI, like double-clicking it in the Project Browser. Identify the target by view_id (an id from get_elements category Views/Sheets or from view/sheet creation) or by name (exact view name, sheet number like 'A-101', or sheet number - name; case-insensitive). Activation is queued and completes the instant this call returns control to Revit — a capture_view immediately after may still show the previous active view. View templates and internal views cannot be opened.";
18
+ public bool Write => false;
19
+
20
+ public object ParametersSchema => new
21
+ {
22
+ type = "object",
23
+ properties = new
24
+ {
25
+ view_id = new { type = "integer", description = "Id of the view or sheet to activate." },
26
+ name = new { type = "string", description = "Exact view name, sheet number, or sheet 'number - name' (case-insensitive). Used when view_id is absent." },
27
+ },
28
+ };
29
+
30
+ public string? PromptSnippet => "Activate a Revit view or sheet in the UI by id or name (like double-clicking it in the Project Browser).";
31
+ public IReadOnlyList<string>? PromptGuidelines => new[]
32
+ {
33
+ "Use open_view to put a view or sheet on the user's screen (e.g. after creating a sheet); activation completes right after the call, so capture_view in the SAME call batch may still show the old view.",
34
+ };
35
+
36
+ public object? Execute(JsonElement args, ToolContext context)
37
+ {
38
+ var doc = context.Document ?? throw new NoActiveDocumentException();
39
+ var uiapp = context.UIApplication ?? throw new NoActiveDocumentException();
40
+ var uidoc = uiapp.ActiveUIDocument ?? throw new NoActiveDocumentException();
41
+
42
+ long? viewId = JsonArgs.GetLong(args, "view_id");
43
+ string? name = JsonArgs.GetString(args, "name");
44
+ if (viewId is null && string.IsNullOrWhiteSpace(name))
45
+ throw new ArgumentException("Pass view_id or name to identify the view or sheet to open.");
46
+
47
+ View view = viewId is { } id ? ResolveById(doc, id) : ResolveByName(doc, name!.Trim());
48
+
49
+ try
50
+ {
51
+ uidoc.RequestViewChange(view);
52
+ }
53
+ catch (Autodesk.Revit.Exceptions.ArgumentException ex)
54
+ {
55
+ throw new ArgumentException($"View '{view.Name}' (id {view.Id.Value}) cannot be activated: {ex.Message}");
56
+ }
57
+ catch (Autodesk.Revit.Exceptions.InvalidOperationException ex)
58
+ {
59
+ throw new InvalidOperationException(
60
+ $"Revit refused to queue the view change to '{view.Name}': {ex.Message} "
61
+ + "The document may be mid-edit; retry after the current operation finishes.");
62
+ }
63
+
64
+ string kind = view is ViewSheet sheet ? $"sheet {sheet.SheetNumber}" : view.ViewType.ToString();
65
+ return new ToolOutput(new
66
+ {
67
+ requestedViewId = view.Id.Value,
68
+ viewName = view.Name,
69
+ viewType = view.ViewType.ToString(),
70
+ }, $"Queued activation of {kind} '{view.Name}' (id {view.Id.Value}); it opens as soon as Revit regains control.");
71
+ }
72
+
73
+ private static View ResolveById(Document doc, long id)
74
+ {
75
+ var view = doc.GetElement(new ElementId(id)) as View
76
+ ?? throw new ArgumentException($"Element {id} is not a view or sheet (or does not exist). Find view ids with get_elements, category 'Views' or 'Sheets'.");
77
+ if (view.IsTemplate)
78
+ throw new ArgumentException($"View '{view.Name}' (id {id}) is a view template; templates cannot be opened.");
79
+ return view;
80
+ }
81
+
82
+ private static View ResolveByName(Document doc, string name)
83
+ {
84
+ var candidates = new FilteredElementCollector(doc)
85
+ .OfClass(typeof(View))
86
+ .Cast<View>()
87
+ .Where(view => !view.IsTemplate)
88
+ .Where(view =>
89
+ string.Equals(view.Name, name, StringComparison.OrdinalIgnoreCase)
90
+ || (view is ViewSheet sheet
91
+ && (string.Equals(sheet.SheetNumber, name, StringComparison.OrdinalIgnoreCase)
92
+ || string.Equals($"{sheet.SheetNumber} - {sheet.Name}", name, StringComparison.OrdinalIgnoreCase))))
93
+ .ToList();
94
+
95
+ if (candidates.Count == 1)
96
+ return candidates[0];
97
+ if (candidates.Count == 0)
98
+ throw new ArgumentException($"No view or sheet named '{name}'. Find the exact name or sheet number with get_elements, category 'Views' or 'Sheets'.");
99
+ string list = string.Join("; ", candidates.Take(8).Select(view => $"'{view.Name}' (id {view.Id.Value}, {view.ViewType})"));
100
+ throw new ArgumentException($"'{name}' matches {candidates.Count} views: {list}. Pass view_id to disambiguate.");
101
+ }
102
+ }
103
+ }