ワンクリックで
add-page
Add a frontend page (AppPageSchema) to an existing Mozaiks app.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Add a frontend page (AppPageSchema) to an existing Mozaiks app.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
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.