@riverbankcms/sdk 0.70.3 → 0.71.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.
Files changed (82) hide show
  1. package/dist/_dts/api/src/contentTypes.d.ts +2 -1
  2. package/dist/_dts/block-form/src/client/index.d.ts +1 -1
  3. package/dist/_dts/block-form/src/fields/types.d.ts +14 -2
  4. package/dist/_dts/block-form/src/registry/schemas.d.ts +2 -0
  5. package/dist/_dts/block-form/src/widgets/MediaField/mediaFieldDefaults.d.ts +2 -2
  6. package/dist/_dts/block-form/src/widgets/MediaField/mediaFieldSchema.d.ts +2 -2
  7. package/dist/_dts/blocks/src/lib/media.d.ts +1 -1
  8. package/dist/_dts/blocks/src/system/blocks/appointment-booking.d.ts +1829 -149
  9. package/dist/_dts/blocks/src/system/blocks/blog-listing.d.ts +1829 -149
  10. package/dist/_dts/blocks/src/system/blocks/cart.d.ts +1829 -149
  11. package/dist/_dts/blocks/src/system/blocks/checkout.d.ts +1829 -149
  12. package/dist/_dts/blocks/src/system/blocks/collection.d.ts +1829 -149
  13. package/dist/_dts/blocks/src/system/blocks/cta-full.d.ts +1 -30727
  14. package/dist/_dts/blocks/src/system/blocks/events/event-spotlight.d.ts +11 -30827
  15. package/dist/_dts/blocks/src/system/blocks/faq.d.ts +1829 -149
  16. package/dist/_dts/blocks/src/system/blocks/file-download.d.ts +3 -3
  17. package/dist/_dts/blocks/src/system/blocks/gifting.d.ts +1829 -149
  18. package/dist/_dts/blocks/src/system/blocks/media-text.d.ts +106 -1
  19. package/dist/_dts/blocks/src/system/blocks/product-detail.d.ts +1829 -149
  20. package/dist/_dts/blocks/src/system/blocks/product-list.d.ts +1829 -149
  21. package/dist/_dts/blocks/src/system/blocks/shop.d.ts +1829 -149
  22. package/dist/_dts/blocks/src/system/defineBlock.d.ts +3 -3
  23. package/dist/_dts/blocks/src/system/fragments/library/card.d.ts +2 -30793
  24. package/dist/_dts/blocks/src/system/fragments/library/ctaRow.d.ts +2 -30727
  25. package/dist/_dts/blocks/src/system/fragments/library/feedbackCallout.d.ts +2 -30779
  26. package/dist/_dts/blocks/src/system/fragments/library/heroCtaRow.d.ts +33 -30743
  27. package/dist/_dts/blocks/src/system/fragments/library/sectionState.d.ts +2 -30770
  28. package/dist/_dts/blocks/src/system/fragments/types.d.ts +8 -7
  29. package/dist/_dts/blocks/src/system/manifest/derivedContent.d.ts +7 -5
  30. package/dist/_dts/blocks/src/system/manifest/fieldValidation/index.d.ts +1 -1
  31. package/dist/_dts/blocks/src/system/manifest/schema.d.ts +192 -3
  32. package/dist/_dts/blocks/src/system/runtime/nodes/file-download.d.ts +1 -1
  33. package/dist/_dts/content-model/src/content-types/schema.d.ts +7 -0
  34. package/dist/_dts/content-model/src/contentManifest.d.ts +4 -1
  35. package/dist/_dts/content-model/src/templates/bindingTypeCompatibility.d.ts +1 -1
  36. package/dist/_dts/core/src/index.d.ts +1 -1
  37. package/dist/_dts/core/src/planner.d.ts +4 -0
  38. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +53 -20
  39. package/dist/_dts/editor-blocks/src/index.d.ts +1 -1
  40. package/dist/_dts/editor-blocks/src/media/MediaField.d.ts +6 -5
  41. package/dist/_dts/media-core/src/videoSlotPolicy.d.ts +0 -1
  42. package/dist/_dts/media-editor/src/context.d.ts +4 -1
  43. package/dist/_dts/media-react/src/hooks/useApiMediaUploader.d.ts +8 -1
  44. package/dist/_dts/media-react/src/index.d.ts +1 -5
  45. package/dist/_dts/preview-next/src/client/sdk/SdkPreviewMode.d.ts +1 -0
  46. package/dist/_dts/preview-next/src/client/widgets/PreviewMediaField.d.ts +2 -1
  47. package/dist/_dts/preview-next/src/client/widgets/widgetOverrides.d.ts +1 -0
  48. package/dist/_dts/sdk/src/config/field-definitions.d.ts +6 -2
  49. package/dist/_dts/sdk/src/config/typed-entries.d.ts +4 -2
  50. package/dist/_dts/sdk/src/contracts/system-block-content.d.ts +2 -1
  51. package/dist/_dts/sdk/src/manifest/prompt/examples/cta.d.ts +5 -5
  52. package/dist/_dts/sdk/src/manifest/prompt/examples/features-grid.d.ts +2 -2
  53. package/dist/_dts/sdk/src/manifest/prompt/examples/hero.d.ts +2 -2
  54. package/dist/_dts/sdk/src/manifest/prompt/examples/pricing.d.ts +10 -10
  55. package/dist/_dts/sdk/src/manifest/prompt/examples/testimonials.d.ts +4 -4
  56. package/dist/_dts/sdk/src/manifest/validation/output-schema.d.ts +11 -1
  57. package/dist/_dts/sdk/src/preview-next/client/index.d.ts +19 -1
  58. package/dist/_dts/sdk/src/public-api/contracts.d.ts +2 -1
  59. package/dist/_dts/sdk/src/version.d.ts +1 -1
  60. package/dist/cli/index.mjs +395 -190
  61. package/dist/cli/init-docs/content/workflow-preview-mode.md +167 -0
  62. package/dist/client/client.mjs +243 -203
  63. package/dist/client/hooks.mjs +172 -121
  64. package/dist/client/rendering/client.mjs +148 -112
  65. package/dist/client/rendering/islands.mjs +80 -31
  66. package/dist/client/rendering.mjs +242 -202
  67. package/dist/preview-next/client/index.mjs +60 -10
  68. package/dist/preview-next/client/runtime.mjs +408 -1141
  69. package/dist/server/components.mjs +219 -171
  70. package/dist/server/config-template-validation.mjs +31 -1
  71. package/dist/server/config-validation.mjs +172 -121
  72. package/dist/server/config.mjs +172 -121
  73. package/dist/server/data.mjs +172 -121
  74. package/dist/server/index.mjs +1 -1
  75. package/dist/server/next.mjs +220 -172
  76. package/dist/server/page-converter.mjs +379 -185
  77. package/dist/server/prebuild.mjs +1 -1
  78. package/dist/server/rendering/server.mjs +219 -171
  79. package/dist/server/rendering.mjs +219 -171
  80. package/dist/server/routing.mjs +175 -124
  81. package/dist/server/server.mjs +173 -122
  82. package/package.json +6 -1
@@ -305,6 +305,28 @@ function resolveRepeaterGridColumnFieldId(options) {
305
305
 
306
306
  // ../blocks/src/system/manifest/schema.ts
307
307
  var visibilityLevels = ["admin", "designer", "author"];
308
+ var MEDIA_KINDS = [
309
+ "image",
310
+ "video",
311
+ "audio",
312
+ "document",
313
+ "spreadsheet",
314
+ "archive"
315
+ ];
316
+ var DISPLAY_MEDIA_KINDS = [
317
+ "image",
318
+ "video",
319
+ "audio"
320
+ ];
321
+ var PUBLIC_FILE_FIELD_KINDS = [
322
+ "document",
323
+ "spreadsheet",
324
+ "archive",
325
+ "audio"
326
+ ];
327
+ function isDisplayMediaKind(kind) {
328
+ return DISPLAY_MEDIA_KINDS.includes(kind);
329
+ }
308
330
  var siteOptionSources = [
309
331
  "eventCategories",
310
332
  "venues",
@@ -468,11 +490,19 @@ var richTextFieldSchema = baseFieldSchema.extend({
468
490
  });
469
491
  var mediaFieldSchema = baseFieldSchema.extend({
470
492
  type: z2.literal("media"),
471
- mediaKinds: z2.array(
472
- z2.enum(["image", "video", "audio", "document", "spreadsheet", "archive"])
473
- ).default(["image"]),
493
+ // Keep full MEDIA_KINDS parse compatibility for pre-file-field manifests.
494
+ // New authoring surfaces should use `file.fileKinds` for document/archive
495
+ // style assets, but old saved schemas must not fail to open.
496
+ mediaKinds: z2.array(z2.enum(MEDIA_KINDS)).default(["image"]).transform((kinds) => {
497
+ const displayKinds = kinds.filter(isDisplayMediaKind);
498
+ return displayKinds.length > 0 ? displayKinds : ["image"];
499
+ }),
474
500
  aspectRatio: z2.string().optional()
475
501
  });
502
+ var fileFieldSchema = baseFieldSchema.extend({
503
+ type: z2.literal("file"),
504
+ fileKinds: z2.array(z2.enum(PUBLIC_FILE_FIELD_KINDS)).default([...PUBLIC_FILE_FIELD_KINDS])
505
+ });
476
506
  var booleanFieldSchema = baseFieldSchema.extend({
477
507
  type: z2.literal("boolean")
478
508
  });
@@ -778,6 +808,7 @@ function getFieldSchemaInternal() {
778
808
  textFieldSchema,
779
809
  richTextFieldSchema,
780
810
  mediaFieldSchema,
811
+ fileFieldSchema,
781
812
  booleanFieldSchema,
782
813
  numberFieldSchema,
783
814
  dateFieldSchema,
@@ -7210,13 +7241,18 @@ var fragmentDataSchema = z25.object({
7210
7241
  loader: dataLoaderSchema.optional()
7211
7242
  });
7212
7243
  var fragmentConfigSchema = z25.object({
7213
- id: z25.string().min(1, "Fragment id is required").regex(FRAGMENT_ID_PATTERN, "Fragment id must be alphanumeric with optional . _ - separators"),
7244
+ id: z25.string().min(1, "Fragment id is required").regex(
7245
+ FRAGMENT_ID_PATTERN,
7246
+ "Fragment id must be alphanumeric with optional . _ - separators"
7247
+ ),
7214
7248
  title: z25.string().optional(),
7215
7249
  description: z25.string().optional(),
7216
7250
  category: z25.enum(["content", "media", "interactive", "layout"]).optional(),
7217
7251
  icon: z25.string().optional(),
7218
7252
  fields: fieldSchema.array().default([]),
7219
- layout: z25.union([NodeSchema, NodeSchema.array()]).transform((value) => Array.isArray(value) ? value : [value]),
7253
+ layout: z25.union([NodeSchema, NodeSchema.array()]).transform(
7254
+ (value) => Array.isArray(value) ? value : [value]
7255
+ ),
7220
7256
  data: fragmentDataSchema.optional()
7221
7257
  });
7222
7258
  var FragmentConfigError = class extends Error {
@@ -7238,13 +7274,19 @@ function defineFragment(config) {
7238
7274
  function scopeFragmentFields(fragment, scope) {
7239
7275
  const normalizedScope = scope.trim();
7240
7276
  if (!normalizedScope) {
7241
- return fragment.fields.map((field) => cloneFieldDefinition2(field));
7277
+ return fragment.fields.map(
7278
+ (field) => cloneFieldDefinition2(field)
7279
+ );
7242
7280
  }
7243
- return fragment.fields.map((field) => prefixFieldId(field, normalizedScope));
7281
+ return fragment.fields.map(
7282
+ (field) => prefixFieldId(field, normalizedScope)
7283
+ );
7244
7284
  }
7245
7285
  function scopeFragmentLayout(fragment, scope) {
7246
7286
  const normalizedScope = scope.trim();
7247
- return fragment.layout.map((node) => cloneAndScopeNode(node, normalizedScope));
7287
+ return fragment.layout.map(
7288
+ (node) => cloneAndScopeNode(node, normalizedScope)
7289
+ );
7248
7290
  }
7249
7291
  function validateFieldDefinitions(fields3, fragmentId) {
7250
7292
  const seen = /* @__PURE__ */ new Set();
@@ -7326,7 +7368,9 @@ function cloneAndScopeNode(node, scope) {
7326
7368
  ...node
7327
7369
  };
7328
7370
  if (node.children) {
7329
- cloned.children = node.children.map((child) => cloneAndScopeNode(child, scope));
7371
+ cloned.children = node.children.map(
7372
+ (child) => cloneAndScopeNode(child, scope)
7373
+ );
7330
7374
  }
7331
7375
  if (node.props) {
7332
7376
  cloned.props = scopePropBindings(node.props, scope);
@@ -7352,7 +7396,9 @@ function scopeBinding(binding, scope) {
7352
7396
  const cloned = {
7353
7397
  ...binding,
7354
7398
  transforms: binding.transforms?.map(
7355
- (step) => ({ ...step })
7399
+ (step) => ({
7400
+ ...step
7401
+ })
7356
7402
  ) ?? []
7357
7403
  };
7358
7404
  cloned.from = scopeContentPath(binding.from, scope);
@@ -7396,7 +7442,9 @@ function scopePropValue(value, scope) {
7396
7442
  };
7397
7443
  }
7398
7444
  const result = {};
7399
- for (const [key, entry] of Object.entries(value)) {
7445
+ for (const [key, entry] of Object.entries(
7446
+ value
7447
+ )) {
7400
7448
  result[key] = scopePropValue(entry, scope);
7401
7449
  }
7402
7450
  return result;
@@ -7845,28 +7893,29 @@ var ctaRowFragment = defineFragment({
7845
7893
  });
7846
7894
 
7847
7895
  // ../blocks/src/system/fragments/library/heroCtaRow.ts
7896
+ var heroCtaFields = [
7897
+ createCtasRepeater({
7898
+ label: "Calls to action",
7899
+ itemLabel: "CTA",
7900
+ maxItems: 3,
7901
+ button: {
7902
+ variants: [
7903
+ { value: "default", label: "Default" },
7904
+ { value: "primary", label: "Primary" },
7905
+ { value: "secondary", label: "Secondary" },
7906
+ { value: "outline", label: "Outline" }
7907
+ ],
7908
+ defaultVariant: "default",
7909
+ includeSize: true,
7910
+ defaultSize: "lg"
7911
+ }
7912
+ })
7913
+ ];
7848
7914
  var heroCtaRowFragment = defineFragment({
7849
7915
  id: "heroCtaRow",
7850
7916
  title: "Hero CTAs",
7851
7917
  description: "Repeatable hero calls to action rendered inline.",
7852
- fields: [
7853
- createCtasRepeater({
7854
- label: "Calls to action",
7855
- itemLabel: "CTA",
7856
- maxItems: 3,
7857
- button: {
7858
- variants: [
7859
- { value: "default", label: "Default" },
7860
- { value: "primary", label: "Primary" },
7861
- { value: "secondary", label: "Secondary" },
7862
- { value: "outline", label: "Outline" }
7863
- ],
7864
- defaultVariant: "default",
7865
- includeSize: true,
7866
- defaultSize: "lg"
7867
- }
7868
- })
7869
- ],
7918
+ fields: heroCtaFields,
7870
7919
  layout: [
7871
7920
  ctaRow({
7872
7921
  collectionPath: "content.ctas",
@@ -12534,8 +12583,7 @@ var PUBLIC_DOWNLOAD_MEDIA_TYPES = [
12534
12583
  "document",
12535
12584
  "spreadsheet",
12536
12585
  "archive",
12537
- "audio",
12538
- "video"
12586
+ "audio"
12539
12587
  ];
12540
12588
  var hotspotSchema2 = z31.object({
12541
12589
  x: z31.number().min(0).max(1),
@@ -14110,104 +14158,107 @@ var courseDetailsBlockDefinition = {
14110
14158
  };
14111
14159
 
14112
14160
  // ../blocks/src/system/blocks/events/event-spotlight.ts
14113
- var eventSpotlightManifest = createBlockManifest({
14161
+ var eventSpotlightFields = [
14162
+ ...sectionHeaderFields("Upcoming Events"),
14163
+ {
14164
+ id: "maxEvents",
14165
+ type: "select",
14166
+ label: "Events to display",
14167
+ description: "Number of events to show",
14168
+ required: false,
14169
+ multiple: false,
14170
+ defaultValue: "3",
14171
+ options: [
14172
+ { value: "1", label: "1 event" },
14173
+ { value: "2", label: "2 events" },
14174
+ { value: "3", label: "3 events" },
14175
+ { value: "4", label: "4 events" },
14176
+ { value: "5", label: "5 events" },
14177
+ { value: "6", label: "6 events" }
14178
+ ],
14179
+ ui: { intent: "display" }
14180
+ },
14181
+ {
14182
+ id: "ctaLabel",
14183
+ type: "text",
14184
+ label: "Section button text",
14185
+ description: "Optional button shown below the spotlight cards",
14186
+ required: false,
14187
+ multiline: false,
14188
+ maxLength: 40,
14189
+ ui: { intent: "content" }
14190
+ },
14191
+ {
14192
+ id: "ctaUrl",
14193
+ type: "link",
14194
+ label: "Section button destination",
14195
+ required: false,
14196
+ ui: { intent: "display" }
14197
+ },
14198
+ layoutField,
14199
+ columnsField(["2", "3"]),
14200
+ ...cardStylingFields,
14201
+ ...eventDisplayFields,
14202
+ emptyStateField,
14203
+ sectionStylesField({
14204
+ id: "_sectionStyles",
14205
+ label: "Section styles"
14206
+ })
14207
+ ];
14208
+ var eventSpotlightLayout = [
14209
+ styledSection({
14210
+ children: sectionContainer([
14211
+ // Optional heading
14212
+ text(
14213
+ { as: "h2" },
14214
+ bind("content.heading"),
14215
+ when("content.heading")
14216
+ ),
14217
+ // Optional description
14218
+ richText(
14219
+ { className: "rb-prose rb-max-w-none" },
14220
+ bind("content.description"),
14221
+ when("content.description")
14222
+ ),
14223
+ // Event spotlight component
14224
+ el("event-spotlight", {
14225
+ events: { $bind: { from: "data.events" } },
14226
+ layout: { $bind: { from: "content.layout", fallback: "grid" } },
14227
+ columns: { $bind: { from: "content.columns", fallback: "3" } },
14228
+ cardVariant: { $bind: { from: "content.cardVariant", fallback: "default" } },
14229
+ buttonVariant: { $bind: { from: "content.buttonVariant", fallback: "primary" } },
14230
+ buttonText: { $bind: { from: "content.buttonText", fallback: "View event" } },
14231
+ ctaLabel: { $bind: { from: "content.ctaLabel" } },
14232
+ ctaUrl: { $bind: { from: "content.ctaUrl", transforms: [{ id: "links.resolve" }] } },
14233
+ showVenue: { $bind: { from: "content.showVenue" } },
14234
+ showMap: { $bind: { from: "content.showMap" } },
14235
+ showCapacity: { $bind: { from: "content.showCapacity" } },
14236
+ emptyMessage: { $bind: { from: "content.emptyMessage" } }
14237
+ })
14238
+ ], {
14239
+ gap: "lg"
14240
+ }),
14241
+ // sectionContainer (rb-container) already provides horizontal padding
14242
+ baseClass: "",
14243
+ spacing: "comfortable"
14244
+ })
14245
+ ];
14246
+ var eventSpotlightConfig = {
14114
14247
  id: "block.event-spotlight",
14115
14248
  version: "1.1.0",
14116
14249
  title: "Event Spotlight",
14117
14250
  titleSource: "heading",
14118
14251
  description: "Feature a few upcoming events on your homepage",
14119
14252
  skipSectionStyles: true,
14120
- additionalFields: [
14121
- ...sectionHeaderFields("Upcoming Events"),
14122
- {
14123
- id: "maxEvents",
14124
- type: "select",
14125
- label: "Events to display",
14126
- description: "Number of events to show",
14127
- required: false,
14128
- multiple: false,
14129
- defaultValue: "3",
14130
- options: [
14131
- { value: "1", label: "1 event" },
14132
- { value: "2", label: "2 events" },
14133
- { value: "3", label: "3 events" },
14134
- { value: "4", label: "4 events" },
14135
- { value: "5", label: "5 events" },
14136
- { value: "6", label: "6 events" }
14137
- ],
14138
- ui: { intent: "display" }
14139
- },
14140
- {
14141
- id: "ctaLabel",
14142
- type: "text",
14143
- label: "Section button text",
14144
- description: "Optional button shown below the spotlight cards",
14145
- required: false,
14146
- multiline: false,
14147
- maxLength: 40,
14148
- ui: { intent: "content" }
14149
- },
14150
- {
14151
- id: "ctaUrl",
14152
- type: "link",
14153
- label: "Section button destination",
14154
- required: false,
14155
- ui: { intent: "display" }
14156
- },
14157
- layoutField,
14158
- columnsField(["2", "3"]),
14159
- ...cardStylingFields,
14160
- ...eventDisplayFields,
14161
- emptyStateField,
14162
- sectionStylesField({
14163
- id: "_sectionStyles",
14164
- label: "Section styles"
14165
- })
14166
- ],
14253
+ additionalFields: eventSpotlightFields,
14167
14254
  styleTokens: { background: "surface", typography: "body", spacing: "md" },
14168
14255
  behaviours: { supportsThemeSwitching: true, inlineEditing: false, animation: false, paletteHidden: false },
14169
14256
  category: "content",
14170
14257
  tags: ["events", "featured", "homepage", "spotlight", "upcoming"],
14171
14258
  icon: "Star",
14172
- layout: [
14173
- styledSection({
14174
- children: sectionContainer([
14175
- // Optional heading
14176
- text(
14177
- { as: "h2" },
14178
- bind("content.heading"),
14179
- when("content.heading")
14180
- ),
14181
- // Optional description
14182
- richText(
14183
- { className: "rb-prose rb-max-w-none" },
14184
- bind("content.description"),
14185
- when("content.description")
14186
- ),
14187
- // Event spotlight component
14188
- el("event-spotlight", {
14189
- events: { $bind: { from: "data.events" } },
14190
- layout: { $bind: { from: "content.layout", fallback: "grid" } },
14191
- columns: { $bind: { from: "content.columns", fallback: "3" } },
14192
- cardVariant: { $bind: { from: "content.cardVariant", fallback: "default" } },
14193
- buttonVariant: { $bind: { from: "content.buttonVariant", fallback: "primary" } },
14194
- buttonText: { $bind: { from: "content.buttonText", fallback: "View event" } },
14195
- ctaLabel: { $bind: { from: "content.ctaLabel" } },
14196
- ctaUrl: { $bind: { from: "content.ctaUrl", transforms: [{ id: "links.resolve" }] } },
14197
- showVenue: { $bind: { from: "content.showVenue" } },
14198
- showMap: { $bind: { from: "content.showMap" } },
14199
- showCapacity: { $bind: { from: "content.showCapacity" } },
14200
- emptyMessage: { $bind: { from: "content.emptyMessage" } }
14201
- })
14202
- ], {
14203
- gap: "lg"
14204
- }),
14205
- // sectionContainer (rb-container) already provides horizontal padding
14206
- baseClass: "",
14207
- spacing: "comfortable"
14208
- })
14209
- ]
14210
- });
14259
+ layout: eventSpotlightLayout
14260
+ };
14261
+ var eventSpotlightManifest = createBlockManifest(eventSpotlightConfig);
14211
14262
  var eventSpotlightBlockDefinition = {
14212
14263
  manifest: eventSpotlightManifest,
14213
14264
  dataSchemas: {
@@ -16821,18 +16872,18 @@ var fileDownloadManifest = createBlockManifest({
16821
16872
  title: "File Download",
16822
16873
  titleSource: ["title", "file.filename"],
16823
16874
  category: "media",
16824
- description: "Add a simple public download link for a document, ZIP, audio, or video file.",
16875
+ description: "Add a simple public download link for a document, ZIP, or audio file.",
16825
16876
  tags: ["file", "download", "document", "zip", "media"],
16826
16877
  icon: "Download",
16827
16878
  includeContainerStyles: true,
16828
16879
  additionalFields: [
16829
16880
  {
16830
16881
  id: "file",
16831
- type: "media",
16882
+ type: "file",
16832
16883
  label: "Download file",
16833
16884
  description: "Choose the file visitors can download.",
16834
16885
  required: false,
16835
- mediaKinds: [...PUBLIC_DOWNLOAD_MEDIA_TYPES],
16886
+ fileKinds: [...PUBLIC_DOWNLOAD_MEDIA_TYPES],
16836
16887
  ui: { intent: "content" }
16837
16888
  },
16838
16889
  {
@@ -20551,7 +20602,14 @@ var titleSourceValueSchema = z54.union([
20551
20602
  ]);
20552
20603
 
20553
20604
  // src/manifest/validation/output-schema.ts
20554
- var blockCategoryEnum2 = z55.enum(["marketing", "content", "blog", "media", "layout", "interactive"]);
20605
+ var blockCategoryEnum2 = z55.enum([
20606
+ "marketing",
20607
+ "content",
20608
+ "blog",
20609
+ "media",
20610
+ "layout",
20611
+ "interactive"
20612
+ ]);
20555
20613
  var llmColorValue = z55.object({
20556
20614
  name: z55.string(),
20557
20615
  hex: z55.string()
@@ -20569,8 +20627,16 @@ var llmThemePatchValue = z55.union([
20569
20627
  z55.array(llmThemePrimitive)
20570
20628
  ]);
20571
20629
  var llmThemePatchOpSchema = z55.discriminatedUnion("op", [
20572
- z55.object({ op: z55.literal("add"), path: z55.string().min(1), value: llmThemePatchValue }),
20573
- z55.object({ op: z55.literal("replace"), path: z55.string().min(1), value: llmThemePatchValue }),
20630
+ z55.object({
20631
+ op: z55.literal("add"),
20632
+ path: z55.string().min(1),
20633
+ value: llmThemePatchValue
20634
+ }),
20635
+ z55.object({
20636
+ op: z55.literal("replace"),
20637
+ path: z55.string().min(1),
20638
+ value: llmThemePatchValue
20639
+ }),
20574
20640
  z55.object({ op: z55.literal("remove"), path: z55.string().min(1) })
20575
20641
  ]);
20576
20642
  var llmCssDeclaration = z55.object({
@@ -20647,7 +20713,11 @@ var llmRichTextFieldSchema = llmBaseFieldSchema.extend({
20647
20713
  });
20648
20714
  var llmMediaFieldSchema = llmBaseFieldSchema.extend({
20649
20715
  type: z55.literal("media"),
20650
- mediaKinds: z55.array(z55.enum(["image", "video", "audio", "document", "spreadsheet", "archive"])).nullable()
20716
+ mediaKinds: z55.array(z55.enum(["image", "video", "audio"])).nullable()
20717
+ });
20718
+ var llmFileFieldSchema = llmBaseFieldSchema.extend({
20719
+ type: z55.literal("file"),
20720
+ fileKinds: z55.array(z55.enum(["document", "spreadsheet", "archive", "audio"])).nullable()
20651
20721
  });
20652
20722
  var llmBooleanFieldSchema = llmBaseFieldSchema.extend({
20653
20723
  type: z55.literal("boolean")
@@ -20657,10 +20727,12 @@ var llmLinkFieldSchema = llmBaseFieldSchema.extend({
20657
20727
  });
20658
20728
  var llmSelectFieldSchema = llmBaseFieldSchema.extend({
20659
20729
  type: z55.literal("select"),
20660
- options: z55.array(z55.object({
20661
- value: z55.string(),
20662
- label: z55.string()
20663
- })),
20730
+ options: z55.array(
20731
+ z55.object({
20732
+ value: z55.string(),
20733
+ label: z55.string()
20734
+ })
20735
+ ),
20664
20736
  multiple: z55.boolean().nullable()
20665
20737
  });
20666
20738
  var llmNumberFieldSchema = llmBaseFieldSchema.extend({
@@ -20696,6 +20768,7 @@ var llmFieldSchema = z55.union([
20696
20768
  llmTextFieldSchema,
20697
20769
  llmRichTextFieldSchema,
20698
20770
  llmMediaFieldSchema,
20771
+ llmFileFieldSchema,
20699
20772
  llmBooleanFieldSchema,
20700
20773
  llmLinkFieldSchema,
20701
20774
  llmSelectFieldSchema,
@@ -20706,9 +20779,11 @@ var llmFieldSchema = z55.union([
20706
20779
  var llmBindingSchema = z55.object({
20707
20780
  from: z55.string(),
20708
20781
  fallback: z55.string().nullable(),
20709
- transforms: z55.array(z55.object({
20710
- id: z55.string()
20711
- })).nullable()
20782
+ transforms: z55.array(
20783
+ z55.object({
20784
+ id: z55.string()
20785
+ })
20786
+ ).nullable()
20712
20787
  });
20713
20788
  var llmRepeatSchema = z55.object({
20714
20789
  collection: z55.object({ from: z55.string() }),
@@ -20767,11 +20842,17 @@ function collectValidationErrors(output) {
20767
20842
  const blockIds = output.blocks.map((b) => b.id);
20768
20843
  const singleBlockId = blockIds.length === 1 ? blockIds[0] ?? null : null;
20769
20844
  for (const block of output.blocks) {
20770
- const layoutErrors = validateLayoutDeep(block.layout, block.id, block.fields);
20845
+ const layoutErrors = validateLayoutDeep(
20846
+ block.layout,
20847
+ block.id,
20848
+ block.fields
20849
+ );
20771
20850
  errors.push(...layoutErrors);
20772
20851
  const fieldInfo = collectFieldInfo(block.fields);
20773
20852
  const used = collectUsedFieldRefs(block.layout);
20774
- const unusedTopLevelFields = Array.from(fieldInfo.topLevelFieldIds).filter((id) => !used.topLevel.has(id));
20853
+ const unusedTopLevelFields = Array.from(fieldInfo.topLevelFieldIds).filter(
20854
+ (id) => !used.topLevel.has(id)
20855
+ );
20775
20856
  if (unusedTopLevelFields.length > 0) {
20776
20857
  errors.push({
20777
20858
  path: `blocks["${block.id}"].fields`,
@@ -20779,10 +20860,15 @@ function collectValidationErrors(output) {
20779
20860
  suggestion: `Every field should be bound to a layout node. Add $bind: { from: "content.${unusedTopLevelFields[0]}" } to an appropriate node, or remove unused fields.`
20780
20861
  });
20781
20862
  }
20782
- for (const [repeaterFieldId, nestedFieldIds] of fieldInfo.repeaterNestedFieldIds.entries()) {
20863
+ for (const [
20864
+ repeaterFieldId,
20865
+ nestedFieldIds
20866
+ ] of fieldInfo.repeaterNestedFieldIds.entries()) {
20783
20867
  if (!used.topLevel.has(repeaterFieldId)) continue;
20784
20868
  const usedNested = used.nestedByRepeater.get(repeaterFieldId) ?? /* @__PURE__ */ new Set();
20785
- const missingNested = Array.from(nestedFieldIds).filter((id) => !usedNested.has(id));
20869
+ const missingNested = Array.from(nestedFieldIds).filter(
20870
+ (id) => !usedNested.has(id)
20871
+ );
20786
20872
  if (missingNested.length === 0) continue;
20787
20873
  const itemName = used.itemNameByRepeater.get(repeaterFieldId) ?? "item";
20788
20874
  errors.push({
@@ -20795,10 +20881,18 @@ function collectValidationErrors(output) {
20795
20881
  errors.push(...missingBindings);
20796
20882
  errors.push(...validateLinkFieldDefaultValues(block));
20797
20883
  errors.push(...findMissingLinkHrefs(block.layout, block.id, block.fields));
20798
- errors.push(...detectRedundantLinkLabels(block.layout, block.id, block.fields));
20884
+ errors.push(
20885
+ ...detectRedundantLinkLabels(block.layout, block.id, block.fields)
20886
+ );
20799
20887
  }
20800
20888
  if (output.customCssRules && output.customCssRules.length > 0) {
20801
- errors.push(...validateCustomCssRules(output.customCssRules, singleBlockId, output.blocks));
20889
+ errors.push(
20890
+ ...validateCustomCssRules(
20891
+ output.customCssRules,
20892
+ singleBlockId,
20893
+ output.blocks
20894
+ )
20895
+ );
20802
20896
  }
20803
20897
  return errors;
20804
20898
  }
@@ -20874,7 +20968,9 @@ function resolveFieldTypeForFrom(allFields, from, repeatStack) {
20874
20968
  const segments = from.slice(prefix.length).split(".").filter(Boolean);
20875
20969
  const [nestedId, ...rest] = segments;
20876
20970
  if (!nestedId) return null;
20877
- let current = ctx.fields.find((f) => f.id === nestedId);
20971
+ let current = ctx.fields.find(
20972
+ (f) => f.id === nestedId
20973
+ );
20878
20974
  if (!current) return null;
20879
20975
  for (const seg of rest) {
20880
20976
  const nested = getNestedFields(current);
@@ -20890,7 +20986,9 @@ function bindingHasTransform(bind2, id) {
20890
20986
  if (!bind2) return false;
20891
20987
  const transforms = bind2.transforms;
20892
20988
  if (!Array.isArray(transforms)) return false;
20893
- return transforms.some((t) => t && typeof t === "object" && t.id === id);
20989
+ return transforms.some(
20990
+ (t) => t && typeof t === "object" && t.id === id
20991
+ );
20894
20992
  }
20895
20993
  function extractNestedHrefBindFromProps(props2) {
20896
20994
  if (!props2 || typeof props2 !== "object") return null;
@@ -20951,7 +21049,15 @@ function findMissingLinkHrefs(node, blockId, fields3, path2 = "layout", repeatSt
20951
21049
  }
20952
21050
  if ("children" in n && Array.isArray(n.children)) {
20953
21051
  for (let i = 0; i < n.children.length; i++) {
20954
- errors.push(...findMissingLinkHrefs(n.children[i], blockId, fields3, `${path2}.children[${i}]`, nextRepeatStack));
21052
+ errors.push(
21053
+ ...findMissingLinkHrefs(
21054
+ n.children[i],
21055
+ blockId,
21056
+ fields3,
21057
+ `${path2}.children[${i}]`,
21058
+ nextRepeatStack
21059
+ )
21060
+ );
20955
21061
  }
20956
21062
  }
20957
21063
  return errors;
@@ -20975,22 +21081,30 @@ function detectRedundantLinkLabels(node, blockId, fields3, path2 = "layout", rep
20975
21081
  }
20976
21082
  if (Array.isArray(n.children) && n.children.length > 1) {
20977
21083
  const children = n.children;
20978
- const redundancies = findRedundantLinkTextSiblings(children, nextRepeatStack, path2);
20979
- errors.push(...redundancies.map((r) => ({
20980
- path: r.path,
20981
- message: `Block "${blockId}": ${r.message}`,
20982
- suggestion: r.suggestion
20983
- })));
21084
+ const redundancies = findRedundantLinkTextSiblings(
21085
+ children,
21086
+ nextRepeatStack,
21087
+ path2
21088
+ );
21089
+ errors.push(
21090
+ ...redundancies.map((r) => ({
21091
+ path: r.path,
21092
+ message: `Block "${blockId}": ${r.message}`,
21093
+ suggestion: r.suggestion
21094
+ }))
21095
+ );
20984
21096
  }
20985
21097
  if (Array.isArray(n.children)) {
20986
21098
  for (let i = 0; i < n.children.length; i++) {
20987
- errors.push(...detectRedundantLinkLabels(
20988
- n.children[i],
20989
- blockId,
20990
- fields3,
20991
- `${path2}.children[${i}]`,
20992
- nextRepeatStack
20993
- ));
21099
+ errors.push(
21100
+ ...detectRedundantLinkLabels(
21101
+ n.children[i],
21102
+ blockId,
21103
+ fields3,
21104
+ `${path2}.children[${i}]`,
21105
+ nextRepeatStack
21106
+ )
21107
+ );
20994
21108
  }
20995
21109
  }
20996
21110
  return errors;
@@ -21044,7 +21158,8 @@ function checkRedundantLabelBinding(linkBindFrom, textBindFrom, repeatStack) {
21044
21158
  const ctx = repeatStack[i];
21045
21159
  if (!ctx) continue;
21046
21160
  const prefix = `${ctx.itemName}.`;
21047
- if (!linkBindFrom.startsWith(prefix) || !textBindFrom.startsWith(prefix)) continue;
21161
+ if (!linkBindFrom.startsWith(prefix) || !textBindFrom.startsWith(prefix))
21162
+ continue;
21048
21163
  const linkFieldId = linkBindFrom.slice(prefix.length).split(".")[0] ?? "";
21049
21164
  const textFieldId = textBindFrom.slice(prefix.length).split(".")[0] ?? "";
21050
21165
  const linkField = ctx.fields.find((f) => f.id === linkFieldId);
@@ -21069,7 +21184,10 @@ function validateCustomCssRules(rules, singleBlockId, blocks) {
21069
21184
  const layoutClassNamesByBlockId = /* @__PURE__ */ new Map();
21070
21185
  const layoutNodeInfosByBlockId = /* @__PURE__ */ new Map();
21071
21186
  for (const block of blocks) {
21072
- layoutClassNamesByBlockId.set(block.id, collectLayoutClassNames(block.layout));
21187
+ layoutClassNamesByBlockId.set(
21188
+ block.id,
21189
+ collectLayoutClassNames(block.layout)
21190
+ );
21073
21191
  layoutNodeInfosByBlockId.set(block.id, collectLayoutNodeInfos(block.layout));
21074
21192
  }
21075
21193
  const missingLayoutHooksByBlockId = /* @__PURE__ */ new Map();
@@ -21138,11 +21256,18 @@ function validateCustomCssRules(rules, singleBlockId, blocks) {
21138
21256
  }
21139
21257
  }
21140
21258
  }
21141
- for (const [blockId, missingClassNames] of missingLayoutHooksByBlockId.entries()) {
21259
+ for (const [
21260
+ blockId,
21261
+ missingClassNames
21262
+ ] of missingLayoutHooksByBlockId.entries()) {
21142
21263
  const missing = Array.from(missingClassNames).sort();
21143
21264
  if (missing.length === 0) continue;
21144
21265
  const layoutNodeInfos = layoutNodeInfosByBlockId.get(blockId) ?? [];
21145
- const attachSuggestions = buildClassHookAttachSuggestions(blockId, missing, layoutNodeInfos);
21266
+ const attachSuggestions = buildClassHookAttachSuggestions(
21267
+ blockId,
21268
+ missing,
21269
+ layoutNodeInfos
21270
+ );
21146
21271
  const attachSuggestionsLine = attachSuggestions ? `
21147
21272
 
21148
21273
  Suggested attachments:
@@ -21176,7 +21301,9 @@ function buildClassHookAttachSuggestions(blockId, missingClassNames, nodes) {
21176
21301
  continue;
21177
21302
  }
21178
21303
  if ((lower.includes("headline") || lower.includes("title")) && (headlineNode ?? firstStack)) {
21179
- lines.push(`- ${className} \u2192 add to ${headlineNode ?? firstStack}.props.className`);
21304
+ lines.push(
21305
+ `- ${className} \u2192 add to ${headlineNode ?? firstStack}.props.className`
21306
+ );
21180
21307
  continue;
21181
21308
  }
21182
21309
  if (lower.includes("highlight") && highlightNode) {
@@ -21184,7 +21311,9 @@ function buildClassHookAttachSuggestions(blockId, missingClassNames, nodes) {
21184
21311
  continue;
21185
21312
  }
21186
21313
  if ((lower.includes("body") || lower.includes("copy") || lower.includes("paragraph")) && (paragraphNode ?? firstStack)) {
21187
- lines.push(`- ${className} \u2192 add to ${paragraphNode ?? firstStack}.props.className`);
21314
+ lines.push(
21315
+ `- ${className} \u2192 add to ${paragraphNode ?? firstStack}.props.className`
21316
+ );
21188
21317
  continue;
21189
21318
  }
21190
21319
  lines.push(`- ${className} \u2192 add to ${rootPath}.props.className (fallback)`);
@@ -21192,7 +21321,10 @@ function buildClassHookAttachSuggestions(blockId, missingClassNames, nodes) {
21192
21321
  return lines.slice(0, 8).join("\n");
21193
21322
  }
21194
21323
  function extractSelectorClasses(selector) {
21195
- const withoutDataBlockAttrs = selector.replace(/\[data-block=(["']).*?\1\]/g, "");
21324
+ const withoutDataBlockAttrs = selector.replace(
21325
+ /\[data-block=(["']).*?\1\]/g,
21326
+ ""
21327
+ );
21196
21328
  const classes = /* @__PURE__ */ new Set();
21197
21329
  const re = /\.([_a-zA-Z][-_a-zA-Z0-9]*)/g;
21198
21330
  let match;
@@ -21209,7 +21341,9 @@ function extractBlockIdFromSelector(selector) {
21209
21341
  return match?.[2] ?? null;
21210
21342
  }
21211
21343
  function validateNodeType(nodeType, path2, ctx) {
21212
- if (!ALLOWED_NODE_TYPES.includes(nodeType)) {
21344
+ if (!ALLOWED_NODE_TYPES.includes(
21345
+ nodeType
21346
+ )) {
21213
21347
  return {
21214
21348
  path: `${path2}.type`,
21215
21349
  message: `Block "${ctx.blockId}": Unknown node type "${nodeType}" at ${path2}`,
@@ -21223,7 +21357,9 @@ function validateTransforms(transforms, path2, ctx) {
21223
21357
  for (let i = 0; i < transforms.length; i++) {
21224
21358
  const transform = transforms[i];
21225
21359
  if (transform.id && typeof transform.id === "string") {
21226
- if (!ALLOWED_TRANSFORM_IDS.includes(transform.id)) {
21360
+ if (!ALLOWED_TRANSFORM_IDS.includes(
21361
+ transform.id
21362
+ )) {
21227
21363
  errors.push({
21228
21364
  path: `${path2}.transforms[${i}].id`,
21229
21365
  message: `Block "${ctx.blockId}": Unknown transform "${transform.id}"`,
@@ -21272,16 +21408,28 @@ function validateLayoutNode(node, path2, ctx) {
21272
21408
  if (typeError) errors.push(typeError);
21273
21409
  }
21274
21410
  if ("$bind" in n && n.$bind && typeof n.$bind === "object") {
21275
- const bindErrors = validateBinding(n.$bind, `${path2}.$bind`, ctx);
21411
+ const bindErrors = validateBinding(
21412
+ n.$bind,
21413
+ `${path2}.$bind`,
21414
+ ctx
21415
+ );
21276
21416
  errors.push(...bindErrors);
21277
21417
  }
21278
21418
  if ("props" in n && n.props && typeof n.props === "object") {
21279
- const propsErrors = validatePropsBindings(n.props, `${path2}.props`, ctx);
21419
+ const propsErrors = validatePropsBindings(
21420
+ n.props,
21421
+ `${path2}.props`,
21422
+ ctx
21423
+ );
21280
21424
  errors.push(...propsErrors);
21281
21425
  }
21282
21426
  if ("children" in n && Array.isArray(n.children)) {
21283
21427
  for (let i = 0; i < n.children.length; i++) {
21284
- const childErrors = validateLayoutNode(n.children[i], `${path2}.children[${i}]`, ctx);
21428
+ const childErrors = validateLayoutNode(
21429
+ n.children[i],
21430
+ `${path2}.children[${i}]`,
21431
+ ctx
21432
+ );
21285
21433
  errors.push(...childErrors);
21286
21434
  }
21287
21435
  }
@@ -21295,7 +21443,11 @@ function validatePropsBindings(props2, path2, ctx) {
21295
21443
  const bindErrors = validateBinding(bind2, `${path2}.${key}.$bind`, ctx);
21296
21444
  errors.push(...bindErrors);
21297
21445
  } else if (value && typeof value === "object" && !Array.isArray(value)) {
21298
- const nestedErrors = validatePropsBindings(value, `${path2}.${key}`, ctx);
21446
+ const nestedErrors = validatePropsBindings(
21447
+ value,
21448
+ `${path2}.${key}`,
21449
+ ctx
21450
+ );
21299
21451
  errors.push(...nestedErrors);
21300
21452
  }
21301
21453
  }
@@ -21336,7 +21488,10 @@ function collectFieldInfo(fields3) {
21336
21488
  topLevelFieldIds.add(field.id);
21337
21489
  if (field.type === "repeater" && field.schema && typeof field.schema === "object" && "fields" in field.schema) {
21338
21490
  const nestedFields = field.schema.fields ?? [];
21339
- repeaterNestedFieldIds.set(field.id, new Set(nestedFields.map((f) => f.id)));
21491
+ repeaterNestedFieldIds.set(
21492
+ field.id,
21493
+ new Set(nestedFields.map((f) => f.id))
21494
+ );
21340
21495
  }
21341
21496
  }
21342
21497
  return { topLevelFieldIds, repeaterNestedFieldIds };
@@ -21456,7 +21611,11 @@ function findMissingBindings(node, blockId, path2 = "layout") {
21456
21611
  }
21457
21612
  if ("children" in n && Array.isArray(n.children)) {
21458
21613
  for (let i = 0; i < n.children.length; i++) {
21459
- const childErrors = findMissingBindings(n.children[i], blockId, `${path2}.children[${i}]`);
21614
+ const childErrors = findMissingBindings(
21615
+ n.children[i],
21616
+ blockId,
21617
+ `${path2}.children[${i}]`
21618
+ );
21460
21619
  errors.push(...childErrors);
21461
21620
  }
21462
21621
  }
@@ -21467,7 +21626,9 @@ function getMaxDepth(node, currentDepth = 0) {
21467
21626
  const n = node;
21468
21627
  if (!n.children || !Array.isArray(n.children)) return currentDepth;
21469
21628
  if (n.children.length === 0) return currentDepth;
21470
- return Math.max(...n.children.map((child) => getMaxDepth(child, currentDepth + 1)));
21629
+ return Math.max(
21630
+ ...n.children.map((child) => getMaxDepth(child, currentDepth + 1))
21631
+ );
21471
21632
  }
21472
21633
  var CONTAINER_NODE_TYPES = ["section", "stack", "grid", "div", "inline"];
21473
21634
  function detectPotentialTruncation(node, maxAllowedDepth, currentDepth = 0, path2 = "layout") {
@@ -21482,7 +21643,12 @@ function detectPotentialTruncation(node, maxAllowedDepth, currentDepth = 0, path
21482
21643
  if (n.children && Array.isArray(n.children)) {
21483
21644
  n.children.forEach((child, i) => {
21484
21645
  truncatedPaths.push(
21485
- ...detectPotentialTruncation(child, maxAllowedDepth, currentDepth + 1, `${path2}.children[${i}]`)
21646
+ ...detectPotentialTruncation(
21647
+ child,
21648
+ maxAllowedDepth,
21649
+ currentDepth + 1,
21650
+ `${path2}.children[${i}]`
21651
+ )
21486
21652
  );
21487
21653
  });
21488
21654
  }
@@ -21497,14 +21663,20 @@ function formatValidationFeedback(errors, output) {
21497
21663
  if (truncatedPaths.length > 0) {
21498
21664
  lines.push("### DEPTH LIMIT WARNING");
21499
21665
  lines.push("");
21500
- lines.push(`The layout reached the maximum depth of ${MAX_LAYOUT_DEPTH} levels.`);
21666
+ lines.push(
21667
+ `The layout reached the maximum depth of ${MAX_LAYOUT_DEPTH} levels.`
21668
+ );
21501
21669
  lines.push("These container nodes at the depth limit have no children:");
21502
21670
  for (const path2 of truncatedPaths) {
21503
21671
  lines.push(` \u26A0\uFE0F ${path2}`);
21504
21672
  }
21505
21673
  lines.push("");
21506
- lines.push("**FIX**: Flatten your layout structure or use $repeat for lists.");
21507
- lines.push("- Combine wrapper nodes where possible (e.g., section > stack > div \u2192 section > stack)");
21674
+ lines.push(
21675
+ "**FIX**: Flatten your layout structure or use $repeat for lists."
21676
+ );
21677
+ lines.push(
21678
+ "- Combine wrapper nodes where possible (e.g., section > stack > div \u2192 section > stack)"
21679
+ );
21508
21680
  lines.push("- Use $repeat instead of manual nesting for repeated items");
21509
21681
  lines.push(`- Current max depth: ${maxDepth}, limit: ${MAX_LAYOUT_DEPTH}`);
21510
21682
  lines.push("");
@@ -21513,13 +21685,19 @@ function formatValidationFeedback(errors, output) {
21513
21685
  if (errors.length === 0) {
21514
21686
  return lines.length > 0 ? lines.join("\n") : "";
21515
21687
  }
21516
- lines.push("## CRITICAL: Your output failed validation. You MUST fix these errors.");
21688
+ lines.push(
21689
+ "## CRITICAL: Your output failed validation. You MUST fix these errors."
21690
+ );
21517
21691
  lines.push("");
21518
21692
  lines.push("Your previous output had the following problems:");
21519
21693
  lines.push("");
21520
21694
  const missingBindErrors = errors.filter((e) => e.message.includes("no $bind"));
21521
- const unusedFieldErrors = errors.filter((e) => e.message.includes("never used"));
21522
- const otherErrors = errors.filter((e) => !e.message.includes("no $bind") && !e.message.includes("never used"));
21695
+ const unusedFieldErrors = errors.filter(
21696
+ (e) => e.message.includes("never used")
21697
+ );
21698
+ const otherErrors = errors.filter(
21699
+ (e) => !e.message.includes("no $bind") && !e.message.includes("never used")
21700
+ );
21523
21701
  if (missingBindErrors.length > 0) {
21524
21702
  lines.push("### MISSING $bind ERRORS (nodes that display nothing)");
21525
21703
  lines.push("");
@@ -21527,16 +21705,22 @@ function formatValidationFeedback(errors, output) {
21527
21705
  lines.push(`\u274C ${error.message}`);
21528
21706
  }
21529
21707
  lines.push("");
21530
- lines.push("**FIX**: Every text, richText, media, button, and link node MUST have $bind.");
21708
+ lines.push(
21709
+ "**FIX**: Every text, richText, media, button, and link node MUST have $bind."
21710
+ );
21531
21711
  lines.push("");
21532
21712
  lines.push("WRONG (displays nothing):");
21533
21713
  lines.push("```json");
21534
- lines.push('{ "type": "text", "props": { "as": "h1" }, "children": null, "$bind": null }');
21714
+ lines.push(
21715
+ '{ "type": "text", "props": { "as": "h1" }, "children": null, "$bind": null }'
21716
+ );
21535
21717
  lines.push("```");
21536
21718
  lines.push("");
21537
21719
  lines.push("CORRECT (bound to field):");
21538
21720
  lines.push("```json");
21539
- lines.push('{ "type": "text", "props": { "as": "h1" }, "children": null, "$bind": { "from": "content.headline", "fallback": null, "transforms": null } }');
21721
+ lines.push(
21722
+ '{ "type": "text", "props": { "as": "h1" }, "children": null, "$bind": { "from": "content.headline", "fallback": null, "transforms": null } }'
21723
+ );
21540
21724
  lines.push("```");
21541
21725
  lines.push("");
21542
21726
  }
@@ -21555,11 +21739,15 @@ function formatValidationFeedback(errors, output) {
21555
21739
  if (fieldNames.length > 0) {
21556
21740
  lines.push("**CHECKLIST - Add a binding for EACH of these fields:**");
21557
21741
  for (const field of fieldNames) {
21558
- lines.push(`- [ ] ${field} \u2192 text node with $bind: { from: "content.${field}" }`);
21742
+ lines.push(
21743
+ `- [ ] ${field} \u2192 text node with $bind: { from: "content.${field}" }`
21744
+ );
21559
21745
  }
21560
21746
  lines.push("");
21561
21747
  }
21562
- lines.push("**COMMON MISTAKE**: Creating container nodes with `children: null` instead of actual bindings.");
21748
+ lines.push(
21749
+ "**COMMON MISTAKE**: Creating container nodes with `children: null` instead of actual bindings."
21750
+ );
21563
21751
  lines.push("");
21564
21752
  lines.push("This is WRONG (container with no content inside):");
21565
21753
  lines.push("```json");
@@ -21569,12 +21757,18 @@ function formatValidationFeedback(errors, output) {
21569
21757
  lines.push("This is CORRECT (container with bound text node):");
21570
21758
  lines.push("```json");
21571
21759
  lines.push('{ "type": "inline", "children": [');
21572
- lines.push(' { "type": "text", "$bind": { "from": "content.fieldName", "fallback": null, "transforms": null }, "props": null, "$repeat": null, "$when": null, "children": null }');
21760
+ lines.push(
21761
+ ' { "type": "text", "$bind": { "from": "content.fieldName", "fallback": null, "transforms": null }, "props": null, "$repeat": null, "$when": null, "children": null }'
21762
+ );
21573
21763
  lines.push("] }");
21574
21764
  lines.push("```");
21575
21765
  lines.push("");
21576
- lines.push("**FIX**: Every field you define MUST be used in a $bind somewhere in the layout.");
21577
- lines.push('If you have a field "highlight", you MUST have a node with $bind: { from: "content.highlight" }');
21766
+ lines.push(
21767
+ "**FIX**: Every field you define MUST be used in a $bind somewhere in the layout."
21768
+ );
21769
+ lines.push(
21770
+ 'If you have a field "highlight", you MUST have a node with $bind: { from: "content.highlight" }'
21771
+ );
21578
21772
  lines.push("");
21579
21773
  }
21580
21774
  if (otherErrors.length > 0) {