Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions AdaptiveRemote.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions backend.slnf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"solution": {
"path": "AdaptiveRemote.sln",
"projects": [
"src\\AdaptiveRemote.Contracts\\AdaptiveRemote.Contracts.csproj"
]
}
}
19 changes: 19 additions & 0 deletions client.slnf
Original file line number Diff line number Diff line change
@@ -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"
]
}
}
4 changes: 4 additions & 0 deletions src/AdaptiveRemote.App/AdaptiveRemote.App.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
<InternalsVisibleTo Include="AdaptiveRemote.EndtoEndTests.TestServices" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\AdaptiveRemote.Contracts\AdaptiveRemote.Contracts.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Azure.Identity" />
<PackageReference Include="Azure.Monitor.OpenTelemetry.Exporter" />
Expand Down
10 changes: 10 additions & 0 deletions src/AdaptiveRemote.Contracts/AdaptiveRemote.Contracts.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<RootNamespace>AdaptiveRemote.Contracts</RootNamespace>
</PropertyGroup>

</Project>
9 changes: 9 additions & 0 deletions src/AdaptiveRemote.Contracts/CommandType.cs
Original file line number Diff line number Diff line change
@@ -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).
// 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 }
14 changes: 14 additions & 0 deletions src/AdaptiveRemote.Contracts/ICommandProperties.cs
Original file line number Diff line number Diff line change
@@ -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; }
}
14 changes: 14 additions & 0 deletions src/AdaptiveRemote.Contracts/LayoutContractsJsonContext.cs
Original file line number Diff line number Diff line change
@@ -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<RawLayout>))]
[JsonSerializable(typeof(IReadOnlyList<CompiledLayout>))]
public partial class LayoutContractsJsonContext : JsonSerializerContext { }
53 changes: 53 additions & 0 deletions src/AdaptiveRemote.Contracts/LayoutElementDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
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.
// ---------------------------------------------------------------------------

// "$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);

// 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<LayoutElementDto> 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<LayoutElementDto> Elements,
string CssDefinitions, // global CSS for the layout grid
DateTimeOffset CompiledAt
);
11 changes: 11 additions & 0 deletions src/AdaptiveRemote.Contracts/PreviewLayout.cs
Original file line number Diff line number Diff line change
@@ -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
);
64 changes: 64 additions & 0 deletions src/AdaptiveRemote.Contracts/RawLayoutElementDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
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.
// ---------------------------------------------------------------------------

// "$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(
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<RawLayoutElementDto> Children,
int GridRow,
int GridColumn,
int GridRowSpan = 1,
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<RawLayoutElementDto> Elements,
int Version,
DateTimeOffset CreatedAt,
DateTimeOffset UpdatedAt,
ValidationResult? ValidationResult // written by LayoutProcessingService via IRawLayoutStatusWriter
);
5 changes: 5 additions & 0 deletions src/AdaptiveRemote.Contracts/ValidationResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace AdaptiveRemote.Contracts;

public record ValidationIssue(string Code, string Message, string? Path);

public record ValidationResult(bool IsValid, IReadOnlyList<ValidationIssue> Issues);
8 changes: 8 additions & 0 deletions src/_doc_Projects.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down