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,164 +1,174 @@
|
|
|
1
|
-
using System.Text.Json;
|
|
2
|
-
using Autodesk.Revit.DB;
|
|
3
|
-
|
|
4
|
-
namespace RevitBridge.Tools;
|
|
5
|
-
|
|
6
|
-
internal sealed class ManageSchedules : ITool
|
|
7
|
-
{
|
|
8
|
-
public string Name => "manage_schedules";
|
|
9
|
-
public string
|
|
10
|
-
public string
|
|
11
|
-
public
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
},
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
{
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}, required = new[] { "
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (name
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
if (
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
{
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
1
|
+
using System.Text.Json;
|
|
2
|
+
using Autodesk.Revit.DB;
|
|
3
|
+
|
|
4
|
+
namespace RevitBridge.Tools;
|
|
5
|
+
|
|
6
|
+
internal sealed class ManageSchedules : ITool
|
|
7
|
+
{
|
|
8
|
+
public string Name => "manage_schedules";
|
|
9
|
+
public IReadOnlyList<string> DocumentKinds => DocumentKind.ProjectOnly;
|
|
10
|
+
public IReadOnlyList<string> Keywords => new[] { "create schedule", "schedule", "add field", "sort", "filter schedule", "quantities", "takeoff" };
|
|
11
|
+
public IReadOnlyList<ToolLimit> Limits => new[]
|
|
12
|
+
{
|
|
13
|
+
new ToolLimit("Calculated (formula) fields", "revit_unsupported", "Revit 2025 API exposes ScheduleFieldType.Formula but no member to author a formula"),
|
|
14
|
+
new ToolLimit("Combined-parameter fields", "api", "ScheduleDefinition.InsertCombinedParameterField"),
|
|
15
|
+
new ToolLimit("Schedule templates, titleblock revision schedules and embedded schedules", "api", "ViewSchedule and ScheduleDefinition members; verify with search_api_docs"),
|
|
16
|
+
new ToolLimit("Placing a schedule on a sheet", "tool", "manage_sheet_placements"),
|
|
17
|
+
};
|
|
18
|
+
public string? Verification => "reread";
|
|
19
|
+
public string Label => "Manage Schedules";
|
|
20
|
+
public string Tier => "advanced";
|
|
21
|
+
public bool Write => true;
|
|
22
|
+
public string Description => "Create or configure a regular schedule in one atomic step. Create requires category and name; optional area_scheme_id supports area schedules. Configure requires schedule_id. Discover add_fields using get_schedule_fields and pass the returned parameter_id + field_type pair, including Count when listed. Count also supports field_type Count without a parameter_id. update_fields, sort_fields and filters use schedule-local field_id from get_schedules. Supplied sort_fields/filters replace their entire lists; empty arrays clear them. Numeric measured filter values require a compatible unit; returned numeric filter values use internal units. Column widths require explicit length units and apply to both grid and sheet. preview=true commit-validates then rolls back; created schedule/field IDs in previews are temporary. Revision schedules, templates, embedded schedules and calculated/combined-field authoring are outside this tool.";
|
|
23
|
+
public object ParametersSchema => new
|
|
24
|
+
{
|
|
25
|
+
type = "object", properties = new
|
|
26
|
+
{
|
|
27
|
+
action = new { type = "string", @enum = new[] { "create", "configure" } },
|
|
28
|
+
schedule_id = new { type = "integer", minimum = 1 }, category = new { type = "string" }, name = new { type = "string", minLength = 1 }, area_scheme_id = new { type = "integer", minimum = 1 },
|
|
29
|
+
is_itemized = new { type = "boolean" }, preview = ModelEditInputs.PreviewSchema,
|
|
30
|
+
add_fields = new { type = "array", maxItems = 50, items = new { type = "object", properties = new
|
|
31
|
+
{
|
|
32
|
+
parameter_id = new { type = "integer", description = "Use the ID returned by get_schedule_fields, including negative built-in IDs. Optional for Count; when supplied, the pair must be eligible for the schedule." }, field_type = new { type = "string" },
|
|
33
|
+
heading = new { type = "string" }, hidden = new { type = "boolean" }, width = new { type = "number", exclusiveMinimum = 0 }, unit = ModelEditInputs.LengthUnitSchema,
|
|
34
|
+
}, required = new[] { "field_type" } } },
|
|
35
|
+
update_fields = new { type = "array", maxItems = 50, items = new { type = "object", properties = new
|
|
36
|
+
{
|
|
37
|
+
field_id = new { type = "integer", minimum = 0 }, heading = new { type = "string" }, hidden = new { type = "boolean" }, width = new { type = "number", exclusiveMinimum = 0 }, unit = ModelEditInputs.LengthUnitSchema,
|
|
38
|
+
}, required = new[] { "field_id" } } },
|
|
39
|
+
sort_fields = new { type = "array", maxItems = 4, items = new { type = "object", properties = new { field_id = new { type = "integer", minimum = 0 }, descending = new { type = "boolean" }, show_header = new { type = "boolean" } }, required = new[] { "field_id" } } },
|
|
40
|
+
filters = new { type = "array", maxItems = 8, items = new { type = "object", properties = new
|
|
41
|
+
{
|
|
42
|
+
field_id = new { type = "integer", minimum = 0 }, comparison = new { type = "string", @enum = new[] { "equals", "not_equals", "contains", "greater_than", "less_than" } },
|
|
43
|
+
value_type = new { type = "string", @enum = new[] { "string", "number", "integer", "element_id" } }, value = new { description = "Filter value matching value_type." }, unit = new { type = "string", description = "Required for measured numbers; compatible forge unit name, e.g. meters, squareMeters, degrees." },
|
|
44
|
+
}, required = new[] { "field_id", "comparison", "value_type", "value" } } },
|
|
45
|
+
}, required = new[] { "action" },
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
public object Execute(JsonElement args, ToolContext context)
|
|
49
|
+
{
|
|
50
|
+
var doc = context.Document ?? throw new NoActiveDocumentException();
|
|
51
|
+
string action = JsonArgs.GetString(args, "action") ?? "";
|
|
52
|
+
if (action is not ("create" or "configure")) throw new ArgumentException("action must be create or configure.");
|
|
53
|
+
var additions = Array(args, "add_fields", 50); var updates = Array(args, "update_fields", 50);
|
|
54
|
+
var sorts = Array(args, "sort_fields", 4); var filters = Array(args, "filters", 8);
|
|
55
|
+
return ModelEditBatch.Run(doc, Name, args, new[] { new ModelEditBatch.Step(new() { ["action"] = action }, () =>
|
|
56
|
+
{
|
|
57
|
+
ViewSchedule schedule;
|
|
58
|
+
if (action == "create")
|
|
59
|
+
{
|
|
60
|
+
string category = JsonArgs.GetString(args, "category") ?? throw new ArgumentException("category is required for creation.");
|
|
61
|
+
var categoryId = CategoryResolver.Resolve(doc, category);
|
|
62
|
+
schedule = JsonArgs.GetLong(args, "area_scheme_id") is long area ? ViewSchedule.CreateSchedule(doc, categoryId, new ElementId(area)) : ViewSchedule.CreateSchedule(doc, categoryId);
|
|
63
|
+
if (!args.TryGetProperty("name", out _)) throw new ArgumentException("name is required for creation.");
|
|
64
|
+
}
|
|
65
|
+
else
|
|
66
|
+
{
|
|
67
|
+
if (args.TryGetProperty("category", out _) || args.TryGetProperty("area_scheme_id", out _)) throw new ArgumentException("category and area_scheme_id are creation-only.");
|
|
68
|
+
schedule = doc.GetElement(new ElementId(JsonArgs.GetLong(args, "schedule_id") ?? 0)) as ViewSchedule ?? throw new ArgumentException("schedule_id is not a schedule.");
|
|
69
|
+
}
|
|
70
|
+
if (schedule.IsTemplate || schedule.IsTitleblockRevisionSchedule || schedule.Definition.IsEmbedded) throw new ArgumentException("Use a regular, non-template schedule.");
|
|
71
|
+
if (args.TryGetProperty("name", out var name))
|
|
72
|
+
{
|
|
73
|
+
if (name.ValueKind != JsonValueKind.String || string.IsNullOrWhiteSpace(name.GetString())) throw new ArgumentException("name must be nonempty.");
|
|
74
|
+
ElementNames.Assign(schedule, name.GetString()!);
|
|
75
|
+
}
|
|
76
|
+
var definition = schedule.Definition;
|
|
77
|
+
var added = new List<int>();
|
|
78
|
+
foreach (var input in additions)
|
|
79
|
+
{
|
|
80
|
+
string typeName = JsonArgs.GetString(input, "field_type") ?? "";
|
|
81
|
+
if (typeName.Length == 0 || !char.IsLetter(typeName[0]) || !Enum.TryParse<ScheduleFieldType>(typeName, false, out var type) || !Enum.IsDefined(type)) throw new ArgumentException("Use a field_type from get_schedule_fields, or Count.");
|
|
82
|
+
ScheduleField field;
|
|
83
|
+
if (type == ScheduleFieldType.Count && !input.TryGetProperty("parameter_id", out _))
|
|
84
|
+
{
|
|
85
|
+
field = definition.AddField(type);
|
|
86
|
+
}
|
|
87
|
+
else
|
|
88
|
+
{
|
|
89
|
+
long parameterId = JsonArgs.GetLong(input, "parameter_id") ?? throw new ArgumentException("parameter_id is required for this field.");
|
|
90
|
+
var eligible = definition.GetSchedulableFields().FirstOrDefault(f => f.ParameterId.Value == parameterId && f.FieldType == type) ?? throw new ArgumentException("That parameter_id/field_type pair is not eligible for this schedule.");
|
|
91
|
+
field = definition.AddField(eligible);
|
|
92
|
+
}
|
|
93
|
+
ConfigureField(field, input); added.Add(field.FieldId.IntegerValue);
|
|
94
|
+
}
|
|
95
|
+
var seen = new HashSet<int>();
|
|
96
|
+
foreach (var input in updates)
|
|
97
|
+
{
|
|
98
|
+
var id = FieldId(input);
|
|
99
|
+
if (!seen.Add(id.IntegerValue)) throw new ArgumentException("update_fields contains a duplicate field_id.");
|
|
100
|
+
ConfigureField(definition.GetField(id), input);
|
|
101
|
+
}
|
|
102
|
+
if (args.TryGetProperty("is_itemized", out _)) definition.IsItemized = Bool(args, "is_itemized");
|
|
103
|
+
if (args.TryGetProperty("sort_fields", out _))
|
|
104
|
+
definition.SetSortGroupFields(sorts.Select(input => new ScheduleSortGroupField(FieldId(input), Bool(input, "descending") ? ScheduleSortOrder.Descending : ScheduleSortOrder.Ascending) { ShowHeader = Bool(input, "show_header") }).ToList());
|
|
105
|
+
if (args.TryGetProperty("filters", out _)) definition.SetFilters(filters.Select(input => Filter(definition, input)).ToList());
|
|
106
|
+
doc.Regenerate();
|
|
107
|
+
return new() { ["schedule_id"] = schedule.Id.Value, ["unique_id"] = schedule.UniqueId, ["name"] = schedule.Name,
|
|
108
|
+
["created"] = action == "create", ["id_is_temporary"] = action == "create" && JsonArgs.GetBool(args, "preview", false),
|
|
109
|
+
["added_field_ids"] = added, ["added_field_ids_are_temporary"] = JsonArgs.GetBool(args, "preview", false),
|
|
110
|
+
["is_itemized"] = definition.IsItemized, ["field_count"] = definition.GetFieldCount(), ["filter_count"] = definition.GetFilterCount(), ["sort_count"] = definition.GetSortGroupFieldCount() };
|
|
111
|
+
}) }).Payload;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private static List<JsonElement> Array(JsonElement args, string name, int max)
|
|
115
|
+
{
|
|
116
|
+
if (!args.TryGetProperty(name, out var value)) return new();
|
|
117
|
+
if (value.ValueKind != JsonValueKind.Array || value.GetArrayLength() > max) throw new ArgumentException($"{name} must be an array of at most {max} entries.");
|
|
118
|
+
var items = value.EnumerateArray().ToList();
|
|
119
|
+
if (items.Any(item => item.ValueKind != JsonValueKind.Object)) throw new ArgumentException($"{name} entries must be objects.");
|
|
120
|
+
return items;
|
|
121
|
+
}
|
|
122
|
+
private static bool Bool(JsonElement args, string name)
|
|
123
|
+
{
|
|
124
|
+
if (!args.TryGetProperty(name, out var value)) return false;
|
|
125
|
+
if (value.ValueKind is not (JsonValueKind.True or JsonValueKind.False)) throw new ArgumentException($"{name} must be boolean.");
|
|
126
|
+
return value.GetBoolean();
|
|
127
|
+
}
|
|
128
|
+
private static ScheduleFieldId FieldId(JsonElement input)
|
|
129
|
+
{
|
|
130
|
+
long id = JsonArgs.GetLong(input, "field_id") ?? -1;
|
|
131
|
+
return id is >= 0 and <= int.MaxValue ? new ScheduleFieldId((int)id) : throw new ArgumentException("field_id must be a nonnegative schedule-local ID.");
|
|
132
|
+
}
|
|
133
|
+
private static void ConfigureField(ScheduleField field, JsonElement input)
|
|
134
|
+
{
|
|
135
|
+
if (input.TryGetProperty("heading", out var heading)) field.ColumnHeading = heading.ValueKind == JsonValueKind.String ? heading.GetString()! : throw new ArgumentException("heading must be a string.");
|
|
136
|
+
if (input.TryGetProperty("hidden", out _)) field.IsHidden = Bool(input, "hidden");
|
|
137
|
+
if (input.TryGetProperty("width", out _))
|
|
138
|
+
{
|
|
139
|
+
double width = ModelEditInputs.Number(input, "width") * ModelEditInputs.LengthScale(input);
|
|
140
|
+
if (width <= 0) throw new ArgumentException("Column width must be positive.");
|
|
141
|
+
field.GridColumnWidth = width; field.SheetColumnWidth = width;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
private static ScheduleFilter Filter(ScheduleDefinition definition, JsonElement input)
|
|
145
|
+
{
|
|
146
|
+
var id = FieldId(input); var field = definition.GetField(id);
|
|
147
|
+
var comparison = JsonArgs.GetString(input, "comparison") switch
|
|
148
|
+
{
|
|
149
|
+
"equals" => ScheduleFilterType.Equal, "not_equals" => ScheduleFilterType.NotEqual, "contains" => ScheduleFilterType.Contains,
|
|
150
|
+
"greater_than" => ScheduleFilterType.GreaterThan, "less_than" => ScheduleFilterType.LessThan, _ => throw new ArgumentException("Unknown comparison."),
|
|
151
|
+
};
|
|
152
|
+
if (comparison == ScheduleFilterType.Contains ? !definition.CanFilterBySubstring(id) : !definition.CanFilterByValue(id)) throw new ArgumentException("This field does not support that filter operation.");
|
|
153
|
+
if (!input.TryGetProperty("value", out var value)) throw new ArgumentException("Filter value is required.");
|
|
154
|
+
string? type = JsonArgs.GetString(input, "value_type");
|
|
155
|
+
if (type == "string" && value.ValueKind == JsonValueKind.String) return new ScheduleFilter(id, comparison, value.GetString()!);
|
|
156
|
+
if (type == "integer" && value.ValueKind == JsonValueKind.Number && value.TryGetInt32(out int integer)) return new ScheduleFilter(id, comparison, integer);
|
|
157
|
+
if (type == "element_id" && value.ValueKind == JsonValueKind.Number && value.TryGetInt64(out long elementId)) return new ScheduleFilter(id, comparison, new ElementId(elementId));
|
|
158
|
+
if (type == "number")
|
|
159
|
+
{
|
|
160
|
+
double number = ModelEditInputs.Number(input, "value");
|
|
161
|
+
var spec = field.GetSpecTypeId(); string? unit = JsonArgs.GetString(input, "unit");
|
|
162
|
+
if (UnitUtils.IsMeasurableSpec(spec))
|
|
163
|
+
{
|
|
164
|
+
if (string.IsNullOrWhiteSpace(unit)) throw new ArgumentException("Measured numeric filters require an explicit unit.");
|
|
165
|
+
var unitId = UnitResolver.Resolve(unit);
|
|
166
|
+
if (!UnitUtils.IsValidUnit(spec, unitId)) throw new ArgumentException("Filter unit does not match the field's specification.");
|
|
167
|
+
number = UnitUtils.ConvertToInternalUnits(number, unitId);
|
|
168
|
+
}
|
|
169
|
+
else if (unit != null) throw new ArgumentException("This numeric field has no measurable unit.");
|
|
170
|
+
return new ScheduleFilter(id, comparison, number);
|
|
171
|
+
}
|
|
172
|
+
throw new ArgumentException("Filter value does not match value_type.");
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -11,8 +11,16 @@ namespace RevitBridge.Tools
|
|
|
11
11
|
/// </summary>
|
|
12
12
|
internal sealed class ManageSelection : ITool
|
|
13
13
|
{
|
|
14
|
-
public string Name => "manage_selection";
|
|
15
|
-
public IReadOnlyList<string>
|
|
14
|
+
public string Name => "manage_selection";
|
|
15
|
+
public IReadOnlyList<string> DocumentKinds => DocumentKind.Both;
|
|
16
|
+
public IReadOnlyList<string> Keywords => new[] { "select", "highlight", "zoom", "isolate", "show in revit", "selected elements", "current selection" };
|
|
17
|
+
public IReadOnlyList<ToolLimit> Limits => new[]
|
|
18
|
+
{
|
|
19
|
+
new ToolLimit("Permanent hiding or view visibility changes", "api", "View.HideElements; View.SetCategoryHidden"),
|
|
20
|
+
new ToolLimit("Saved selection sets", "api", "SelectionFilterElement.Create"),
|
|
21
|
+
};
|
|
22
|
+
public string? Verification => "reread";
|
|
23
|
+
public IReadOnlyList<string> Effects => new[] { "ui", "model" };
|
|
16
24
|
public string Label => "Manage Selection";
|
|
17
25
|
public string Description => "Manage the Revit selection and view focus. action 'get' returns the currently selected elements (id, name, category, typeName, levelId); 'set'/'add'/'remove' change the selection with element_ids; 'clear' empties it; 'zoom' zooms open views to element_ids (default: the current selection). isolate_in_view=true additionally applies Revit's Temporary Hide/Isolate to the affected elements in the active view — temporary view state, not a model change (Revit needs a brief internal transaction to toggle it); with an empty target set (action 'clear', or 'get' with nothing selected) it resets the temporary isolate instead. Selection and zoom never modify model data.";
|
|
18
26
|
|
|
@@ -137,25 +145,25 @@ namespace RevitBridge.Tools
|
|
|
137
145
|
compactParts.Add($"ids not found: {string.Join(", ", notFound)}");
|
|
138
146
|
}
|
|
139
147
|
|
|
140
|
-
if (isolateInView)
|
|
141
|
-
{
|
|
142
|
-
var view = uiDocument.ActiveGraphicalView;
|
|
143
|
-
IReadOnlyList<string> warnings;
|
|
144
|
-
try
|
|
145
|
-
{
|
|
146
|
-
if (view is null)
|
|
147
|
-
throw new ArgumentException("isolate_in_view requires an active graphical view in Revit.");
|
|
148
|
-
warnings = ApplyTemporaryIsolate(doc, view, isolateTargets);
|
|
149
|
-
}
|
|
150
|
-
catch (Exception ex)
|
|
151
|
-
{
|
|
152
|
-
throw new InvalidOperationException($"Selection action '{action}' completed before temporary isolate failed: {ex.Message}. Selection/zoom changes were not rolled back.", ex);
|
|
153
|
-
}
|
|
154
|
-
if (warnings.Count > 0)
|
|
155
|
-
{
|
|
156
|
-
payload["commitWarnings"] = warnings;
|
|
157
|
-
compactParts.Add($"{warnings.Count} Revit warning(s) auto-dismissed (see commitWarnings)");
|
|
158
|
-
}
|
|
148
|
+
if (isolateInView)
|
|
149
|
+
{
|
|
150
|
+
var view = uiDocument.ActiveGraphicalView;
|
|
151
|
+
IReadOnlyList<string> warnings;
|
|
152
|
+
try
|
|
153
|
+
{
|
|
154
|
+
if (view is null)
|
|
155
|
+
throw new ArgumentException("isolate_in_view requires an active graphical view in Revit.");
|
|
156
|
+
warnings = ApplyTemporaryIsolate(doc, view, isolateTargets);
|
|
157
|
+
}
|
|
158
|
+
catch (Exception ex)
|
|
159
|
+
{
|
|
160
|
+
throw new InvalidOperationException($"Selection action '{action}' completed before temporary isolate failed: {ex.Message}. Selection/zoom changes were not rolled back.", ex);
|
|
161
|
+
}
|
|
162
|
+
if (warnings.Count > 0)
|
|
163
|
+
{
|
|
164
|
+
payload["commitWarnings"] = warnings;
|
|
165
|
+
compactParts.Add($"{warnings.Count} Revit warning(s) auto-dismissed (see commitWarnings)");
|
|
166
|
+
}
|
|
159
167
|
payload["viewId"] = view.Id.Value;
|
|
160
168
|
payload["viewName"] = view.Name;
|
|
161
169
|
if (isolateTargets.Count == 0)
|
|
@@ -204,28 +212,28 @@ namespace RevitBridge.Tools
|
|
|
204
212
|
/// only changes it inside an open transaction — so exactly this branch wraps
|
|
205
213
|
/// a small one while the tool stays Write = false.
|
|
206
214
|
/// </summary>
|
|
207
|
-
private static IReadOnlyList<string> ApplyTemporaryIsolate(Document doc, View view, ICollection<ElementId> elementIds)
|
|
208
|
-
{
|
|
209
|
-
using var transaction = new Transaction(doc, "manage_selection: temporary isolate");
|
|
210
|
-
if (transaction.Start() != TransactionStatus.Started)
|
|
211
|
-
throw new InvalidOperationException("Unable to start the temporary-isolate transaction.");
|
|
212
|
-
var failureGuard = FailureGuard.Attach(transaction);
|
|
215
|
+
private static IReadOnlyList<string> ApplyTemporaryIsolate(Document doc, View view, ICollection<ElementId> elementIds)
|
|
216
|
+
{
|
|
217
|
+
using var transaction = new Transaction(doc, "manage_selection: temporary isolate");
|
|
218
|
+
if (transaction.Start() != TransactionStatus.Started)
|
|
219
|
+
throw new InvalidOperationException("Unable to start the temporary-isolate transaction.");
|
|
220
|
+
var failureGuard = FailureGuard.Attach(transaction);
|
|
213
221
|
try
|
|
214
222
|
{
|
|
215
223
|
if (elementIds.Count == 0)
|
|
216
224
|
view.DisableTemporaryViewMode(TemporaryViewMode.TemporaryHideIsolate);
|
|
217
225
|
else
|
|
218
226
|
view.IsolateElementsTemporary(elementIds);
|
|
219
|
-
var status = transaction.Commit();
|
|
220
|
-
var finalStatus = transaction.GetStatus();
|
|
221
|
-
if (status != TransactionStatus.Committed || finalStatus != TransactionStatus.Committed)
|
|
222
|
-
throw new InvalidOperationException($"The temporary-isolate commit returned {status}; current transaction status is {finalStatus}." + failureGuard.DescribeErrors());
|
|
223
|
-
return failureGuard.Warnings;
|
|
224
|
-
}
|
|
225
|
-
catch (Exception ex)
|
|
226
|
-
{
|
|
227
|
-
throw new InvalidOperationException($"Temporary isolate in view '{view.Name}' failed: {ex.Message} {FailureGuard.RollBackAndDescribe(transaction)}", ex);
|
|
228
|
-
}
|
|
227
|
+
var status = transaction.Commit();
|
|
228
|
+
var finalStatus = transaction.GetStatus();
|
|
229
|
+
if (status != TransactionStatus.Committed || finalStatus != TransactionStatus.Committed)
|
|
230
|
+
throw new InvalidOperationException($"The temporary-isolate commit returned {status}; current transaction status is {finalStatus}." + failureGuard.DescribeErrors());
|
|
231
|
+
return failureGuard.Warnings;
|
|
232
|
+
}
|
|
233
|
+
catch (Exception ex)
|
|
234
|
+
{
|
|
235
|
+
throw new InvalidOperationException($"Temporary isolate in view '{view.Name}' failed: {ex.Message} {FailureGuard.RollBackAndDescribe(transaction)}", ex);
|
|
236
|
+
}
|
|
229
237
|
}
|
|
230
238
|
}
|
|
231
239
|
}
|