Skip to content

Scene object model, and split into Model / Engine / React #1

Description

@einari

Context

What was scoped as a single "Screen" project (component structure, base
types, rendering engine, React runtime, and the editor) actually bundles
four different lifecycles into one. Chronicle/Arc/Components/AuthProxy are
already separate for exactly this reason — they change at different rates
and some need to be swappable. The UI editor also doesn't belong here at
all (see Studio's architecture issue) — Scene owns the technical/runtime
side only.

Stage needs Scene to expose an object model representing screens,
layouts, forms, contribution points, ui profiles, and themes — this is
what Screenplay's parsed language constructs get hooked up to at runtime
(see the Stage issues), and what Studio's design-time tooling reads and
writes.

Proposal

Split into three projects, following the existing family's separation
pattern:

Scene.Model    — component structure, base types, layout primitives,
                 contribution points, ui profile, theme. Platform-agnostic.
                 This is the object model Screenplay's `screen` / `layout`
                 / `form` constructs compile into, and what both Stage and
                 Studio consume.

Scene.Engine   — runtime that walks a Scene graph, resolves bindings
                 (data / action / form / navigation contribution), and
                 drives whichever renderer is plugged in. Does not know
                 React exists — same separation Chronicle keeps between
                 the event model and its storage backends.

Scene.React    — one renderer implementing Scene.Model's abstract
                 vocabulary (the `core` package) against real React/DOM.
                 Sibling position reserved for future Scene.Native,
                 Scene.Desktop — not special-cased.

Vendor/internal component packages (PrimeReact, internal widget library)
are adapters plugging into Scene.React's renderer contract — they
extend it, they are not part of it. This preserves the package
priority/shadowing story from the ui profile design.

Open questions

  • Exact shape of the object model API surface Stage needs vs. what Studio
    needs — likely overlapping but not identical (Stage wants to execute
    it, Studio wants to edit it). Worth defining as two read/write
    contracts over the same Scene.Model types rather than two models.

Dependencies

Root of the screen work alongside Cratis/Screenplay#94. Every other Scene issue, the Stage seam, and Studio's editor all sit on this split.

Blocks

The open question above — one object model with two read/write contracts for Stage (execute) and Studio (edit) — is answered jointly with Cratis/Stage#37 and Cratis/StudioIssues#156.

Part of the screen work — build order and full dependency map: #7


Decisions taken — build against these

1. Scene is a dual-stack repo: C# contracts with a TypeScript mirror.

  • Scene.Model is C# — the source of truth. Stage references it directly as a project/package reference and constructs instances (Hookup Screenplay with Scene Stage#37), getting compile-time typing across the seam.
  • A TypeScript mirror of those records is what Scene.Engine and Scene.React consume in the browser.
  • Scene.Engine and Scene.React are TypeScript. The engine resolves bindings client-side; it is not a server-driven UI.

This follows the dual-stack shape framework.md describes for Arc and Fundamentals: Source/DotNET + Source/JavaScript.

2. The TypeScript mirror is not free. Arc's proxy generator is driven by [Command] / [ReadModel] discovery in Arc.Core.Generators; there is no [GenerateProxy]-style attribute that would emit TypeScript for a standalone object model. Decide in this issue between a hand-maintained mirror guarded by a parity spec that fails when the two shapes diverge (recommended — cheapest, no new generator to own) and a small dedicated generator. Do not assume Arc's generator covers it.

Repo state — this is greenfield

Cratis/Scene contains only LICENSE and README.md. It is not cloned locally; gh repo clone Cratis/Scene /Volumes/Code/Cratis/Scene first. This issue bootstraps the whole repository, so copy the conventions rather than inventing them — take Directory.Build.props, Directory.Packages.props, .editorconfig, .globalconfig, .github/workflows, .ai/, AGENTS.md and the .slnx shape from Cratis/Stage or Cratis/Screenplay, which are the closest siblings.

Suggested layout, to confirm as part of the work:

Scene.slnx
Source/DotNET/Model/Model.csproj          → Cratis.Scene.Model
Source/JavaScript/model/                  → @cratis/scene.model   (mirror)
Source/JavaScript/engine/                 → @cratis/scene.engine
Source/JavaScript/react/                  → @cratis/scene.react
Documentation/

Profile: framework. Scene builds Cratis.* / @cratis/* packages, so .ai/rules/framework.md applies — no vertical slices, no [Command]/[ReadModel] artifacts, no MVVM app components in Scene source.

Metadata

Metadata

Assignees

No one assigned

    Labels

    screenScreen/UI work: Screenplay UI constructs, Scene runtime, Stage rendering, Studio designers

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions