From 36b8b9d8f13b4bac6c2647fdbdbaeb610c80cfa0 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 7 Apr 2026 22:02:03 +0000 Subject: [PATCH 1/2] ADR-166: Add AdaptiveRemote.Contracts shared library and solution filters - Add AdaptiveRemote.Contracts project (net10.0, no platform-specific dependencies) containing all layout definition DTOs, enums, and LayoutContractsJsonContext from the spec's Shared Contracts section: CommandType enum, ICommandProperties interface, LayoutElementDto hierarchy (compiled), RawLayoutElementDto hierarchy (raw), RawLayout/CompiledLayout/ PreviewLayout top-level records, ValidationIssue/ValidationResult records, and source-generated LayoutContractsJsonContext for consistent serialization across all consumers including Native AOT Lambda functions. - Add AdaptiveRemote.Contracts to AdaptiveRemote.sln with full build configurations. - Reference AdaptiveRemote.Contracts from AdaptiveRemote.App. - Add client.slnf and backend.slnf solution filters. - Update _doc_Projects.md to document the new shared contracts project. https://claude.ai/code/session_01T3tonn7C9F6TYbqH23KmG1 --- AdaptiveRemote.sln | 14 ++++++ backend.slnf | 8 ++++ client.slnf | 19 ++++++++ .../AdaptiveRemote.App.csproj | 4 ++ .../AdaptiveRemote.Contracts.csproj | 10 ++++ src/AdaptiveRemote.Contracts/CommandType.cs | 9 ++++ .../ICommandProperties.cs | 14 ++++++ .../LayoutContractsJsonContext.cs | 14 ++++++ .../LayoutElementDto.cs | 35 ++++++++++++++ src/AdaptiveRemote.Contracts/Layouts.cs | 41 ++++++++++++++++ .../RawLayoutElementDto.cs | 48 +++++++++++++++++++ .../ValidationResult.cs | 5 ++ src/_doc_Projects.md | 8 ++++ 13 files changed, 229 insertions(+) create mode 100644 backend.slnf create mode 100644 client.slnf create mode 100644 src/AdaptiveRemote.Contracts/AdaptiveRemote.Contracts.csproj create mode 100644 src/AdaptiveRemote.Contracts/CommandType.cs create mode 100644 src/AdaptiveRemote.Contracts/ICommandProperties.cs create mode 100644 src/AdaptiveRemote.Contracts/LayoutContractsJsonContext.cs create mode 100644 src/AdaptiveRemote.Contracts/LayoutElementDto.cs create mode 100644 src/AdaptiveRemote.Contracts/Layouts.cs create mode 100644 src/AdaptiveRemote.Contracts/RawLayoutElementDto.cs create mode 100644 src/AdaptiveRemote.Contracts/ValidationResult.cs diff --git a/AdaptiveRemote.sln b/AdaptiveRemote.sln index a63a24a4..c2ff52d3 100644 --- a/AdaptiveRemote.sln +++ b/AdaptiveRemote.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 18.0.11217.181 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AdaptiveRemote.App", "src\AdaptiveRemote.App\AdaptiveRemote.App.csproj", "{6C7C380B-D7A4-412E-8487-2AFC89EA802F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AdaptiveRemote.Contracts", "src\AdaptiveRemote.Contracts\AdaptiveRemote.Contracts.csproj", "{F81FEF3B-DB7A-4C04-9DF0-72E98382097A}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AdaptiveRemote", "src\AdaptiveRemote\AdaptiveRemote.csproj", "{7BE31162-0D09-4F80-8CE5-978F7AECC1EF}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AdaptiveRemote.Console", "src\AdaptiveRemote.Console\AdaptiveRemote.Console.csproj", "{345B73FC-07F9-490F-B566-2677D10B1834}" @@ -188,6 +190,18 @@ Global {54522D5A-CEB3-F5B9-2654-1005EF1C3262}.Release|x64.Build.0 = Release|Any CPU {54522D5A-CEB3-F5B9-2654-1005EF1C3262}.Release|x86.ActiveCfg = Release|Any CPU {54522D5A-CEB3-F5B9-2654-1005EF1C3262}.Release|x86.Build.0 = Release|Any CPU + {F81FEF3B-DB7A-4C04-9DF0-72E98382097A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F81FEF3B-DB7A-4C04-9DF0-72E98382097A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F81FEF3B-DB7A-4C04-9DF0-72E98382097A}.Debug|x64.ActiveCfg = Debug|Any CPU + {F81FEF3B-DB7A-4C04-9DF0-72E98382097A}.Debug|x64.Build.0 = Debug|Any CPU + {F81FEF3B-DB7A-4C04-9DF0-72E98382097A}.Debug|x86.ActiveCfg = Debug|Any CPU + {F81FEF3B-DB7A-4C04-9DF0-72E98382097A}.Debug|x86.Build.0 = Debug|Any CPU + {F81FEF3B-DB7A-4C04-9DF0-72E98382097A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F81FEF3B-DB7A-4C04-9DF0-72E98382097A}.Release|Any CPU.Build.0 = Release|Any CPU + {F81FEF3B-DB7A-4C04-9DF0-72E98382097A}.Release|x64.ActiveCfg = Release|Any CPU + {F81FEF3B-DB7A-4C04-9DF0-72E98382097A}.Release|x64.Build.0 = Release|Any CPU + {F81FEF3B-DB7A-4C04-9DF0-72E98382097A}.Release|x86.ActiveCfg = Release|Any CPU + {F81FEF3B-DB7A-4C04-9DF0-72E98382097A}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/backend.slnf b/backend.slnf new file mode 100644 index 00000000..bda5c53b --- /dev/null +++ b/backend.slnf @@ -0,0 +1,8 @@ +{ + "solution": { + "path": "AdaptiveRemote.sln", + "projects": [ + "src\\AdaptiveRemote.Contracts\\AdaptiveRemote.Contracts.csproj" + ] + } +} diff --git a/client.slnf b/client.slnf new file mode 100644 index 00000000..1991596c --- /dev/null +++ b/client.slnf @@ -0,0 +1,19 @@ +{ + "solution": { + "path": "AdaptiveRemote.sln", + "projects": [ + "src\\AdaptiveRemote.Contracts\\AdaptiveRemote.Contracts.csproj", + "src\\AdaptiveRemote.App\\AdaptiveRemote.App.csproj", + "src\\AdaptiveRemote\\AdaptiveRemote.csproj", + "src\\AdaptiveRemote.Console\\AdaptiveRemote.Console.csproj", + "src\\AdaptiveRemote.Headless\\AdaptiveRemote.Headless.csproj", + "test\\AdaptiveRemote.App.Tests\\AdaptiveRemote.App.Tests.csproj", + "test\\AdaptiveRemote.Speech.Tests\\AdaptiveRemote.Speech.Tests.csproj", + "test\\AdaptiveRemote.EndtoEndTests.TestServices\\AdaptiveRemote.EndtoEndTests.TestServices.csproj", + "test\\AdaptiveRemote.EndToEndTests.Steps\\AdaptiveRemote.EndToEndTests.Steps.csproj", + "test\\AdaptiveRemote.EndToEndTests.Host.Headless\\AdaptiveRemote.EndToEndTests.Host.Headless.csproj", + "test\\AdaptiveRemote.EndToEndTests.Host.Wpf\\AdaptiveRemote.EndToEndTests.Host.Wpf.csproj", + "test\\AdaptiveRemote.EndtoEndTests.Host.Console\\AdaptiveRemote.EndToEndTests.Host.Console.csproj" + ] + } +} diff --git a/src/AdaptiveRemote.App/AdaptiveRemote.App.csproj b/src/AdaptiveRemote.App/AdaptiveRemote.App.csproj index 396f43ff..e104217b 100644 --- a/src/AdaptiveRemote.App/AdaptiveRemote.App.csproj +++ b/src/AdaptiveRemote.App/AdaptiveRemote.App.csproj @@ -12,6 +12,10 @@ + + + + diff --git a/src/AdaptiveRemote.Contracts/AdaptiveRemote.Contracts.csproj b/src/AdaptiveRemote.Contracts/AdaptiveRemote.Contracts.csproj new file mode 100644 index 00000000..d804afe1 --- /dev/null +++ b/src/AdaptiveRemote.Contracts/AdaptiveRemote.Contracts.csproj @@ -0,0 +1,10 @@ + + + + net10.0 + enable + enable + AdaptiveRemote.Contracts + + + diff --git a/src/AdaptiveRemote.Contracts/CommandType.cs b/src/AdaptiveRemote.Contracts/CommandType.cs new file mode 100644 index 00000000..3f0e3e92 --- /dev/null +++ b/src/AdaptiveRemote.Contracts/CommandType.cs @@ -0,0 +1,9 @@ +namespace AdaptiveRemote.Contracts; + +// Identifies the runtime command type. The client uses this to instantiate the correct +// App runtime type (TiVoCommand, IRCommand, LifecycleCommand, ActionCommand). +// Type-specific execution parameters are resolved by the client from its own configuration: +// TiVo — CommandId = Name.ToUpperInvariant() (existing convention) +// IR — payload programmed via remote, stored in ProgrammaticSettings +// Others — keyed by Name +public enum CommandType { Lifecycle, TiVo, IR, Action } diff --git a/src/AdaptiveRemote.Contracts/ICommandProperties.cs b/src/AdaptiveRemote.Contracts/ICommandProperties.cs new file mode 100644 index 00000000..51d7f273 --- /dev/null +++ b/src/AdaptiveRemote.Contracts/ICommandProperties.cs @@ -0,0 +1,14 @@ +namespace AdaptiveRemote.Contracts; + +// Shared behavioral interface — prevents drift between the compiled and raw command types. +// Adding a new behavioral property means updating this interface first; the compiler +// will flag any implementing record that doesn't follow. +public interface ICommandProperties +{ + CommandType Type { get; } + string Name { get; } + string Label { get; } + string? Glyph { get; } + string SpeakPhrase { get; } + string? Reverse { get; } +} diff --git a/src/AdaptiveRemote.Contracts/LayoutContractsJsonContext.cs b/src/AdaptiveRemote.Contracts/LayoutContractsJsonContext.cs new file mode 100644 index 00000000..13684759 --- /dev/null +++ b/src/AdaptiveRemote.Contracts/LayoutContractsJsonContext.cs @@ -0,0 +1,14 @@ +using System.Text.Json.Serialization; + +namespace AdaptiveRemote.Contracts; + +// Source-generated JSON context — required for Native AOT Lambda functions; +// shared by all consumers to ensure consistent serialization behaviour. +[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)] +[JsonSerializable(typeof(RawLayout))] +[JsonSerializable(typeof(CompiledLayout))] +[JsonSerializable(typeof(PreviewLayout))] +[JsonSerializable(typeof(ValidationResult))] +[JsonSerializable(typeof(IReadOnlyList))] +[JsonSerializable(typeof(IReadOnlyList))] +public partial class LayoutContractsJsonContext : JsonSerializerContext { } diff --git a/src/AdaptiveRemote.Contracts/LayoutElementDto.cs b/src/AdaptiveRemote.Contracts/LayoutElementDto.cs new file mode 100644 index 00000000..d9e2651a --- /dev/null +++ b/src/AdaptiveRemote.Contracts/LayoutElementDto.cs @@ -0,0 +1,35 @@ +using System.Text.Json.Serialization; + +namespace AdaptiveRemote.Contracts; + +// --------------------------------------------------------------------------- +// Compiled layout element DTOs +// Used in CompiledLayout.Elements. Deserialized directly by the client application. +// Contains only behavioral properties — grid positions and CSS overrides have been +// compiled into CssDefinitions and are not needed by the client. +// --------------------------------------------------------------------------- + +[JsonPolymorphic(TypeDiscriminatorPropertyName = "type")] +[JsonDerivedType(typeof(CommandDefinitionDto), "command")] +[JsonDerivedType(typeof(LayoutGroupDefinitionDto), "group")] +public abstract record LayoutElementDto(string CssId); + +// Maps to AdaptiveRemote.App.Models.Command at layout-apply time (client epic). +// Type carries the CommandType discriminator so the client knows which runtime type to instantiate. +// No subtype hierarchy is used — all behavioral properties are flat; type-specific execution +// parameters are resolved by the client from its own configuration (see CommandType above). +public record CommandDefinitionDto( + CommandType Type, + string Name, + string Label, + string? Glyph, + string SpeakPhrase, + string? Reverse, + string CssId +) : LayoutElementDto(CssId), ICommandProperties; + +// Maps to AdaptiveRemote.App.Models.LayoutGroup at layout-apply time (client epic). +public record LayoutGroupDefinitionDto( + string CssId, + IReadOnlyList Children +) : LayoutElementDto(CssId); diff --git a/src/AdaptiveRemote.Contracts/Layouts.cs b/src/AdaptiveRemote.Contracts/Layouts.cs new file mode 100644 index 00000000..912819ca --- /dev/null +++ b/src/AdaptiveRemote.Contracts/Layouts.cs @@ -0,0 +1,41 @@ +namespace AdaptiveRemote.Contracts; + +// --------------------------------------------------------------------------- +// Top-level layout records +// --------------------------------------------------------------------------- + +// Administrator-editable source format. Elements are typed; no opaque JSON string. +public record RawLayout( + Guid Id, + string UserId, + string Name, + IReadOnlyList Elements, + int Version, + DateTimeOffset CreatedAt, + DateTimeOffset UpdatedAt, + ValidationResult? ValidationResult // written by LayoutProcessingService via IRawLayoutStatusWriter +); + +// Client-consumable format produced by LayoutCompilerService. +// Deserialized directly by the client application — no intermediate parsing model needed. +// The client maps Elements → runtime Command objects at layout-apply time (client epic). +public record CompiledLayout( + Guid Id, + Guid RawLayoutId, + string UserId, + bool IsActive, + int Version, + IReadOnlyList Elements, + string CssDefinitions, // global CSS for the layout grid + DateTimeOffset CompiledAt +); + +// Editor-consumable preview format, produced by LayoutCompilerService. +public record PreviewLayout( + Guid RawLayoutId, + int Version, + string RenderedHtml, + string RenderedCss, + DateTimeOffset CompiledAt, + ValidationResult ValidationResult +); diff --git a/src/AdaptiveRemote.Contracts/RawLayoutElementDto.cs b/src/AdaptiveRemote.Contracts/RawLayoutElementDto.cs new file mode 100644 index 00000000..7da5bf77 --- /dev/null +++ b/src/AdaptiveRemote.Contracts/RawLayoutElementDto.cs @@ -0,0 +1,48 @@ +using System.Text.Json.Serialization; + +namespace AdaptiveRemote.Contracts; + +// --------------------------------------------------------------------------- +// Raw layout element DTOs +// Shared between the editor application (serialization) and LayoutCompilerService +// (deserialization). Extends behavioral properties with authoring properties that +// the compiler resolves into CssDefinitions and strips from the compiled output. +// --------------------------------------------------------------------------- + +[JsonPolymorphic(TypeDiscriminatorPropertyName = "type")] +[JsonDerivedType(typeof(RawCommandDefinitionDto), "command")] +[JsonDerivedType(typeof(RawLayoutGroupDefinitionDto), "group")] +public abstract record RawLayoutElementDto( + string CssId, + int GridRow, + int GridColumn, + int GridRowSpan = 1, + int GridColumnSpan = 1, + string? AdditionalCss = null // per-element CSS overrides (e.g. red background for Power) +); + +public record RawCommandDefinitionDto( + CommandType Type, + string Name, + string Label, + string? Glyph, + string SpeakPhrase, + string? Reverse, + string CssId, + int GridRow, + int GridColumn, + int GridRowSpan = 1, + int GridColumnSpan = 1, + string? AdditionalCss = null +) : RawLayoutElementDto(CssId, GridRow, GridColumn, GridRowSpan, GridColumnSpan, AdditionalCss), + ICommandProperties; + +public record RawLayoutGroupDefinitionDto( + string CssId, + IReadOnlyList Children, + int GridRow, + int GridColumn, + int GridRowSpan = 1, + int GridColumnSpan = 1, + string? AdditionalCss = null +) : RawLayoutElementDto(CssId, GridRow, GridColumn, GridRowSpan, GridColumnSpan, AdditionalCss); diff --git a/src/AdaptiveRemote.Contracts/ValidationResult.cs b/src/AdaptiveRemote.Contracts/ValidationResult.cs new file mode 100644 index 00000000..81c3e7b5 --- /dev/null +++ b/src/AdaptiveRemote.Contracts/ValidationResult.cs @@ -0,0 +1,5 @@ +namespace AdaptiveRemote.Contracts; + +public record ValidationIssue(string Code, string Message, string? Path); + +public record ValidationResult(bool IsValid, IReadOnlyList Issues); diff --git a/src/_doc_Projects.md b/src/_doc_Projects.md index b786872f..e81f19de 100644 --- a/src/_doc_Projects.md +++ b/src/_doc_Projects.md @@ -33,6 +33,14 @@ This document describes the high-level organization of the AdaptiveRemote reposi - Minimal code to launch the WPF app with console logging. - No business logic or features. +### AdaptiveRemote.Contracts +- **Purpose:** Shared class library containing layout definition DTOs, enums, interfaces, and the source-generated `LayoutContractsJsonContext` used by both the client application and backend services. +- **Guidance:** _No platform-specific dependencies._ Targets `net10.0` only. Contains pure data types (records, enums, interfaces) with no behavior. +- **Boundaries:** + - No WPF, Windows APIs, or Blazor dependencies. + - No MVVM or runtime behavior — DTOs only. + - Included in both `client.slnf` and `backend.slnf`. + ## Test Projects ### AdaptiveRemote.App.Tests From 46ebd1db9732e5c7527f80520a4fe03c0fb3ae30 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 7 Apr 2026 23:36:39 +0000 Subject: [PATCH 2/2] Address PR review comments - Remove Action from CommandType enum (it's a WPF adapter, not a command type) - Change type discriminator from "type" to "\$type" on both LayoutElementDto and RawLayoutElementDto to avoid conflict with the behavioral Type property on CommandDefinitionDto and RawCommandDefinitionDto - Move RawLayout into RawLayoutElementDto.cs, CompiledLayout into LayoutElementDto.cs, PreviewLayout into its own PreviewLayout.cs; delete Layouts.cs https://claude.ai/code/session_01T3tonn7C9F6TYbqH23KmG1 --- src/AdaptiveRemote.Contracts/CommandType.cs | 4 +- .../LayoutElementDto.cs | 20 ++++++++- src/AdaptiveRemote.Contracts/Layouts.cs | 41 ------------------- src/AdaptiveRemote.Contracts/PreviewLayout.cs | 11 +++++ .../RawLayoutElementDto.cs | 18 +++++++- 5 files changed, 49 insertions(+), 45 deletions(-) delete mode 100644 src/AdaptiveRemote.Contracts/Layouts.cs create mode 100644 src/AdaptiveRemote.Contracts/PreviewLayout.cs diff --git a/src/AdaptiveRemote.Contracts/CommandType.cs b/src/AdaptiveRemote.Contracts/CommandType.cs index 3f0e3e92..5ea8a4f9 100644 --- a/src/AdaptiveRemote.Contracts/CommandType.cs +++ b/src/AdaptiveRemote.Contracts/CommandType.cs @@ -1,9 +1,9 @@ namespace AdaptiveRemote.Contracts; // Identifies the runtime command type. The client uses this to instantiate the correct -// App runtime type (TiVoCommand, IRCommand, LifecycleCommand, ActionCommand). +// App runtime type (TiVoCommand, IRCommand, LifecycleCommand). // Type-specific execution parameters are resolved by the client from its own configuration: // TiVo — CommandId = Name.ToUpperInvariant() (existing convention) // IR — payload programmed via remote, stored in ProgrammaticSettings // Others — keyed by Name -public enum CommandType { Lifecycle, TiVo, IR, Action } +public enum CommandType { Lifecycle, TiVo, IR } diff --git a/src/AdaptiveRemote.Contracts/LayoutElementDto.cs b/src/AdaptiveRemote.Contracts/LayoutElementDto.cs index d9e2651a..92cf7d51 100644 --- a/src/AdaptiveRemote.Contracts/LayoutElementDto.cs +++ b/src/AdaptiveRemote.Contracts/LayoutElementDto.cs @@ -9,7 +9,8 @@ namespace AdaptiveRemote.Contracts; // compiled into CssDefinitions and are not needed by the client. // --------------------------------------------------------------------------- -[JsonPolymorphic(TypeDiscriminatorPropertyName = "type")] +// "$type" avoids conflict with the behavioral Type property on CommandDefinitionDto. +[JsonPolymorphic(TypeDiscriminatorPropertyName = "$type")] [JsonDerivedType(typeof(CommandDefinitionDto), "command")] [JsonDerivedType(typeof(LayoutGroupDefinitionDto), "group")] public abstract record LayoutElementDto(string CssId); @@ -33,3 +34,20 @@ public record LayoutGroupDefinitionDto( string CssId, IReadOnlyList Children ) : LayoutElementDto(CssId); + +// --------------------------------------------------------------------------- +// Client-consumable format produced by LayoutCompilerService. +// Deserialized directly by the client application — no intermediate parsing model needed. +// The client maps Elements → runtime Command objects at layout-apply time (client epic). +// --------------------------------------------------------------------------- + +public record CompiledLayout( + Guid Id, + Guid RawLayoutId, + string UserId, + bool IsActive, + int Version, + IReadOnlyList Elements, + string CssDefinitions, // global CSS for the layout grid + DateTimeOffset CompiledAt +); diff --git a/src/AdaptiveRemote.Contracts/Layouts.cs b/src/AdaptiveRemote.Contracts/Layouts.cs deleted file mode 100644 index 912819ca..00000000 --- a/src/AdaptiveRemote.Contracts/Layouts.cs +++ /dev/null @@ -1,41 +0,0 @@ -namespace AdaptiveRemote.Contracts; - -// --------------------------------------------------------------------------- -// Top-level layout records -// --------------------------------------------------------------------------- - -// Administrator-editable source format. Elements are typed; no opaque JSON string. -public record RawLayout( - Guid Id, - string UserId, - string Name, - IReadOnlyList Elements, - int Version, - DateTimeOffset CreatedAt, - DateTimeOffset UpdatedAt, - ValidationResult? ValidationResult // written by LayoutProcessingService via IRawLayoutStatusWriter -); - -// Client-consumable format produced by LayoutCompilerService. -// Deserialized directly by the client application — no intermediate parsing model needed. -// The client maps Elements → runtime Command objects at layout-apply time (client epic). -public record CompiledLayout( - Guid Id, - Guid RawLayoutId, - string UserId, - bool IsActive, - int Version, - IReadOnlyList Elements, - string CssDefinitions, // global CSS for the layout grid - DateTimeOffset CompiledAt -); - -// Editor-consumable preview format, produced by LayoutCompilerService. -public record PreviewLayout( - Guid RawLayoutId, - int Version, - string RenderedHtml, - string RenderedCss, - DateTimeOffset CompiledAt, - ValidationResult ValidationResult -); diff --git a/src/AdaptiveRemote.Contracts/PreviewLayout.cs b/src/AdaptiveRemote.Contracts/PreviewLayout.cs new file mode 100644 index 00000000..2e968d29 --- /dev/null +++ b/src/AdaptiveRemote.Contracts/PreviewLayout.cs @@ -0,0 +1,11 @@ +namespace AdaptiveRemote.Contracts; + +// Editor-consumable preview format, produced by LayoutCompilerService. +public record PreviewLayout( + Guid RawLayoutId, + int Version, + string RenderedHtml, + string RenderedCss, + DateTimeOffset CompiledAt, + ValidationResult ValidationResult +); diff --git a/src/AdaptiveRemote.Contracts/RawLayoutElementDto.cs b/src/AdaptiveRemote.Contracts/RawLayoutElementDto.cs index 7da5bf77..3bf30c8e 100644 --- a/src/AdaptiveRemote.Contracts/RawLayoutElementDto.cs +++ b/src/AdaptiveRemote.Contracts/RawLayoutElementDto.cs @@ -9,7 +9,8 @@ namespace AdaptiveRemote.Contracts; // the compiler resolves into CssDefinitions and strips from the compiled output. // --------------------------------------------------------------------------- -[JsonPolymorphic(TypeDiscriminatorPropertyName = "type")] +// "$type" avoids conflict with the behavioral Type property on RawCommandDefinitionDto. +[JsonPolymorphic(TypeDiscriminatorPropertyName = "$type")] [JsonDerivedType(typeof(RawCommandDefinitionDto), "command")] [JsonDerivedType(typeof(RawLayoutGroupDefinitionDto), "group")] public abstract record RawLayoutElementDto( @@ -46,3 +47,18 @@ public record RawLayoutGroupDefinitionDto( int GridColumnSpan = 1, string? AdditionalCss = null ) : RawLayoutElementDto(CssId, GridRow, GridColumn, GridRowSpan, GridColumnSpan, AdditionalCss); + +// --------------------------------------------------------------------------- +// Administrator-editable source format. Elements are typed; no opaque JSON string. +// --------------------------------------------------------------------------- + +public record RawLayout( + Guid Id, + string UserId, + string Name, + IReadOnlyList Elements, + int Version, + DateTimeOffset CreatedAt, + DateTimeOffset UpdatedAt, + ValidationResult? ValidationResult // written by LayoutProcessingService via IRawLayoutStatusWriter +);