diff --git a/Source/DotNET/Model/Profiles/UiProfile.cs b/Source/DotNET/Model/Profiles/UiProfile.cs index 68da08c..fb7e6b2 100644 --- a/Source/DotNET/Model/Profiles/UiProfile.cs +++ b/Source/DotNET/Model/Profiles/UiProfile.cs @@ -6,13 +6,36 @@ namespace Cratis.Scene.Model.Profiles; /// -/// A named target: a platform, a default size class, and an ordered list of component packages. Declaration -/// order in is override priority — a later package shadows an earlier one when both -/// resolve the same bare component name. core is always the final fallback, so a minimum vocabulary -/// resolves regardless of which packages a profile lists. +/// A named target: a platform, a default size class, an ordered list of component packages, and the +/// application shell and visual theme it selects. Declaration order in is override +/// priority - a later package shadows an earlier one when both resolve the same bare component name. +/// core is always the final fallback, so a minimum vocabulary resolves regardless of which packages +/// a profile lists. /// /// The profile's name. /// The platform this profile targets (e.g. web, ios, android, desktop). /// The component packages this profile draws from, in override-priority order. /// The size class assumed when the renderer cannot otherwise determine one. -public record UiProfile(string Name, string TargetPlatform, IReadOnlyList Packages, SizeClass? DefaultSizeClass = null); +/// +/// The name of the this profile renders inside - the application's base +/// navigational shell, normally provided by a in +/// . when the profile does not select one. +/// +/// +/// The name of the this profile applies, or when it selects +/// none. A theme is only meaningful relative to a set of packages, which is why it is chosen here rather +/// than by a screen. +/// +/// +/// A profile is where the choices that make a description concrete are made: which component vocabulary, +/// which shell, which theme. Deliberately none of them are stated by a - that +/// is what keeps a screen portable across targets, and what lets one application ship a different shell to +/// the web than to a phone. +/// +public record UiProfile( + string Name, + string TargetPlatform, + IReadOnlyList Packages, + SizeClass? DefaultSizeClass = null, + string? Layout = null, + string? Theme = null); diff --git a/Source/JavaScript/model/profiles/UiProfile.ts b/Source/JavaScript/model/profiles/UiProfile.ts index 0de53ba..4904acc 100644 --- a/Source/JavaScript/model/profiles/UiProfile.ts +++ b/Source/JavaScript/model/profiles/UiProfile.ts @@ -4,16 +4,55 @@ import { SizeClass } from '../sizeClasses'; /** - * A named target: a platform, a default size class, and an ordered list of component packages. - * Declaration order in `packages` is override priority — a later package shadows an earlier one when - * both resolve the same bare component name. `core` is always the final fallback, so a minimum - * vocabulary resolves regardless of which packages a profile lists. + * A named target: a platform, a default size class, an ordered list of component packages, and the + * application shell and visual theme it selects. Declaration order in `packages` is override priority — a + * later package shadows an earlier one when both resolve the same bare component name. `core` is always + * the final fallback, so a minimum vocabulary resolves regardless of which packages a profile lists. + * + * A profile is where the choices that make a description concrete are made: which component vocabulary, + * which shell, which theme. Deliberately none of them are stated by a {@link Screen} — that is what keeps + * a screen portable across targets, and what lets one application ship a different shell to the web than + * to a phone. */ export interface UiProfile { + /** + * The profile's name. + */ name: string; + + /** + * The platform this profile targets (e.g. `web`, `ios`, `android`, `desktop`). + */ targetPlatform: string; + + /** + * The component packages this profile draws from, in override-priority order. + */ packages: string[]; + + /** + * The size class assumed when the renderer cannot otherwise determine one. + */ defaultSizeClass?: SizeClass; + + /** + * The name of the {@link Layout} this profile renders inside — the application's base navigational + * shell, normally provided by a blueprint in `packages`. Absent when the profile does not select one. + */ + layout?: string; + + /** + * The name of the {@link Theme} this profile applies, or absent when it selects none. A theme is only + * meaningful relative to a set of packages, which is why it is chosen here rather than by a screen. + */ + theme?: string; } -export const UiProfilePropertyNames: (keyof UiProfile)[] = ['name', 'targetPlatform', 'packages', 'defaultSizeClass']; +export const UiProfilePropertyNames: (keyof UiProfile)[] = [ + 'name', + 'targetPlatform', + 'packages', + 'defaultSizeClass', + 'layout', + 'theme', +]; diff --git a/scene-model-shape.json b/scene-model-shape.json index 7b9dab9..1e1a988 100644 --- a/scene-model-shape.json +++ b/scene-model-shape.json @@ -179,7 +179,9 @@ "name", "targetPlatform", "packages", - "defaultSizeClass" + "defaultSizeClass", + "layout", + "theme" ], "Theme": [ "name",