Skip to content

Components 4, and the panel vocabulary Studio's canvas needs - #25

Merged
einari merged 3 commits into
mainfrom
feature/components-4-and-studio-scene
Sep 2, 2026
Merged

Components 4, and the panel vocabulary Studio's canvas needs#25
einari merged 3 commits into
mainfrom
feature/components-4-and-studio-scene

Conversation

@einari

@einari einari commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Added

  • The panel vocabulary a canvas is drawn inCanvas, StackPanel, DockPanel, WrapPanel and Grid on the existing Panel base, with RowDefinition, ColumnDefinition, GridLength, GridUnitType, Orientation and Dock. Studio's prototype canvas models arrangement this way and Scene had no equivalent: a screen could say what an element is, but not that five of them sit in a grid.

    Kept pure, the way Screenplay is — no attributes, no discriminators, no serialization, and Cratis.Scene.Model still has no package dependencies. Attached properties stay in the Properties bag the model already has (Canvas.Left, Grid.Row, Dock), because a model with no attached properties of its own is the right place to keep carrying them that way.

    These render through the existing structural walk — every one satisfies isPanel, so renderElement already reaches them. What they do not yet do is arrange: honouring orientation, spacing, grid tracks and docking is the layout engine's job (Layout engine: flow and freeform arrangement rendering #4). This change is vocabulary, not behavior.

Changed

  • @cratis/components 4. Components 4 owns its own markup, styling and types and no longer sits on PrimeReact, so scene.components and scene.blueprint.components take ^4.1.1 and their peer range moves with it — a breaking change for anyone consuming those two packages.
  • scene.components no longer depends on PrimeReact at all. Its one PrimeReactProvider was in a spec, needed only because Components 3's tooltip bottomed out in a PrimeReact component; Components 4 owns its own. primeicons stays as a Storybook-only devDependency, because the stories demonstrate the consumer-owned icon-font class string that Components 4's Icon still accepts.
  • scene.blueprint.components keeps its PrimeReact peers, and now supplies the provider itself. It composes blueprint.default's shell, which reaches for PrimeReact directly in five places. Until Components 3 that provider came for free because CratisComponentsProvider wrapped PrimeReactProvider; under Components 4 it does not, so the Storybook preview declares both providers rather than relying on hoisting.
  • scene.primereact and scene.blueprint.default are unchanged — they target PrimeReact deliberately.

Fixed

  • SceneTooltip passed a slot straight to Tooltip. Components 4 takes one focusable trigger element, which it clones to attach its own part attributes, and a Scene slot is a list — so it now wraps the slot in a single element rather than failing to type.

Both shape specs cover the new surface: scene-model-shape.json gains 8 types and 3 enums, and the C# spec asserts the model declares exactly those.

The three documented Components codemods scanned 543 files and changed none — Scene already imported explicit subpaths throughout. Note for anyone following the migration guide: they do not run on the current migrator at all. @cratis/components.migrator@4.1.1 fails its own compatibility preflight because the bundled manifest still pins the migrator at 4.0.0 (Cratis/Components#232, which predicted exactly this recurrence). Pinning @cratis/components.migrator@4.0.0 works.

Verified: 8 JavaScript workspaces build, 898 specs pass, lint clean, all five Storybooks build, and .NET is 5 projects / 0 errors / 0 warnings with 37 specs passing.

🤖 Generated with Claude Code

Components 4 owns its own markup, styling and types and drops PrimeReact
from underneath, so the packages built on it stop carrying PrimeReact and
the ones targeting PrimeReact keep it:

- scene.components and scene.blueprint.components take ^4.1.1.
- scene.components drops PrimeReact entirely. Its one PrimeReactProvider
  was in a spec, needed only because Components 3's tooltip bottomed out
  in a PrimeReact component; Components 4's is its own. primeicons stays
  as a devDependency because the stories demonstrate the consumer-owned
  icon-font string Components 4 still accepts.
- scene.blueprint.components keeps the PrimeReact peers. It composes
  blueprint.default's shell, which reaches for PrimeReact directly in
  five places, and CratisComponentsProvider no longer supplies
  PrimeReactProvider on its behalf - so its Storybook preview now
  supplies both providers explicitly rather than relying on hoisting.
- scene.primereact and scene.blueprint.default are unchanged: they target
  PrimeReact deliberately.

SceneTooltip wraps its slot in a single element. Components 4 takes one
focusable trigger, which it clones to attach its own part attributes, and
a Scene slot is a list.

The three documented codemods scanned 543 files and changed none - Scene
already imported explicit subpaths throughout.

Verified: 8 JavaScript workspaces build, 888 specs pass, lint clean, all
five Storybooks build, and .NET is 5 projects / 0 warnings / 37 specs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@einari einari added the major label Sep 2, 2026
Studio's prototype canvas models arrangement with a WPF-style panel set
that Scene had no equivalent for: a screen could say what an element is,
but not that five of them sit in a grid. Scene owns the object model, so
the vocabulary belongs here.

Added to Cratis.Scene.Model and mirrored in @cratis/scene.model:

- Canvas, StackPanel, DockPanel, WrapPanel and Grid, on the existing
  Panel base.
- RowDefinition and ColumnDefinition, with GridLength and GridUnitType.
- Orientation and Dock.

Kept pure, the way Screenplay is. No attributes, no discriminators, no
serialization, and no package dependency - Cratis.Scene.Model still has
none. Where Studio's model reaches for a ConceptAs<double> these use
double, and where it uses a Dimension sentinel for "unbounded" these use
double.PositiveInfinity rather than double.MaxValue.

Attached properties stay in the Properties bag the model already has -
Canvas.Left/Top/Right/Bottom, Grid.Row/Column/RowSpan/ColumnSpan and Dock
- because a model with no attached properties of its own is the right
place to keep carrying them that way.

These render through the existing structural walk: every one satisfies
isPanel, so renderElement already reaches them. What they do not yet do
is arrange - honouring orientation, spacing, grid tracks and docking is
the layout engine's job (#4), which is why this change is vocabulary
rather than behavior.

Both shape specs cover the new surface: the manifest gains 8 types and 3
enums, and the C# spec asserts the model declares exactly those.

Verified: JS build clean, 898 specs pass (up from 888), lint clean, .NET
5 projects / 0 errors / 0 warnings with 37 specs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@einari einari changed the title Move to @cratis/components 4 Components 4, and the panel vocabulary Studio's canvas needs Sep 2, 2026
The panel vocabulary said what an arrangement is; nothing turned it into
one. `renderPanel` emitted a bare `div`, so a grid and a stack looked the
same. Scene#4 settled which arrangement applies for a size class, not
what it looks like - that is the renderer's half, and this is it.

- `panelKind` tells the concrete panels apart the way `elementKind`
  already tells the element kinds apart: by the property each one alone
  declares, since the model carries no discriminator. A plain Panel
  matches none of them, which is a real case - a panel that says nothing
  about arrangement is a grouping.
- `panelLayout` maps them to CSS. Grid tracks become `grid-template-*`
  with star as `fr`, auto as `auto`, absolute as pixels, and a bounded
  track as `minmax`. Stack and wrap become flex along their orientation.
  Dock becomes a column whose last child can take what is left.
  Per-child placement - a grid cell, a docked edge, a wrap panel's item
  size - is applied by wrapping that child, which the renderer can do
  because the rendered children arrive in `element.children` order.

The mapping lives in Scene.React rather than the model or the engine,
because it is the answer for one platform; a native or desktop renderer
answers it with its own primitives.

Canvas is removed. Absolute placement is already the layout model's job -
FreeformArrangement carries an ElementPlacement per element *per size
class*, which says strictly more than attached Left/Top coordinates can -
and a Canvas panel declared no properties of its own, so nothing could
have recognized it at render time either. The rationale sits next to the
panels that remain.

Verified: 922 specs pass (up from 898), build and lint clean, five
Storybooks build, .NET 5 projects / 0 warnings / 37 specs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@einari
einari merged commit ba045de into main Sep 2, 2026
6 checks passed
@woksin
woksin deleted the feature/components-4-and-studio-scene branch September 9, 2026 10:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant