create-momentum-app 0.5.3 → 0.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -32,3 +32,4 @@ npx create-momentum-app my-app --flavor angular --database postgres
32
32
  - Drizzle ORM with PostgreSQL or SQLite
33
33
  - Docker Compose setup for PostgreSQL (when using postgres)
34
34
  - Tailwind CSS with the Momentum admin theme
35
+ - Project-local Claude Code skills under `.claude/skills`, including `headless-ui` for custom app UI built on `@momentumcms/headless`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-momentum-app",
3
- "version": "0.5.3",
3
+ "version": "0.5.5",
4
4
  "description": "Create a new Momentum CMS application",
5
5
  "license": "MIT",
6
6
  "author": "Momentum CMS Contributors",
@@ -2,6 +2,7 @@ import 'dotenv/config';
2
2
  import {
3
3
  initializeMomentumAPI,
4
4
  registerWebhookHooks,
5
+ syncDatabaseSchema,
5
6
  } from '@momentumcms/server-core';
6
7
  import { initializeMomentumLogger, createLogger } from '@momentumcms/logger';
7
8
  import { PluginRunner } from '@momentumcms/plugins-core';
@@ -44,19 +45,7 @@ async function initialize(): Promise<void> {
44
45
 
45
46
  registerWebhookHooks(momentumConfig.collections);
46
47
 
47
- if (momentumConfig.db.adapter.initialize) {
48
- log.info('Initializing database schema...');
49
- await momentumConfig.db.adapter.initialize(momentumConfig.collections);
50
- }
51
-
52
- if (
53
- momentumConfig.db.adapter.initializeGlobals &&
54
- momentumConfig.globals &&
55
- momentumConfig.globals.length > 0
56
- ) {
57
- log.info(`Initializing globals table for ${momentumConfig.globals.length} global(s)...`);
58
- await momentumConfig.db.adapter.initializeGlobals(momentumConfig.globals);
59
- }
48
+ await syncDatabaseSchema(momentumConfig, log);
60
49
 
61
50
  log.info('Initializing API...');
62
51
  const api = initializeMomentumAPI(momentumConfig);
@@ -17,16 +17,17 @@ Momentum CMS is a headless CMS built with Angular. You define collections in Typ
17
17
 
18
18
  ## Available Skills
19
19
 
20
- | Skill | Usage | Description |
21
- | ---------------------- | ------------------------------ | ---------------------------------------------------------------- |
22
- | `/collection <name>` | `/collection products` | Generate a new collection with fields, access control, and hooks |
23
- | `/momentum-api <op>` | `/momentum-api crud posts` | Guide for using `injectMomentumAPI()` in Angular components |
24
- | `/add-plugin <name>` | `/add-plugin analytics` | Add and configure a Momentum CMS plugin |
25
- | `/admin-config <what>` | `/admin-config field-renderer` | Wire admin routes, plugin imports, and custom field renderers |
26
- | `/api-route <name>` | `/api-route health` | Generate API route handlers for Express or Analog.js |
27
- | `/component <name>` | `/component post-card` | Generate an Angular component with signals and OnPush |
28
- | `/e2e-test <feature>` | `/e2e-test posts` | Write Playwright E2E tests (dashboard-first, no blind tests) |
29
- | `/migrations <op>` | `/migrations generate` | Run migrations, generate schemas, and manage code generation |
20
+ | Skill | Usage | Description |
21
+ | ------------------------- | ------------------------------ | ---------------------------------------------------------------- |
22
+ | `/collection <name>` | `/collection products` | Generate a new collection with fields, access control, and hooks |
23
+ | `/momentum-api <op>` | `/momentum-api crud posts` | Guide for using `injectMomentumAPI()` in Angular components |
24
+ | `/add-plugin <name>` | `/add-plugin analytics` | Add and configure a Momentum CMS plugin |
25
+ | `/admin-config <what>` | `/admin-config field-renderer` | Wire admin routes, plugin imports, and custom field renderers |
26
+ | `/admin-customize <what>` | `/admin-customize slot` | Swappable pages, layout slots, per-collection overrides |
27
+ | `/api-route <name>` | `/api-route health` | Generate API route handlers for Express or Analog.js |
28
+ | `/component <name>` | `/component post-card` | Generate an Angular component with signals and OnPush |
29
+ | `/e2e-test <feature>` | `/e2e-test posts` | Write Playwright E2E tests (dashboard-first, no blind tests) |
30
+ | `/migrations <op>` | `/migrations generate` | Run migrations, generate schemas, and manage code generation |
30
31
 
31
32
  ## Common Workflows
32
33
 
@@ -46,6 +46,12 @@ browserImports: {
46
46
  }
47
47
  ```
48
48
 
49
+ ### Component loaders in generated config
50
+
51
+ The generator emits `admin.components` (global) and per-collection `admin.components` as lazy-loading functions with rewritten import paths. This means `momentum.config.ts` is the single source of truth for page overrides and layout slots.
52
+
53
+ For swappable pages and layout slots, see `/admin-customize`.
54
+
49
55
  ## Custom Field Renderers
50
56
 
51
57
  Field renderers are lazily loaded via `FieldRendererRegistry`. Built-in renderers are registered with `provideMomentumFieldRenderers()`.
@@ -0,0 +1,185 @@
1
+ ---
2
+ name: admin-customize
3
+ description: Customize the admin UI with swappable pages and layout slots. Use when replacing built-in pages (dashboard, list, edit, view), injecting content into layout slots (header, footer, sidebar, before/after), or registering per-collection overrides.
4
+ argument-hint: <page-override|slot|per-collection>
5
+ ---
6
+
7
+ # Admin Customization — Swappable Pages & Layout Slots
8
+
9
+ Guide for customizing the Momentum CMS admin UI via page replacements and layout slot injection.
10
+
11
+ ## Arguments
12
+
13
+ - `$ARGUMENTS` - What to customize: `page-override`, `slot`, or `per-collection`
14
+
15
+ ## Two Registration Methods
16
+
17
+ ### 1. Config-level (momentum.config.ts)
18
+
19
+ Registered in the server config. The code generator emits these as loader functions in the browser-safe generated config. This is the recommended approach — single source of truth.
20
+
21
+ After changes, run: `npm run generate`
22
+
23
+ ### 2. Provider-level (app.config.ts)
24
+
25
+ Registered via Angular DI providers. Takes effect immediately, no generation needed. Useful for app-specific customizations or login page slots.
26
+
27
+ ## Swappable Pages (Full Replacement)
28
+
29
+ ### Available Page Keys
30
+
31
+ | Key | Built-in Page |
32
+ | ----------------- | --------------- |
33
+ | `dashboard` | Dashboard |
34
+ | `login` | Login |
35
+ | `media` | Media Library |
36
+ | `collection-list` | Collection List |
37
+ | `collection-edit` | Collection Edit |
38
+ | `collection-view` | Collection View |
39
+ | `global-edit` | Global Edit |
40
+
41
+ ### Config-level (momentum.config.ts)
42
+
43
+ ```typescript
44
+ admin: {
45
+ components: {
46
+ dashboard: () =>
47
+ import('./app/custom-dashboard.component').then((m) => m.CustomDashboard),
48
+ },
49
+ },
50
+ ```
51
+
52
+ ### Provider-level (app.config.ts)
53
+
54
+ ```typescript
55
+ import { provideAdminComponent } from '@momentumcms/admin';
56
+
57
+ providers: [
58
+ provideAdminComponent('dashboard', () =>
59
+ import('./custom-dashboard.component').then((m) => m.CustomDashboard),
60
+ ),
61
+ ],
62
+ ```
63
+
64
+ ### Per-Collection Page Overrides
65
+
66
+ Override pages for a specific collection only:
67
+
68
+ #### Config-level (on collection admin.components)
69
+
70
+ ```typescript
71
+ admin: {
72
+ components: {
73
+ list: () => import('./custom-articles-list.component').then((m) => m.CustomArticlesList),
74
+ },
75
+ },
76
+ ```
77
+
78
+ #### Provider-level
79
+
80
+ ```typescript
81
+ provideAdminComponent('collections/articles/list', () =>
82
+ import('./custom-articles-list.component').then((m) => m.CustomArticlesList),
83
+ );
84
+ ```
85
+
86
+ ### Resolution Chain
87
+
88
+ 1. Per-collection override (`collections/{slug}/{type}`)
89
+ 2. Global override (`collection-list`)
90
+ 3. Built-in default
91
+
92
+ ## Layout Slots (Additive Injection)
93
+
94
+ Slots inject content around existing pages. Multiple components can register for the same slot.
95
+
96
+ ### Available Slots
97
+
98
+ | Slot Key | Position |
99
+ | -------------------------------------- | ------------------------------- |
100
+ | `shell:header` | Top of main content area |
101
+ | `shell:footer` | Bottom of main content area |
102
+ | `shell:nav-start` | After Dashboard link in sidebar |
103
+ | `shell:nav-end` | After plugin routes in sidebar |
104
+ | `dashboard:before/after` | Around dashboard content |
105
+ | `collection-list:before/after` | Around list tables |
106
+ | `collection-edit:before/after/sidebar` | Around edit forms |
107
+ | `collection-view:before/after` | Around view pages |
108
+ | `login:before/after` | Around login form |
109
+
110
+ ### Config-level (momentum.config.ts — admin.components)
111
+
112
+ | Config Key | Slot Position |
113
+ | ------------------ | ------------------ |
114
+ | `beforeNavigation` | `shell:nav-start` |
115
+ | `afterNavigation` | `shell:nav-end` |
116
+ | `header` | `shell:header` |
117
+ | `footer` | `shell:footer` |
118
+ | `beforeDashboard` | `dashboard:before` |
119
+ | `afterDashboard` | `dashboard:after` |
120
+ | `beforeLogin` | `login:before` |
121
+ | `afterLogin` | `login:after` |
122
+
123
+ ```typescript
124
+ admin: {
125
+ components: {
126
+ beforeDashboard: () =>
127
+ import('./app/announcement-banner.component').then((m) => m.AnnouncementBanner),
128
+ footer: () =>
129
+ import('./app/custom-footer.component').then((m) => m.CustomFooter),
130
+ },
131
+ },
132
+ ```
133
+
134
+ ### Per-collection config keys
135
+
136
+ `beforeList`, `afterList`, `beforeEdit`, `afterEdit`, `editSidebar`, `beforeView`, `afterView`
137
+
138
+ ### Provider-level (app.config.ts)
139
+
140
+ ```typescript
141
+ import { provideAdminSlot } from '@momentumcms/admin';
142
+
143
+ providers: [
144
+ provideAdminSlot('shell:header', () =>
145
+ import('./env-banner.component').then((m) => m.EnvBanner),
146
+ ),
147
+ // Per-collection:
148
+ provideAdminSlot('collection-list:before:articles', () =>
149
+ import('./articles-filter.component').then((m) => m.ArticlesFilter),
150
+ ),
151
+ ],
152
+ ```
153
+
154
+ ## Component Template
155
+
156
+ ```typescript
157
+ import { ChangeDetectionStrategy, Component, input } from '@angular/core';
158
+ import type { CollectionConfig } from '@momentumcms/core';
159
+
160
+ @Component({
161
+ selector: 'app-custom-slot',
162
+ host: { class: 'block' },
163
+ template: `
164
+ <div class="p-4 bg-mcms-muted rounded-lg">
165
+ @if (collection(); as col) {
166
+ <p>Collection: {{ col.slug }}</p>
167
+ }
168
+ </div>
169
+ `,
170
+ changeDetection: ChangeDetectionStrategy.OnPush,
171
+ })
172
+ export class CustomSlotComponent {
173
+ readonly collection = input<CollectionConfig>();
174
+ readonly entityId = input<string>();
175
+ }
176
+ ```
177
+
178
+ ## Exports from @momentumcms/admin
179
+
180
+ ```typescript
181
+ import {
182
+ provideAdminComponent, // Register page override via DI
183
+ provideAdminSlot, // Register slot component via DI
184
+ } from '@momentumcms/admin';
185
+ ```
@@ -0,0 +1,77 @@
1
+ ---
2
+ name: headless-ui
3
+ description: Use @momentumcms/headless inside generated Momentum apps. Use when building custom public UI, composing accessible primitives, configuring global styles for hdl-* elements, or adding app-level tests around headless interactions.
4
+ argument-hint: <feature-or-primitive>
5
+ ---
6
+
7
+ # Use Headless UI In Generated Apps
8
+
9
+ Use this skill when a generated app needs custom UI built on `@momentumcms/headless`.
10
+
11
+ ## Prefer Headless For
12
+
13
+ - Public-facing UI that should not look like the admin
14
+ - App-specific design systems that still need solid keyboard and ARIA behavior
15
+ - Custom menus, dialogs, comboboxes, chips, tabs, and form fields
16
+
17
+ Use `@momentumcms/admin` when you want the built-in admin screens instead of custom surfaces.
18
+
19
+ ## Workflow
20
+
21
+ 1. Import the needed primitive classes into the Angular component that renders them.
22
+ 2. Style them from `src/styles.css` or your global Tailwind layer, not from the library.
23
+ 3. Target `data-slot`, `data-state`, `data-disabled`, and overlay classes such as `.hdl-dialog-panel`.
24
+ 4. Add visible state readouts for important interactions so browser tests can prove the UI intention.
25
+ 5. Update app routes or showcase pages if you are adding a reusable demo surface.
26
+
27
+ ## Styling Rules
28
+
29
+ - Start with design tokens in `@layer base`.
30
+ - Add shared recipes in `@layer components`.
31
+ - Use host `class=""` only for local one-off overrides.
32
+ - For projected child visuals, style the child markup you render inside the primitive.
33
+ - Keep `[hidden]` behavior intact for slots that collapse or unmount visually.
34
+
35
+ ## Example Shape
36
+
37
+ ```typescript
38
+ import { ChangeDetectionStrategy, Component, computed, signal } from '@angular/core';
39
+ import { HdlCombobox, HdlComboboxInput, HdlComboboxPopup, HdlOption } from '@momentumcms/headless';
40
+
41
+ @Component({
42
+ selector: 'app-filter-combobox',
43
+ imports: [HdlCombobox, HdlComboboxInput, HdlComboboxPopup, HdlOption],
44
+ template: `
45
+ <hdl-combobox [value]="selected()" (valueChange)="selected.set($event)">
46
+ <input hdlComboboxInput [value]="query()" (input)="query.set(search.value)" #search />
47
+ <hdl-combobox-popup>
48
+ @for (item of filteredItems(); track item) {
49
+ <hdl-option [value]="item">{{ item }}</hdl-option>
50
+ }
51
+ </hdl-combobox-popup>
52
+ </hdl-combobox>
53
+
54
+ <p>Selected: {{ selected() || 'none' }}</p>
55
+ `,
56
+ changeDetection: ChangeDetectionStrategy.OnPush,
57
+ })
58
+ export class FilterComboboxComponent {
59
+ readonly items = ['Articles', 'Pages', 'Authors', 'Media'];
60
+ readonly query = signal('');
61
+ readonly selected = signal('');
62
+ readonly filteredItems = computed(() => {
63
+ const query = this.query().trim().toLowerCase();
64
+ return query ? this.items.filter((item) => item.toLowerCase().includes(query)) : this.items;
65
+ });
66
+ }
67
+ ```
68
+
69
+ ## Test Expectations
70
+
71
+ - Unit tests should cover app-specific state mapping and conditional rendering.
72
+ - Browser tests should assert the intended behavior, not just the presence of `hdl-*` tags.
73
+ - Good assertions: combobox filtering works, menu clicks update the readout, chips remove correctly, dialog actions dismiss as expected.
74
+
75
+ ## If You Need More Than Consumption
76
+
77
+ If the app task actually requires changing the library itself, switch to the repo skill for maintaining `libs/headless` instead of patching around the library from the app.