con un clic
add-page
Add a frontend page (AppPageSchema) to an existing Mozaiks app.
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Menú
Add a frontend page (AppPageSchema) to an existing Mozaiks app.
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Basado en la clasificación ocupacional SOC
Set up Mozaiks from scratch. Walks through Docker, Python, Node, environment variables, and verification.
Customize app shell branding - themes, colors, navigation, logos. Works with app/brand/ and app/config/ declarative files.
Add features to an existing Mozaiks project. Helps users upgrade tiers or enable individual capabilities.
Add a backend module (deterministic CRUD/action handler) to an existing Mozaiks app.
Review or implement a change to AgentGenerator prompts, workflow bundle structured outputs, workflow scaffolds, universal prompt injection, or workflow-agent safety guidance.
Review or implement a change to AppGenerator prompts, AppBuildPlan contracts, file contracts, assembly behavior, generated UI quality gates, or app-bundle generation guidance.
| name | add-page |
| description | Add a frontend page (AppPageSchema) to an existing Mozaiks app. |
| argument-hint | [page name or description] |
Before starting: git fetch origin && gh pr list --state open && git log origin/main --oneline -3 — if another agent has an open PR touching the same files you need, wait for it to merge or branch off it instead of main.
Help the user add a page to an existing Mozaiks application.
Pages in Mozaiks are declared as YAML schemas (AppPageSchema) — not raw React.
The platform shell renders them automatically using pre-built primitives.
app/ui/pages/
└── <name>.yaml ← the page schema
# Or folder form:
app/ui/pages/<name>/
└── page.yaml
The SchemaPage route component fetches /api/pages/<name>, and PageRenderer
assembles the layout from the schema using the primitive registry.
touch app/ui/pages/<name>.yaml
name: <name>
title: <Page Title>
layout: grid # grid | sidebar | full-width | split
shell_mode: standard # standard | workspace | conversation | focused | immersive | public
sections:
- id: <section_id>
title: <Section Title>
primitive: DataTable # see primitives below
config:
columns:
- { key: id, label: ID }
- { key: name, label: Name }
api_endpoint: /api/modules/<module>/<action> # optional live data
mozaiks serve .
Pages are loaded at startup. The route /<name> is served automatically.
If the page should be globally reachable, prefer route-level navigation metadata on the page itself:
navigation:
scope: global
icon: dashboard
order: 20
Use scope: local for workspace/module subsections that should render through
local navigation instead of crowding the global shell.
Choose shell_mode for route chrome:
standard: normal app page.workspace: dense dashboard, admin/profile/module workspace, or local nav surface.conversation: chat, DM, inbox thread, or support conversation where the composer owns the bottom edge.focused: onboarding, setup, review, approval, or checkout-style route.immersive: map, canvas, media, game, or full-viewport route.public: legal, marketing, or unauthenticated information route.Use compact shell shortcuts in app/config/shell.json for built-in chrome such
as profile/auth/footer items:
{
"shortcuts": {
"header": ["dashboard", "<name>"],
"mobile": ["dashboard", "<name>", "profile"]
}
}
Use explicit header, profile, or mobile.bottomBar entries only when the
label, icon, role gate, or path needs to differ from the route/page catalog.
Use app/config/shell.json -> navigation.policy when the app needs a different
placement model, such as desktop sidebar global nav or mobile local sheet nav.
Use app/config/shell.json -> chrome only to override app-wide behavior for the
standard shell modes. Do not encode per-route chrome there; the page owns
shell_mode.
| Layout | Description | Best for |
|---|---|---|
grid | 2–3 column responsive grid | Dashboards, overview pages |
sidebar | First section → aside, rest → main | Master-detail, filtered lists |
full-width | Single column, full width | Forms, detail views, reports |
split | Two equal columns | Comparison views, side-by-side |
| Primitive | Use case | Key config |
|---|---|---|
PageHeader | Durable page title and primary actions | title, subtitle, actions[] |
ResourceTable | Primary record/index page table | columns[], api_endpoint, data_key, actions[] |
DataTable | Dense operational record lists | columns[], api_endpoint, data_key |
Form | Data entry | fields[], onSubmit, submitLabel |
SummaryStrip | 2-4 useful page metrics | items[] with value or value_key |
Metric | Single supporting metric | label, value or value_key, detail |
Panel / SurfaceCard | Purposeful grouped support surface | title, subtitle, children |
Grid | Small child primitive layout | children[], columns (2|3|4) |
Button | Call to action | label, variant, onClick |
Modal | Overlay dialog | id, title, open |
Alert | Inline message | message, variant (info|success|warning|error) |
StatusPill | Compact status label | label, tone |
Skeleton | Loading / empty state | rows, height |
Removed primitives: do not use Card, Stat, or Badge. Use SurfaceCard or Panel, SummaryStrip or Metric, and StatusPill.
name: dashboard
title: Dashboard
layout: grid
shell_mode: workspace
sections:
- id: dashboard-header
primitive: PageHeader
config:
title: Dashboard
subtitle: Monitor current user and order activity.
- id: user_summary
primitive: SummaryStrip
config:
api_endpoint: /api/modules/users/stats
items:
- label: Users
value_key: total_users
format: number
- id: recent_orders
title: Recent Orders
primitive: ResourceTable
config:
api_endpoint: /api/modules/orders/list_orders
columns:
- { key: id, label: Order ID }
- { key: total, label: Total }
- { key: status, label: Status, type: status }
name: new-customer
title: New Customer
layout: full-width
shell_mode: focused
sections:
- id: customer_form
title: Customer Details
primitive: Form
config:
fields:
- { name: name, label: Full Name, type: text, required: true }
- { name: email, label: Email, type: email, required: true }
- { name: phone, label: Phone, type: text }
submitLabel: Create Customer
api_endpoint: /api/modules/customers/create_customer
When a section has api_endpoint, SectionRenderer fetches that endpoint on mount
and injects the response into the primitive's rows / value props.
The agent can also trigger a refresh by emitting ui.datatable.refresh with the section id.
For cases the declarative schema cannot express, use the escape hatch:
app/ui/pages/custom/<name>.jsx ← custom React page
app/ui/route_manifest.json ← registers the route
app/ui/index.js ← registers the component key used by the route
// app/ui/route_manifest.json
{
"pages": [
{
"id": "<name>",
"label": "<Name>",
"path": "/custom/<name>",
"component": "<Name>Page",
"requiresAuth": true,
"order": 20,
"meta": { "title": "<Name>" },
"purpose": "Persistent interaction that cannot be expressed with declarative page primitives."
}
]
}
app/ui/index.js must export register(registerComponent) and call
registerComponent("<Name>Page", Component, meta) for every route component.
The route component string and registration key must match exactly. Prefer the
AppGenerator custom_route_bundle contract for generated apps because it
creates route_manifest.json, ui/pages/custom/*.jsx, and ui/index.js
together.
Route/component registration contract:
app/ui/route_manifest.json declares custom full-page routes only.app/ui/index.js must register every component referenced by route_manifest.json.app/ui/pages/custom/*.jsx page file.admin/admin_registry.yaml is for admin page and panel metadata, not arbitrary full-page custom route ownership.Use custom routes sparingly. Declarative app/ui/pages/ is the default.
When a custom route is truly needed, it must still use the shared primitive
contract. Import shared primitives from @mozaiks/chat-ui/ui for actions,
status, cards/panels, metrics, search/list/table surfaces, loading/error states,
and empty states. Do not define local visual clones such as StatusPill,
MetricTile, StatCard, or Badge. Do not render raw primary-styled buttons
such as <button className="...bg-primary..."> when Button exists.
Use semantic tokens/classes only; visual values come from
app/brand/theme_config.json and the shell/theme token layer.
Use semantic variants on shared primitives (for example Button variant,
StatusPill tone, and panel/card variants) rather than local one-off visual
systems.
Do not hardcode hex/rgb/hsl colors or direct font-family declarations in custom
route React.
Do not define page-local palette, colors, or theme objects in custom
route React.
Avoid repeated local rounded card shells; use SurfaceCard/Panel when that
surface shape already exists.
Generated React audit scope note:
docs/** and tests/** fixture paths to reduce false positives.Brand and theme rules:
app/brand/theme_config.json is the visual identity source of truth.app/config/shell.json owns shell/navigation/chrome behavior only.variant, status tone, card
or panel variant, density, and radius.app/brand/fonts/ and are referenced as
/fonts/...; never copy font binaries elsewhere.app/ui/pages/ — never in a module's backend directoryapi_endpoint paths must be /api/modules/{name}/{action_id} routes with no query strings or fragments. Put limits in page_size and filters/selected-row values in action payload, form state, or module action input schemas.navigation field. Use app/config/shell.json -> shortcuts for built-in chrome and navigation.policy for app-wide placement behavior.shell_mode; use conversation for DM/chat routes and workspace for dense module/profile/admin-like pages.route_manifest.json, ui/pages/custom/*.jsx, and ui/index.js.