pi-revit 0.4.0 → 0.5.0
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/AGENTS.md +167 -0
- package/CHANGELOG.md +465 -430
- package/README.md +604 -548
- package/bin/pi-revit.js +9 -9
- package/docs/architecture.md +271 -0
- package/docs/evaluation.md +434 -0
- package/docs/invariants.json +147 -0
- package/extensions/pi-revit/completion-monitor.ts +55 -0
- package/extensions/pi-revit/contracts.ts +146 -0
- package/extensions/pi-revit/discovery.ts +93 -0
- package/extensions/pi-revit/index.ts +342 -255
- package/extensions/pi-revit/instance-router.ts +86 -86
- package/extensions/pi-revit/platform-prompt.ts +40 -0
- package/extensions/pi-revit/scope-monitor.ts +114 -0
- package/extensions/pi-revit/script-library.ts +144 -144
- package/extensions/pi-revit/tool-catalog.ts +113 -14
- package/extensions/pi-revit/tool-documentation.ts +72 -0
- package/extensions/pi-revit/tool-schema.ts +8 -0
- package/package.json +8 -2
- package/scripts/build.ps1 +9 -9
- package/scripts/check-sdk.ps1 +66 -66
- package/scripts/check-tool-documentation.mjs +287 -0
- package/scripts/deploy.ps1 +16 -16
- package/scripts/generate-contracts.mjs +80 -0
- package/scripts/lib/platform.mjs +226 -0
- package/scripts/test-extension.mjs +15 -0
- package/skills/pi-revit/SKILL.md +30 -218
- package/skills/pi-revit/contracts.generated.json +3524 -0
- package/skills/pi-revit/references/execution-rules.md +41 -0
- package/skills/pi-revit/references/model-audit-export.md +38 -27
- package/skills/pi-revit/references/operation-recovery.md +33 -0
- package/skills/pi-revit/references/room-documentation.md +37 -26
- package/skills/pi-revit/references/tool-index.md +89 -0
- package/skills/pi-revit/references/tools/capture_view.md +62 -0
- package/skills/pi-revit/references/tools/change_element_types.md +65 -0
- package/skills/pi-revit/references/tools/create_tags.md +85 -0
- package/skills/pi-revit/references/tools/delete_elements.md +66 -0
- package/skills/pi-revit/references/tools/execute_csharp.md +81 -0
- package/skills/pi-revit/references/tools/export_documents.md +75 -0
- package/skills/pi-revit/references/tools/find_revit_tools.md +96 -0
- package/skills/pi-revit/references/tools/get_element_details.md +66 -0
- package/skills/pi-revit/references/tools/get_element_relationships.md +61 -0
- package/skills/pi-revit/references/tools/get_element_types.md +67 -0
- package/skills/pi-revit/references/tools/get_elements.md +87 -0
- package/skills/pi-revit/references/tools/get_linked_elements.md +79 -0
- package/skills/pi-revit/references/tools/get_linked_models.md +57 -0
- package/skills/pi-revit/references/tools/get_model_coordinates.md +64 -0
- package/skills/pi-revit/references/tools/get_model_health.md +53 -0
- package/skills/pi-revit/references/tools/get_model_overview.md +57 -0
- package/skills/pi-revit/references/tools/get_revit_operation.md +54 -0
- package/skills/pi-revit/references/tools/get_schedule_fields.md +62 -0
- package/skills/pi-revit/references/tools/get_schedules.md +71 -0
- package/skills/pi-revit/references/tools/manage_element_sets.md +92 -0
- package/skills/pi-revit/references/tools/manage_revit_instances.md +63 -0
- package/skills/pi-revit/references/tools/manage_revit_scripts.md +109 -0
- package/skills/pi-revit/references/tools/manage_schedules.md +90 -0
- package/skills/pi-revit/references/tools/manage_selection.md +66 -0
- package/skills/pi-revit/references/tools/manage_sheet_placements.md +82 -0
- package/skills/pi-revit/references/tools/manage_sheets.md +71 -0
- package/skills/pi-revit/references/tools/manage_views.md +95 -0
- package/skills/pi-revit/references/tools/measure_geometry.md +71 -0
- package/skills/pi-revit/references/tools/open_view.md +59 -0
- package/skills/pi-revit/references/tools/ping.md +41 -0
- package/skills/pi-revit/references/tools/query_spatial_elements.md +74 -0
- package/skills/pi-revit/references/tools/read_revit_result.md +53 -0
- package/skills/pi-revit/references/tools/search_api_docs.md +65 -0
- package/skills/pi-revit/references/tools/set_parameters.md +75 -0
- package/skills/pi-revit/references/tools/summarize_elements.md +64 -0
- package/skills/pi-revit/references/tools/transform_elements.md +79 -0
- package/skills/pi-revit/references/visual-verification.md +36 -0
- package/skills/pi-revit/tool-manifest.json +338 -0
- package/src/Revit/BridgeServer.cs +93 -87
- package/src/Revit/OperationStore.cs +178 -178
- package/src/Revit/ToolRegistry.cs +88 -57
- package/src/Revit/Tools/CaptureView.cs +10 -2
- package/src/Revit/Tools/ChangeElementTypes.cs +74 -60
- package/src/Revit/Tools/ChangeSet.cs +39 -0
- package/src/Revit/Tools/CreateTags.cs +107 -95
- package/src/Revit/Tools/DeleteElements.cs +53 -44
- package/src/Revit/Tools/DocumentGuard.cs +74 -64
- package/src/Revit/Tools/ElementNames.cs +103 -0
- package/src/Revit/Tools/ElementQueryScope.cs +27 -27
- package/src/Revit/Tools/ElementTraits.cs +53 -0
- package/src/Revit/Tools/ExecuteCsharp.cs +54 -45
- package/src/Revit/Tools/ExportDocuments.cs +129 -121
- package/src/Revit/Tools/GetElementDetails.cs +37 -41
- package/src/Revit/Tools/GetElementRelationships.cs +82 -76
- package/src/Revit/Tools/GetElementTypes.cs +8 -0
- package/src/Revit/Tools/GetElements.cs +75 -86
- package/src/Revit/Tools/GetLinkedElements.cs +89 -82
- package/src/Revit/Tools/GetLinkedModels.cs +73 -66
- package/src/Revit/Tools/GetModelCoordinates.cs +56 -49
- package/src/Revit/Tools/GetModelHealth.cs +7 -0
- package/src/Revit/Tools/GetModelOverview.cs +185 -158
- package/src/Revit/Tools/GetScheduleFields.cs +44 -37
- package/src/Revit/Tools/GetSchedules.cs +96 -89
- package/src/Revit/Tools/InheritedState.Summary.cs +57 -0
- package/src/Revit/Tools/InheritedState.cs +144 -0
- package/src/Revit/Tools/ManageElementSets.cs +114 -106
- package/src/Revit/Tools/ManageSchedules.cs +174 -164
- package/src/Revit/Tools/ManageSelection.cs +45 -37
- package/src/Revit/Tools/ManageSheetPlacements.cs +113 -97
- package/src/Revit/Tools/ManageSheets.cs +72 -63
- package/src/Revit/Tools/ManageViews.cs +115 -100
- package/src/Revit/Tools/MeasureGeometry.cs +60 -54
- package/src/Revit/Tools/ModelChanges.cs +154 -0
- package/src/Revit/Tools/ModelEditBatch.cs +105 -102
- package/src/Revit/Tools/ModelEditInputs.cs +49 -49
- package/src/Revit/Tools/OpenView.cs +9 -2
- package/src/Revit/Tools/ParameterResolver.cs +94 -0
- package/src/Revit/Tools/QuerySpatialElements.cs +70 -63
- package/src/Revit/Tools/SearchApiDocs.cs +72 -4
- package/src/Revit/Tools/SetParameters.cs +60 -79
- package/src/Revit/Tools/SpatialBounds.cs +30 -30
- package/src/Revit/Tools/SummarizeElements.cs +94 -87
- package/src/Revit/Tools/ToolContract.cs +48 -0
- package/src/Revit/Tools/ToolSupport.cs +5 -1
- package/src/Revit/Tools/TransformElements.cs +72 -57
- package/workspace/AGENTS.md +26 -20
|
@@ -1,60 +1,74 @@
|
|
|
1
|
-
using System.Text.Json;
|
|
2
|
-
using Autodesk.Revit.DB;
|
|
3
|
-
|
|
4
|
-
namespace RevitBridge.Tools;
|
|
5
|
-
|
|
6
|
-
internal sealed class ChangeElementTypes : ITool
|
|
7
|
-
{
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
public string
|
|
11
|
-
public
|
|
12
|
-
public string
|
|
13
|
-
public
|
|
14
|
-
{
|
|
15
|
-
type
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
1
|
+
using System.Text.Json;
|
|
2
|
+
using Autodesk.Revit.DB;
|
|
3
|
+
|
|
4
|
+
namespace RevitBridge.Tools;
|
|
5
|
+
|
|
6
|
+
internal sealed class ChangeElementTypes : ITool
|
|
7
|
+
{
|
|
8
|
+
/// <summary>Updates whose per-view graphics are scanned; each scan visits every view.</summary>
|
|
9
|
+
private const int ViewGraphicsLimit = 20;
|
|
10
|
+
public string Name => "change_element_types";
|
|
11
|
+
public IReadOnlyList<string> DocumentKinds => DocumentKind.Both;
|
|
12
|
+
public IReadOnlyList<string> Keywords => new[] { "swap type", "replace type", "change family type", "retype", "switch type" };
|
|
13
|
+
public IReadOnlyList<ToolLimit> Limits => new[]
|
|
14
|
+
{
|
|
15
|
+
new ToolLimit("Editing a type definition's parameters", "tool", "set_parameters"),
|
|
16
|
+
new ToolLimit("Creating a new type", "api", "ElementType.Duplicate"),
|
|
17
|
+
new ToolLimit("Loading a family", "api", "Document.LoadFamily"),
|
|
18
|
+
};
|
|
19
|
+
public string? Verification => "reread";
|
|
20
|
+
public string Label => "Change Element Types";
|
|
21
|
+
public string Tier => "advanced";
|
|
22
|
+
public bool Write => true;
|
|
23
|
+
public string Description => "Change the type of 1–200 elements using explicit element_id/type_id pairs. Revit validates each type against the target; invalid or pinned targets are reported per update. Some type changes replace the original element: always use resulting_id/unique_id afterward. Each update reports inherited_state: instance values, traits and, for the first 20 updates, views that hide or override the retyped element. Default partial success; atomic=true rolls back all if any update fails. preview=true commit-validates then rolls back, and replacement IDs from previews are temporary. Revit constraints can affect connected/hosted elements; results describe requested targets, not every dependent effect.";
|
|
24
|
+
public object ParametersSchema => new
|
|
25
|
+
{
|
|
26
|
+
type = "object", properties = new
|
|
27
|
+
{
|
|
28
|
+
updates = new { type = "array", minItems = 1, maxItems = 200, items = new
|
|
29
|
+
{
|
|
30
|
+
type = "object", properties = new { element_id = new { type = "integer", minimum = 1 }, type_id = new { type = "integer", minimum = 1 } },
|
|
31
|
+
required = new[] { "element_id", "type_id" },
|
|
32
|
+
} },
|
|
33
|
+
preview = ModelEditInputs.PreviewSchema,
|
|
34
|
+
atomic = new { type = "boolean", description = "Roll back the complete batch if any update fails. Default false." },
|
|
35
|
+
}, required = new[] { "updates" },
|
|
36
|
+
};
|
|
37
|
+
public object Execute(JsonElement args, ToolContext context)
|
|
38
|
+
{
|
|
39
|
+
var doc = context.Document ?? throw new NoActiveDocumentException();
|
|
40
|
+
if (!args.TryGetProperty("updates", out var updates) || updates.ValueKind != JsonValueKind.Array || updates.GetArrayLength() is < 1 or > 200)
|
|
41
|
+
throw new ArgumentException("updates must contain 1–200 element_id/type_id pairs.");
|
|
42
|
+
var seen = new HashSet<long>();
|
|
43
|
+
var steps = new List<ModelEditBatch.Step>();
|
|
44
|
+
foreach (var update in updates.EnumerateArray())
|
|
45
|
+
{
|
|
46
|
+
long elementId = JsonArgs.GetLong(update, "element_id") ?? 0;
|
|
47
|
+
long typeId = JsonArgs.GetLong(update, "type_id") ?? 0;
|
|
48
|
+
if (elementId <= 0 || typeId <= 0 || !seen.Add(elementId)) throw new ArgumentException("Each update needs positive IDs, and element_id must be unique within the batch.");
|
|
49
|
+
int index = steps.Count;
|
|
50
|
+
steps.Add(new(new() { ["element_id"] = elementId, ["type_id"] = typeId }, () =>
|
|
51
|
+
{
|
|
52
|
+
var element = doc.GetElement(new ElementId(elementId)) ?? throw new ArgumentException($"Element {elementId} not found.");
|
|
53
|
+
if (element.Pinned) throw new ArgumentException("Target is pinned; it was not unpinned.");
|
|
54
|
+
var type = doc.GetElement(new ElementId(typeId)) as ElementType ?? throw new ArgumentException($"Type {typeId} not found.");
|
|
55
|
+
if (!element.IsValidType(type.Id)) throw new ArgumentException($"Type {typeId} is not valid for element {elementId}.");
|
|
56
|
+
var before = ModelEditInputs.Snapshot(element);
|
|
57
|
+
var replacement = element.ChangeTypeId(type.Id);
|
|
58
|
+
doc.Regenerate();
|
|
59
|
+
var resultId = replacement == ElementId.InvalidElementId ? new ElementId(elementId) : replacement;
|
|
60
|
+
var result = doc.GetElement(resultId) ?? throw new InvalidOperationException("Changed element could not be resolved.");
|
|
61
|
+
return new() { ["before"] = before, ["after"] = ModelEditInputs.Snapshot(result),
|
|
62
|
+
["resulting_id"] = result.Id.Value, ["unique_id"] = result.UniqueId,
|
|
63
|
+
["replaced"] = result.Id.Value != elementId,
|
|
64
|
+
["resulting_id_is_temporary"] = result.Id.Value != elementId && JsonArgs.GetBool(args, "preview", false),
|
|
65
|
+
// What the retyped element keeps: instance values, traits and per-view graphics (inv:derived-state-reported).
|
|
66
|
+
["inherited_state"] = InheritedState.OfElement(result, result.Id.Value != elementId ? elementId : null, viewGraphics: index < ViewGraphicsLimit) };
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
69
|
+
var batch = ModelEditBatch.Run(doc, Name, args, steps);
|
|
70
|
+
foreach (var row in batch.Proposed)
|
|
71
|
+
if (row["replaced"] is true) row["resulting_id_is_temporary"] = true;
|
|
72
|
+
return batch.Payload;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
namespace RevitBridge.Tools
|
|
2
|
+
{
|
|
3
|
+
/// <summary>
|
|
4
|
+
/// Net element changes of one tool call, from Revit's DocumentChanged operations. Pure, so it
|
|
5
|
+
/// is tested offline. A rolled-back or undone group discards what was recorded before it, so a
|
|
6
|
+
/// preview (commit inside a rolled-back group) reports no net change.
|
|
7
|
+
/// </summary>
|
|
8
|
+
internal sealed class ChangeSet
|
|
9
|
+
{
|
|
10
|
+
private readonly HashSet<long> _added = new(), _modified = new(), _deleted = new();
|
|
11
|
+
public bool Observed { get; private set; }
|
|
12
|
+
public bool RolledBack { get; private set; }
|
|
13
|
+
public IReadOnlyCollection<long> Added => _added;
|
|
14
|
+
public IReadOnlyCollection<long> Modified => _modified;
|
|
15
|
+
public IReadOnlyCollection<long> Deleted => _deleted;
|
|
16
|
+
|
|
17
|
+
public void Record(string operation, IEnumerable<long> added, IEnumerable<long> modified, IEnumerable<long> deleted)
|
|
18
|
+
{
|
|
19
|
+
Observed = true;
|
|
20
|
+
if (operation is not ("TransactionCommitted" or "TransactionRedone"))
|
|
21
|
+
{
|
|
22
|
+
// Undo or rollback: everything recorded in this call is no longer a net change.
|
|
23
|
+
_added.Clear(); _modified.Clear(); _deleted.Clear();
|
|
24
|
+
RolledBack = true;
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
foreach (var id in added) { _added.Add(id); _deleted.Remove(id); }
|
|
28
|
+
foreach (var id in modified) if (!_added.Contains(id)) _modified.Add(id);
|
|
29
|
+
foreach (var id in deleted)
|
|
30
|
+
{
|
|
31
|
+
_modified.Remove(id);
|
|
32
|
+
// Created and deleted within one call leaves nothing behind.
|
|
33
|
+
if (!_added.Remove(id)) _deleted.Add(id);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public bool IsEmpty => _added.Count == 0 && _modified.Count == 0 && _deleted.Count == 0;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -1,95 +1,107 @@
|
|
|
1
|
-
using System.Text.Json;
|
|
2
|
-
using Autodesk.Revit.DB;
|
|
3
|
-
using Autodesk.Revit.DB.Architecture;
|
|
4
|
-
using Autodesk.Revit.DB.Mechanical;
|
|
5
|
-
|
|
6
|
-
namespace RevitBridge.Tools;
|
|
7
|
-
|
|
8
|
-
internal sealed class CreateTags : ITool
|
|
9
|
-
{
|
|
10
|
-
public string Name => "create_tags";
|
|
11
|
-
public string
|
|
12
|
-
public string
|
|
13
|
-
public
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
{
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
spatial
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
1
|
+
using System.Text.Json;
|
|
2
|
+
using Autodesk.Revit.DB;
|
|
3
|
+
using Autodesk.Revit.DB.Architecture;
|
|
4
|
+
using Autodesk.Revit.DB.Mechanical;
|
|
5
|
+
|
|
6
|
+
namespace RevitBridge.Tools;
|
|
7
|
+
|
|
8
|
+
internal sealed class CreateTags : ITool
|
|
9
|
+
{
|
|
10
|
+
public string Name => "create_tags";
|
|
11
|
+
public IReadOnlyList<string> DocumentKinds => DocumentKind.ProjectOnly;
|
|
12
|
+
public IReadOnlyList<string> Keywords => new[] { "tag", "label", "annotate", "annotation", "room tag", "door tag" };
|
|
13
|
+
public IReadOnlyList<ToolLimit> Limits => new[]
|
|
14
|
+
{
|
|
15
|
+
new ToolLimit("Tagging elements inside linked models", "api", "Reference.CreateLinkReference; IndependentTag.Create"),
|
|
16
|
+
new ToolLimit("Tagging faces or subelements", "api", "IndependentTag.Create with a face Reference"),
|
|
17
|
+
new ToolLimit("Creating or loading tag families", "api", "Document.LoadFamily"),
|
|
18
|
+
new ToolLimit("Text notes, dimensions and other annotation", "api", "TextNote.Create; Creation.ItemFactoryBase.NewDimension"),
|
|
19
|
+
};
|
|
20
|
+
public string? Verification => "capture";
|
|
21
|
+
public string Label => "Create Tags";
|
|
22
|
+
public string Tier => "advanced";
|
|
23
|
+
public bool Write => true;
|
|
24
|
+
public string Description => "Create up to 100 tags for host-document elements in one explicit view using a loaded tag FamilySymbol. kind=element uses IndependentTag; room, space and area use their corresponding spatial tag APIs. Targets contain element_id and head_position [x,y,z] in document internal coordinates with explicit length unit. head_position always means the tag head, including with leader=true. Spatial tags require a compatible plan view and positions at that spatial element's level; orientation applies only to element tags. Templates, perspective views and unlocked 3D views cannot host independent tags. Default partial success; atomic=true rolls back all on any failure. preview=true commit-validates then rolls back; all proposed tag IDs are temporary. Linked targets and face/subelement references are outside this tool.";
|
|
25
|
+
public object ParametersSchema => new
|
|
26
|
+
{
|
|
27
|
+
type = "object", properties = new
|
|
28
|
+
{
|
|
29
|
+
kind = new { type = "string", @enum = new[] { "element", "room", "space", "area" } },
|
|
30
|
+
view_id = new { type = "integer", minimum = 1 }, tag_type_id = new { type = "integer", minimum = 1 }, unit = ModelEditInputs.LengthUnitSchema,
|
|
31
|
+
targets = new { type = "array", minItems = 1, maxItems = 100, items = new { type = "object", properties = new { element_id = new { type = "integer", minimum = 1 }, head_position = ModelEditInputs.VectorSchema("Tag head in document internal coordinates, in unit.") }, required = new[] { "element_id", "head_position" } } },
|
|
32
|
+
leader = new { type = "boolean" }, orientation = new { type = "string", @enum = new[] { "horizontal", "vertical" } },
|
|
33
|
+
preview = ModelEditInputs.PreviewSchema, atomic = new { type = "boolean" },
|
|
34
|
+
}, required = new[] { "kind", "view_id", "tag_type_id", "unit", "targets" },
|
|
35
|
+
};
|
|
36
|
+
public object Execute(JsonElement args, ToolContext context)
|
|
37
|
+
{
|
|
38
|
+
var doc = context.Document ?? throw new NoActiveDocumentException();
|
|
39
|
+
string kind = JsonArgs.GetString(args, "kind") ?? "";
|
|
40
|
+
if (kind is not ("element" or "room" or "space" or "area")) throw new ArgumentException("Unknown tag kind.");
|
|
41
|
+
var view = doc.GetElement(new ElementId(JsonArgs.GetLong(args, "view_id") ?? 0)) as View ?? throw new ArgumentException("view_id is not a view.");
|
|
42
|
+
if (view.IsTemplate || (view is View3D three && (three.IsPerspective || !three.IsLocked))) throw new ArgumentException("Tags require a non-template view; 3D views must be orthographic and locked.");
|
|
43
|
+
if (kind != "element" && view is not ViewPlan) throw new ArgumentException("Spatial tags require a plan view.");
|
|
44
|
+
var type = doc.GetElement(new ElementId(JsonArgs.GetLong(args, "tag_type_id") ?? 0)) as FamilySymbol ?? throw new ArgumentException("tag_type_id must identify a loaded tag FamilySymbol.");
|
|
45
|
+
bool leader = false;
|
|
46
|
+
if (args.TryGetProperty("leader", out var leaderValue))
|
|
47
|
+
{
|
|
48
|
+
if (leaderValue.ValueKind is not (JsonValueKind.True or JsonValueKind.False)) throw new ArgumentException("leader must be boolean.");
|
|
49
|
+
leader = leaderValue.GetBoolean();
|
|
50
|
+
}
|
|
51
|
+
if (kind != "element" && args.TryGetProperty("orientation", out _)) throw new ArgumentException("orientation applies only to element tags.");
|
|
52
|
+
var orientation = (JsonArgs.GetString(args, "orientation") ?? "horizontal") switch
|
|
53
|
+
{
|
|
54
|
+
"horizontal" => TagOrientation.Horizontal, "vertical" => TagOrientation.Vertical, _ => throw new ArgumentException("Invalid orientation."),
|
|
55
|
+
};
|
|
56
|
+
if (!args.TryGetProperty("targets", out var targets) || targets.ValueKind != JsonValueKind.Array || targets.GetArrayLength() is < 1 or > 100) throw new ArgumentException("targets must contain 1–100 tag requests.");
|
|
57
|
+
double scale = ModelEditInputs.LengthScale(args);
|
|
58
|
+
var steps = new List<ModelEditBatch.Step>();
|
|
59
|
+
foreach (var input in targets.EnumerateArray())
|
|
60
|
+
{
|
|
61
|
+
if (input.ValueKind != JsonValueKind.Object) throw new ArgumentException("Each target must be an object.");
|
|
62
|
+
long id = JsonArgs.GetLong(input, "element_id") ?? 0;
|
|
63
|
+
if (id <= 0) throw new ArgumentException("Target element_id must be positive.");
|
|
64
|
+
var point = ModelEditInputs.Vector(input, "head_position").Multiply(scale);
|
|
65
|
+
steps.Add(new(new() { ["element_id"] = id }, () =>
|
|
66
|
+
{
|
|
67
|
+
var element = doc.GetElement(new ElementId(id)) ?? throw new ArgumentException($"Element {id} not found.");
|
|
68
|
+
if (!type.IsActive) { type.Activate(); doc.Regenerate(); }
|
|
69
|
+
Element tag;
|
|
70
|
+
if (kind == "element")
|
|
71
|
+
{
|
|
72
|
+
var independent = IndependentTag.Create(doc, type.Id, view.Id, new Reference(element), false, orientation, point);
|
|
73
|
+
independent.HasLeader = leader;
|
|
74
|
+
independent.TagHeadPosition = point;
|
|
75
|
+
tag = independent;
|
|
76
|
+
}
|
|
77
|
+
else
|
|
78
|
+
{
|
|
79
|
+
if (element.Location is not LocationPoint location || view.GenLevel == null || element.LevelId != view.GenLevel.Id)
|
|
80
|
+
throw new ArgumentException("A spatial tag target must be placed on the plan view's level.");
|
|
81
|
+
var anchor = new UV(location.Point.X, location.Point.Y);
|
|
82
|
+
SpatialElementTag spatial = kind switch
|
|
83
|
+
{
|
|
84
|
+
"room" when element is Room => doc.Create.NewRoomTag(new LinkElementId(element.Id), anchor, view.Id),
|
|
85
|
+
"space" when element is Space space => doc.Create.NewSpaceTag(space, anchor, view),
|
|
86
|
+
"area" when element is Area area => doc.Create.NewAreaTag((ViewPlan)view, area, anchor),
|
|
87
|
+
_ => throw new ArgumentException($"Element {id} does not match kind {kind}."),
|
|
88
|
+
};
|
|
89
|
+
if (!spatial.IsValidType(type.Id)) throw new ArgumentException("Tag type does not match the spatial tag category.");
|
|
90
|
+
var replacement = spatial.ChangeTypeId(type.Id);
|
|
91
|
+
if (replacement != ElementId.InvalidElementId) spatial = (SpatialElementTag)doc.GetElement(replacement);
|
|
92
|
+
spatial.HasLeader = leader; spatial.TagHeadPosition = point;
|
|
93
|
+
tag = spatial;
|
|
94
|
+
}
|
|
95
|
+
doc.Regenerate();
|
|
96
|
+
var actual = tag is IndependentTag independentResult ? independentResult.TagHeadPosition : ((SpatialElementTag)tag).TagHeadPosition;
|
|
97
|
+
return new() { ["tag_id"] = tag.Id.Value, ["unique_id"] = tag.UniqueId, ["tag_type_id"] = tag.GetTypeId().Value, ["view_id"] = tag.OwnerViewId.Value,
|
|
98
|
+
["kind"] = kind, ["head_position"] = new[] { actual.X, actual.Y, actual.Z }, ["unit"] = "feet", ["leader"] = leader, ["id_is_temporary"] = false,
|
|
99
|
+
// A retyped spatial tag may be replaced by a new element (inv:derived-state-reported).
|
|
100
|
+
["inherited_state"] = InheritedState.OfElement(tag, null) };
|
|
101
|
+
}));
|
|
102
|
+
}
|
|
103
|
+
var batch = ModelEditBatch.Run(doc, Name, args, steps);
|
|
104
|
+
foreach (var row in batch.Proposed) row["id_is_temporary"] = true;
|
|
105
|
+
return batch.Payload;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -1,44 +1,53 @@
|
|
|
1
|
-
using System.Text.Json;
|
|
2
|
-
using Autodesk.Revit.DB;
|
|
3
|
-
|
|
4
|
-
namespace RevitBridge.Tools;
|
|
5
|
-
|
|
6
|
-
internal sealed class DeleteElements : ITool
|
|
7
|
-
{
|
|
8
|
-
public string Name => "delete_elements";
|
|
9
|
-
public string
|
|
10
|
-
public string
|
|
11
|
-
public
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
public object
|
|
23
|
-
{
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
1
|
+
using System.Text.Json;
|
|
2
|
+
using Autodesk.Revit.DB;
|
|
3
|
+
|
|
4
|
+
namespace RevitBridge.Tools;
|
|
5
|
+
|
|
6
|
+
internal sealed class DeleteElements : ITool
|
|
7
|
+
{
|
|
8
|
+
public string Name => "delete_elements";
|
|
9
|
+
public IReadOnlyList<string> DocumentKinds => DocumentKind.Both;
|
|
10
|
+
public IReadOnlyList<string> Keywords => new[] { "remove", "erase", "cleanup", "delete view", "delete sheet" };
|
|
11
|
+
public IReadOnlyList<ToolLimit> Limits => new[]
|
|
12
|
+
{
|
|
13
|
+
new ToolLimit("Pinned elements (rejected, never unpinned automatically)", "user", "Confirm unpinning (Element.Pinned) first"),
|
|
14
|
+
new ToolLimit("Elements inside linked models", "user", "Edit the linked model itself"),
|
|
15
|
+
new ToolLimit("One-step purge of unused types", "tool", "get_element_types with include_instance_count, then delete_elements"),
|
|
16
|
+
};
|
|
17
|
+
public string? Verification => "reread";
|
|
18
|
+
public string Label => "Delete Elements";
|
|
19
|
+
public string Tier => "advanced";
|
|
20
|
+
public bool Write => true;
|
|
21
|
+
public string Description => "Delete 1–200 explicitly selected elements in one atomic step, returning the full set of IDs returned by Revit's deletion API, including dependents. Use preview=true to inspect that set with commit validation and confirmed rollback. Optional expected_deleted_ids rejects a changed deletion set before commit; pass the full preview set to bind a later deletion to that set. Pinned requested elements are rejected. At most 10,000 deleted IDs are permitted; a larger cascade rolls back. Returned IDs include dependencies removed by Document.Delete, not a complete audit of surviving elements modified by Revit.";
|
|
22
|
+
public object ParametersSchema => new
|
|
23
|
+
{
|
|
24
|
+
type = "object", properties = new
|
|
25
|
+
{
|
|
26
|
+
element_ids = ModelEditInputs.IdsSchema,
|
|
27
|
+
preview = ModelEditInputs.PreviewSchema,
|
|
28
|
+
expected_deleted_ids = new { type = "array", minItems = 1, maxItems = 10000, uniqueItems = true, items = new { type = "integer", minimum = 1 }, description = "Exact full deleted_ids set from a previous preview; mismatches roll back." },
|
|
29
|
+
}, required = new[] { "element_ids" },
|
|
30
|
+
};
|
|
31
|
+
public object Execute(JsonElement args, ToolContext context)
|
|
32
|
+
{
|
|
33
|
+
var doc = context.Document ?? throw new NoActiveDocumentException();
|
|
34
|
+
var ids = ModelEditInputs.Ids(args);
|
|
35
|
+
List<long>? expected = args.TryGetProperty("expected_deleted_ids", out _) ? JsonArgs.GetLongArray(args, "expected_deleted_ids") : null;
|
|
36
|
+
if (expected != null && (expected.Count is < 1 or > 10000 || expected.Any(x => x <= 0) || expected.Distinct().Count() != expected.Count))
|
|
37
|
+
throw new ArgumentException("expected_deleted_ids must contain 1–10,000 distinct positive IDs.");
|
|
38
|
+
return ModelEditBatch.Run(doc, Name, args, new[] { new ModelEditBatch.Step(
|
|
39
|
+
new() { ["element_ids"] = ids.Select(x => x.Value).ToArray() }, () =>
|
|
40
|
+
{
|
|
41
|
+
foreach (var id in ids)
|
|
42
|
+
{
|
|
43
|
+
var element = doc.GetElement(id) ?? throw new ArgumentException($"Element {id.Value} not found.");
|
|
44
|
+
if (element.Pinned) throw new ArgumentException($"Element {id.Value} is pinned; no elements were unpinned.");
|
|
45
|
+
}
|
|
46
|
+
var deleted = doc.Delete(ids).Select(id => id.Value).OrderBy(id => id).ToArray();
|
|
47
|
+
if (deleted.Length > 10000) throw new ArgumentException("Deletion exceeds the 10,000-ID cascade limit.");
|
|
48
|
+
if (expected != null && !deleted.ToHashSet().SetEquals(expected)) throw new ArgumentException("The deletion set changed since the preview; deletion was rolled back. Run a fresh preview.");
|
|
49
|
+
return new() { ["deleted_ids"] = deleted, ["deleted_count"] = deleted.Length,
|
|
50
|
+
["dependent_ids"] = deleted.Except(ids.Select(x => x.Value)).ToArray() };
|
|
51
|
+
}) }).Payload;
|
|
52
|
+
}
|
|
53
|
+
}
|