canonrs-core 0.1.0

CanonRS core types, traits and primitives
# canonrs-core

> Single Source of Truth for the CanonRS design system.
> All metadata, constraints, and API contracts are generated from `*_ui.rs` headers.

---

## Architecture
```
*_ui.rs  (@canon-* headers + Leptos props)
    build.rs  (orchestrates all generators)
src/generated/
  ├── catalog.rs            — CatalogEntry with kind, parts, regions, accept rules
  ├── component_meta.rs     — ComponentMeta with intent, capabilities, required parts
  ├── block_meta.rs         — BlockMeta for all blocks and layouts
  ├── block_definitions.rs  — BlockDefinition with regions, props, presets, AcceptRule
  ├── layout_definitions.rs — LayoutDefinition with slots and descriptions
  ├── component_definitions.rs — ComponentDefinition unifying meta + catalog
  ├── llm_components.md     — LLM context for UI components
  ├── llm_blocks.md         — LLM context for blocks
  └── llm_layouts.md        — LLM context for layouts
```

---

## SSOT Pipeline

The single source of truth flows in one direction only:
```
NEVER edit generated files manually
ALWAYS edit *_ui.rs headers or Leptos props
```

### Component headers (`*_ui.rs`)

Every UI component declares its contract via `@canon-*` doc comments:
```rust
//! @canon-id: button
//! @canon-label: Button
//! @canon-family: interactive
//! @canon-category: Action
//! @canon-intent: Trigger an action or event
//! @canon-description: Action button with variant and size
//! @canon-composable: false
//! @canon-capabilities: Disabled
//! @canon-required-parts:
//! @canon-optional-parts:
//! @canon-tags: button, action, submit, click, cta
```

### Block/Layout headers

Blocks and layouts extend the schema with structural metadata:
```rust
//! @canon-id: card
//! @canon-type: block
//! @canon-category: layout
//! @canon-variant: structure
//! @canon-container: true
//! @canon-regions: header, content, footer
//! @canon-label: Card
//! @canon-description: Container with header/content/footer regions
//! @canon-tags: card, container, box, content
//! @canon-slot-accepts: header=Any,content=Any,footer=Action
```

---

## Generated Artifacts

### `catalog.rs`

Unified registry of all components, blocks, and layouts with:
- `CatalogKind``Component | Block | Layout`
- `CatalogAcceptRule` — declarative accept rules per entry
- `CatalogRegionRule` — per-region accept rules derived from `@canon-slot-accepts`
```rust
CatalogEntry {
    id: "block.card",
    kind: CatalogKind::Block,
    category: CatalogCategory::Layout,
    parts: &[],
    regions: &["header", "content", "footer"],
    accepts: &[CatalogAcceptRule::AnyComponent, CatalogAcceptRule::AnyBlock],
    region_rules: &[
        CatalogRegionRule { region: "header",  accepts: &[CatalogAcceptRule::AnyComponent] },
        CatalogRegionRule { region: "content", accepts: &[CatalogAcceptRule::AnyComponent, CatalogAcceptRule::AnyBlock] },
        CatalogRegionRule { region: "footer",  accepts: &[CatalogAcceptRule::ComponentCategory(CatalogCategory::Action)] },
    ],
}
```

### `component_meta.rs`

Runtime statics for AI/RAG/Decision Engine:
```rust
pub static ACCORDION_META: ComponentMeta = ComponentMeta {
    id: "accordion",
    name: "Accordion",
    family: ComponentFamily::Layout,
    intent: "Expand and collapse content sections",
    capabilities: &[Capability::OpenClose, Capability::Multiple],
    composable: true,
    required_parts: &["AccordionItem", "AccordionTrigger", "AccordionContent"],
    optional_parts: &[],
};
```

### `block_definitions.rs`

Full structural definitions for blocks with semantic `AcceptRule` per region:
```rust
BlockDefinition {
    id: "card",
    regions: &[
        BlockRegion { id: "header",  accepts: &[AcceptRule::Any], layout: RegionLayout::Vertical },
        BlockRegion { id: "content", accepts: &[AcceptRule::Any], layout: RegionLayout::Vertical },
        BlockRegion { id: "footer",  accepts: &[AcceptRule::Category(BlockCategory::Form)], layout: RegionLayout::Horizontal },
    ],
    ...
}
```

### `api.rs` (per component)

Generated in `canonrs-server/src/ui/*/api.rs`:
```rust
pub const BUTTON_API: ComponentApi = ComponentApi {
    id: "button",
    props: &[
        PropDef { name: "children", kind: PropType::Children, required: true, default: None },
        PropDef { name: "variant",  kind: PropType::Enum(&["primary","solid","secondary",...]), required: false, default: Some("primary") },
        PropDef { name: "size",     kind: PropType::Bool, required: false, default: Some("md") },
        PropDef { name: "disabled", kind: PropType::Bool, required: false, default: Some("false") },
    ],
};
```

---

## Constraint Engine

`src/infra/constraint_engine.rs` — validates composition at runtime:
```rust
// Can a block be inserted into a region?
ConstraintEngine::can_insert("card", "footer", "button", 0)
// → ValidationResult::Valid

// Does a component have all required parts?
ConstraintEngine::validate_parts("accordion", &["AccordionItem", "AccordionTrigger"])
// → ValidationResult::Invalid([MissingParts { missing: ["AccordionContent"] }])

// What can go into a region?
ConstraintEngine::valid_children_for("card", "content")
// → ["button", "input", "badge", ...]

// Can a catalog entry nest inside another?
ConstraintEngine::catalog_can_nest_in_region(&dashboard, "header", &button)
// → true (header accepts Navigation category)
```

### Running tests
```bash
cargo test constraint_engine
```

---

## Build Generators

Located in `build/generators/`:

| Generator | Output | Source |
|-----------|--------|--------|
| `gen_catalog.rs` | `catalog.rs` | `@canon-*` headers |
| `gen_meta.rs` | `component_meta.rs`, `block_meta.rs` | `@canon-*` headers |
| `gen_definitions.rs` | `block_definitions.rs`, `layout_definitions.rs` | `@canon-*` headers + props |
| `gen_api/` | `*/api.rs` | Leptos `#[component]` props |
| `gen_component_definitions.rs` | `component_definitions.rs` | `@canon-*` headers |
| `gen_llm.rs` | `llm_*.md` | All of the above |

---

## Adding a New Component

1. Create `canonrs-server/src/ui/<name>/<name>_ui.rs` with `@canon-*` headers
2. Implement the Leptos `#[component]`
3. Run `cargo build` — all generated files update automatically

## Adding a New Block

1. Create `canonrs-server/src/blocks/<name>/<name>_block.rs` with `@canon-*` headers
2. Add `@canon-slot-accepts` for region rules
3. Run `cargo build`

---

## LLM Context

Three markdown files are generated for LLM consumption:

- `llm_components.md` — intent, capabilities, relationships, usage context
- `llm_blocks.md` — regions, props, presets, region rules, typical children
- `llm_layouts.md` — slots, slot rules, use when context

These files are the recommended input for AI page generation tasks.

---

## Crate Structure
```
canonrs-core/
├── build/
│   ├── types.rs         — shared build types
│   ├── utils.rs         — helpers (pascal_to_kebab, to_const_name)
│   ├── parsers.rs       — parse @canon-* headers, props, presets
│   └── generators/      — all code generators
├── src/
│   ├── infra/
│   │   ├── constraint_engine.rs  — runtime validation engine
│   │   ├── state_engine.rs       — aria-* / data-rs-state
│   │   └── dom_contract.rs       — required parts validation
│   ├── primitives/      — 82 pure HTML primitives
│   ├── generated/       — auto-generated (do not edit)
│   ├── catalog_types.rs — CatalogEntry, CatalogAcceptRule, PropType
│   ├── block_types.rs   — BlockDefinition, AcceptRule, BlockRegion
│   ├── meta_types.rs    — ComponentMeta, Capability, ComponentFamily
│   └── lib.rs
└── build.rs             — orchestrates all generators
```