@agntcms/next 0.2.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.
@@ -0,0 +1,806 @@
1
+ import { P as Page, D as Section, S as SectionSchema, a as FormSchema, b as FormDefinition, x as FieldValueFor, n as FormFieldOverrides, A as AnyFormDefinition, d as ImageValue, e as VideoValue, h as LinkValue, i as ButtonValue, E as SelectOption, z as ListItem } from './form-BqY0H1V5.cjs';
2
+ export { q as FormFieldOverride } from './form-BqY0H1V5.cjs';
3
+ import { A as AnySectionDefinition, E as EditableSlot, c as SlotItem } from './defineSection-9qQ5ulAH.cjs';
4
+ import { ReactNode, ReactElement } from 'react';
5
+
6
+ interface PageRendererProps {
7
+ /** The page data from getContent. */
8
+ readonly page: Page;
9
+ /** The registry of section definitions. */
10
+ readonly definitions: readonly AnySectionDefinition[];
11
+ }
12
+ /**
13
+ * Renders all sections of a page in order. Wraps them in a `<div>` tagged
14
+ * with `data-agntcms-page` for debugging and testing (not for styling).
15
+ */
16
+ declare function PageRenderer(props: PageRendererProps): React.ReactElement;
17
+
18
+ interface SectionRendererProps {
19
+ /** The section data from the page's sections array. */
20
+ readonly section: Section;
21
+ /** The registry of section definitions to look up the component. */
22
+ readonly definitions: readonly AnySectionDefinition[];
23
+ }
24
+ /**
25
+ * Looks up `section.type` in the `definitions` array and renders the
26
+ * matching component with `section.data` as props.
27
+ *
28
+ * If no matching definition is found: in development, renders a visible
29
+ * red-bordered error box; in production, returns `null` silently.
30
+ */
31
+ declare function SectionRenderer(props: SectionRendererProps): React.ReactElement | null;
32
+
33
+ /** Preview or published — matches the runtime concept but defined locally for the client bundle. */
34
+ type PreviewMode = 'preview' | 'published';
35
+ interface PreviewContextValue {
36
+ readonly mode: PreviewMode;
37
+ }
38
+ interface PreviewProviderProps {
39
+ readonly mode: PreviewMode;
40
+ readonly children: ReactNode;
41
+ }
42
+ /**
43
+ * Wraps the page tree to broadcast the current preview mode.
44
+ *
45
+ * In preview mode, an <AgentTaskProvider> is automatically mounted
46
+ * between the PreviewContext and the children so every descendant shares
47
+ * one task coordinator. In published mode, no AgentTaskProvider is
48
+ * mounted — there is no preview UI to drive.
49
+ */
50
+ declare function PreviewProvider({ mode, children }: PreviewProviderProps): React.ReactElement;
51
+ /** Returns the current preview mode. Defaults to 'published' outside a PreviewProvider. */
52
+ declare function usePreviewMode(): PreviewMode;
53
+
54
+ /**
55
+ * Minimal shape of a section definition needed by the picker.
56
+ * Kept deliberately narrow so the modal doesn't depend on the full
57
+ * domain `SectionDefinition` type at runtime — only at the call site.
58
+ *
59
+ * `schema` and `label` are optional because the picker itself does
60
+ * not need them. They are surfaced here so that other preview-time
61
+ * consumers (notably `<SectionSettingsModal>`) can find a section's
62
+ * field-descriptor map and human label without a parallel definitions
63
+ * channel. At runtime every real `AnySectionDefinition` carries both,
64
+ * so the optional declaration is a type-system relaxation rather than
65
+ * a runtime contract change.
66
+ */
67
+ interface DefinitionLike {
68
+ readonly name: string;
69
+ readonly category?: string;
70
+ readonly component: (props: never) => unknown;
71
+ /**
72
+ * Pre-computed defaults. Values are `unknown` because each field
73
+ * kind produces its own default shape — v1 built-ins all resolve
74
+ * to strings (text, image-filename, reference id), but keeping the
75
+ * channel lossless avoids breaking downstream if a future built-in
76
+ * introduces a non-string value. Preview cards cast the definition's
77
+ * component to the right shape at render time.
78
+ */
79
+ readonly defaults?: Record<string, unknown>;
80
+ /**
81
+ * Field-descriptor schema. Optional in the type because tests and
82
+ * isolated callers may build minimal stubs; in practice every real
83
+ * definition supplies it.
84
+ */
85
+ readonly schema?: SectionSchema;
86
+ /** Optional human-friendly label. Falls back to `name`. */
87
+ readonly label?: string;
88
+ /**
89
+ * Optional layout names declared on the section definition. When a
90
+ * section with layouts is being edited (caller passes `currentSection`
91
+ * + `onSelectLayout`), the modal renders a "Layout" tab with a
92
+ * preview card per layout. Undefined means no Layout tab.
93
+ */
94
+ readonly layouts?: readonly string[];
95
+ }
96
+ /**
97
+ * A global content block entry as returned by /api/agntcms/global/list.
98
+ * Kept minimal — we only need name and type for display and selection.
99
+ */
100
+ interface GlobalEntry {
101
+ readonly name: string;
102
+ readonly type: string;
103
+ }
104
+ interface SectionPickerModalProps {
105
+ /** Whether the modal is open. */
106
+ readonly open: boolean;
107
+ /** Called when the modal should close. */
108
+ readonly onClose: () => void;
109
+ /** Called when a section type is selected. */
110
+ readonly onSelect: (typeName: string) => void;
111
+ /** Available section definitions to display. */
112
+ readonly definitions: readonly DefinitionLike[];
113
+ /** Title shown in the modal header. */
114
+ readonly title: string;
115
+ /** Called when a global is selected instead of a section type.
116
+ * When omitted, the globals group is not shown (backwards compatible). */
117
+ readonly onSelectGlobal?: (globalName: string) => void;
118
+ /**
119
+ * The section currently being edited. When provided together with
120
+ * `onSelectLayout` AND the matching definition declares `layouts` with
121
+ * length >= 2, the modal exposes a "Layout" tab next to "Replace".
122
+ *
123
+ * `data` is a plain record (not a PreviewField-wrapped one). Callers in
124
+ * preview mode must strip wrappers before passing — the Layout tab
125
+ * renders `<Component {...data} layout={v} />` and React would reject
126
+ * PreviewField objects as children. See SectionEditControls'
127
+ * `stripSectionData` for the stripper.
128
+ */
129
+ readonly currentSection?: {
130
+ readonly id: string;
131
+ readonly type: string;
132
+ readonly data: Record<string, unknown>;
133
+ };
134
+ /**
135
+ * Called when the user picks a layout from the Layout tab. Omitting
136
+ * this prop hides the Layout tab even if `currentSection` is present —
137
+ * the callback is the only way a click can actually change anything.
138
+ */
139
+ readonly onSelectLayout?: (layoutName: string) => void;
140
+ }
141
+ declare function SectionPickerModal(props: SectionPickerModalProps): ReactElement | null;
142
+
143
+ interface PreviewToolbarProps {
144
+ /**
145
+ * Optional section definitions. When provided, the Admin modal's
146
+ * Edit-global flow renders the real section component with inline
147
+ * editable fields instead of a generic form. Forwarded verbatim to
148
+ * AdminModal.
149
+ */
150
+ readonly definitions?: readonly DefinitionLike[];
151
+ }
152
+ /**
153
+ * Fixed-position bar at the bottom of the viewport, visible only in preview mode.
154
+ * Layout: "Preview Mode" | Publish | M | Agent | Exit Preview.
155
+ */
156
+ declare function PreviewToolbar(props?: PreviewToolbarProps): React.ReactElement | null;
157
+
158
+ interface SectionEditControlsProps {
159
+ /** The page whose sections are being edited. */
160
+ readonly page: Page;
161
+ /** Section definitions — used both to render sections and to populate the
162
+ * "add section" picker. Replaces the old `renderSection` callback + separate
163
+ * `sectionTypes` list: passing definitions is RSC-serializable (component
164
+ * refs are serializable as module references), while a function callback is
165
+ * not. */
166
+ readonly definitions: readonly DefinitionLike[];
167
+ /** Called after a mutation succeeds. Typically triggers a router refresh. */
168
+ readonly onMutated?: () => void;
169
+ }
170
+ /**
171
+ * Wraps the section list with add/remove controls visible only in preview
172
+ * mode. In published mode, renders the plain section list with no overhead.
173
+ */
174
+ declare function SectionEditControls(props: SectionEditControlsProps): React.ReactElement;
175
+
176
+ /**
177
+ * Derived payload shape for a `FormDefinition<S>`. Maps every field to its
178
+ * runtime value type. We re-derive locally rather than re-using
179
+ * `domain/schema.ts:DataOf<S>` because forms don't use the section data
180
+ * record shape (`hasUniqueSectionIds`, etc.) — they use a flat record.
181
+ */
182
+ type PayloadOf<S extends FormSchema> = {
183
+ readonly [K in keyof S]: FieldValueFor<S[K]>;
184
+ };
185
+ interface FormProps<S extends FormSchema> {
186
+ /** The form definition produced by `defineForm`. Drives schema-based rendering. */
187
+ readonly definition: FormDefinition<S>;
188
+ /**
189
+ * Content rendered in place of the form on a successful submit. Default:
190
+ * a plain "Thanks — we'll be in touch." message. Set to `null` to render
191
+ * nothing on success.
192
+ */
193
+ readonly successMessage?: ReactNode;
194
+ /** Submit endpoint. Defaults to `/api/agntcms/forms/submit`. */
195
+ readonly endpoint?: string;
196
+ /** Called with the submitted payload after a successful (stored) submit. */
197
+ readonly onSubmitSuccess?: (payload: PayloadOf<S>) => void;
198
+ /** Called instead of POSTing when the surrounding `<PreviewProvider mode="preview">` is active. */
199
+ readonly onPreviewSubmit?: (payload: PayloadOf<S>) => void;
200
+ /** Class applied to the root `<form>` element. */
201
+ readonly className?: string;
202
+ /** Submit button label. Defaults to "Submit". */
203
+ readonly submitLabel?: string;
204
+ /**
205
+ * Per-section-instance display chrome and instance defaults for the form's
206
+ * fields. See `FormFieldOverrides` (domain/formOverrides.ts) and
207
+ * ARCHITECTURE.md §6.5. Field NAMES, KINDS, and VALIDATION are NOT
208
+ * overridable — only `label`, `placeholder`, `helpText`, `hidden`,
209
+ * `order`, and `default`.
210
+ *
211
+ * A hidden field with a `default` is included in the submitted payload as
212
+ * if the user had typed it. A hidden field WITHOUT a default is omitted
213
+ * entirely — the server-side schema validation will reject if that field
214
+ * is required, by design (callers must provide a default whenever they
215
+ * hide a required field).
216
+ */
217
+ readonly overrides?: FormFieldOverrides;
218
+ }
219
+ /**
220
+ * End-user form. Renders one input per declared field, validates server-side
221
+ * via the submit handler, and surfaces error/success states.
222
+ *
223
+ * In preview mode (inside a `<PreviewProvider mode="preview">`) the submit
224
+ * is intercepted: no network call is made, an inline notice is shown, and
225
+ * the optional `onPreviewSubmit` callback receives the typed payload.
226
+ */
227
+ declare function Form<S extends FormSchema>(props: FormProps<S>): ReactElement;
228
+
229
+ /**
230
+ * The registry value plumbed through context. An empty map is the
231
+ * default — components that look up a form by name and miss render a
232
+ * graceful "form not found" message instead of crashing.
233
+ */
234
+ type FormsByName = ReadonlyMap<string, AnyFormDefinition>;
235
+ interface FormsRegistryProviderProps {
236
+ /**
237
+ * Either a `ReadonlyMap<string, AnyFormDefinition>` (used directly)
238
+ * or a list of definitions (the provider builds the map). The list
239
+ * shape is convenient for callers who already hold the array from
240
+ * `defineConfig({ forms })`.
241
+ */
242
+ readonly forms?: FormsByName | ReadonlyArray<AnyFormDefinition>;
243
+ readonly children: ReactNode;
244
+ }
245
+ /**
246
+ * Provider that exposes the registered forms to descendant preview
247
+ * widgets via context. When `forms` is omitted, descendants see the
248
+ * default (empty map) — same outcome as no provider at all.
249
+ *
250
+ * Usage:
251
+ * <FormsRegistryProvider forms={[Contact, Newsletter]}>
252
+ * <SectionEditControls ... />
253
+ * </FormsRegistryProvider>
254
+ */
255
+ declare function FormsRegistryProvider(props: FormsRegistryProviderProps): React.ReactElement;
256
+ /**
257
+ * Returns the current forms registry. Defaults to an empty map outside
258
+ * a `FormsRegistryProvider`. Consumers MUST handle the empty case
259
+ * gracefully — a missing form name renders a helpful inline message,
260
+ * not a crash.
261
+ */
262
+ declare function useFormsRegistry(): FormsByName;
263
+
264
+ interface SectionReplaceOverlayProps {
265
+ /** The section being replaced (id + current type). */
266
+ readonly sectionId: string;
267
+ readonly currentType: string;
268
+ readonly pageSlug: string;
269
+ /** Section definitions with live preview components. */
270
+ readonly definitions: readonly DefinitionLike[];
271
+ /** Whether we're in preview mode (only show in preview). */
272
+ readonly isPreview: boolean;
273
+ /** Callback after the replacement task completes. */
274
+ readonly onReplaced?: () => void;
275
+ /** Called when a global is selected as the replacement.
276
+ * When provided, the picker shows globals alongside section types. */
277
+ readonly onSelectGlobal?: (globalName: string) => void;
278
+ /**
279
+ * The section currently being edited. Forwarded to `SectionPickerModal`
280
+ * to enable the "Layout" tab for sections with declared layouts. Data
281
+ * must be PreviewField-stripped by the caller (SectionEditControls does
282
+ * this via its `stripSectionData` helper). When omitted, the Layout tab
283
+ * is hidden regardless of `onSelectLayout`.
284
+ */
285
+ readonly currentSection?: {
286
+ readonly id: string;
287
+ readonly type: string;
288
+ readonly data: Record<string, unknown>;
289
+ };
290
+ /** Called when a layout is picked from the Layout tab. */
291
+ readonly onSelectLayout?: (layoutName: string) => void;
292
+ }
293
+ declare function SectionReplaceOverlay(props: SectionReplaceOverlayProps): React.ReactElement | null;
294
+
295
+ interface SectionWrapperProps {
296
+ readonly sectionId: string;
297
+ readonly sectionType: string;
298
+ readonly pageSlug: string;
299
+ readonly definitions: readonly DefinitionLike[];
300
+ readonly isPreview: boolean;
301
+ readonly onReplaced?: () => void;
302
+ /** Forwarded to the picker to allow replacing a section with a global. */
303
+ readonly onSelectGlobal?: (globalName: string) => void;
304
+ /**
305
+ * The section currently being edited. Forwarded to the picker modal so
306
+ * sections declaring `layouts` can expose a "Layout" tab. `data` must
307
+ * be PreviewField-stripped by the caller — the picker renders the section
308
+ * component with this data and React rejects wrapper objects as props.
309
+ */
310
+ readonly currentSection?: {
311
+ readonly id: string;
312
+ readonly type: string;
313
+ readonly data: Record<string, unknown>;
314
+ };
315
+ /** Forwarded to the picker; invoked when a layout is chosen. */
316
+ readonly onSelectLayout?: (layoutName: string) => void;
317
+ /**
318
+ * Optional slot rendered inside the hover-controls area next to the ⇄
319
+ * replace button. The caller composes any React node (typically an
320
+ * <AgentActionButton />) and passes it in. SectionWrapper stays
321
+ * agnostic — it does NOT import from react/agent/ (invariant 2 keeps
322
+ * this module a leaf).
323
+ */
324
+ readonly agentAction?: React.ReactNode;
325
+ /**
326
+ * Optional slot for the delete (×) button. Rendered INSIDE the
327
+ * .agntcms-section-wrap div so the single group-hover rule in
328
+ * HOVER_STYLE reveals it together with ⇄ and ✨. The caller owns the
329
+ * button's appearance and behavior (confirm, fetch, etc.); SectionWrapper
330
+ * just places it in the hover scope.
331
+ */
332
+ readonly deleteAction?: React.ReactNode;
333
+ /**
334
+ * Optional slot for the edit (✎) button. Same composition pattern
335
+ * as `agentAction` and `deleteAction`: rendered inside the hover scope
336
+ * so the same group-hover rule fades it in. The caller positions and
337
+ * styles the button; SectionWrapper stays agnostic of the modal it
338
+ * opens (which would otherwise pull preview-only deps into this leaf).
339
+ */
340
+ readonly settingsAction?: React.ReactNode;
341
+ readonly children: React.ReactNode;
342
+ }
343
+ /**
344
+ * Wraps a rendered section with a hover overlay showing the replace
345
+ * button. In published mode, renders children with no wrapper.
346
+ */
347
+ declare function SectionWrapper(props: SectionWrapperProps): React.ReactElement;
348
+
349
+ type TaskStatus = 'idle' | 'dispatching' | 'in_progress' | 'completed' | 'failed';
350
+ interface TaskState {
351
+ readonly status: TaskStatus;
352
+ readonly messages: readonly string[];
353
+ readonly result?: string;
354
+ readonly error?: string;
355
+ }
356
+ /**
357
+ * Manages the full lifecycle of a single MCP task:
358
+ * idle -> dispatching -> (in_progress | completed | failed).
359
+ *
360
+ * `dispatch` POSTs to the MCP handler, then subscribes to SSE for progress.
361
+ * `reset` returns to idle so the hook can be reused for the next task.
362
+ */
363
+ declare function useTaskEvents(): {
364
+ state: TaskState;
365
+ dispatch: (type: string, payload: Record<string, unknown>) => Promise<void>;
366
+ reset: () => void;
367
+ };
368
+
369
+ interface AdminModalProps {
370
+ readonly open: boolean;
371
+ readonly onClose: () => void;
372
+ /**
373
+ * Optional registered section definitions. When provided and a global's
374
+ * `type` matches a definition, the Edit-global modal renders the real
375
+ * section component with inline `EditableText` / `EditableImage`
376
+ * affordances — same experience as editing a page section. When absent
377
+ * (or no matching definition), it falls back to a generic field form.
378
+ */
379
+ readonly definitions?: readonly DefinitionLike[];
380
+ }
381
+ declare function AdminModal(props: AdminModalProps): ReactElement | null;
382
+
383
+ /**
384
+ * The origin metadata carried by a preview-mode field. Mirrors
385
+ * `PreviewFieldOrigin` from runtime types but defined locally to avoid
386
+ * importing server code into the client bundle.
387
+ *
388
+ * `kind` and `globalName` were added in v0.2 to support `<GlobalSlot>`:
389
+ * fields that come from a global carry `kind: 'global'` and
390
+ * `globalName: <name>`, which lets the save dispatcher route to
391
+ * `/api/agntcms/global/save` instead of the page draft endpoint.
392
+ * The discriminator is OPTIONAL for backward compatibility — when
393
+ * absent, the field is treated as a page-origin field.
394
+ */
395
+ interface PreviewFieldOriginLike {
396
+ readonly pageSlug: string;
397
+ readonly sectionId: string;
398
+ readonly fieldPath: string;
399
+ readonly source: 'draft' | 'published';
400
+ readonly revision: string;
401
+ readonly kind?: 'page' | 'global';
402
+ readonly globalName?: string;
403
+ }
404
+ /**
405
+ * Structural replica of `PreviewField<T>` for use in client components.
406
+ * See file header for why this is duplicated rather than imported.
407
+ */
408
+ interface PreviewFieldLike<T = unknown> {
409
+ readonly __agntcmsPreview: true;
410
+ readonly value: T;
411
+ readonly origin: PreviewFieldOriginLike;
412
+ }
413
+ /**
414
+ * Runtime check: is the given value a preview-mode wrapped field?
415
+ *
416
+ * Uses the `__agntcmsPreview` brand property that the runtime sets on
417
+ * every `PreviewField<T>` instance. This is a cheap property-existence
418
+ * check — no deep validation of the origin shape.
419
+ */
420
+ declare function isPreviewField<T>(field: unknown): field is PreviewFieldLike<T>;
421
+
422
+ /**
423
+ * Collapse an `EditableSlot<K, V>` to its bare value `V`.
424
+ *
425
+ * In published mode, `slot.value` is already `V`, so the helper is a
426
+ * straight read. In preview mode, `slot.value` is a `PreviewFieldLike<V>`
427
+ * carrying origin metadata; the helper unwraps to `.value`.
428
+ *
429
+ * The slot kind `K` is irrelevant to the unwrap and is therefore widened
430
+ * via `string` in the parameter type — section authors call `read(linkSlot)`
431
+ * without knowing or caring which slot kind they hold.
432
+ */
433
+ declare function read<V>(slot: EditableSlot<string, V>): V;
434
+ /**
435
+ * Is this slot currently rendering in preview mode?
436
+ *
437
+ * In preview mode, `slot.value` is a `PreviewFieldLike<V>` carrying origin
438
+ * metadata. In published mode, it is the bare `V`. This helper is the
439
+ * canonical signal an author should use to keep an OPTIONAL link/image/etc.
440
+ * field VISIBLE and CLICKABLE in preview when it would otherwise be hidden
441
+ * by an empty-value short-circuit.
442
+ *
443
+ * The canonical idiom for an optional CTA:
444
+ *
445
+ * const cta = read(rawCta)
446
+ * const showCta = Boolean(hrefOf(cta)) || isSlotInPreview(rawCta)
447
+ * {showCta && <a ...><EditableLink field={rawCta} ... /></a>}
448
+ *
449
+ * Without the `|| isSlotInPreview(...)` clause, an unconfigured optional CTA
450
+ * disappears in preview mode and the author has no click target to open the
451
+ * link picker. With it, the `<a>` (and the `<EditableLink>` inside) stays
452
+ * mounted in preview, but the published site still hides it.
453
+ *
454
+ * The slot kind `K` is irrelevant to the check (the preview wrapper shape is
455
+ * the same for every kind), so it is widened via `string` in the parameter
456
+ * type — authors call `isSlotInPreview(linkSlot)` without naming the kind.
457
+ */
458
+ declare function isSlotInPreview<V>(slot: EditableSlot<string, V>): boolean;
459
+
460
+ interface EditableTextProps {
461
+ /**
462
+ * The slot for this text field. SectionRenderer hands every editable
463
+ * field through `wrapAsSlot` (EDITABILITY_DESIGN.md sub-task 2), so the
464
+ * component receives an `EditableSlot<'text', string>` whose `.value`
465
+ * is either a bare string (published) or `PreviewFieldLike<string>`
466
+ * (preview). The slot kind `'text'` makes a mismatched widget
467
+ * (`<EditableImage field={textSlot}>`) a TS error.
468
+ */
469
+ readonly field: EditableSlot<'text', string>;
470
+ /** HTML tag to render. Default: 'div'. */
471
+ readonly as?: keyof React.JSX.IntrinsicElements;
472
+ /** Additional className applied to the outer element. */
473
+ readonly className?: string;
474
+ /** Called when the user saves an edit in preview mode. */
475
+ readonly onSave?: (origin: PreviewFieldLike<string>['origin'], newValue: string) => void;
476
+ }
477
+ /**
478
+ * Renders a plain-text field value. In published mode, renders the raw
479
+ * string in the specified tag with zero overhead. In preview mode,
480
+ * delegates to an inner component that provides click-to-edit via a modal.
481
+ */
482
+ declare function EditableText(props: EditableTextProps): React.ReactElement;
483
+
484
+ interface EditableRichTextProps {
485
+ /**
486
+ * The slot for this rich-text field. The slot kind `'richText'` is
487
+ * distinct from `'text'` so passing a `RichTextField` slot to
488
+ * `<EditableText>` (or vice versa) is a TS error — closing the
489
+ * substring-overlap blind spot the old heuristic gate had.
490
+ * `slot.value` is `string | PreviewFieldLike<string>`.
491
+ */
492
+ readonly field: EditableSlot<'richText', string>;
493
+ /** HTML tag to render. Default: 'div'. */
494
+ readonly as?: keyof React.JSX.IntrinsicElements;
495
+ /** Additional className applied to the outer element. */
496
+ readonly className?: string;
497
+ /** Called when the user saves an edit in preview mode. */
498
+ readonly onSave?: (origin: PreviewFieldLike<string>['origin'], newValue: string) => void;
499
+ }
500
+ /**
501
+ * Renders a rich-text field value with markdown processing. In published
502
+ * mode, renders markdown-derived HTML in the specified tag with zero
503
+ * overhead. In preview mode, delegates to an inner component that provides
504
+ * click-to-edit via a modal markdown editor.
505
+ */
506
+ declare function EditableRichText(props: EditableRichTextProps): React.ReactElement;
507
+
508
+ interface EditableImageProps {
509
+ /**
510
+ * The slot for this image field. `slot.value` is either a bare
511
+ * `ImageValue` (published) or `PreviewFieldLike<ImageValue>` (preview).
512
+ * The slot kind `'image'` rejects mismatched widgets at compile time.
513
+ */
514
+ readonly field: EditableSlot<'image', ImageValue>;
515
+ /** Additional className applied to the outer element. */
516
+ readonly className?: string;
517
+ /** Called when the user picks a new image in preview mode. */
518
+ readonly onSave?: (origin: PreviewFieldLike<ImageValue>['origin'], newValue: ImageValue) => void;
519
+ }
520
+ /**
521
+ * Renders an image field value. In published mode, renders a plain
522
+ * `<img>` with zero overhead. In preview mode, delegates to an inner
523
+ * component that opens the image picker on click.
524
+ */
525
+ declare function EditableImage(props: EditableImageProps): React.ReactElement;
526
+
527
+ interface EditableVideoProps {
528
+ /**
529
+ * The slot for this video field. `slot.value` is either a bare
530
+ * `VideoValue` (published) or `PreviewFieldLike<VideoValue>`
531
+ * (preview). The slot kind `'video'` rejects mismatched widgets at
532
+ * compile time.
533
+ */
534
+ readonly field: EditableSlot<'video', VideoValue>;
535
+ /** Additional className applied to the outer element. */
536
+ readonly className?: string;
537
+ /**
538
+ * Force a specific aspect ratio regardless of `field.value.aspectRatio`.
539
+ * Useful when a section design fixes the ratio and the per-instance
540
+ * choice is not meaningful — the picker still surfaces the ratio
541
+ * selector, but the rendered iframe ignores it.
542
+ */
543
+ readonly aspectRatioOverride?: VideoValue['aspectRatio'];
544
+ /** Called when the user picks a new value in preview mode. */
545
+ readonly onSave?: (origin: PreviewFieldLike<VideoValue>['origin'], newValue: VideoValue) => void;
546
+ }
547
+ /**
548
+ * Renders a video field value. In published mode, renders the provider
549
+ * iframe with no hooks (or `null` when there is no URL). In preview
550
+ * mode, delegates to an inner component that opens the URL picker on
551
+ * click and shows a placeholder when the URL is empty/unrecognised.
552
+ */
553
+ declare function EditableVideo(props: EditableVideoProps): ReactElement | null;
554
+
555
+ interface EditableLinkProps {
556
+ /**
557
+ * The slot for this link field. `slot.value` is either a bare
558
+ * `LinkValue` (published) or `PreviewFieldLike<LinkValue>` (preview).
559
+ * The slot kind `'link'` rejects mismatched widgets at compile time.
560
+ */
561
+ readonly field: EditableSlot<'link', LinkValue>;
562
+ /** Additional className applied to the outer element. */
563
+ readonly className?: string;
564
+ /** Called when the user saves an edit in preview mode. */
565
+ readonly onSave?: (origin: PreviewFieldLike<LinkValue>['origin'], newValue: LinkValue) => void;
566
+ }
567
+ declare function EditableLink(props: EditableLinkProps): React.ReactElement;
568
+
569
+ interface EditableButtonProps {
570
+ /**
571
+ * The slot for this button field. `slot.value` is either a bare
572
+ * `ButtonValue` (published) or `PreviewFieldLike<ButtonValue>`
573
+ * (preview). The slot kind `'button'` rejects mismatched widgets at
574
+ * compile time.
575
+ */
576
+ readonly field: EditableSlot<'button', ButtonValue>;
577
+ /**
578
+ * The closed list of variants the section supports — surfaced in
579
+ * the picker's `<select>`. Should match the schema's `ButtonField`
580
+ * variants list. Required because EditableButton has no link to
581
+ * the descriptor at runtime.
582
+ */
583
+ readonly variants: ReadonlyArray<SelectOption>;
584
+ /** Additional className applied to the outer element. */
585
+ readonly className?: string;
586
+ /** Called when the user saves an edit in preview mode. */
587
+ readonly onSave?: (origin: PreviewFieldLike<ButtonValue>['origin'], newValue: ButtonValue) => void;
588
+ }
589
+ declare function EditableButton(props: EditableButtonProps): React.ReactElement;
590
+
591
+ interface ButtonPickerModalProps {
592
+ readonly open: boolean;
593
+ readonly onClose: () => void;
594
+ /**
595
+ * Fired when the user clicks Save. The caller is responsible for
596
+ * persisting the value; the modal also calls `onClose` immediately
597
+ * afterwards.
598
+ */
599
+ readonly onInsert: (value: ButtonValue) => void;
600
+ /** Variants list from the descriptor. */
601
+ readonly variants: ReadonlyArray<SelectOption>;
602
+ /** Current value, used to seed the inputs on open. */
603
+ readonly initialValue?: ButtonValue;
604
+ /**
605
+ * Stacking override. Defaults to 100000 — same plane as
606
+ * VideoPickerModal / ImagePickerModal.
607
+ */
608
+ readonly zIndex?: number;
609
+ }
610
+ declare function ButtonPickerModal(props: ButtonPickerModalProps): ReactElement | null;
611
+
612
+ interface EditableNumberProps {
613
+ /**
614
+ * The slot for this number field. `slot.value` is either a bare
615
+ * number (published) or `PreviewFieldLike<number>` (preview). The
616
+ * slot kind `'number'` rejects mismatched widgets at compile time.
617
+ */
618
+ readonly field: EditableSlot<'number', number>;
619
+ /** HTML tag to render in display mode. Default: 'span'. */
620
+ readonly as?: keyof React.JSX.IntrinsicElements;
621
+ /** Additional className applied to the outer element. */
622
+ readonly className?: string;
623
+ /** Lower bound HINT for the input's clamping. Optional. */
624
+ readonly min?: number;
625
+ /** Upper bound HINT for the input's clamping. Optional. */
626
+ readonly max?: number;
627
+ /** Step HINT for the input. Default 1 (integer-friendly). */
628
+ readonly step?: number;
629
+ /** Called when the user commits a new value in preview mode. */
630
+ readonly onSave?: (origin: PreviewFieldLike<number>['origin'], newValue: number) => void;
631
+ }
632
+ declare function EditableNumber(props: EditableNumberProps): React.ReactElement;
633
+
634
+ interface EditableBooleanProps {
635
+ /**
636
+ * The slot for this boolean field. `slot.value` is either a bare
637
+ * boolean (published) or `PreviewFieldLike<boolean>` (preview). The
638
+ * slot kind `'boolean'` rejects mismatched widgets at compile time.
639
+ */
640
+ readonly field: EditableSlot<'boolean', boolean>;
641
+ /** Additional className applied to the wrapper. */
642
+ readonly className?: string;
643
+ /** Optional label rendered alongside the toggle in preview mode. */
644
+ readonly label?: string;
645
+ /** Called when the user toggles the value in preview mode. */
646
+ readonly onSave?: (origin: PreviewFieldLike<boolean>['origin'], newValue: boolean) => void;
647
+ }
648
+ declare function EditableBoolean(props: EditableBooleanProps): React.ReactElement | null;
649
+
650
+ interface EditableSelectProps {
651
+ /**
652
+ * The slot for this select field. `slot.value` is either a bare
653
+ * string (published) or `PreviewFieldLike<string>` (preview). The
654
+ * slot kind `'select'` rejects mismatched widgets at compile time.
655
+ */
656
+ readonly field: EditableSlot<'select', string>;
657
+ /**
658
+ * The option set. The section component passes this — typically the
659
+ * same array used in its descriptor — so the dropdown stays in sync
660
+ * with the schema.
661
+ */
662
+ readonly options: readonly SelectOption[];
663
+ /** Additional className applied to the outer element. */
664
+ readonly className?: string;
665
+ /** Called when the user picks a different option. */
666
+ readonly onSave?: (origin: PreviewFieldLike<string>['origin'], newValue: string) => void;
667
+ }
668
+ declare function EditableSelect(props: EditableSelectProps): React.ReactElement;
669
+
670
+ type AnyItem = ListItem<SectionSchema>;
671
+ interface EditableListProps<S extends SectionSchema = SectionSchema> {
672
+ /**
673
+ * The slot for this list field. SectionRenderer hands every editable
674
+ * field through `wrapAsSlot` (EDITABILITY_DESIGN.md sub-tasks 2 / 3),
675
+ * so the component receives an
676
+ * `EditableSlot<'list', ReadonlyArray<SlotItem<S>>>` whose `slot.value`
677
+ * is either a raw items array (published) or a
678
+ * `PreviewFieldLike<ReadonlyArray<…>>` carrying the list-level origin
679
+ * (preview).
680
+ *
681
+ * The runtime element type inside the array is `ListItem<S>` (bare
682
+ * items); the type asserts `SlotItem<S>` because the section author
683
+ * never sees the bare item directly — `<EditableList renderItem>`
684
+ * produces the slot-typed shape at render time via
685
+ * `wrapItemForPreview` (preview) or `wrapItemAsSlot` (published).
686
+ * Items are structurally compatible: the slot brand is phantom and
687
+ * adds no runtime keys.
688
+ */
689
+ readonly field: EditableSlot<'list', ReadonlyArray<SlotItem<S>>>;
690
+ /**
691
+ * The item schema. Section components MUST pass the same schema they
692
+ * declared in their descriptor — this is the schema the editor uses
693
+ * to render per-field controls inside each item card.
694
+ */
695
+ readonly itemSchema: S;
696
+ /** Optional minimum number of items the editor will keep visible. */
697
+ readonly min?: number;
698
+ /** Optional maximum number of items the editor will allow. */
699
+ readonly max?: number;
700
+ /** Additional className applied to the wrapper. */
701
+ readonly className?: string;
702
+ /**
703
+ * Render an item.
704
+ *
705
+ * `item` arrives as a `SlotItem<S>` in BOTH modes: every editable
706
+ * field on the schema is an `EditableSlot<K, V>`, and the section
707
+ * author drops `<EditableRichText field={item.title}>` straight into
708
+ * the card. In preview mode the slot's `value` carries a
709
+ * `PreviewFieldLike<…>` (with origin + inline-save closure); in
710
+ * published mode the slot's `value` is the bare data. A single
711
+ * `renderItem` body works in both modes.
712
+ *
713
+ * Falls back to `JSON.stringify` (using the bare items) when not
714
+ * provided so the editor at least sees something. */
715
+ readonly renderItem?: (item: SlotItem<S>, index: number) => React.ReactNode;
716
+ /** Called when the user mutates the array in preview mode. */
717
+ readonly onSave?: (origin: PreviewFieldLike<ReadonlyArray<AnyItem>>['origin'], newValue: ReadonlyArray<AnyItem>) => void;
718
+ }
719
+ declare function EditableList<S extends SectionSchema = SectionSchema>(props: EditableListProps<S>): React.ReactElement;
720
+
721
+ interface VideoPickerModalProps {
722
+ readonly open: boolean;
723
+ readonly onClose: () => void;
724
+ /**
725
+ * Fired when the user clicks Save. The caller is responsible for
726
+ * persisting the value; the modal also calls `onClose` immediately
727
+ * afterwards.
728
+ */
729
+ readonly onInsert: (value: VideoValue) => void;
730
+ /** Current value, used to seed the URL + ratio inputs on open. */
731
+ readonly initialValue?: VideoValue;
732
+ /**
733
+ * Stacking override. Defaults to 100000 — same plane as
734
+ * ImagePickerModal / MarkdownEditorModal. Callers that mount this
735
+ * picker on top of another modal should pass a higher value so the
736
+ * shared document-level Escape handler dismisses only the topmost
737
+ * modal instead of both at once.
738
+ */
739
+ readonly zIndex?: number;
740
+ }
741
+ declare function VideoPickerModal(props: VideoPickerModalProps): ReactElement | null;
742
+
743
+ /**
744
+ * Function signature for field-level save. Accepts the origin metadata
745
+ * (which section + field) and the new field value.
746
+ *
747
+ * `newValue` is `unknown` because the save callback stores the value
748
+ * verbatim into `section.data[fieldPath]`, and each editable component
749
+ * is responsible for producing the right value shape before calling
750
+ * save. In v1 every built-in field resolves to a string, but keeping
751
+ * the channel lossless avoids a breaking change if a future BUILT-IN
752
+ * field type introduces a non-string value.
753
+ */
754
+ type SaveFieldFn = (origin: PreviewFieldOriginLike, newValue: unknown) => void;
755
+ interface SaveProviderProps {
756
+ readonly saveField: SaveFieldFn;
757
+ readonly children: React.ReactNode;
758
+ }
759
+ /**
760
+ * Provides a `saveField` callback to all EditableText/EditableImage
761
+ * descendants. Typically placed by SectionEditControls around the
762
+ * preview section list.
763
+ */
764
+ declare function SaveProvider(props: SaveProviderProps): React.ReactElement;
765
+ /**
766
+ * Returns the `saveField` function from the nearest SaveProvider, or
767
+ * `null` if no provider is present (e.g., published mode).
768
+ */
769
+ declare function useSaveField(): SaveFieldFn | null;
770
+
771
+ interface GlobalSaveProviderProps {
772
+ readonly globalName: string;
773
+ readonly globalType: string;
774
+ /**
775
+ * The global's data as returned by `getGlobal` in preview mode —
776
+ * each value is a `PreviewField<T>`. We strip the wrappers when
777
+ * constructing the save payload so the on-disk global stores plain
778
+ * values. Keeping `initialData` as a snapshot lets the provider patch
779
+ * a single field without re-fetching the whole global.
780
+ */
781
+ readonly initialData: Readonly<Record<string, unknown>>;
782
+ readonly children: React.ReactNode;
783
+ }
784
+ /**
785
+ * Provides a `saveField` callback that routes a single field-level
786
+ * edit to `/api/agntcms/global/save`. After a successful save we
787
+ * trigger a full page reload so layout-level globals (header, footer)
788
+ * pick up server-recomputed history/dedup state. We don't use Next.js's
789
+ * `useRouter().refresh()` because `<GlobalSlot>` typically renders at
790
+ * `app/layout.tsx` level where router context is unreliable; the reload
791
+ * is the conservative pattern `SectionEditControls` already uses for
792
+ * its global-ref save path.
793
+ *
794
+ * Concurrency note: two synchronous `saveField` calls in the same
795
+ * render cycle (e.g. a UI that batches two edits) MUST compose. The
796
+ * first call's update has to be visible to the second call. `useState`
797
+ * alone breaks this — `setLatestData` schedules an update for the next
798
+ * render, so the second call inside the same tick still reads the old
799
+ * snapshot. We back the state with a `useRef` that we mutate
800
+ * synchronously inside the callback; the `useState` mirror exists only
801
+ * so the React tree re-renders if the provider's children depend on
802
+ * data identity.
803
+ */
804
+ declare function GlobalSaveProvider(props: GlobalSaveProviderProps): React.ReactElement;
805
+
806
+ export { AdminModal, type AdminModalProps, ButtonPickerModal, type ButtonPickerModalProps, type DefinitionLike, EditableBoolean, type EditableBooleanProps, EditableButton, type EditableButtonProps, EditableImage, type EditableImageProps, EditableLink, type EditableLinkProps, EditableList, type EditableListProps, EditableNumber, type EditableNumberProps, EditableRichText, type EditableRichTextProps, EditableSelect, type EditableSelectProps, EditableSlot, EditableText, type EditableTextProps, EditableVideo, type EditableVideoProps, Form, FormFieldOverrides, type FormProps, type FormsByName, FormsRegistryProvider, type FormsRegistryProviderProps, type GlobalEntry, GlobalSaveProvider, type GlobalSaveProviderProps, PageRenderer, type PageRendererProps, type PayloadOf, type PreviewContextValue, type PreviewFieldLike, type PreviewFieldOriginLike, type PreviewMode, PreviewProvider, type PreviewProviderProps, PreviewToolbar, type PreviewToolbarProps, type SaveFieldFn, SaveProvider, type SaveProviderProps, SectionEditControls, type SectionEditControlsProps, SectionPickerModal, type SectionPickerModalProps, SectionRenderer, type SectionRendererProps, SectionReplaceOverlay, type SectionReplaceOverlayProps, SectionWrapper, type SectionWrapperProps, SlotItem, type TaskState, type TaskStatus, VideoPickerModal, type VideoPickerModalProps, isPreviewField, isSlotInPreview, read, useFormsRegistry, usePreviewMode, useSaveField, useTaskEvents };