@nuasite/cms 0.27.0 → 0.28.0
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 +103 -0
- package/dist/editor.js +11536 -11345
- package/package.json +1 -1
- package/src/collection-scanner.ts +152 -12
- package/src/editor/components/fields.tsx +8 -2
- package/src/editor/components/frontmatter-fields.tsx +13 -3
- package/src/editor/components/link-edit-popover.tsx +232 -0
- package/src/editor/components/markdown-inline-editor.tsx +25 -52
- package/src/editor/components/mdx-block-view.tsx +20 -17
- package/src/editor/hooks/useLinkPopover.ts +64 -0
- package/src/editor/milkdown-utils.ts +21 -0
- package/src/field-types.ts +109 -27
- package/src/index.ts +2 -0
- package/src/types.ts +18 -0
package/README.md
CHANGED
|
@@ -213,6 +213,96 @@ The integration auto-detects Astro content collections in `src/content/`. For ea
|
|
|
213
213
|
- Provides markdown CRUD endpoints for creating/updating entries
|
|
214
214
|
- Parses frontmatter with `yaml` (no `gray-matter` dependency needed)
|
|
215
215
|
|
|
216
|
+
### Schema Helpers (`n`)
|
|
217
|
+
|
|
218
|
+
Use the `n` helper instead of `z` (Zod) in your content config. It provides CMS-aware field types that tell the editor which input to render, and accepts an options object that both validates data and configures the editor UI.
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
import { n } from '@nuasite/cms'
|
|
222
|
+
import { glob } from 'astro/loaders'
|
|
223
|
+
import { defineCollection, reference } from 'astro:content'
|
|
224
|
+
|
|
225
|
+
const tagsCollection = defineCollection({
|
|
226
|
+
loader: glob({ pattern: '**/*.json', base: 'src/content/tags' }),
|
|
227
|
+
schema: n.object({
|
|
228
|
+
name: n.string(),
|
|
229
|
+
}),
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
const blogCollection = defineCollection({
|
|
233
|
+
loader: glob({ pattern: '**/*.{md,mdx}', base: 'src/content/blog' }),
|
|
234
|
+
schema: n.object({
|
|
235
|
+
title: n.text({ placeholder: 'Enter title', maxLength: 120 }),
|
|
236
|
+
author: n.text(),
|
|
237
|
+
date: n.date().orderBy('desc'),
|
|
238
|
+
tags: n.array(reference('tags')),
|
|
239
|
+
excerpt: n.textarea({ rows: 2, maxLength: 300 }),
|
|
240
|
+
coverImage: n.image(),
|
|
241
|
+
featured: n.boolean().default(false),
|
|
242
|
+
}),
|
|
243
|
+
})
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
All `n` methods return standard Zod schemas, so `.optional()`, `.nullable()`, `.default()`, and other Zod chainable methods work as usual.
|
|
247
|
+
|
|
248
|
+
### Field Types
|
|
249
|
+
|
|
250
|
+
| Method | Editor input | Underlying Zod type |
|
|
251
|
+
| ----------------- | -------------- | --------------------------------- |
|
|
252
|
+
| `n.text()` | Text input | `z.string()` |
|
|
253
|
+
| `n.textarea()` | Multiline | `z.string()` |
|
|
254
|
+
| `n.number()` | Number input | `z.number()` |
|
|
255
|
+
| `n.boolean()` | Checkbox | `z.boolean()` |
|
|
256
|
+
| `n.image()` | Image picker | `z.string()` |
|
|
257
|
+
| `n.url()` | URL input | `z.string()` |
|
|
258
|
+
| `n.email()` | Email input | `z.string()` |
|
|
259
|
+
| `n.color()` | Color picker | `z.string()` |
|
|
260
|
+
| `n.date()` | Date picker | `z.string()` (coerces YAML dates) |
|
|
261
|
+
| `n.datetime()` | Datetime input | `z.string()` (coerces YAML dates) |
|
|
262
|
+
| `n.time()` | Time input | `z.string()` |
|
|
263
|
+
| `n.string()` | Auto-detected | `z.string()` (no CMS hint) |
|
|
264
|
+
| `n.object()` | — | `z.object()` |
|
|
265
|
+
| `n.array()` | — | `z.array()` |
|
|
266
|
+
| `n.enum()` | — | `z.enum()` |
|
|
267
|
+
| `n.coerce.date()` | — | `z.coerce.date()` |
|
|
268
|
+
|
|
269
|
+
### Field Hints
|
|
270
|
+
|
|
271
|
+
Pass an options object to configure both Zod validation and editor input attributes in one place:
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
n.number({ min: 1, max: 100, step: 1 }) // <input type="number" min="1" max="100" step="1">
|
|
275
|
+
n.text({ placeholder: 'Enter title', maxLength: 120 })
|
|
276
|
+
n.textarea({ rows: 5, maxLength: 500, placeholder: '...' })
|
|
277
|
+
n.date({ min: '2024-01-01', max: '2030-12-31' })
|
|
278
|
+
n.image({ accept: 'image/png,image/jpeg' })
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
| Field type | Available hints |
|
|
282
|
+
| -------------- | --------------------------------------- |
|
|
283
|
+
| `n.number()` | `min`, `max`, `step`, `placeholder` |
|
|
284
|
+
| `n.text()` | `placeholder`, `maxLength`, `minLength` |
|
|
285
|
+
| `n.textarea()` | `placeholder`, `maxLength`, `rows` |
|
|
286
|
+
| `n.url()` | `placeholder`, `maxLength`, `minLength` |
|
|
287
|
+
| `n.email()` | `placeholder`, `maxLength`, `minLength` |
|
|
288
|
+
| `n.date()` | `min`, `max` |
|
|
289
|
+
| `n.datetime()` | `min`, `max` |
|
|
290
|
+
| `n.time()` | `min`, `max` |
|
|
291
|
+
| `n.image()` | `accept` |
|
|
292
|
+
|
|
293
|
+
Numeric hints (`min`, `max`, `step`, `maxLength`, `minLength`) also apply Zod validation — out-of-range values will be rejected at content build time.
|
|
294
|
+
|
|
295
|
+
### Collection Ordering
|
|
296
|
+
|
|
297
|
+
Chain `.orderBy()` on any scalar field to control entry order in the collections browser:
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
n.number({ min: 1, max: 100 }).orderBy('asc') // ascending (default)
|
|
301
|
+
n.date().orderBy('desc') // descending (newest first)
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
The direction defaults to `'asc'` if omitted. Entries with a missing order field sort to the end.
|
|
305
|
+
|
|
216
306
|
## Component Operations
|
|
217
307
|
|
|
218
308
|
Components in `componentDirs` (default: `src/components/`) are scanned for props and registered as insertable/removable elements. The editor can:
|
|
@@ -334,6 +424,16 @@ Deselects the currently selected component and closes the block editor. No addit
|
|
|
334
424
|
// Default export
|
|
335
425
|
import nuaCms from '@nuasite/cms'
|
|
336
426
|
|
|
427
|
+
// Schema helpers
|
|
428
|
+
import { n } from '@nuasite/cms'
|
|
429
|
+
import type {
|
|
430
|
+
DateHints,
|
|
431
|
+
ImageHints,
|
|
432
|
+
NumberHints,
|
|
433
|
+
TextareaHints,
|
|
434
|
+
TextHints,
|
|
435
|
+
} from '@nuasite/cms'
|
|
436
|
+
|
|
337
437
|
// Media adapters
|
|
338
438
|
import { contemberMedia, localMedia, s3Media } from '@nuasite/cms'
|
|
339
439
|
|
|
@@ -341,7 +441,10 @@ import { contemberMedia, localMedia, s3Media } from '@nuasite/cms'
|
|
|
341
441
|
import type { MediaItem, MediaStorageAdapter } from '@nuasite/cms'
|
|
342
442
|
import type {
|
|
343
443
|
CmsManifest,
|
|
444
|
+
CollectionDefinition,
|
|
344
445
|
ComponentDefinition,
|
|
446
|
+
FieldDefinition,
|
|
447
|
+
FieldHints,
|
|
345
448
|
ManifestEntry,
|
|
346
449
|
} from '@nuasite/cms'
|
|
347
450
|
|