Skip to content

Add component packages, blueprints and the layout/template distinction - #16

Merged
einari merged 7 commits into
mainfrom
feature/component-packages
Aug 16, 2026
Merged

Add component packages, blueprints and the layout/template distinction#16
einari merged 7 commits into
mainfrom
feature/component-packages

Conversation

@einari

@einari einari commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Added

  • Packages. A ui profile lists packages by name; ScenePackage is the declaration behind such a name — what it contributes (components, layouts, screen and dialog templates, themes) and what else has to be active for it to work. Kinds are ComponentLibrary, Styling and Blueprint.
  • Dependency resolution. PackageDependencyResolver expands a chosen set of packages into the complete list, ordered so a package always follows what it depends on — which is also the correct override priority. Missing dependencies, version conflicts and cycles are reported rather than guessed at. PackageCatalog answers what a package picker asks: which component libraries are a base to build on, and what else fits what is already selected.
  • Screen and dialog templates. ScreenTemplate is a reusable shape that goes inside a layout, at module, feature or slice level; FitsSlot names the slot on its parent it fills, and ScreenTemplateResolver turns those names into a tree. The same rule applies at every level, so nesting has no depth limit. DialogTemplate is the same for overlays, which fill no slot.
  • Themes carry attribution. Theme gained design tokens plus Author, AuthorUrl and License, so a theme adopted from elsewhere credits its original creator.
  • @cratis/scene.primereact — 87 component names across PrimeReact's families, and 25 of PrimeTek's free themes, each credited.
  • @cratis/scene.components — the Arc-bound Cratis Components composites, with a binding registry so a screen can name a query or command that a host resolves to a real class at render time.
  • @cratis/scene.tailwind — the styling package component libraries declare themselves written against.
  • @cratis/scene.blueprint.default — application shells with eight menu modes, 23 screen templates, 3 dialog templates and two themes.
  • @cratis/scene.blueprint.components — whole Arc-bound pages built from Cratis Components, layered on the default blueprint rather than shipping a rival shell.

Changed

  • A layout and a template are now different things. A Layout is an application's base navigational look and an application has one; a ScreenTemplate goes inside it and an application has many. They were previously the same word for both.
  • FlowContainer carries its Kind. C# distinguished a FlowRow from a FlowColumn by type, but the TypeScript mirror could not — interfaces with identical members are the same type — so a renderer had no way to tell a row from a column.

🤖 Generated with Claude Code

einari and others added 5 commits August 16, 2026 10:21
A `ui profile` lists packages by name; nothing until now said what a
name means or what else has to be active for it to work. `ScenePackage`
is that declaration - kind, version, dependencies, and what the package
contributes (components, layouts, themes).

`PackageDependencyResolver` expands a chosen set into the complete,
topologically ordered list a profile needs, so a package always outranks
what it depends on. Missing dependencies, version conflicts and cycles
are reported rather than silently resolved. `PackageCatalog` answers the
questions a picker asks: which component libraries are a base to build
on, and what else fits what is already chosen.

`Theme` gains design tokens plus author, link and license, so a theme
adopted from elsewhere credits its original creator rather than
appearing to be ours.

Both languages assert the same `package-dependency-fixtures.json`
corpus, the pattern already used for model shape, package resolution,
layout evaluation and theme compatibility.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A layout and a template were the same word for two different things. They
are now separate, and the difference is the point:

A `Layout` is an application's base navigational look - the shell. An
application has one, and selects it. A `ScreenTemplate` is a reusable
shape that goes inside that shell, at module, feature or slice level, and
an application has many. A `DialogTemplate` is the same for content that
opens over the application rather than sitting inside it.

`ScreenTemplate.FitsSlot` is what makes them compose. A template states
the name of the slot it fills, never which container owns it - so a
feature's template says "I go in the module content area" rather than
naming one module, and stays reusable. `ScreenTemplateResolver` turns
those names into a tree by finding which layout or template declares
each. The same rule applies at every level, so nesting has no depth limit
and no second mechanism.

What it cannot resolve, it reports: a slot nothing declares, a slot name
two containers declare, and templates that nest inside themselves.
Guessing a parent renders content in the wrong region, which is far
harder to diagnose than being told the name is ambiguous.

`PackageKind.Layout` becomes `Blueprint` - the package that ships a
coherent set of layouts, templates and the components filling them, and
which an application selects one of. `ScenePackage` gains
`ScreenTemplates` and `DialogTemplates` beside `Layouts`, and
`validatePackageBundle` checks all three.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two component libraries a `ui profile` can now resolve against, and the
Tailwind styling package both declare a dependency on.

PrimeReact contributes 87 component names across its families, mapped onto
the real components, plus 25 of PrimeTek's free themes. Cratis Components
contributes 37 more - the Arc-bound data, form and dialog composites -
and declares PrimeReact and Tailwind as dependencies, which is the case
the whole dependency mechanism exists for.

Both deliberately reuse names the packages beneath them already declare -
`button`, `card`, `table`, `dialog` - because that is what override
priority is for: a profile listing core, PrimeReact and Cratis Components
resolves `table` to the last one and reports the others as shadowed.

Themes carry their attribution. Every one of PrimeReact's is PrimeTek's
work under MIT, and says so through `author`, `authorUrl` and `license`
rather than appearing to be ours.

A Scene element carries plain values, so it cannot carry the query or
command class an Arc-bound component needs. Cratis Components resolves
that with a binding registry: a host registers real generated proxies
under the names a screen refers to, and an unregistered name renders a
placeholder naming what is missing rather than throwing - which is the
state a design-time preview is usually in.

The root tsconfig now references every package, so a workspace typecheck
actually covers them; it did not before.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…lumns

The default blueprint ships what an application needs to look like a
product on day one: two layouts (an application shell and a full-page
shell for sign-in and error screens), 16 shell components implementing
all eight PrimeTek menu modes, 23 screen templates and 3 dialog
templates with seeded content, and a light and a dark theme.

The three-level chain is real, not illustrative: `ModuleWorkspace` fits
the shell's content slot, `FeatureSection` fits a slot the module
template declares, and `SliceSection` fits one the feature's declares -
each proven by a spec through the real resolver.

`FlowContainer` gains a `Kind`. C# tells a `FlowRow` from a `FlowColumn`
by type, but the TypeScript mirror cannot: interfaces with identical
members are the same type, and there are no runtime types to test. A
renderer walking an arrangement tree had no way to tell a row from a
column - the one thing it most needs to know - so the two stacks could
not agree on what a layout meant. The kind carries that decision as
data, and `flowNodeKind` exposes the guards for it, matching how
`elementKind` already distinguishes element kinds.

The Tailwind package gains the bundle spec every package owes; without
one its test run failed outright rather than passing vacuously.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Eleven Arc-bound screen templates and three dialog templates built from
Cratis Components composites, so a data list, a master-detail screen or a
command form is a template an application picks rather than a page it
assembles.

It is the first blueprint layered on another. It declares no layouts and
depends on `Cratis.Blueprint.Default` for the application shell instead
of shipping a rival one - which is what the dependency is for, and what
keeps an application from ending up with a sidebar from one design
language and a form from another.

Templates carry query and command *names*, not classes. The binding
registry turns a name into a class at render time, and a name nothing
registered renders as a placeholder saying so - which is the state a
design-time preview is normally in, and should look incomplete rather
than quietly wrong.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@einari einari added screen Screen/UI work: Screenplay UI constructs, Scene runtime, Stage rendering, Studio designers major labels Aug 16, 2026
einari and others added 2 commits August 16, 2026 12:32
`body` is a good name for a slot at every level of a nesting chain, so
several templates legitimately declare one - and a bare `body` then has
no single answer. Both blueprints hit this: the components blueprint had
to pick `content`/`body`/`primary` to keep every fitted name unique, and
the default blueprint's own three-level chain does not resolve once both
are in scope together.

A `FitsSlot` may now name its container - `ModuleWorkspace.body` - and
resolve straight to it. The same rule component names already use: a bare
name searches, a qualified one goes to what it names. A qualifier naming
a container that does not declare that slot stays unplaced rather than
quietly falling back to a search.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`**/packages/*` is NuGet's Package Restore rule, and it matched
`Source/JavaScript/model/packages/` and `react/packages/` - so the
package model, `ScenePackage`, `ScenePackageBundle` and
`validatePackageBundle` were never committed at all.

Nothing caught it locally: the files are on disk, so every build, spec
and typecheck was green. CI clones fresh, found no `./packages` module,
and failed on the first workspace it reached.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@einari
einari merged commit 35a2936 into main Aug 16, 2026
5 checks passed
@einari
einari deleted the feature/component-packages branch August 16, 2026 11:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

major screen Screen/UI work: Screenplay UI constructs, Scene runtime, Stage rendering, Studio designers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant