refactor(scripts): scoped parent query + parent picker for multi-parent templates

Two caveats from the script-scope rollout addressed:

1. ITemplateEngineRepository.GetTemplatesComposingAsync — a scoped
   query that returns only the templates referencing a given template
   via Compositions, eager-loaded with their Attributes / Scripts /
   Compositions. Replaces the GetAllTemplatesAsync + filter pattern
   in TemplateEdit so the Monaco metadata fetch doesn't pull the
   entire template catalog to find one parent.

2. Multi-parent picker. The previous implementation suppressed Parent
   assistance entirely when more than one template composes the open
   one. Now TemplateEdit collects every parent into _editorParents
   and renders a small `select` above the script editor when there
   are >1, letting the user choose which parent's metadata drives
   Parent.Attributes / Parent.CallScript completion + diagnostics.
   Single-parent templates skip the picker (no UI change). Zero
   parents (root template) hide the picker and surface no Parent
   assistance.

Browser-verified on the Sensor Module template (composed by both Pump
and Variable Speed Motor): picker shows both options, switching
updates the editor's parent metadata immediately via the existing
GetContext callback.

Test counts unchanged (159 / 199); the new repo method is exercised
end-to-end by the parent-picker browser path.
This commit is contained in:
Joseph Doherty
2026-05-12 06:00:02 -04:00
parent 0b24b4537d
commit 0139c9ca83
3 changed files with 67 additions and 13 deletions

View File

@@ -10,6 +10,13 @@ public interface ITemplateEngineRepository
Task<Template?> GetTemplateByIdAsync(int id, CancellationToken cancellationToken = default);
Task<Template?> GetTemplateWithChildrenAsync(int id, CancellationToken cancellationToken = default);
Task<IReadOnlyList<Template>> GetAllTemplatesAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Returns every template that contains a composition referencing
/// <paramref name="composedTemplateId"/>. Each result is eager-loaded with
/// its Attributes / Scripts / Compositions so the caller can build a
/// CompositionContext without a follow-up round-trip per parent.
/// </summary>
Task<IReadOnlyList<Template>> GetTemplatesComposingAsync(int composedTemplateId, CancellationToken cancellationToken = default);
Task AddTemplateAsync(Template template, CancellationToken cancellationToken = default);
Task UpdateTemplateAsync(Template template, CancellationToken cancellationToken = default);
Task DeleteTemplateAsync(int id, CancellationToken cancellationToken = default);