riverbankcms 0.22.0 → 0.22.1

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.
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { $ as createNavigationRef, $n as cardRoleSchema, $r as __toESM, A as eventManagementTiming, An as runtimeMediaSchema, Ar as backgroundColorStyle, At as BREAKPOINT_SM, B as bookingPaymentCollectionOptionSchema, Bn as deriveVideoSlotPolicy, Br as PUBLIC_BUTTON_ROLES, Bt as themeButtonSizeClassName, C as stableStringify, Cn as getDirectImageUrlForRelativePath, Cr as tileTreatmentValues, Ct as themeSchema, Dn as mediaPosterSchema, Dr as surfaceDecorationColors, Dt as renderHeaderPaintTargetRules, E as mapEventCommandContent, En as isSiteScopedDirectImageStoragePath, Er as resolveSurface, Et as getHeaderPaintTargets, F as pageIdentifierForCommand, Fn as buildMediaIdentifierFromFilename, Fr as opaqueSurfaceDeclarations, Ft as getComponentShadowVar, G as createBlockRef, Gr as surfaceForegroundInputFromResolved, Gt as buildTextContrastShadow, H as require_jsx_runtime, Hr as surfaceToneFromForegroundToken, Ht as parseThemeForPersistence, I as pagePathForCommand, In as parseCanonicalMediaIdentifier, Ir as isCssColorLiteral, It as ICON_CONFIG, J as createContentEntryRef, Jr as expandPalette, Jt as arrangementSlotNames, K as createBlocksRef, Kn as parseRelativeStoragePath, Kr as surfaceForegroundStyle, Kt as generateNavLinkCSS, L as hasConfiguredBookingVertical, Ln as parseMediaIdentifier, Lr as resolveOnColorForThemeToken, Lt as generateDefaultButtonSystem, M as blockOrderIdentifiersForCommand, Mn as resolveManagedMediaReference, Mr as mergeStyles, Mt as MQ_MD_DOWN, N as entryIdentifierForCommand, Nn as mediaTypeDiscriminants, Nr as rgbColorValue, Nt as headerVariantValues, On as mediaSchema, Or as resolveSurfaceForeground, Ot as BREAKPOINT_LG, P as pageIdentifierForBlockCommand, Pn as mediaIdentifierSchema, Pr as textColorStyle, Pt as getComponentBorderWidthPx, Q as createFormRef, Qn as renderCssDeclarations, Qr as __commonJSMin, R as eventCommandScheduleSchema, Rr as resolveThemeColorReferenceValue, Rt as parseThemeButtonVariantId, S as sortRecord, Sn as resolveMediaTransformForAspect, Sr as testimonialsCompositionTreatmentValues, St as deriveSiteStyleBlockPolicy, Tn as getImageUrl, Tr as visibleHeadingDecorationFamilyValues, Tt as generateMobileOverlayBackdropFilterFix, U as require_react, Un as getMimeType, Ur as defaultThemeTokens, V as bookingPaymentTermsSchema, Vn as resolveVideoSelectionIntentForMediaField, Vr as selectLogoVariant, Vt as themeButtonVariantClassNames, W as ianaTimezoneSchema, Wn as asRecord, Wr as surfaceForegroundDeclarations, Wt as footerForegroundCandidates, X as createEventSeriesRef, Xn as managementMediaLibraryUploadLimitForMimeType, Xr as parseThemeColorToken, Xt as resolveLookSlotSurfaces, Y as createEventCategoryRef, Yn as classifyKnownMimeType, Yr as getTextColorForBackground, Yt as resolveItemSurface, Z as createFooterRef, Zn as parseSafeDownloadFilename, Zr as parseBlockColorToken, _n as looksLikeUuid, _r as featureCardCompositionTreatmentValues, _t as requireCuratedSiteStyle, an as parseCanonicalRuntimeBackground, ar as cardListKnobBlocks, at as effectiveHeaderNavLinkStyle, b as isEventVisibility, bn as isExplicitRectCrop, br as makeBoundaryId, bt as getGradientPreset, c as customLinkSchema$1, cn as boxBackgroundOverlaySchema, cr as asLayoutVariantKey, ct as transparentHeaderForegroundInput, d as internalLinkSchema, dn as siteChromeBackgroundOverrideOwner, dr as assertNever$11, dt as readHeaderNavFontSizePreference, en as headerRootPaint, er as cardVariantRoleValues, et as createPageRef, f as internalResolvedLinkSchema, fn as stripStaleTransitionStyleGroup, fr as defineBlockDesignCapability, ft as arrangementManifestFromVariants, g as linkSchema, gn as deleteEnvSpecificKeys, gr as eyebrowTreatmentValues, gt as resolvePublishedLook, h as isResolvedInternalLinkEntityType, hn as walkAndCollectImageAttrs, hr as eventListTreatmentValues, ht as resolveModeArrangement, in as deriveSectionGround, ir as cardListFamilyBlocks, j as blockIdentifierForCommand, jn as buildRenderableMediaAssetIndex, jr as borderColorStyle, jt as BREAKPOINT_XL, kn as persistableMediaSchema, kr as TokenResolver, kt as BREAKPOINT_MD, l as entryLinkSchema$1, ln as boxBackgroundSchema, lt as readOpeningForegroundProjection, mn as summarizePortableMediaReferences, mr as defineNoBlockDesignCapability, mt as resolveEffectiveArrangement, nn as composeBoundary, nr as defineBlockDesignMetadata, nt as createVenueRef, o as classifySdkEventBookingOffer, on as resolveSectionBackground, ot as headerForegroundCandidates, p as internalRouteOnlyLinkSchema, pn as collectPortableMediaReferencesFromContent, pr as defineFrameCapability, pt as readStoredMode, q as createCommandRef, qn as FILE_SIZE_LIMITS, qr as surfaceForegroundValue, qt as arrangementLookHooks, rn as sectionFactsForContent, rr as resolveEffectiveKnobs, rt as refSlug, s as RESOLVED_INTERNAL_LINK_ENTITY_TYPES, sn as resolveSectionPhotoPlacement, sr as asDesignBlockId, st as headerLeafTextColor, t as classifySdkEventTiming, tn as boundaryLayout, tr as normalizeCardRole, u as externalLinkSchema$1, un as runtimeBoxBackgroundSchema, ur as asSiteStyleId, ut as mergeHeaderNavTypographyPreference, v as pageLinkSchema$1, vn as rotateNormalizedPoint, vr as headingDecorationColorTokenValues, vt as compiledRuntimeStyleFieldNames, wn as getDirectImageUrlForSiteScopedPath, wr as toNonEmptyReadonlyArray, wt as generateHeaderScrollTreatmentStyles, x as venueManagementFields, xn as parseAspectRatioValue, xr as offeringsPreviewCompositionTreatmentValues, xt as parseGradientPresetId, yn as normalizeWidthCandidates, yr as introCompositionTreatmentValues, yt as gradientToCss, zr as resolveTokenOrCssColorValue, zt as themeButtonExtraClassesWithTrailingIcon } from "../sdk-event-upsert-BqjzK2zT.mjs";
3
- import { $ as compareLocalScheduleDates, $t as parseCustomerId, A as DEFAULT_MAILING_LIST_SUCCESS_MESSAGE, At as formatEventPricing, Bn as ok, Bt as mapReadonlyNonEmptyArray, Cn as unsafeAsOccurrenceId, Ct as omitZeroAmountFutureRows, D as parseMailingListSignedSourceToken, Dt as deriveTotalTicketQuantity, E as parseMailingListContentVersionId, En as unsafeAsPositiveByteCount, Et as resolveFlexibleBalanceInitialPaymentCents, F as DEFAULT_MAILING_LIST_TARGETING, Ft as publicEventPricingType, G as formatDateRange, Gt as toNonNegativeCents, H as toLocationCoordinates, Ht as parseNonNegativeCents, I as planMailingListChoicePresentation, It as resolveExactOrFreePriceDisplay, J as formatTime$1, Jt as formatCurrencyAmount, K as formatDateTime, Kt as unsafeAsNonNegativeCents, L as isCanonicalRichTextDoc, Lt as resolvePricingOptionsPriceDisplay, M as parseFooterCompositionV1, Mt as publicEventPricingCurrency, Nt as publicEventPricingDisplay, O as parseMailingListRuntimeContext, On as unsafeAsPricingOptionId, Ot as parseEventPageSurfaceSubject, P as parseMailingListPlacementTargeting, Pt as publicEventPricingOptions, Q as buildBookingUtcDateWindow, Qt as parseApiKeyId, Rt as fromArray, S as parseManagementKey, Sn as unsafeAsMediaAssetId, St as getBookingInstalmentProtectedDepositAmountCents, T as parseMailingListBlockId, Tn as unsafeAsPassId, Tt as resolveFlexibleBalanceDueAt, U as selectCategoryView, Ut as parsePositiveCents, V as normalizeLocationAddressText, Vt as isNonNegativeWholeCents$1, W as formatDate, Wt as toCents, X as inferSiteDateLocale, Xt as assertNever, Y as formatTimeRange, Yt as parseCurrencyCode, Z as resolveBookingDisplayContext, Zt as formatDurationUnitLabel, _ as orderedLiveRunSessions, _t as isSettleableOrderInstalmentPlanStatus, b as createSecret, bt as getBookingInstalmentDepositRefundability, c as resolveSiteVerticalBookingConfig, cn as parseSiteTermsVersionId, ct as resolveBookingBusinessTimeZone, dt as toScheduledBookingDateTime, en as parseDatedOfferingCapacityHoldAuditRef, et as formatLocalScheduleDate, f as SURFACE_EVENT_PRESET_VALUES, ft as toUtcIsoDateTimeString, g as nextLiveRunSessionStartMs, gt as isSettleableFlexibleBalanceContractStatus, h as CANCELLED_SESSION_STATUS, hn as unsafeAsDemoCapability, ht as isLiveOrderInstalmentPlanStatus, in as parseMembershipId, it as getLocalScheduleTime, j as parseMailingListProviderScope, jt as isPublicEventPricingOptionDisplayable, k as assessMailingListTargeting, kt as formatBookingPriceDisplay, ln as parseUserId, lt as resolveBookingTimeZoneId, mt as validateBookingPaymentOptionKinds, n as EVENT_PRESET_VOCABULARY, nn as parseEventSeriesId, nt as getBookingLocalDateKey, on as parseRetiredCourseId, ot as parseBookingLocalDate, p as WHOLE_SERIES_EVENT_PRESET_VALUES, pn as unsafeAsCheckoutLineId, q as formatDateTimeRange, qt as unsafeAsPositiveCents, r as NAVIGATION_LOCATIONS, rt as getLocalScheduleDate, sn as parseSiteId, st as parseUtcIsoDateTimeString, tn as parseDatedOfferingCapacityHoldId, tt as formatLocalScheduleTime, u as EVENT_PRESET_VALUES, ut as toBookingTimeZone, v as resolveWholeSeriesBookingPhase, vn as unsafeAsEventInstanceId, vt as addIntervalPreservingMonthAnchor, w as slugify, wt as splitAmountWithRemainderInFirst, x as exposeSecret, xt as getBookingInstalmentPlanIntervalCount, y as resolveWholeSeriesRunLifecycle, yt as clampConfiguredAmount, z as assertAllowedPublicUrl, zn as err } from "../src-zzwJUef5.mjs";
4
- import { $ as buildStorageUrl, $t as createClientForEnv, A as formatDiffDetail, At as convertPulledFooterToLocal, B as formatFieldPath, Bn as linkPayloadSchema, Bt as extractApiErrorMessage, C as reportFromOutcomes, Cn as planEnvironmentWriteAuthorization, Ct as readJsonFile, D as normalizeRemoteHomepageReference, Dn as formatError, Dt as duplicateNavigationIdentityMessage, E as createEmptySyncResult, En as ErrorCodes, Et as isMediaValue, F as calculatePullDiff, Fn as loadSdkEnvironment, Ft as applyPushConfigPlan, G as prepareLocalMediaForPush, Gt as createBearerAPIClient, Hn as sdkConfiguredBookingVerticalConfigSchema, Ht as isRecord$2, I as buildJsonDiff, In as createManagementClient, It as createPushConfigCommand, J as writeBinaryFileAtomic, Jt as deriveCmsApiUrlFromDashboardUrl, K as admitBinaryWriteDestination, Kt as ApiEnvelopeError, Ln as ManagementApiError, Lt as loadPushConfigTarget, M as hasPendingChanges, Mn as getEnvironmentDebugInfo, Mt as convertPulledPagesToLocal, N as findChangedFieldsWithValues, Nn as isLocalConfigured, Nt as navigationCommandItemToManagementInput, O as calculateDiff, On as transportSuggestion, Ot as navigationIdentity, P as formatHumanDiff, Pn as isRemoteConfigured, Pt as normalizeNavigationConfigItemForCommand, Q as walkLocalContentMedia, Qt as confirmAction, R as buildFieldChanges, Rn as sdkEventScheduleSchema, Rt as preparePushConfigTarget, S as planCliCommandBatchForBindings, Sn as planEnvironmentExecution, St as listFiles, T as executeResidualSettingsSyncPlan, Tn as withRiverbankEnv, Tt as writeJsonFileAtomic, U as syncLocalMediaForPull, Ut as APPOINTMENT_SERVICE_VISIBILITY_VALUES, V as revalidateLocalMediaPushPlan, Vn as portablePageDesignSchema, Vt as normalizeApiError, W as applyLocalMediaPush, Wt as ApiRequestError, X as normalizeMediaIdentifierInput, Xt as mapEnvironmentExecutionOutcomesJson, Y as sha256, Yt as API_ENDPOINTS, Z as normalizeMediaValueToIdentifierOnly, Zt as bindRequestTimeout, _ as commandExecutionToReport, _n as withConfirmation, _t as readMetadataFile, a as executeEntryCommandPlanAsReport, an as formatDateShort, at as readEventCategoriesMeta, b as executeCompiledCliCommandPlanAsReport, bn as executeEnvironmentPlan, bt as ensureDir, c as titleFromEntryData, cn as handleCommandError, ct as readFormsMeta, d as unsafeAsCliEntryIdentifier, dn as parseJsonArray, dt as readSettingsMeta, en as createCommandContext, et as downloadMedia, f as unsafeAsCliFormSlug, fn as parseJsonData, ft as readVenuesMeta, g as commandCompileFailureToReport, gn as withCommandContext, gt as metadataTargetFromEnvironment, h as mutationCounterForEffect, hn as runWithInvocation, ht as metadataFilePath, i as entrySiteCommandTypes, in as currentInvocationEnvelope, it as readAllMeta, j as formatDiffSummary, jn as getEnvPrefix, jt as convertPulledNavigationToLocal, k as calculateFooterDiff, kn as CliExit, kt as navigationIdentityMap, l as unsafeAsCliBlockIdentifier, ln as managementClientConfigFor, lt as readNavigationMeta, m as commandReportToSyncResult, mn as resolveCommandPath, mt as ensureMetadataDir, n as entryCommandCompileErrorToReport, nn as createListCommand, nt as findLegacyEntryFiles, o as executeEntryCommandPlanAsSyncResult, on as getOutputContext, ot as readEventsMeta, p as unsafeAsCliPageIdentifier, pn as parsePaginationOptions, pt as writeMediaManifest, q as readStableBinaryFile, qt as unwrapResponse, r as entryCommandReportToSyncResult, rn as createOutputForCommand, rt as readEntries, s as syncResultForEntryCommandCompileError, sn as getOutputOnlyContext, st as readFooterMeta, t as compileEntryPushCommands, tn as createGetCommand, tt as extractMediaIdentifiers, u as unsafeAsCliContentTypeKey, un as mapEntryForOutput, ut as readPagesMeta, v as commandReportHasFailures, vn as withErrorHandling, vt as writeMetadataFile, w as mergeManySyncResults, wn as requireEnvironmentWriteAuthorization, wt as writeJsonFile, x as formatManagementError, xn as parseEnvironmentSelection, xt as fileExists, y as executeCliCommandPlan, yn as withOutputOnlyErrorHandling, yt as writeMetadataRecordMap, z as formatFieldChange, zn as sdkSettingsUpdateSchema, zt as revalidatePushConfigPlan } from "../entryCommands-CoGu0G2R.mjs";
5
- import { r as CLI_VERSION, t as loadConfigFile } from "../load-config-j58KSqxl.mjs";
6
- import { a as RichTextDocSchema, c as isRecord$18, i as findUnfilledSentinels, n as buildEntryScaffold, o as isRichTextDoc, r as validateEntryInstance, s as isObjectRecord, t as eventUpsertInputSchema } from "../eventUpsertInputSchema-B0ubhUu5.mjs";
7
- import { a as prepareScaffoldExecution, c as LOCAL_PACKAGE_VENDOR_DIR, d as loadGeneratedPackageRanges, i as preflightScaffoldTargets, n as createScaffoldCompletion, s as deriveScaffoldLocalManifest, t as deriveScaffoldPlan } from "../plan-C4IphkPc.mjs";
2
+ import { $ as createFormRef, $n as managementMediaLibraryUploadLimitForMimeType, $r as parseThemeColorToken, An as mediaPosterSchema, Ar as surfaceDecorationColors, At as BREAKPOINT_MD, Bn as parseMediaIdentifier, Br as resolveOnColorForThemeToken, Bt as themeButtonExtraClassesWithTrailingIcon, C as sortRecord, Cn as isExplicitRectCrop, Cr as makeBoundaryId, Ct as deriveSiteStyleBlockPolicy, D as mapEventCommandContent, Dn as getDirectImageUrlForSiteScopedPath, Dr as toNonEmptyReadonlyArray, Dt as getHeaderPaintTargets, En as getDirectImageUrlForRelativePath, Er as tileTreatmentValues, Et as generateMobileOverlayBackdropFilterFix, F as pageIdentifierForBlockCommand, Fn as resolveManagedMediaReference, Fr as mergeStyles, Ft as getComponentBorderWidthPx, G as ianaTimezoneSchema, Gr as surfaceToneFromForegroundToken, Gt as footerForegroundCandidates, H as bookingPaymentTermsSchema, Hr as resolveTokenOrCssColorValue, Ht as themeButtonVariantClassNames, I as pageIdentifierForCommand, In as mediaTypeDiscriminants, Ir as rgbColorValue, It as getComponentShadowVar, J as createCommandRef, Jr as surfaceForegroundInputFromResolved, Jt as arrangementLookHooks, K as createBlockRef, Kn as getMimeType, Kr as defaultThemeTokens, Kt as buildTextContrastShadow, L as pagePathForCommand, Ln as mediaIdentifierSchema, Lr as textColorStyle, Lt as ICON_CONFIG, M as blockIdentifierForCommand, Mn as persistableMediaSchema, Mr as TokenResolver, Mt as BREAKPOINT_XL, N as blockOrderIdentifiersForCommand, Nn as runtimeMediaSchema, Nr as backgroundColorStyle, Nt as MQ_MD_DOWN, On as getImageUrl, Or as visibleHeadingDecorationFamilyValues, Ot as renderHeaderPaintTargetRules, P as entryIdentifierForCommand, Pn as buildRenderableMediaAssetIndex, Pr as borderColorStyle, Pt as headerVariantValues, Q as createFooterRef, Qn as classifyKnownMimeType, Qr as getTextColorForBackground, R as hasConfiguredBookingVertical, Rn as buildMediaIdentifierFromFilename, Rr as opaqueSurfaceDeclarations, Rt as generateDefaultButtonSystem, S as venueManagementFields, Sn as normalizeWidthCandidates, Sr as introCompositionTreatmentValues, St as parseGradientPresetId, Tn as resolveMediaTransformForAspect, Tr as testimonialsCompositionTreatmentValues, Tt as generateHeaderScrollTreatmentStyles, U as require_jsx_runtime, Un as deriveVideoSlotPolicy, Ur as PUBLIC_BUTTON_ROLES, Ut as parseThemeForPersistence, V as bookingPaymentCollectionOptionSchema, Vr as resolveThemeColorReferenceValue, Vt as themeButtonSizeClassName, W as require_react, Wn as resolveVideoSelectionIntentForMediaField, Wr as selectLogoVariant, X as createEventCategoryRef, Xn as FILE_SIZE_LIMITS, Xr as surfaceForegroundValue, Xt as resolveItemSurface, Y as createContentEntryRef, Yn as parseRelativeStoragePath, Yr as surfaceForegroundStyle, Yt as arrangementSlotNames, Z as createEventSeriesRef, Zr as expandPalette, Zt as resolveLookSlotSurfaces, _n as summarizePortableMediaReferences, _r as defineNoBlockDesignCapability, _t as resolvePublishedLook, an as deriveSectionGround, ar as defineBlockDesignMetadata, b as isEventVisibility, bn as looksLikeUuid, br as featureCardCompositionTreatmentValues, bt as gradientToCss, c as customLinkSchema$1, cn as resolveSectionBackground, cr as cardListKnobBlocks, ct as headerLeafTextColor, d as internalLinkSchema, dn as boxBackgroundOverlaySchema, dr as asLayoutVariantKey, dt as mergeHeaderNavTypographyPreference, ei as parseBlockColorToken, er as parseSafeDownloadFilename, et as createNavigationRef, f as internalResolvedLinkSchema, fn as boxBackgroundSchema, ft as readHeaderNavFontSizePreference, g as linkSchema, gn as collectPortableMediaReferencesFromContent, gr as defineFrameCapability, gt as resolveModeArrangement, h as isResolvedInternalLinkEntityType, hn as stripStaleTransitionStyleGroup, hr as defineBlockDesignCapability, ht as resolveEffectiveArrangement, in as sectionFactsForContent, ir as normalizeCardRole, it as refSlug, j as eventManagementTiming, jn as mediaSchema, jr as resolveSurfaceForeground, jt as BREAKPOINT_SM, kn as isSiteScopedDirectImageStoragePath, kr as resolveSurface, kt as BREAKPOINT_LG, l as entryLinkSchema$1, ln as resolveSectionPhotoPlacement, lt as transparentHeaderForegroundInput, mn as siteChromeBackgroundOverrideOwner, mr as assertNever$11, mt as readStoredMode, ni as __toESM, nn as boundaryLayout, nr as cardRoleSchema, o as classifySdkEventBookingOffer, on as heroPhotoPaintsGround, or as resolveEffectiveKnobs, ot as effectiveHeaderNavLinkStyle, p as internalRouteOnlyLinkSchema, pn as runtimeBoxBackgroundSchema, pr as asSiteStyleId, pt as arrangementManifestFromVariants, q as createBlocksRef, qn as asRecord, qr as surfaceForegroundDeclarations, qt as generateNavLinkCSS, rn as composeBoundary, rr as cardVariantRoleValues, rt as createVenueRef, s as RESOLVED_INTERNAL_LINK_ENTITY_TYPES, sn as parseCanonicalRuntimeBackground, sr as cardListFamilyBlocks, st as headerForegroundCandidates, t as classifySdkEventTiming, ti as __commonJSMin, tn as headerRootPaint, tr as renderCssDeclarations, tt as createPageRef, u as externalLinkSchema$1, un as sectionForegroundBackground, ur as asDesignBlockId, ut as readOpeningForegroundProjection, v as pageLinkSchema$1, vn as walkAndCollectImageAttrs, vr as eventListTreatmentValues, vt as requireCuratedSiteStyle, w as stableStringify, wn as parseAspectRatioValue, wr as offeringsPreviewCompositionTreatmentValues, wt as themeSchema, xn as rotateNormalizedPoint, xr as headingDecorationColorTokenValues, xt as getGradientPreset, yn as deleteEnvSpecificKeys, yr as eyebrowTreatmentValues, yt as compiledRuntimeStyleFieldNames, z as eventCommandScheduleSchema, zn as parseCanonicalMediaIdentifier, zr as isCssColorLiteral, zt as parseThemeButtonVariantId } from "../sdk-event-upsert-B3gflo1K.mjs";
3
+ import { $ as resolveBookingDisplayContext, $t as formatDurationUnitLabel, A as DEFAULT_MAILING_LIST_SUCCESS_MESSAGE, An as unsafeAsPricingOptionId, At as parseEventPageSurfaceSubject, B as parseWeeklyOpeningHours, Bt as fromArray, Ct as getBookingInstalmentPlanIntervalCount, D as parseMailingListSignedSourceToken, Dn as unsafeAsPassId, Dt as resolveFlexibleBalanceDueAt, E as parseMailingListContentVersionId, Et as splitAmountWithRemainderInFirst, F as DEFAULT_MAILING_LIST_TARGETING, Ft as publicEventPricingDisplay, G as selectCategoryView, Gt as parsePositiveCents, Hn as ok, Ht as mapReadonlyNonEmptyArray, I as planMailingListChoicePresentation, It as publicEventPricingOptions, J as formatDateTime, Jt as unsafeAsNonNegativeCents, K as formatDate, Kt as toCents, L as isCanonicalRichTextDoc, Lt as publicEventPricingType, M as parseFooterCompositionV1, Mt as formatEventPricing, Nt as isPublicEventPricingOptionDisplayable, O as parseMailingListRuntimeContext, On as unsafeAsPositiveByteCount, Ot as resolveFlexibleBalanceInitialPaymentCents, P as parseMailingListPlacementTargeting, Pt as publicEventPricingCurrency, Q as inferSiteDateLocale, Qt as assertNever, R as formatOpeningHoursRows, Rt as resolveExactOrFreePriceDisplay, S as parseManagementKey, St as getBookingInstalmentDepositRefundability, T as parseMailingListBlockId, Tn as unsafeAsOccurrenceId, Tt as omitZeroAmountFutureRows, U as normalizeLocationAddressText, Ut as isNonNegativeWholeCents$1, V as assertAllowedPublicUrl, Vn as err, W as toLocationCoordinates, Wt as parseNonNegativeCents, X as formatTime$1, Xt as formatCurrencyAmount, Y as formatDateTimeRange, Yt as unsafeAsPositiveCents, Z as formatTimeRange, Zt as parseCurrencyCode, _ as orderedLiveRunSessions, _n as unsafeAsDemoCapability, _t as isLiveOrderInstalmentPlanStatus, at as getLocalScheduleDate, b as createSecret, bn as unsafeAsEventInstanceId, bt as addIntervalPreservingMonthAnchor, c as resolveSiteVerticalBookingConfig, cn as parseRetiredCourseId, ct as parseBookingLocalDate, dn as parseUserId, dt as resolveBookingTimeZoneId, en as parseApiKeyId, et as buildBookingUtcDateWindow, f as SURFACE_EVENT_PRESET_VALUES, ft as toBookingTimeZone, g as nextLiveRunSessionStartMs, gt as validateBookingPaymentOptionKinds, h as CANCELLED_SESSION_STATUS, hn as unsafeAsCheckoutLineId, in as parseEventSeriesId, it as getBookingLocalDateKey, j as parseMailingListProviderScope, jt as formatBookingPriceDisplay, k as assessMailingListTargeting, kt as deriveTotalTicketQuantity, ln as parseSiteId, lt as parseUtcIsoDateTimeString, mt as toUtcIsoDateTimeString, n as EVENT_PRESET_VOCABULARY, nn as parseDatedOfferingCapacityHoldAuditRef, nt as formatLocalScheduleDate, on as parseMembershipId, ot as getLocalScheduleTime, p as WHOLE_SERIES_EVENT_PRESET_VALUES, pt as toScheduledBookingDateTime, q as formatDateRange, qt as toNonNegativeCents, r as NAVIGATION_LOCATIONS, rn as parseDatedOfferingCapacityHoldId, rt as formatLocalScheduleTime, tn as parseCustomerId, tt as compareLocalScheduleDates, u as EVENT_PRESET_VALUES, un as parseSiteTermsVersionId, ut as resolveBookingBusinessTimeZone, v as resolveWholeSeriesBookingPhase, vt as isSettleableFlexibleBalanceContractStatus, w as slugify, wn as unsafeAsMediaAssetId, wt as getBookingInstalmentProtectedDepositAmountCents, x as exposeSecret, xt as clampConfiguredAmount, y as resolveWholeSeriesRunLifecycle, yt as isSettleableOrderInstalmentPlanStatus, zt as resolvePricingOptionsPriceDisplay } from "../src-RfhyMOpX.mjs";
4
+ import { $ as normalizeMediaValueToIdentifierOnly, $t as bindRequestTimeout, A as formatDiffDetail, An as transportSuggestion, At as navigationIdentity, B as buildFieldChanges, Bn as sdkEventScheduleSchema, Bt as preparePushConfigTarget, C as reportFromOutcomes, Cn as parseEnvironmentSelection, Ct as fileExists, D as normalizeRemoteHomepageReference, Dn as withRiverbankEnv, Dt as writeJsonFileAtomic, E as createEmptySyncResult, En as requireEnvironmentWriteAuthorization, Et as writeJsonFile, F as calculatePullDiff, Fn as isLocalConfigured, Ft as navigationCommandItemToManagementInput, G as syncLocalMediaForPull, Gt as APPOINTMENT_SERVICE_VISIBILITY_VALUES, H as formatFieldPath, Hn as linkPayloadSchema, Ht as extractApiErrorMessage, I as buildJsonDiff, In as isRemoteConfigured, It as normalizeNavigationConfigItemForCommand, J as admitBinaryWriteDestination, Jt as ApiEnvelopeError, K as applyLocalMediaPush, Kt as ApiRequestError, L as hasPageOperations, Ln as loadSdkEnvironment, Lt as applyPushConfigPlan, M as hasPendingChanges, Mt as convertPulledFooterToLocal, N as findChangedFieldsWithValues, Nn as getEnvPrefix, Nt as convertPulledNavigationToLocal, O as calculateDiff, On as ErrorCodes, Ot as isMediaValue, P as formatHumanDiff, Pn as getEnvironmentDebugInfo, Pt as convertPulledPagesToLocal, Q as normalizeMediaIdentifierInput, Qt as mapEnvironmentExecutionOutcomesJson, R as pageMetadataForUpsert, Rn as createManagementClient, Rt as createPushConfigCommand, S as planCliCommandBatchForBindings, Sn as executeEnvironmentPlan, St as ensureDir, T as executeResidualSettingsSyncPlan, Tn as planEnvironmentWriteAuthorization, Tt as readJsonFile, U as revalidateLocalMediaPushPlan, Un as portablePageDesignSchema, Ut as normalizeApiError, V as formatFieldChange, Vn as sdkSettingsUpdateSchema, Vt as revalidatePushConfigPlan, Wn as sdkConfiguredBookingVerticalConfigSchema, Wt as isRecord$2, X as writeBinaryFileAtomic, Xt as deriveCmsApiUrlFromDashboardUrl, Y as readStableBinaryFile, Yt as unwrapResponse, Z as sha256, Zt as API_ENDPOINTS, _ as commandExecutionToReport, _n as runWithInvocation, _t as metadataFilePath, a as executeEntryCommandPlanAsReport, an as createOutputForCommand, at as readEntries, b as executeCompiledCliCommandPlanAsReport, bn as withErrorHandling, bt as writeMetadataFile, c as titleFromEntryData, cn as getOutputContext, ct as readEventsMeta, d as unsafeAsCliEntryIdentifier, dn as managementClientConfigFor, dt as readNavigationMeta, en as confirmAction, et as walkLocalContentMedia, f as unsafeAsCliFormSlug, fn as mapEntryForOutput, ft as readPagesMeta, g as commandCompileFailureToReport, gn as resolveCommandPath, gt as ensureMetadataDir, h as mutationCounterForEffect, hn as parsePaginationOptions, ht as writeMediaManifest, i as entrySiteCommandTypes, in as createListCommand, it as findLegacyEntryFiles, j as formatDiffSummary, jn as CliExit, jt as navigationIdentityMap, k as calculateFooterDiff, kn as formatError, kt as duplicateNavigationIdentityMessage, l as unsafeAsCliBlockIdentifier, ln as getOutputOnlyContext, lt as readFooterMeta, m as commandReportToSyncResult, mn as parseJsonData, mt as readVenuesMeta, n as entryCommandCompileErrorToReport, nn as createCommandContext, nt as downloadMedia, o as executeEntryCommandPlanAsSyncResult, on as currentInvocationEnvelope, ot as readAllMeta, p as unsafeAsCliPageIdentifier, pn as parseJsonArray, pt as readSettingsMeta, q as prepareLocalMediaForPush, qt as createBearerAPIClient, r as entryCommandReportToSyncResult, rn as createGetCommand, rt as extractMediaIdentifiers, s as syncResultForEntryCommandCompileError, sn as formatDateShort, st as readEventCategoriesMeta, t as compileEntryPushCommands, tn as createClientForEnv, tt as buildStorageUrl, u as unsafeAsCliContentTypeKey, un as handleCommandError, ut as readFormsMeta, v as commandReportHasFailures, vn as withCommandContext, vt as metadataTargetFromEnvironment, w as mergeManySyncResults, wn as planEnvironmentExecution, wt as listFiles, x as formatManagementError, xn as withOutputOnlyErrorHandling, xt as writeMetadataRecordMap, y as executeCliCommandPlan, yn as withConfirmation, yt as readMetadataFile, zn as ManagementApiError, zt as loadPushConfigTarget } from "../entryCommands-Bu-z9kxb.mjs";
5
+ import { r as CLI_VERSION, t as loadConfigFile } from "../load-config-pAPKpeE0.mjs";
6
+ import { a as RichTextDocSchema, c as isRecord$18, i as findUnfilledSentinels, n as buildEntryScaffold, o as isRichTextDoc, r as validateEntryInstance, s as isObjectRecord, t as eventUpsertInputSchema } from "../eventUpsertInputSchema-xHmuEKwM.mjs";
7
+ import { a as prepareScaffoldExecution, c as LOCAL_PACKAGE_VENDOR_DIR, d as loadGeneratedPackageRanges, i as preflightScaffoldTargets, n as createScaffoldCompletion, s as deriveScaffoldLocalManifest, t as deriveScaffoldPlan } from "../plan-DUdJbgTV.mjs";
8
8
  import { config } from "dotenv";
9
9
  import { Command, CommanderError } from "commander";
10
10
  import * as fs$2 from "fs/promises";
@@ -6001,7 +6001,8 @@ const DEFAULT_REPEATER_GRID_SPAN_FIELD_ID = "gridSpan";
6001
6001
  const DEFAULT_REPEATER_GRID_ROW_FIELD_ID = "gridRow";
6002
6002
  const DEFAULT_REPEATER_GRID_COLUMN_FIELD_ID = "gridColumn";
6003
6003
  const MIN_COLUMNS = 1;
6004
- const MAX_COLUMNS = 4;
6004
+ const MAX_REPEATER_GRID_COLUMNS = 6;
6005
+ const MAX_COLUMNS = MAX_REPEATER_GRID_COLUMNS;
6005
6006
  function normalizeInteger(value) {
6006
6007
  if (typeof value === "number" && Number.isFinite(value)) {
6007
6008
  return Math.trunc(value);
@@ -6073,7 +6074,7 @@ function createRepeaterGridSpanField(options) {
6073
6074
  const fieldId = resolveRepeaterGridSpanFieldId(options);
6074
6075
  const label = asNonEmptyString(options?.label) ?? "Grid span";
6075
6076
  const description = asNonEmptyString(options?.description) ?? "Columns occupied by this item in layout-grid mode.";
6076
- const defaultSpan = normalizeRepeaterGridColumns(options?.defaultSpan ?? 1);
6077
+ const defaultSpan = clamp(normalizeRepeaterGridColumns(options?.defaultSpan ?? 1), 1, 4);
6077
6078
  return {
6078
6079
  id: fieldId,
6079
6080
  type: "select",
@@ -6192,7 +6193,26 @@ function resolveRepeaterGridColumnsForBreakpoint(values, bindings, breakpoint) {
6192
6193
  }
6193
6194
  return MIN_COLUMNS;
6194
6195
  }
6195
- function normalizeRepeaterGridSpan(value, activeColumns) {
6196
+ /** Strict admission; rendering normalization never grants permission to persist. */
6197
+ function parseRepeaterGridInteger(value) {
6198
+ if (typeof value !== "number" && (typeof value !== "string" || !value.trim())) return null;
6199
+ const number = Number(value);
6200
+ return Number.isInteger(number) ? number : null;
6201
+ }
6202
+ function isAcceptedRepeaterGridSpan(value, columns, acceptedSpans$1) {
6203
+ const span = parseRepeaterGridInteger(value);
6204
+ return span != null && span >= 1 && span <= columns && (!acceptedSpans$1 || acceptedSpans$1.includes(span));
6205
+ }
6206
+ function resolveRepeaterGridAcceptedSpans(fields$1, spanField) {
6207
+ const field = fields$1.find((candidate) => candidate.id === spanField);
6208
+ return field?.type === "select" ? (field.options ?? []).map((option) => Number(option.value)) : undefined;
6209
+ }
6210
+ function normalizeRepeaterGridSpan(value, activeColumns, acceptedSpans$1) {
6211
+ if (acceptedSpans$1) {
6212
+ const valid = acceptedSpans$1.filter((span) => span <= activeColumns);
6213
+ const parsed$1 = parseRepeaterGridInteger(value);
6214
+ return parsed$1 != null && valid.includes(parsed$1) ? parsed$1 : valid.length ? Math.min(...valid) : 1;
6215
+ }
6196
6216
  const parsed = normalizeInteger(value);
6197
6217
  if (parsed == null) {
6198
6218
  return 1;
@@ -6237,12 +6257,14 @@ function getOverlappingItemIds(input) {
6237
6257
  return new Set(buildPlacementCells(input).flatMap((col) => Array.from(input.occupancy.get(toRowCellKey(input.row, col, input.columns)) ?? []).filter((id) => !input.excludedIds?.has(id))));
6238
6258
  }
6239
6259
  function normalizeInputItem(item, columns, sourceIndex) {
6240
- const span = normalizeRepeaterGridSpan(item.span, columns);
6260
+ const span = normalizeRepeaterGridSpan(item.span, columns, item.acceptedSpans);
6241
6261
  const row$1 = normalizeRepeaterGridRow(item.row);
6242
6262
  const colStart = normalizeRepeaterGridColStart(item.colStart, columns, span);
6243
6263
  const colEnd = colStart + span - 1;
6244
6264
  return {
6245
6265
  id: item.id,
6266
+ startNewRow: item.startNewRow,
6267
+ acceptedSpans: item.acceptedSpans,
6246
6268
  sourceIndex,
6247
6269
  span,
6248
6270
  row: row$1,
@@ -6251,8 +6273,132 @@ function normalizeInputItem(item, columns, sourceIndex) {
6251
6273
  startCell: toRowCellKey(row$1, colStart, columns)
6252
6274
  };
6253
6275
  }
6276
+ /** Ordered, non-dense placement. Rendering clamps widths; admission checks originals separately. */
6277
+ function flowRepeaterGridItems(items, columns) {
6278
+ const count = normalizeRepeaterGridColumns(columns);
6279
+ return items.reduce((placed, item) => {
6280
+ const span = normalizeRepeaterGridSpan(item.span, count);
6281
+ const previous = placed.at(-1);
6282
+ const nextColumn = previous ? previous.colStart + previous.span : 1;
6283
+ const wraps = previous != null && (item.startNewRow === true || nextColumn + span - 1 > count);
6284
+ return [...placed, {
6285
+ ...item,
6286
+ span,
6287
+ row: (previous?.row ?? 1) + (wraps ? 1 : 0),
6288
+ colStart: wraps ? 1 : nextColumn
6289
+ }];
6290
+ }, []);
6291
+ }
6292
+ function orderedGridItems(layout) {
6293
+ return [...layout.placements].sort((a$1, b$1) => a$1.sourceIndex - b$1.sourceIndex);
6294
+ }
6295
+ function isRepeaterGridLayoutInvalid(layout) {
6296
+ return layout.hasInvalidItems || layout.hasCollisions || layout.overCapacity;
6297
+ }
6298
+ function hasInvalidGridItems(items, columns, policy) {
6299
+ const flow = policy === "flow" ? flowRepeaterGridItems(items, columns) : null;
6300
+ return items.some((item, index) => !isValidGridItemGeometry(item, columns) || flow != null && (item.row !== flow[index]?.row || item.colStart !== flow[index]?.colStart));
6301
+ }
6302
+ function isValidGridItemGeometry(item, columns) {
6303
+ return isAcceptedRepeaterGridSpan(item.span, columns, item.acceptedSpans) && parseRepeaterGridInteger(item.row) != null && item.row >= 1 && parseRepeaterGridInteger(item.colStart) != null && item.colStart >= 1 && item.colStart + item.span - 1 <= columns;
6304
+ }
6305
+ function findFlowAppendPlacement(layout, span, startNewRow) {
6306
+ const next = flowRepeaterGridItems([...orderedGridItems(layout), {
6307
+ id: "new",
6308
+ span,
6309
+ row: 1,
6310
+ colStart: 1,
6311
+ startNewRow
6312
+ }], layout.columns).at(-1);
6313
+ return !isRepeaterGridLayoutInvalid(layout) && next && (layout.maxRows == null || next.row <= layout.maxRows) ? {
6314
+ row: next.row,
6315
+ colStart: next.colStart
6316
+ } : null;
6317
+ }
6318
+ function planFlowItems(layout, items) {
6319
+ const placed = flowRepeaterGridItems(items, layout.columns);
6320
+ if (isRepeaterGridLayoutInvalid(layout) || placed.some((item) => !isAcceptedRepeaterGridSpan(item.span, layout.columns, item.acceptedSpans) || layout.maxRows != null && item.row > layout.maxRows)) return { kind: "blocked" };
6321
+ const [first, ...rest] = placed.map(({ id, row: row$1, colStart, span }) => ({
6322
+ id,
6323
+ row: row$1,
6324
+ colStart,
6325
+ span
6326
+ }));
6327
+ return first ? {
6328
+ kind: "move",
6329
+ updates: [first, ...rest]
6330
+ } : { kind: "unchanged" };
6331
+ }
6332
+ /** Insertion reorder, with a complete coordinate write for the resulting order. */
6333
+ function planRepeaterGridReorder(layout, id, to, interaction = "step") {
6334
+ if (layout.placementPolicy !== "flow") return { kind: "blocked" };
6335
+ const items = orderedGridItems(layout);
6336
+ const from = items.findIndex((item) => item.id === id);
6337
+ if (from < 0 || to < 0 || to >= items.length) return { kind: "blocked" };
6338
+ if (from === to) return { kind: "unchanged" };
6339
+ const active = items[from];
6340
+ if (!active) return { kind: "blocked" };
6341
+ return decideFlowReorder({
6342
+ layout,
6343
+ items,
6344
+ active,
6345
+ from,
6346
+ to,
6347
+ interaction
6348
+ });
6349
+ }
6350
+ function decideFlowReorder(input) {
6351
+ const { layout, items, active, from, to, interaction } = input;
6352
+ const id = active.id;
6353
+ const remaining = items.filter((item) => item.id !== id);
6354
+ const inserted = [
6355
+ ...remaining.slice(0, to),
6356
+ active,
6357
+ ...remaining.slice(to)
6358
+ ];
6359
+ const clearRowBreak = interaction === "drag" && active.startNewRow === true && dragInsertsWithinRow(items, from, to, layout.columns);
6360
+ const nextItems = clearRowBreak ? inserted.map((item) => item.id === id ? {
6361
+ ...item,
6362
+ startNewRow: false
6363
+ } : item) : inserted;
6364
+ const decision = planFlowItems(layout, nextItems);
6365
+ return decision.kind === "move" ? {
6366
+ ...decision,
6367
+ reorder: {
6368
+ id,
6369
+ from,
6370
+ to
6371
+ },
6372
+ ...clearRowBreak ? { startNewRowUpdate: {
6373
+ id,
6374
+ value: false
6375
+ } } : {}
6376
+ } : decision;
6377
+ }
6378
+ /** Read the insertion bar position, independently of the dragged width. */
6379
+ function dragInsertsWithinRow(items, from, to, columns) {
6380
+ const target = items[to];
6381
+ if (!target) return false;
6382
+ const column = from < to ? target.colEnd + 1 : target.colStart;
6383
+ return column > 1 && column <= columns;
6384
+ }
6385
+ function planRepeaterGridStartNewRow(layout, id, value) {
6386
+ const active = layout.byId[id];
6387
+ if (layout.placementPolicy !== "flow" || !active) return { kind: "blocked" };
6388
+ const decision = planFlowItems(layout, orderedGridItems(layout).map((item) => item.id === id ? {
6389
+ ...item,
6390
+ startNewRow: value
6391
+ } : item));
6392
+ return decision.kind === "move" ? {
6393
+ ...decision,
6394
+ startNewRowUpdate: {
6395
+ id,
6396
+ value
6397
+ }
6398
+ } : decision;
6399
+ }
6254
6400
  function buildRepeaterGridLayout(input) {
6255
- const columns = clamp(Math.max(1, Math.trunc(input.columns)), MIN_COLUMNS, MAX_COLUMNS);
6401
+ const columns = normalizeRepeaterGridColumns(input.columns);
6256
6402
  const maxRows = input.maxRows != null ? Math.max(1, Math.trunc(input.maxRows)) : undefined;
6257
6403
  const capacityCells = maxRows != null ? maxRows * columns : undefined;
6258
6404
  const placements = input.items.map((item, sourceIndex) => normalizeInputItem(item, columns, sourceIndex)).sort((a$1, b$1) => {
@@ -6265,51 +6411,50 @@ function buildRepeaterGridLayout(input) {
6265
6411
  const { hasCollisions } = buildOccupancyByCell(placements, columns);
6266
6412
  const overCapacity = maxRows != null && maxRowUsed > maxRows;
6267
6413
  return {
6414
+ placementPolicy: input.placementPolicy,
6268
6415
  columns,
6269
6416
  maxRows,
6270
6417
  capacityCells,
6271
6418
  maxRowUsed,
6272
6419
  overCapacity,
6273
6420
  hasCollisions,
6421
+ hasInvalidItems: hasInvalidGridItems(input.items, columns, input.placementPolicy),
6274
6422
  placements,
6275
6423
  byId
6276
6424
  };
6277
6425
  }
6278
6426
  function findFirstAvailableRepeaterGridPlacement(input) {
6279
- const columns = clamp(Math.max(1, Math.trunc(input.columns)), MIN_COLUMNS, MAX_COLUMNS);
6427
+ const columns = normalizeRepeaterGridColumns(input.columns);
6280
6428
  const maxRows = input.maxRows != null ? Math.max(1, Math.trunc(input.maxRows)) : undefined;
6281
- const span = normalizeRepeaterGridSpan(input.newItemSpan, columns);
6429
+ if (!isAcceptedRepeaterGridSpan(input.newItemSpan, columns, input.acceptedSpans)) return null;
6430
+ const span = normalizeRepeaterGridSpan(input.newItemSpan, columns, input.acceptedSpans);
6282
6431
  const layout = buildRepeaterGridLayout({
6283
6432
  items: input.items,
6284
6433
  columns,
6285
- maxRows
6434
+ maxRows,
6435
+ placementPolicy: input.placementPolicy
6286
6436
  });
6437
+ if (input.placementPolicy === "flow") return findFlowAppendPlacement(layout, span, input.startNewRow);
6287
6438
  const { occupancy } = buildOccupancyByCell(layout.placements, columns);
6288
6439
  const rowLimit = maxRows ?? Math.max(1, layout.maxRowUsed + 1);
6289
- for (let row$1 = 1; row$1 <= rowLimit; row$1 += 1) {
6290
- const maxColStart = Math.max(1, columns - span + 1);
6291
- for (let colStart = 1; colStart <= maxColStart; colStart += 1) {
6292
- const overlaps = getOverlappingItemIds({
6293
- occupancy,
6294
- row: row$1,
6295
- colStart,
6296
- span,
6297
- columns
6298
- });
6299
- if (overlaps.size === 0) {
6300
- return {
6301
- row: row$1,
6302
- colStart
6303
- };
6304
- }
6305
- }
6306
- }
6307
- return null;
6440
+ if (isRepeaterGridLayoutInvalid(layout)) return null;
6441
+ const maxColStart = columns - span + 1;
6442
+ return Array.from({ length: rowLimit * maxColStart }, (_$1, index) => ({
6443
+ row: Math.floor(index / maxColStart) + 1,
6444
+ colStart: index % maxColStart + 1
6445
+ })).find(({ row: row$1, colStart }) => getOverlappingItemIds({
6446
+ occupancy,
6447
+ row: row$1,
6448
+ colStart,
6449
+ span,
6450
+ columns
6451
+ }).size === 0) ?? null;
6308
6452
  }
6309
6453
  function canAppendRepeaterGridItem(input) {
6310
6454
  return findFirstAvailableRepeaterGridPlacement({
6311
6455
  items: input.items,
6312
6456
  newItemSpan: input.newItemSpan,
6457
+ acceptedSpans: input.acceptedSpans,
6313
6458
  columns: input.columns,
6314
6459
  maxRows: input.maxRows
6315
6460
  }) != null;
@@ -6336,7 +6481,7 @@ function swapPositionUpdates(active, target) {
6336
6481
  function validateGridMove(layout, updates) {
6337
6482
  const moved = updates;
6338
6483
  const stationary = layout.placements.filter((placement) => !updates.some((update) => update.id === placement.id));
6339
- const valid = moved.every((placement, index) => placement.colStart >= 1 && placement.colStart + placement.span - 1 <= layout.columns && placement.row >= 1 && (layout.maxRows == null || placement.row <= layout.maxRows) && !stationary.some((other) => placementsOverlap(placement, other)) && !moved.slice(index + 1).some((other) => placementsOverlap(placement, other)));
6484
+ const valid = !layout.hasInvalidItems && !layout.hasCollisions && !layout.overCapacity && moved.every((placement, index) => isAcceptedRepeaterGridSpan(placement.span, layout.columns, layout.byId[placement.id]?.acceptedSpans) && placement.colStart >= 1 && placement.colStart + placement.span - 1 <= layout.columns && placement.row >= 1 && (layout.maxRows == null || placement.row <= layout.maxRows) && !stationary.some((other) => placementsOverlap(placement, other)) && !moved.slice(index + 1).some((other) => placementsOverlap(placement, other)));
6340
6485
  return valid ? {
6341
6486
  kind: "move",
6342
6487
  updates
@@ -6358,7 +6503,14 @@ function decideGridDrop(layout, active, destination) {
6358
6503
  function buildRepeaterGridDropTargets(input) {
6359
6504
  const { layout, draggedId, rowsToRender } = input;
6360
6505
  const active = layout.byId[draggedId];
6361
- if (!active) return [];
6506
+ if (!active || isRepeaterGridLayoutInvalid(layout)) return [];
6507
+ if (layout.placementPolicy === "flow") return orderedGridItems(layout).map((item, index) => ({
6508
+ id: `repeater-grid-drop-${item.row}-${item.colStart}`,
6509
+ row: item.row,
6510
+ colStart: item.colStart,
6511
+ cell: item.startCell,
6512
+ decision: planRepeaterGridReorder(layout, draggedId, index, input.interaction ?? "drag")
6513
+ }));
6362
6514
  const rowCeiling = layout.maxRows ?? Math.max(1, rowsToRender ?? layout.maxRowUsed + 1, active.row + 1);
6363
6515
  return Array.from({ length: rowCeiling * layout.columns }, (_$1, index) => {
6364
6516
  const row$1 = Math.floor(index / layout.columns) + 1;
@@ -6375,6 +6527,166 @@ function buildRepeaterGridDropTargets(input) {
6375
6527
  };
6376
6528
  });
6377
6529
  }
6530
+ /** Width edits keep the item's start when it fits; otherwise relocate it to the first free cell.
6531
+ * Neighbours keep their positions. A full bounded grid blocks the change without partial writes.
6532
+ */
6533
+ function planRepeaterGridSpanChange(input) {
6534
+ const { layout, id, span } = input;
6535
+ const active = layout.byId[id];
6536
+ if (isRepeaterGridLayoutInvalid(layout)) return { kind: "blocked" };
6537
+ if (!active || !isAcceptedRepeaterGridSpan(span, layout.columns, active.acceptedSpans)) return { kind: "blocked" };
6538
+ const nextSpan = Number(span);
6539
+ if (active.span === nextSpan) return { kind: "unchanged" };
6540
+ if (layout.placementPolicy === "flow") return planFlowItems(layout, orderedGridItems(layout).map((item) => item.id === id ? {
6541
+ ...item,
6542
+ span: nextSpan
6543
+ } : item));
6544
+ const atCurrent = validateGridMove(layout, [{
6545
+ id,
6546
+ span: nextSpan,
6547
+ row: active.row,
6548
+ colStart: active.colStart
6549
+ }]);
6550
+ if (atCurrent.kind === "move") return atCurrent;
6551
+ const destination = findFirstAvailableRepeaterGridPlacement({
6552
+ items: layout.placements.filter((item) => item.id !== id),
6553
+ newItemSpan: nextSpan,
6554
+ acceptedSpans: active.acceptedSpans,
6555
+ columns: layout.columns,
6556
+ maxRows: layout.maxRows
6557
+ });
6558
+ return destination ? validateGridMove(layout, [{
6559
+ id,
6560
+ span: nextSpan,
6561
+ ...destination
6562
+ }]) : { kind: "blocked" };
6563
+ }
6564
+ /** Narrow rendering is a projection only. Never feed these placements into an authoring write. */
6565
+ function projectRepeaterGridLayout(layout, columns) {
6566
+ if (isRepeaterGridLayoutInvalid(layout)) return projectRepeaterGridLayoutForRendering(layout, columns);
6567
+ if (columns === layout.columns) return layout;
6568
+ if (layout.placementPolicy === "flow") return buildProjectedRepeaterGridLayout(layout, flowRepeaterGridItems(orderedGridItems(layout), columns), columns);
6569
+ const items = layout.placements.reduce((placed, item) => {
6570
+ const span = normalizeRepeaterGridSpan(item.span, columns);
6571
+ const destination = findFirstAvailableRepeaterGridPlacement({
6572
+ items: placed,
6573
+ newItemSpan: span,
6574
+ columns
6575
+ });
6576
+ return [...placed, {
6577
+ id: item.id,
6578
+ span,
6579
+ row: destination?.row ?? placed.length + 1,
6580
+ colStart: destination?.colStart ?? 1
6581
+ }];
6582
+ }, []);
6583
+ return buildProjectedRepeaterGridLayout(layout, items, columns);
6584
+ }
6585
+ function buildProjectedRepeaterGridLayout(source, items, columns) {
6586
+ const projected = buildRepeaterGridLayout({
6587
+ items: items.map(({ id, span, row: row$1, colStart, startNewRow }) => ({
6588
+ id,
6589
+ span,
6590
+ row: row$1,
6591
+ colStart,
6592
+ startNewRow
6593
+ })),
6594
+ columns,
6595
+ placementPolicy: source.placementPolicy === "flow" || isRepeaterGridLayoutInvalid(source) ? "flow" : source.placementPolicy
6596
+ });
6597
+ const placements = projected.placements.map((item) => ({
6598
+ ...item,
6599
+ sourceIndex: source.byId[item.id]?.sourceIndex ?? item.sourceIndex
6600
+ }));
6601
+ return {
6602
+ ...projected,
6603
+ placements,
6604
+ byId: Object.fromEntries(placements.map((item) => [item.id, item]))
6605
+ };
6606
+ }
6607
+ /** Public fallback: preserve saved array order, use non-dense auto-placement, and ignore authoring capacity. */
6608
+ function projectRepeaterGridLayoutForRendering(layout, columns = layout.columns) {
6609
+ if (!isRepeaterGridLayoutInvalid(layout)) {
6610
+ return projectRepeaterGridLayout(layout, columns);
6611
+ }
6612
+ const count = normalizeRepeaterGridColumns(columns);
6613
+ return buildProjectedRepeaterGridLayout(layout, flowRepeaterGridItems(orderedGridItems(layout), count), count);
6614
+ }
6615
+
6616
+ //#endregion
6617
+ //#region ../blocks/src/system/manifest/layoutGridConfiguration.ts
6618
+ const itemFieldsSchema = z.object({ fields: z.array(z.object({
6619
+ id: z.string(),
6620
+ type: z.string(),
6621
+ defaultValue: z.unknown().optional(),
6622
+ options: z.array(z.object({ value: z.string() })).optional(),
6623
+ multiple: z.boolean().optional(),
6624
+ source: z.object({ kind: z.string() }).optional()
6625
+ })) });
6626
+ function validateSpanSelect(span, columns, ctx) {
6627
+ if (span.type !== "select") return;
6628
+ const options = span.options ?? [];
6629
+ const invalidOptions = options.some((option) => !isAcceptedRepeaterGridSpan(option.value, columns) || String(Number(option.value)) !== option.value);
6630
+ if (span.multiple || options.length === 0 || invalidOptions || span.source && span.source.kind !== "static") {
6631
+ ctx.addIssue({
6632
+ code: "custom",
6633
+ path: ["ui", "layoutGridSpanField"],
6634
+ message: `Layout-grid span select must have single, static integer options within 1..${columns}`
6635
+ });
6636
+ }
6637
+ if (span.defaultValue !== undefined && !options.some((option) => option.value === span.defaultValue)) {
6638
+ ctx.addIssue({
6639
+ code: "custom",
6640
+ path: ["ui", "layoutGridSpanField"],
6641
+ message: "Layout-grid default span must be one of the span select options"
6642
+ });
6643
+ }
6644
+ }
6645
+ /** Both monomorphic and polymorphic item schemas have the same layout contract. */
6646
+ function validateLayoutGridConfiguration(data, ctx) {
6647
+ const ui = data.ui;
6648
+ if (ui?.repeaterPreset !== "layoutGrid") return;
6649
+ if (ui.layoutGridColumns !== undefined === (ui.layoutGridColumnsFields !== undefined)) {
6650
+ ctx.addIssue({
6651
+ code: "custom",
6652
+ path: ["ui", "layoutGridColumns"],
6653
+ message: "Layout-grid requires exactly one of ui.layoutGridColumns or ui.layoutGridColumnsFields.lg; do not set both"
6654
+ });
6655
+ }
6656
+ const spanId = resolveRepeaterGridSpanFieldId({ id: ui.layoutGridSpanField });
6657
+ const requiredIds = [
6658
+ spanId,
6659
+ resolveRepeaterGridRowFieldId({ id: ui.layoutGridRowField }),
6660
+ resolveRepeaterGridColumnFieldId({ id: ui.layoutGridColumnField })
6661
+ ];
6662
+ const schemas = data.polymorphic && data.itemTypes ? Object.values(data.itemTypes) : [data.schema];
6663
+ schemas.forEach((schema) => {
6664
+ const parsed = itemFieldsSchema.safeParse(schema);
6665
+ if (!parsed.success) return;
6666
+ if (!requiredIds.every((id) => parsed.data.fields.some((field) => field.id === id))) {
6667
+ ctx.addIssue({
6668
+ code: "custom",
6669
+ path: ["ui", "layoutGridRowField"],
6670
+ message: `layoutGrid repeaters require span/row/column fields (${requiredIds.join(", ")}) to exist in repeater item fields`
6671
+ });
6672
+ }
6673
+ if (ui.layoutGridStartNewRowField && !parsed.data.fields.some((field) => field.id === ui.layoutGridStartNewRowField && field.type === "boolean")) {
6674
+ ctx.addIssue({
6675
+ code: "custom",
6676
+ path: ["ui", "layoutGridStartNewRowField"],
6677
+ message: "Layout-grid Start a new row field must reference a boolean item field"
6678
+ });
6679
+ }
6680
+ if (ui.layoutGridStartNewRowField && ui.layoutGridColumns === undefined) {
6681
+ ctx.addIssue({
6682
+ code: "custom",
6683
+ path: ["ui", "layoutGridStartNewRowField"],
6684
+ message: "Layout-grid Start a new row requires fixed columns (flow placement)"
6685
+ });
6686
+ }
6687
+ parsed.data.fields.filter((field) => field.id === spanId).forEach((span) => validateSpanSelect(span, ui.layoutGridColumns ?? MAX_REPEATER_GRID_COLUMNS, ctx));
6688
+ });
6689
+ }
6378
6690
 
6379
6691
  //#endregion
6380
6692
  //#region ../blocks/src/system/icons/presets.ts
@@ -6560,6 +6872,14 @@ const uiSchema = z.object({
6560
6872
  thumbnailLabelField: z.string().optional(),
6561
6873
  thumbnailAspectRatio: z.string().optional(),
6562
6874
  thumbnailCardDensity: z.enum(["compact", "comfortable"]).optional(),
6875
+ layoutGridColumns: z.union([
6876
+ z.literal(1),
6877
+ z.literal(2),
6878
+ z.literal(3),
6879
+ z.literal(4),
6880
+ z.literal(5),
6881
+ z.literal(6)
6882
+ ]).optional(),
6563
6883
  layoutGridColumnsFields: z.object({
6564
6884
  base: z.string().optional(),
6565
6885
  sm: z.string().optional(),
@@ -6568,6 +6888,7 @@ const uiSchema = z.object({
6568
6888
  xl: z.string().optional()
6569
6889
  }).optional(),
6570
6890
  layoutGridSpanField: z.string().optional(),
6891
+ layoutGridStartNewRowField: z.string().optional(),
6571
6892
  layoutGridRowField: z.string().optional(),
6572
6893
  layoutGridColumnField: z.string().optional(),
6573
6894
  layoutGridMaxRows: z.number().int().positive().optional(),
@@ -6589,7 +6910,26 @@ const uiSchema = z.object({
6589
6910
  "style"
6590
6911
  ]).optional(),
6591
6912
  valueContract: z.enum(["boxBackground"]).optional()
6592
- }).partial();
6913
+ }).partial().superRefine((ui, ctx) => {
6914
+ if (ui.layoutGridColumns !== undefined && ui.layoutGridColumnsFields !== undefined) {
6915
+ ctx.addIssue({
6916
+ code: "custom",
6917
+ path: ["layoutGridColumns"],
6918
+ message: "Layout-grid requires exactly one of ui.layoutGridColumns or ui.layoutGridColumnsFields.lg; do not set both"
6919
+ });
6920
+ }
6921
+ }).transform((input) => {
6922
+ const { layoutGridColumns, layoutGridColumnsFields, ...ui } = input;
6923
+ if (layoutGridColumns !== undefined) return {
6924
+ ...ui,
6925
+ layoutGridColumns
6926
+ };
6927
+ if (layoutGridColumnsFields !== undefined) return {
6928
+ ...ui,
6929
+ layoutGridColumnsFields
6930
+ };
6931
+ return ui;
6932
+ });
6593
6933
  const baseFieldSchema = z.object({
6594
6934
  id: z.string().min(1, "Field id is required"),
6595
6935
  label: z.string().min(1, "Field label is required"),
@@ -6775,44 +7115,7 @@ const repeaterFieldSchema = baseFieldSchema.extend({
6775
7115
  const hasSchema = data.schema !== undefined;
6776
7116
  const hasPolymorphic = data.polymorphic === true && data.itemTypes !== undefined;
6777
7117
  return hasSchema !== hasPolymorphic;
6778
- }, { message: "Repeater must have either 'schema' (monomorphic) or 'polymorphic: true' with 'itemTypes' (polymorphic)" }).refine((data) => {
6779
- if (data.ui?.repeaterPreset !== "layoutGrid") {
6780
- return true;
6781
- }
6782
- const columnsFields = data.ui.layoutGridColumnsFields;
6783
- return !!columnsFields?.lg;
6784
- }, {
6785
- message: "ui.layoutGridColumnsFields.lg is required when ui.repeaterPreset is 'layoutGrid'",
6786
- path: [
6787
- "ui",
6788
- "layoutGridColumnsFields",
6789
- "lg"
6790
- ]
6791
- }).refine((data) => {
6792
- if (data.ui?.repeaterPreset !== "layoutGrid") {
6793
- return true;
6794
- }
6795
- const spanField = resolveRepeaterGridSpanFieldId({ id: typeof data.ui.layoutGridSpanField === "string" ? data.ui.layoutGridSpanField : undefined });
6796
- const rowField = resolveRepeaterGridRowFieldId({ id: typeof data.ui.layoutGridRowField === "string" ? data.ui.layoutGridRowField : undefined });
6797
- const columnField = resolveRepeaterGridColumnFieldId({ id: typeof data.ui.layoutGridColumnField === "string" ? data.ui.layoutGridColumnField : undefined });
6798
- const requiredFieldIds = [
6799
- spanField,
6800
- rowField,
6801
- columnField
6802
- ];
6803
- if (data.polymorphic && data.itemTypes) {
6804
- const itemTypes = Object.values(data.itemTypes);
6805
- return itemTypes.every((itemType) => Array.isArray(itemType.fields) && requiredFieldIds.every((requiredId) => itemType.fields?.some((field) => field.id === requiredId)));
6806
- }
6807
- const schemaFields = data.schema?.fields;
6808
- if (!schemaFields) {
6809
- return true;
6810
- }
6811
- return requiredFieldIds.every((requiredId) => schemaFields.some((field) => field.id === requiredId));
6812
- }, {
6813
- message: `layoutGrid repeaters require span/row/column fields (defaults: '${DEFAULT_REPEATER_GRID_SPAN_FIELD_ID}', '${DEFAULT_REPEATER_GRID_ROW_FIELD_ID}', '${DEFAULT_REPEATER_GRID_COLUMN_FIELD_ID}') to exist in repeater item fields`,
6814
- path: ["ui", "layoutGridRowField"]
6815
- }).refine((data) => {
7118
+ }, { message: "Repeater must have either 'schema' (monomorphic) or 'polymorphic: true' with 'itemTypes' (polymorphic)" }).superRefine(validateLayoutGridConfiguration).refine((data) => {
6816
7119
  const thumbnailAspectRatio = data.ui?.thumbnailAspectRatio;
6817
7120
  if (!thumbnailAspectRatio) {
6818
7121
  return true;
@@ -7144,6 +7447,187 @@ const manifestSchema = z.object({
7144
7447
  icon: z.string().optional()
7145
7448
  });
7146
7449
 
7450
+ //#endregion
7451
+ //#region ../blocks/src/lib/media.ts
7452
+ const MEDIA_TYPE_DISCRIMINANTS = mediaTypeDiscriminants;
7453
+ const PUBLIC_DOWNLOAD_MEDIA_TYPES = [
7454
+ "document",
7455
+ "spreadsheet",
7456
+ "archive",
7457
+ "audio"
7458
+ ];
7459
+ function isMediaTypeDiscriminant(value) {
7460
+ return typeof value === "string" && MEDIA_TYPE_DISCRIMINANTS.includes(value);
7461
+ }
7462
+ function mediaTypeDiscriminantFromMimeType(mimeType) {
7463
+ const classified = classifyKnownMimeType(mimeType);
7464
+ if (!classified.ok) return null;
7465
+ switch (classified.value.tag) {
7466
+ case "image":
7467
+ case "video":
7468
+ case "audio":
7469
+ case "document":
7470
+ case "spreadsheet":
7471
+ case "archive": return classified.value.tag;
7472
+ case "font": return null;
7473
+ }
7474
+ }
7475
+
7476
+ //#endregion
7477
+ //#region ../blocks/src/system/manifest/fieldValidation/writeCanonicalization.ts
7478
+ function allGroupChildPlans(plan) {
7479
+ return plan.childSelection.kind === "all" ? plan.childSelection.fields : plan.childSelection.tabs.flatMap((tab) => tab.fields);
7480
+ }
7481
+ function selectedGroupChildPlans(plan, value) {
7482
+ if (plan.childSelection.kind === "all") return plan.childSelection.fields;
7483
+ if (!isPlainObject$7(value)) return [];
7484
+ const selectedType = value[plan.childSelection.activeTabField];
7485
+ return plan.childSelection.tabs.find((tab) => tab.typeId === selectedType)?.fields ?? [];
7486
+ }
7487
+ function canonicalizeObjectForWrite(plans, value) {
7488
+ if (!isPlainObject$7(value)) return value;
7489
+ const canonical$1 = { ...value };
7490
+ for (const plan of plans) {
7491
+ if (!(plan.fieldId in canonical$1)) continue;
7492
+ canonical$1[plan.fieldId] = canonicalizeFieldValue(plan, canonical$1[plan.fieldId]);
7493
+ }
7494
+ return omitUndefined(canonical$1);
7495
+ }
7496
+ function canonicalizeFieldValue(plan, value) {
7497
+ switch (plan.kind) {
7498
+ case "media": return canonicalizeMedia(value);
7499
+ case "group": return plan.childSelection.kind === "discriminatedTabs" ? canonicalizeDiscriminatedGroup(plan, value) : canonicalizeObjectForWrite(plan.childSelection.fields, value);
7500
+ case "repeater": return canonicalizeRepeater(plan, value);
7501
+ case "string":
7502
+ case "number":
7503
+ case "boolean":
7504
+ case "richText":
7505
+ case "inlineText":
7506
+ case "link":
7507
+ case "select":
7508
+ case "passthrough": return value;
7509
+ }
7510
+ }
7511
+ function canonicalizeMedia(value) {
7512
+ if (!isPlainObject$7(value)) return value;
7513
+ const canonical$1 = { ...value };
7514
+ if (mediaIdentifierSchema.safeParse(canonical$1.identifier).success) {
7515
+ deleteEnvSpecificKeys(canonical$1);
7516
+ }
7517
+ return canonical$1;
7518
+ }
7519
+ function canonicalizeRepeater(plan, value) {
7520
+ if (!isUnknownArray$3(value)) return value;
7521
+ return value.map((item) => {
7522
+ if (!isPlainObject$7(item)) return item;
7523
+ const childPlans = plan.repeatedItemVariants ? plan.repeatedItemVariants.find((variant) => variant.typeId === item._type)?.fields : plan.repeatedItemPlan;
7524
+ return childPlans ? canonicalizeObjectForWrite(childPlans, item) : item;
7525
+ });
7526
+ }
7527
+ function canonicalizeDiscriminatedGroup(plan, value) {
7528
+ if (!isPlainObject$7(value) || plan.childSelection.kind !== "discriminatedTabs") {
7529
+ return value;
7530
+ }
7531
+ const selection = plan.childSelection;
7532
+ const selectedPlans = selectedGroupChildPlans(plan, value);
7533
+ const selectedIds = rootFieldIds(selectedPlans);
7534
+ const allChildIds = rootFieldIds(allGroupChildPlans(plan));
7535
+ const projected = Object.fromEntries(Object.entries(value).filter(([key]) => key === selection.activeTabField || selectedIds.has(key) || !allChildIds.has(key)));
7536
+ const canonical$1 = omitUndefined(canonicalizeObjectForWrite(selectedPlans, projected));
7537
+ if (plan.valueContract !== "boxBackground") return canonical$1;
7538
+ const parsed = boxBackgroundSchema.safeParse(canonical$1);
7539
+ return parsed.success ? parsed.data : canonical$1;
7540
+ }
7541
+ function rootFieldIds(plans) {
7542
+ return new Set(plans.map((child) => child.fieldId.split(".")[0] ?? child.fieldId));
7543
+ }
7544
+ function omitUndefined(value) {
7545
+ if (isUnknownArray$3(value)) return value.map(omitUndefined);
7546
+ if (!isPlainObject$7(value)) return value;
7547
+ return Object.fromEntries(Object.entries(value).filter(([, field]) => field !== undefined).map(([key, field]) => [key, omitUndefined(field)]));
7548
+ }
7549
+ function isPlainObject$7(value) {
7550
+ return typeof value === "object" && value !== null && !Array.isArray(value);
7551
+ }
7552
+ function isUnknownArray$3(value) {
7553
+ return Array.isArray(value);
7554
+ }
7555
+
7556
+ //#endregion
7557
+ //#region ../blocks/src/system/manifest/fieldValidation/layoutGrid.ts
7558
+ function layoutGridRecord(value) {
7559
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
7560
+ }
7561
+ function resolveLayoutGridItemPlans(plan, item) {
7562
+ return plan.repeatedItemVariants?.find((variant) => variant.typeId === item._type)?.fields ?? plan.repeatedItemPlan ?? [];
7563
+ }
7564
+ function layoutGridCoordinateFieldIds(plan) {
7565
+ const grid$5 = plan.layoutGrid;
7566
+ return grid$5 ? [
7567
+ grid$5.spanField,
7568
+ grid$5.rowField,
7569
+ grid$5.columnField
7570
+ ] : [];
7571
+ }
7572
+ function acceptedSpans(plans, spanField) {
7573
+ const span = plans.find((field) => field.fieldId === spanField);
7574
+ if (span?.kind !== "select") return undefined;
7575
+ const constraint = span.constraints.find((entry) => entry.kind === "selectOption");
7576
+ return constraint?.kind === "selectOption" && constraint.source.kind === "static" ? constraint.source.values.map(Number) : [];
7577
+ }
7578
+ function gridIssue(plan, path$3, message) {
7579
+ return {
7580
+ kind: "invalidLayoutGrid",
7581
+ fieldId: plan.fieldId,
7582
+ label: plan.label,
7583
+ path: path$3,
7584
+ message
7585
+ };
7586
+ }
7587
+ function buildLayoutGridForPlan(plan, value, values, ids) {
7588
+ const grid$5 = plan.layoutGrid;
7589
+ if (!grid$5) return null;
7590
+ const columns = grid$5.columns ?? (grid$5.columnsFields ? resolveRepeaterGridColumnsForBreakpoint(values, grid$5.columnsFields, "lg") : 1);
7591
+ const items = value.flatMap((item, index) => {
7592
+ const row$1 = layoutGridRecord(item);
7593
+ return row$1 ? [{
7594
+ id: ids?.[index] ?? String(index),
7595
+ startNewRow: grid$5.startNewRowField ? row$1[grid$5.startNewRowField] === true : false,
7596
+ span: parseRepeaterGridInteger(row$1[grid$5.spanField]) ?? NaN,
7597
+ row: parseRepeaterGridInteger(row$1[grid$5.rowField]) ?? NaN,
7598
+ colStart: parseRepeaterGridInteger(row$1[grid$5.columnField]) ?? NaN,
7599
+ acceptedSpans: acceptedSpans(resolveLayoutGridItemPlans(plan, row$1), grid$5.spanField)
7600
+ }] : [];
7601
+ });
7602
+ return buildRepeaterGridLayout({
7603
+ items,
7604
+ columns,
7605
+ maxRows: grid$5.maxRows,
7606
+ placementPolicy: grid$5.columns === undefined ? "free" : "flow"
7607
+ });
7608
+ }
7609
+ function validateGrid(plan, value, values, path$3) {
7610
+ const layout = buildLayoutGridForPlan(plan, value, values);
7611
+ if (!layout) return [];
7612
+ if (layout.hasCollisions || layout.overCapacity) return [gridIssue(plan, path$3, "Some items overlap or do not fit in this layout. Adjust their widths or positions before saving.")];
7613
+ return layout.hasInvalidItems ? [gridIssue(plan, path$3, "Choose an available width and a valid position for each item before saving.")] : [];
7614
+ }
7615
+ /** Root values resolve the same column bindings as the editor; recurse without changing any content. */
7616
+ function validateLayoutGridPlans(plans, value, rootValues = layoutGridRecord(value) ?? {}, parent = []) {
7617
+ const values = layoutGridRecord(value);
7618
+ if (!values) return [];
7619
+ return plans.flatMap((plan) => {
7620
+ const fieldValue = values[plan.fieldId];
7621
+ const path$3 = [...parent, plan.fieldId];
7622
+ if (plan.kind === "group") return validateLayoutGridPlans(allGroupChildPlans(plan), fieldValue, rootValues, path$3);
7623
+ if (plan.kind !== "repeater" || !Array.isArray(fieldValue)) return [];
7624
+ return [...validateGrid(plan, fieldValue, rootValues, path$3), ...fieldValue.flatMap((item, index) => {
7625
+ const row$1 = layoutGridRecord(item);
7626
+ return row$1 ? validateLayoutGridPlans(resolveLayoutGridItemPlans(plan, row$1), row$1, rootValues, [...path$3, index]) : [];
7627
+ })];
7628
+ });
7629
+ }
7630
+
7147
7631
  //#endregion
7148
7632
  //#region ../blocks/src/system/inlineText.ts
7149
7633
  const inlineTextAccentTones = [
@@ -8602,112 +9086,6 @@ function isGeneratedBlockIconStale(value, currentStyleGuideVersionId) {
8602
9086
  return getGeneratedBlockIconStyleState(value, currentStyleGuideVersionId).kind === "stale";
8603
9087
  }
8604
9088
 
8605
- //#endregion
8606
- //#region ../blocks/src/lib/media.ts
8607
- const MEDIA_TYPE_DISCRIMINANTS = mediaTypeDiscriminants;
8608
- const PUBLIC_DOWNLOAD_MEDIA_TYPES = [
8609
- "document",
8610
- "spreadsheet",
8611
- "archive",
8612
- "audio"
8613
- ];
8614
- function isMediaTypeDiscriminant(value) {
8615
- return typeof value === "string" && MEDIA_TYPE_DISCRIMINANTS.includes(value);
8616
- }
8617
- function mediaTypeDiscriminantFromMimeType(mimeType) {
8618
- const classified = classifyKnownMimeType(mimeType);
8619
- if (!classified.ok) return null;
8620
- switch (classified.value.tag) {
8621
- case "image":
8622
- case "video":
8623
- case "audio":
8624
- case "document":
8625
- case "spreadsheet":
8626
- case "archive": return classified.value.tag;
8627
- case "font": return null;
8628
- }
8629
- }
8630
-
8631
- //#endregion
8632
- //#region ../blocks/src/system/manifest/fieldValidation/writeCanonicalization.ts
8633
- function allGroupChildPlans(plan) {
8634
- return plan.childSelection.kind === "all" ? plan.childSelection.fields : plan.childSelection.tabs.flatMap((tab) => tab.fields);
8635
- }
8636
- function selectedGroupChildPlans(plan, value) {
8637
- if (plan.childSelection.kind === "all") return plan.childSelection.fields;
8638
- if (!isPlainObject$7(value)) return [];
8639
- const selectedType = value[plan.childSelection.activeTabField];
8640
- return plan.childSelection.tabs.find((tab) => tab.typeId === selectedType)?.fields ?? [];
8641
- }
8642
- function canonicalizeObjectForWrite(plans, value) {
8643
- if (!isPlainObject$7(value)) return value;
8644
- const canonical$1 = { ...value };
8645
- for (const plan of plans) {
8646
- if (!(plan.fieldId in canonical$1)) continue;
8647
- canonical$1[plan.fieldId] = canonicalizeFieldValue(plan, canonical$1[plan.fieldId]);
8648
- }
8649
- return omitUndefined(canonical$1);
8650
- }
8651
- function canonicalizeFieldValue(plan, value) {
8652
- switch (plan.kind) {
8653
- case "media": return canonicalizeMedia(value);
8654
- case "group": return plan.childSelection.kind === "discriminatedTabs" ? canonicalizeDiscriminatedGroup(plan, value) : canonicalizeObjectForWrite(plan.childSelection.fields, value);
8655
- case "repeater": return canonicalizeRepeater(plan, value);
8656
- case "string":
8657
- case "number":
8658
- case "boolean":
8659
- case "richText":
8660
- case "inlineText":
8661
- case "link":
8662
- case "select":
8663
- case "passthrough": return value;
8664
- }
8665
- }
8666
- function canonicalizeMedia(value) {
8667
- if (!isPlainObject$7(value)) return value;
8668
- const canonical$1 = { ...value };
8669
- if (mediaIdentifierSchema.safeParse(canonical$1.identifier).success) {
8670
- deleteEnvSpecificKeys(canonical$1);
8671
- }
8672
- return canonical$1;
8673
- }
8674
- function canonicalizeRepeater(plan, value) {
8675
- if (!isUnknownArray$3(value)) return value;
8676
- return value.map((item) => {
8677
- if (!isPlainObject$7(item)) return item;
8678
- const childPlans = plan.repeatedItemVariants ? plan.repeatedItemVariants.find((variant) => variant.typeId === item._type)?.fields : plan.repeatedItemPlan;
8679
- return childPlans ? canonicalizeObjectForWrite(childPlans, item) : item;
8680
- });
8681
- }
8682
- function canonicalizeDiscriminatedGroup(plan, value) {
8683
- if (!isPlainObject$7(value) || plan.childSelection.kind !== "discriminatedTabs") {
8684
- return value;
8685
- }
8686
- const selection = plan.childSelection;
8687
- const selectedPlans = selectedGroupChildPlans(plan, value);
8688
- const selectedIds = rootFieldIds(selectedPlans);
8689
- const allChildIds = rootFieldIds(allGroupChildPlans(plan));
8690
- const projected = Object.fromEntries(Object.entries(value).filter(([key]) => key === selection.activeTabField || selectedIds.has(key) || !allChildIds.has(key)));
8691
- const canonical$1 = omitUndefined(canonicalizeObjectForWrite(selectedPlans, projected));
8692
- if (plan.valueContract !== "boxBackground") return canonical$1;
8693
- const parsed = boxBackgroundSchema.safeParse(canonical$1);
8694
- return parsed.success ? parsed.data : canonical$1;
8695
- }
8696
- function rootFieldIds(plans) {
8697
- return new Set(plans.map((child) => child.fieldId.split(".")[0] ?? child.fieldId));
8698
- }
8699
- function omitUndefined(value) {
8700
- if (isUnknownArray$3(value)) return value.map(omitUndefined);
8701
- if (!isPlainObject$7(value)) return value;
8702
- return Object.fromEntries(Object.entries(value).filter(([, field]) => field !== undefined).map(([key, field]) => [key, omitUndefined(field)]));
8703
- }
8704
- function isPlainObject$7(value) {
8705
- return typeof value === "object" && value !== null && !Array.isArray(value);
8706
- }
8707
- function isUnknownArray$3(value) {
8708
- return Array.isArray(value);
8709
- }
8710
-
8711
9089
  //#endregion
8712
9090
  //#region ../blocks/src/system/fields/backgroundContentAudit.ts
8713
9091
  const BACKGROUND_SLOTS = [
@@ -8773,7 +9151,7 @@ function validationContext(options = {}) {
8773
9151
  const isDraft = options.mode === "draft";
8774
9152
  return {
8775
9153
  allowNull: options.allowNull ?? true,
8776
- allowIncomplete: isDraft || (options.allowIncomplete ?? false),
9154
+ allowIncomplete: options.mode === "storable" ? false : isDraft || (options.allowIncomplete ?? false),
8777
9155
  mode: options.mode ?? "strict"
8778
9156
  };
8779
9157
  }
@@ -8797,18 +9175,20 @@ function parseFieldPathKey(key) {
8797
9175
  return Number.isInteger(numeric) && String(numeric) === segment ? numeric : segment;
8798
9176
  });
8799
9177
  }
9178
+ const simpleIssueMessages = {
9179
+ required: "This field is required",
9180
+ invalidEmail: "Enter a valid email address",
9181
+ invalidPhone: "Enter a valid phone number",
9182
+ invalidPattern: "Check the format and try again",
9183
+ invalidUrl: "Enter a valid web address",
9184
+ invalidDate: "Choose a valid date",
9185
+ invalidTime: "Choose a valid time",
9186
+ invalidDateTime: "Choose a valid date and time"
9187
+ };
8800
9188
  function fieldIssueToMessage(issue$1) {
8801
9189
  switch (issue$1.kind) {
8802
- case "required": return "This field is required";
8803
9190
  case "wrongType": return `${issue$1.label} needs ${issue$1.expected}`;
8804
9191
  case "tooLong": return `Use ${issue$1.maximum} characters or fewer`;
8805
- case "invalidEmail": return "Enter a valid email address";
8806
- case "invalidPhone": return "Enter a valid phone number";
8807
- case "invalidPattern": return "Check the format and try again";
8808
- case "invalidUrl": return "Enter a valid web address";
8809
- case "invalidDate": return "Choose a valid date";
8810
- case "invalidTime": return "Choose a valid time";
8811
- case "invalidDateTime": return "Choose a valid date and time";
8812
9192
  case "invalidNumber": return `${issue$1.label} needs a number`;
8813
9193
  case "tooSmall": return `${issue$1.label} must be ${issue$1.minimum} or more`;
8814
9194
  case "tooLarge": return `${issue$1.label} must be ${issue$1.maximum} or less`;
@@ -8821,7 +9201,16 @@ function fieldIssueToMessage(issue$1) {
8821
9201
  case "invalidMedia": return `${issue$1.label} must be a media object with an identifier`;
8822
9202
  case "invalidBoxBackground": return `${issue$1.label} must use a valid background type and matching fields`;
8823
9203
  case "invalidLink": return `${issue$1.label} must be a valid link`;
9204
+ case "invalidLayoutGrid": return issue$1.message;
8824
9205
  case "invalidReference": return `${issue$1.label} must reference a valid item`;
9206
+ case "required":
9207
+ case "invalidDate":
9208
+ case "invalidDateTime":
9209
+ case "invalidEmail":
9210
+ case "invalidPattern":
9211
+ case "invalidPhone":
9212
+ case "invalidTime":
9213
+ case "invalidUrl": return simpleIssueMessages[issue$1.kind];
8825
9214
  default: return assertNever$12(issue$1);
8826
9215
  }
8827
9216
  }
@@ -8936,11 +9325,7 @@ function deriveFieldValidationPlan(field, path$3 = [field.id]) {
8936
9325
  }
8937
9326
  case "link": return makePlan("link", "object", field, path$3, required, commonConstraints(required, { kind: "linkObject" }));
8938
9327
  case "boolean": return makePlan("boolean", "boolean", field, path$3, required, requiredConstraints(required));
8939
- case "repeater": return {
8940
- ...makePlan("repeater", "array", field, path$3, required, commonConstraints(required, { kind: "arrayType" })),
8941
- ...field.polymorphic && field.itemTypes ? { repeatedItemVariants: deriveRepeatedItemVariants(field, [...path$3, 0]) } : { repeatedItemPlan: deriveNestedFieldPlans(getRepeaterFields(field), [...path$3, 0]) },
8942
- constraints: withRepeaterItemConstraints(field, commonConstraints(required, { kind: "arrayType" }))
8943
- };
9328
+ case "repeater": return deriveRepeaterFieldValidationPlan(field, path$3);
8944
9329
  case "group":
8945
9330
  case "modal": return {
8946
9331
  ...makePlan("group", "object", field, path$3, required, requiredConstraints(required)),
@@ -8957,6 +9342,27 @@ function deriveFieldValidationPlan(field, path$3 = [field.id]) {
8957
9342
  default: return assertNever$12(field);
8958
9343
  }
8959
9344
  }
9345
+ function deriveRepeaterFieldValidationPlan(field, path$3) {
9346
+ const required = Boolean(field.required);
9347
+ const constraints = commonConstraints(required, { kind: "arrayType" });
9348
+ return {
9349
+ ...makePlan("repeater", "array", field, path$3, required, constraints),
9350
+ ...field.ui?.repeaterPreset === "layoutGrid" ? { layoutGrid: {
9351
+ startNewRowField: field.ui.layoutGridStartNewRowField,
9352
+ columns: field.ui.layoutGridColumns,
9353
+ columnsFields: field.ui.layoutGridColumnsFields,
9354
+ spanField: resolveRepeaterGridSpanFieldId({ id: field.ui.layoutGridSpanField }),
9355
+ rowField: resolveRepeaterGridRowFieldId({ id: field.ui.layoutGridRowField }),
9356
+ columnField: resolveRepeaterGridColumnFieldId({ id: field.ui.layoutGridColumnField }),
9357
+ maxRows: field.ui.layoutGridMaxRows
9358
+ } } : {},
9359
+ ...field.polymorphic && field.itemTypes ? { repeatedItemVariants: deriveRepeatedItemVariants(field, [...path$3, 0]) } : {
9360
+ repeatedItemPlan: deriveNestedFieldPlans(getRepeaterFields(field), [...path$3, 0]),
9361
+ flattenedGroupIds: getFlattenedGroupIds(field.schema?.fields ?? [])
9362
+ },
9363
+ constraints: withRepeaterItemConstraints(field, constraints)
9364
+ };
9365
+ }
8960
9366
  function deriveFieldValidationPlans(fields$1, parentPath = []) {
8961
9367
  return fields$1.map((field) => deriveFieldValidationPlan(field, [...parentPath, field.id]));
8962
9368
  }
@@ -9006,7 +9412,8 @@ function withRepeaterItemConstraints(field, constraints) {
9006
9412
  function deriveRepeatedItemVariants(field, parentPath) {
9007
9413
  return Object.entries(field.itemTypes ?? {}).map(([typeId, itemType]) => ({
9008
9414
  typeId,
9009
- fields: deriveNestedFieldPlans(getFlattenedRepeaterFields(itemType.fields), parentPath)
9415
+ fields: deriveNestedFieldPlans(getFlattenedRepeaterFields(itemType.fields), parentPath),
9416
+ flattenedGroupIds: getFlattenedGroupIds(itemType.fields)
9010
9417
  }));
9011
9418
  }
9012
9419
  function deriveNestedFieldPlans(fields$1, parentPath) {
@@ -9016,6 +9423,11 @@ function getRepeaterFields(field) {
9016
9423
  const fields$1 = field.polymorphic && field.itemTypes ? Object.values(field.itemTypes).flatMap((itemType) => itemType.fields) : field.schema?.fields ?? [];
9017
9424
  return getFlattenedRepeaterFields(fields$1);
9018
9425
  }
9426
+ /** Only group ids absent from the actual data fields are forbidden containers. */
9427
+ function getFlattenedGroupIds(fields$1) {
9428
+ const dataIds = new Set(getFlattenedRepeaterFields(fields$1).map((field) => field.id));
9429
+ return fields$1.flatMap((field) => field.type === "group" && field.ui?.flattenInRepeater && !dataIds.has(field.id) ? [field.id] : []);
9430
+ }
9019
9431
  function getFlattenedRepeaterFields(fields$1) {
9020
9432
  return fields$1.flatMap((child) => {
9021
9433
  if (child.type === "group" && child.ui?.flattenInRepeater) {
@@ -9096,6 +9508,32 @@ function fieldPlanToNativeInputProps(plan) {
9096
9508
  function normalizeFieldInput(plan, value, options = {}) {
9097
9509
  return normalizeFieldValue(plan, value, validationContext(options));
9098
9510
  }
9511
+ /** Drop obsolete presentation containers only when reading persisted content.
9512
+ * Flat data remains authoritative; unknown variants and legitimate groups survive.
9513
+ */
9514
+ function normalizeLegacyStoredGroupContainers(plans, content) {
9515
+ if (!isPlainObject$6(content)) return content;
9516
+ const planMap = new Map(plans.map((plan) => [plan.fieldId, plan]));
9517
+ const entries = Object.entries(content).map(([key, value]) => {
9518
+ const plan = planMap.get(key);
9519
+ return [key, plan ? normalizeLegacyStoredField(plan, value) : value];
9520
+ });
9521
+ return entries.every(([key, value]) => value === content[key]) ? content : Object.fromEntries(entries);
9522
+ }
9523
+ function normalizeLegacyStoredField(plan, value) {
9524
+ if (plan.kind === "group") {
9525
+ return normalizeLegacyStoredGroupContainers(selectedGroupChildPlans(plan, value), value);
9526
+ }
9527
+ if (plan.kind !== "repeater" || !isUnknownArray$2(value)) return value;
9528
+ const items = value.map((item) => {
9529
+ if (!isPlainObject$6(item)) return item;
9530
+ const selection = selectRepeaterItemPlan(plan, item);
9531
+ if (selection.kind !== "fields") return item;
9532
+ const retained = selection.flattenedGroupIds.some((key) => Object.prototype.hasOwnProperty.call(item, key)) ? Object.fromEntries(Object.entries(item).filter(([key]) => !selection.flattenedGroupIds.includes(key))) : item;
9533
+ return normalizeLegacyStoredGroupContainers(selection.fields, retained);
9534
+ });
9535
+ return items.every((item, index) => item === value[index]) ? value : items;
9536
+ }
9099
9537
  function normalizeStoredContentValues(plans, content, options = {}) {
9100
9538
  if (!content || typeof content !== "object" || Array.isArray(content)) {
9101
9539
  return {};
@@ -9158,7 +9596,8 @@ function normalizeStoredRepeaterItems(plan, value, ctx) {
9158
9596
  return item;
9159
9597
  }
9160
9598
  const itemRecord = item;
9161
- const fields$1 = plan.repeatedItemVariants ? plan.repeatedItemVariants.find((variant) => variant.typeId === itemRecord._type)?.fields : plan.repeatedItemPlan;
9599
+ const selection = selectRepeaterItemPlan(plan, itemRecord);
9600
+ const fields$1 = selection.kind === "fields" ? selection.fields : undefined;
9162
9601
  if (!fields$1) {
9163
9602
  return item;
9164
9603
  }
@@ -9215,7 +9654,8 @@ function normalizeRepeaterItems(plan, value, ctx) {
9215
9654
  return value.map((item) => {
9216
9655
  if (!item || typeof item !== "object" || Array.isArray(item)) return item;
9217
9656
  const itemRecord = item;
9218
- const fields$1 = plan.repeatedItemVariants ? plan.repeatedItemVariants.find((variant) => variant.typeId === itemRecord._type)?.fields : plan.repeatedItemPlan;
9657
+ const selection = selectRepeaterItemPlan(plan, itemRecord);
9658
+ const fields$1 = selection.kind === "fields" ? selection.fields : undefined;
9219
9659
  if (!fields$1) return item;
9220
9660
  return normalizeObjectChildren(fields$1, itemRecord, ctx);
9221
9661
  });
@@ -9230,13 +9670,13 @@ function acceptsOptionalBlankAsMissing(plan, value) {
9230
9670
  return plan.constraints.some((constraint) => constraint.kind === "urlFormat" || constraint.kind === "slugFormat" || constraint.kind === "emailFormat" || constraint.kind === "phoneFormat" || constraint.kind === "patternFormat");
9231
9671
  }
9232
9672
  function validateNormalizedFieldValue(plan, value, ctx) {
9233
- if (value === null && (ctx.allowNull || acceptsOptionalNullValue(plan))) return [];
9234
- if (isMissingForPlan(plan, value)) {
9673
+ if (value === null && acceptsNullValue(plan, ctx)) return [];
9674
+ if (isMissingForPlan(plan, value, ctx.mode)) {
9235
9675
  return shouldRequireValue(plan, value, ctx) ? [requiredIssue(plan, value)] : [];
9236
9676
  }
9237
9677
  switch (plan.kind) {
9238
9678
  case "string": return validateStringPlan(plan, value, ctx);
9239
- case "number": return validateNumberPlan(plan, value);
9679
+ case "number": return validateNumberPlan(plan, value, ctx);
9240
9680
  case "boolean": return typeof value === "boolean" ? [] : [issue(plan, "wrongType", { expected: "a true/false value" })];
9241
9681
  case "select": return validateSelectPlan(plan, value);
9242
9682
  case "richText": return isValidRichTextDoc(value) ? [] : [issue(plan, "invalidRichText")];
@@ -9260,14 +9700,18 @@ function validateMediaPlan(plan, value, ctx) {
9260
9700
  }
9261
9701
  function shouldRequireValue(plan, value, ctx) {
9262
9702
  if (!plan.required || ctx.allowIncomplete) return false;
9263
- if (value === null && ctx.allowNull) return false;
9703
+ if (value === null && ctx.allowNull && ctx.mode !== "storable") return false;
9264
9704
  return true;
9265
9705
  }
9706
+ function acceptsNullValue(plan, ctx) {
9707
+ return ctx.mode === "storable" ? !plan.required : ctx.allowNull || acceptsOptionalNullValue(plan);
9708
+ }
9266
9709
  function acceptsOptionalNullValue(plan) {
9267
9710
  return !plan.required && (plan.kind === "media" || plan.kind === "number");
9268
9711
  }
9269
- function isMissingForPlan(plan, value) {
9712
+ function isMissingForPlan(plan, value, mode) {
9270
9713
  if (value === undefined) return true;
9714
+ if (mode === "storable") return value === null || value === "";
9271
9715
  if (value === null) return plan.required;
9272
9716
  if (typeof value === "string" && value.trim() === "") return true;
9273
9717
  if (Array.isArray(value) && value.length === 0) return true;
@@ -9286,7 +9730,7 @@ function validateStringConstraint(plan, constraint, value, ctx) {
9286
9730
  return isValidIconPickerValue(value) ? [] : [issue(plan, "wrongType", { expected: "text or icon" })];
9287
9731
  }
9288
9732
  if (typeof value !== "string") return [issue(plan, "wrongType", { expected: "text" })];
9289
- if (constraint.kind === "textType" || constraint.kind === "referenceText") return [];
9733
+ if (constraint.kind === "textType" || constraint.kind === "referenceText" || ctx.mode === "storable") return [];
9290
9734
  return constraint.kind === "maxLength" ? validateAuthoredTextLength(plan, value, constraint.maximum, ctx) : validateStringFormat(plan, constraint, value);
9291
9735
  }
9292
9736
  const SIMPLE_STRING_FORMATS = {
@@ -9328,7 +9772,7 @@ function validateStringFormat(plan, constraint, value) {
9328
9772
  }
9329
9773
  /** Authoring limits govern stored block copy; resolved entry text has its own owner. */
9330
9774
  function validateAuthoredTextLength(plan, text$1, maximum, ctx) {
9331
- return ctx.mode !== "render" && maximum !== undefined && text$1.length > maximum ? [issue(plan, "tooLong", { maximum })] : [];
9775
+ return ctx.mode !== "render" && ctx.mode !== "storable" && maximum !== undefined && text$1.length > maximum ? [issue(plan, "tooLong", { maximum })] : [];
9332
9776
  }
9333
9777
  function isValidIconPickerValue(value) {
9334
9778
  if (typeof value === "string") return true;
@@ -9336,8 +9780,9 @@ function isValidIconPickerValue(value) {
9336
9780
  if (value.kind === "none") return true;
9337
9781
  return parseBlockIconValue(value).kind !== "none";
9338
9782
  }
9339
- function validateNumberPlan(plan, value) {
9783
+ function validateNumberPlan(plan, value, ctx) {
9340
9784
  if (!isValidNumber(value)) return [issue(plan, "invalidNumber")];
9785
+ if (ctx.mode === "storable") return [];
9341
9786
  return plan.constraints.flatMap((constraint) => validateNumberConstraint(plan, constraint, Number(value)));
9342
9787
  }
9343
9788
  function validateNumberConstraint(plan, constraint, value) {
@@ -9356,7 +9801,7 @@ function validateSelectPlan(plan, value) {
9356
9801
  }
9357
9802
  function validateRepeaterPlan(plan, value, ctx) {
9358
9803
  if (!Array.isArray(value)) return [issue(plan, "wrongType", { expected: "a list" })];
9359
- const constraintIssues = plan.constraints.flatMap((constraint) => {
9804
+ const constraintIssues = ctx.mode === "storable" ? [] : plan.constraints.flatMap((constraint) => {
9360
9805
  switch (constraint.kind) {
9361
9806
  case "required":
9362
9807
  case "arrayType": return [];
@@ -9373,6 +9818,22 @@ function validateRepeaterPlan(plan, value, ctx) {
9373
9818
  });
9374
9819
  return [...constraintIssues, ...value.flatMap((item, index) => validateRepeaterItem(plan, item, index, ctx))];
9375
9820
  }
9821
+ /** Selects both the value fields and forbidden presentation containers together. */
9822
+ function selectRepeaterItemPlan(plan, item) {
9823
+ if (plan.repeatedItemVariants) {
9824
+ const variant = plan.repeatedItemVariants.find((candidate) => candidate.typeId === item._type);
9825
+ return variant ? {
9826
+ kind: "fields",
9827
+ fields: variant.fields,
9828
+ flattenedGroupIds: variant.flattenedGroupIds ?? []
9829
+ } : { kind: "invalidType" };
9830
+ }
9831
+ return plan.repeatedItemPlan ? {
9832
+ kind: "fields",
9833
+ fields: plan.repeatedItemPlan,
9834
+ flattenedGroupIds: plan.flattenedGroupIds ?? []
9835
+ } : { kind: "untyped" };
9836
+ }
9376
9837
  function validateRepeaterItem(plan, item, index, ctx) {
9377
9838
  if (!isPlainObject$6(item)) {
9378
9839
  return [{
@@ -9383,9 +9844,8 @@ function validateRepeaterItem(plan, item, index, ctx) {
9383
9844
  expected: "an object"
9384
9845
  }];
9385
9846
  }
9386
- const itemType = item._type;
9387
- const variant = plan.repeatedItemVariants ? plan.repeatedItemVariants.find((candidate) => candidate.typeId === itemType) : undefined;
9388
- if (plan.repeatedItemVariants && !variant) {
9847
+ const selection = selectRepeaterItemPlan(plan, item);
9848
+ if (selection.kind === "invalidType") {
9389
9849
  return [{
9390
9850
  kind: "invalidOption",
9391
9851
  path: [
@@ -9397,12 +9857,27 @@ function validateRepeaterItem(plan, item, index, ctx) {
9397
9857
  label: `${plan.label} item type`
9398
9858
  }];
9399
9859
  }
9400
- const fields$1 = variant?.fields ?? plan.repeatedItemPlan;
9401
- if (!fields$1) return [];
9402
- return fields$1.flatMap((childPlan) => {
9860
+ if (selection.kind === "untyped") return [];
9861
+ const containerIssues = validateFlattenedGroupContainers(selection.flattenedGroupIds, item, plan, index, ctx);
9862
+ return [...containerIssues, ...selection.fields.flatMap((childPlan) => {
9863
+ if (ctx.layoutGridCoordinates === "projected" && layoutGridCoordinateFieldIds(plan).includes(childPlan.fieldId)) return [];
9403
9864
  const indexedPlan = materializeRepeaterItemPlan(childPlan, plan.path, index);
9404
9865
  return validateNormalizedFieldValue(indexedPlan, item[childPlan.fieldId], ctx);
9405
- });
9866
+ })];
9867
+ }
9868
+ function validateFlattenedGroupContainers(groupIds, item, plan, index, ctx) {
9869
+ if (ctx.mode === "render") return [];
9870
+ return groupIds.flatMap((fieldId) => Object.prototype.hasOwnProperty.call(item, fieldId) ? [{
9871
+ kind: "wrongType",
9872
+ path: [
9873
+ ...plan.path,
9874
+ index,
9875
+ fieldId
9876
+ ],
9877
+ fieldId,
9878
+ label: plan.label,
9879
+ expected: "valid settings"
9880
+ }] : []);
9406
9881
  }
9407
9882
  function validateGroupPlan(plan, value, ctx) {
9408
9883
  if (!isPlainObject$6(value)) return [issue(plan, "wrongType", { expected: "an object" })];
@@ -9505,19 +9980,22 @@ function requiredIssue(plan, value) {
9505
9980
  if (Array.isArray(value)) return issue(plan, "required", { emptyOrigin: "array" });
9506
9981
  return issue(plan, "required");
9507
9982
  }
9508
- function fieldPlanToZod(plan, options = {}) {
9509
- const ctx = validationContext(options);
9983
+ function fieldPlanToZod(plan, options = {}, layoutGridCoordinates = "validate") {
9984
+ const ctx = {
9985
+ ...validationContext(options),
9986
+ layoutGridCoordinates
9987
+ };
9510
9988
  return z.any().transform((value) => normalizeFieldValue(plan, value, ctx)).superRefine((value, zodCtx) => {
9511
- for (const validationIssue of validateNormalizedFieldValue(plan, value, ctx)) {
9989
+ validateNormalizedFieldValue(plan, value, ctx).forEach((validationIssue) => {
9512
9990
  addFieldIssueToZodContext(zodCtx, validationIssue, plan.path);
9513
- }
9991
+ });
9514
9992
  });
9515
9993
  }
9516
9994
  function fieldPresenceForPlan(plan) {
9517
9995
  return plan.required ? { kind: "required" } : { kind: "optional" };
9518
9996
  }
9519
- function fieldPlanToObjectPropertyZod(plan, options = {}) {
9520
- const schema = fieldPlanToZod(plan, options);
9997
+ function fieldPlanToObjectPropertyZod(plan, options = {}, layoutGridCoordinates = "validate") {
9998
+ const schema = fieldPlanToZod(plan, options, layoutGridCoordinates);
9521
9999
  const presence = fieldPresenceForPlan(plan);
9522
10000
  switch (presence.kind) {
9523
10001
  case "required": return schema;
@@ -9526,11 +10004,14 @@ function fieldPlanToObjectPropertyZod(plan, options = {}) {
9526
10004
  }
9527
10005
  }
9528
10006
  /** Validate stored or runtime fields without applying editor/write normalization. */
9529
- function fieldPlanToStrictObjectPropertyZod(plan, mode = "strict") {
9530
- const ctx = strictValidationContext({
9531
- allowNull: false,
9532
- mode
9533
- });
10007
+ function fieldPlanToStrictObjectPropertyZod(plan, mode = "strict", layoutGridCoordinates = "validate") {
10008
+ const ctx = {
10009
+ ...strictValidationContext({
10010
+ allowNull: false,
10011
+ mode
10012
+ }),
10013
+ layoutGridCoordinates
10014
+ };
9534
10015
  const schema = z.any().superRefine((value, zodCtx) => {
9535
10016
  validateNormalizedFieldValue(plan, value, ctx).forEach((validationIssue) => {
9536
10017
  addFieldIssueToZodContext(zodCtx, validationIssue, plan.path);
@@ -9538,76 +10019,35 @@ function fieldPlanToStrictObjectPropertyZod(plan, mode = "strict") {
9538
10019
  });
9539
10020
  return plan.required ? schema : schema.optional();
9540
10021
  }
9541
- function addFieldIssueToZodContext(zodCtx, validationIssue, rootPath) {
9542
- const message = fieldIssueToMessage(validationIssue);
9543
- const path$3 = relativeZodIssuePath(validationIssue.path, rootPath);
9544
- const issuePath = path$3.length > 0 ? { path: toZodIssuePath(path$3) } : {};
9545
- switch (validationIssue.kind) {
9546
- case "required":
9547
- if (validationIssue.emptyOrigin) {
9548
- zodCtx.addIssue({
9549
- ...issuePath,
9550
- code: "too_small",
9551
- origin: validationIssue.emptyOrigin,
9552
- minimum: 1,
9553
- inclusive: true,
9554
- message
9555
- });
9556
- return;
9557
- }
9558
- zodCtx.addIssue({
9559
- ...issuePath,
9560
- code: "invalid_type",
9561
- expected: "nonoptional",
9562
- message
9563
- });
9564
- return;
9565
- case "tooLong":
9566
- zodCtx.addIssue({
9567
- ...issuePath,
9568
- code: "too_big",
9569
- origin: "string",
9570
- maximum: validationIssue.maximum,
9571
- inclusive: true,
9572
- message
9573
- });
9574
- return;
9575
- case "invalidOption":
9576
- zodCtx.addIssue({
9577
- ...issuePath,
9578
- code: "invalid_value",
9579
- values: [],
9580
- message
9581
- });
9582
- return;
9583
- case "wrongType":
9584
- case "invalidEmail":
9585
- case "invalidPhone":
9586
- case "invalidPattern":
9587
- case "invalidUrl":
9588
- case "invalidDate":
9589
- case "invalidTime":
9590
- case "invalidDateTime":
9591
- case "invalidNumber":
9592
- case "tooSmall":
9593
- case "tooLarge":
9594
- case "invalidSlug":
9595
- case "tooFewItems":
9596
- case "tooManyItems":
9597
- case "invalidRichText":
9598
- case "invalidInlineText":
9599
- case "invalidMedia":
9600
- case "invalidBoxBackground":
9601
- case "invalidLink":
9602
- case "invalidReference":
9603
- zodCtx.addIssue({
9604
- ...issuePath,
9605
- code: "custom",
9606
- message
9607
- });
9608
- return;
9609
- default: assertNever$12(validationIssue);
9610
- }
10022
+ function fieldIssueToZodDetail(issue$1) {
10023
+ if (issue$1.kind === "required") return issue$1.emptyOrigin ? {
10024
+ code: "too_small",
10025
+ origin: issue$1.emptyOrigin,
10026
+ minimum: 1,
10027
+ inclusive: true
10028
+ } : {
10029
+ code: "invalid_type",
10030
+ expected: "nonoptional"
10031
+ };
10032
+ if (issue$1.kind === "tooLong") return {
10033
+ code: "too_big",
10034
+ origin: "string",
10035
+ maximum: issue$1.maximum,
10036
+ inclusive: true
10037
+ };
10038
+ if (issue$1.kind === "invalidOption") return {
10039
+ code: "invalid_value",
10040
+ values: []
10041
+ };
10042
+ return { code: "custom" };
10043
+ }
10044
+ function addFieldIssueToZodContext(zodCtx, issue$1, rootPath) {
10045
+ const detail = fieldIssueToZodDetail(issue$1);
10046
+ zodCtx.addIssue({
10047
+ ...detail,
10048
+ path: toZodIssuePath(relativeZodIssuePath(issue$1.path, rootPath)),
10049
+ message: fieldIssueToMessage(issue$1)
10050
+ });
9611
10051
  }
9612
10052
  function relativeZodIssuePath(path$3, rootPath) {
9613
10053
  return startsWithFieldPath(path$3, rootPath) ? path$3.slice(rootPath.length) : path$3;
@@ -9668,7 +10108,7 @@ function sameFieldPath(left, right) {
9668
10108
  function validateManifestValues(fields$1, values, options = {}) {
9669
10109
  const plans = deriveFieldValidationPlans(fields$1);
9670
10110
  const normalized = normalizeStoredContentValues(plans, values, options);
9671
- return plans.flatMap((plan) => validateFieldValue(plan, normalized[plan.fieldId], options));
10111
+ return [...plans.flatMap((plan) => validateFieldValue(plan, normalized[plan.fieldId], options)), ...validateLayoutGridPlans(plans, normalized)];
9672
10112
  }
9673
10113
  function compilePattern(pattern) {
9674
10114
  try {
@@ -9771,6 +10211,60 @@ function isEmptyRichTextDoc(value) {
9771
10211
  return first?.type === "paragraph" && (!Array.isArray(first.content) || first.content.length === 0);
9772
10212
  }
9773
10213
 
10214
+ //#endregion
10215
+ //#region ../blocks/src/system/manifest/fieldValidation/layoutGridRendering.ts
10216
+ function hasInvalidCoordinateValues(plan, items) {
10217
+ const ids = layoutGridCoordinateFieldIds(plan);
10218
+ return items.some((item) => {
10219
+ const row$1 = layoutGridRecord(item);
10220
+ return row$1 !== null && resolveLayoutGridItemPlans(plan, row$1).some((field) => ids.includes(field.fieldId) && !fieldPlanToStrictObjectPropertyZod(field).safeParse(row$1[field.fieldId]).success);
10221
+ });
10222
+ }
10223
+ /** Shared raw-coordinate admission for editor and public rendering; retains diagnostics. */
10224
+ function admitLayoutGridForPlan(plan, value, rootValues, ids) {
10225
+ const layout = buildLayoutGridForPlan(plan, value, rootValues, ids);
10226
+ return layout && !layout.hasInvalidItems && hasInvalidCoordinateValues(plan, value) ? {
10227
+ ...layout,
10228
+ hasInvalidItems: true
10229
+ } : layout;
10230
+ }
10231
+ function projectGrid(plan, value, rootValues) {
10232
+ const grid$5 = plan.layoutGrid;
10233
+ const layout = admitLayoutGridForPlan(plan, value, rootValues);
10234
+ if (!grid$5 || !layout) return value;
10235
+ const authored = layout;
10236
+ const projected = projectRepeaterGridLayoutForRendering(authored);
10237
+ if (projected === authored) return value;
10238
+ return value.map((item, index) => {
10239
+ const row$1 = layoutGridRecord(item);
10240
+ const placement = projected.byId[String(index)];
10241
+ if (!row$1 || !placement) return item;
10242
+ const spanPlan = resolveLayoutGridItemPlans(plan, row$1).find((field) => field.fieldId === grid$5.spanField);
10243
+ return {
10244
+ ...row$1,
10245
+ [grid$5.spanField]: spanPlan?.kind === "number" ? placement.span : String(placement.span),
10246
+ [grid$5.rowField]: placement.row,
10247
+ [grid$5.columnField]: placement.colStart
10248
+ };
10249
+ });
10250
+ }
10251
+ /** Only layout coordinates are projected. Array order, item data and media references remain intact. */
10252
+ function projectLayoutGridPlansForRendering(plans, value, rootValues = layoutGridRecord(value) ?? {}) {
10253
+ const values = layoutGridRecord(value);
10254
+ if (!values) return value;
10255
+ return plans.reduce((projected, plan) => {
10256
+ const fieldValue = projected[plan.fieldId];
10257
+ const next = plan.kind === "group" ? projectLayoutGridPlansForRendering(allGroupChildPlans(plan), fieldValue, rootValues) : plan.kind === "repeater" && Array.isArray(fieldValue) ? projectGrid(plan, fieldValue, rootValues).map((item) => {
10258
+ const row$1 = layoutGridRecord(item);
10259
+ return row$1 ? projectLayoutGridPlansForRendering(resolveLayoutGridItemPlans(plan, row$1), row$1, rootValues) : item;
10260
+ }) : fieldValue;
10261
+ return next === fieldValue ? projected : {
10262
+ ...projected,
10263
+ [plan.fieldId]: next
10264
+ };
10265
+ }, values);
10266
+ }
10267
+
9774
10268
  //#endregion
9775
10269
  //#region ../blocks/src/system/manifest/validation.ts
9776
10270
  const compiledManifestContentContract = Symbol("compiledManifestContentContract");
@@ -9802,6 +10296,7 @@ function compileManifestContentContract(manifestOrFields, options = {}) {
9802
10296
  return Object.freeze({ [compiledManifestContentContract]: Object.freeze({
9803
10297
  plans,
9804
10298
  strictStoredSchema,
10299
+ renderSourceSchema: deriveStrictContentZodFromPlans(plans, "render-source"),
9805
10300
  writeSchema: deriveNormalizedContentZodFromPlans(plans, strictOptions, normalizeManifestContentForWrite).superRefine((value, ctx) => {
9806
10301
  if (options.unknownFields !== "reject") return;
9807
10302
  unknownManifestFieldPaths(plans, value).forEach((path$3) => {
@@ -9817,6 +10312,11 @@ function compileManifestContentContract(manifestOrFields, options = {}) {
9817
10312
  allowIncomplete: true,
9818
10313
  mode: "draft"
9819
10314
  }, normalizeStoredContentValues),
10315
+ renderDraftSchema: deriveNormalizedContentZodFromPlans(plans, {
10316
+ allowNull: true,
10317
+ allowIncomplete: true,
10318
+ mode: "draft"
10319
+ }, normalizeStoredContentValues, false),
9820
10320
  renderSchema: deriveStrictContentZodFromPlans(plans, "render"),
9821
10321
  findNonPortableMediaPaths: (content) => findNonPortableMediaPaths(plans, content)
9822
10322
  }) });
@@ -9855,7 +10355,8 @@ function findNonPortableManifestMediaPaths(contract, content) {
9855
10355
  /** Strictly admits already-persisted content without repairing it. */
9856
10356
  function admitStoredManifestContent(contract, content) {
9857
10357
  const state = contract[compiledManifestContentContract];
9858
- const nonPortablePaths = state.findNonPortableMediaPaths(content);
10358
+ const normalized = normalizeLegacyStoredGroupContainers(state.plans, content);
10359
+ const nonPortablePaths = state.findNonPortableMediaPaths(normalized);
9859
10360
  if (nonPortablePaths.length > 0) {
9860
10361
  return {
9861
10362
  ok: false,
@@ -9865,10 +10366,32 @@ function admitStoredManifestContent(contract, content) {
9865
10366
  }
9866
10367
  };
9867
10368
  }
9868
- const parsed = state.strictStoredSchema.safeParse(content);
10369
+ const parsed = state.strictStoredSchema.safeParse(normalized);
9869
10370
  return parsed.success ? {
9870
10371
  ok: true,
9871
- value: content
10372
+ value: normalized
10373
+ } : {
10374
+ ok: false,
10375
+ error: zodAdmissionError(parsed.error)
10376
+ };
10377
+ }
10378
+ /** Public reads alone may project invalid grid geometry. All other stored-shape and portability checks remain strict. */
10379
+ function admitStoredManifestContentForRendering(contract, content) {
10380
+ const state = contract[compiledManifestContentContract];
10381
+ const normalized = normalizeLegacyStoredGroupContainers(state.plans, content);
10382
+ const paths = state.findNonPortableMediaPaths(normalized);
10383
+ if (paths.length > 0) return {
10384
+ ok: false,
10385
+ error: {
10386
+ kind: "non_portable_media",
10387
+ paths
10388
+ }
10389
+ };
10390
+ const projected = projectLayoutGridPlansForRendering(state.plans, normalized);
10391
+ const parsed = state.renderSourceSchema.safeParse(projected);
10392
+ return parsed.success ? {
10393
+ ok: true,
10394
+ value: projected
9872
10395
  } : {
9873
10396
  ok: false,
9874
10397
  error: zodAdmissionError(parsed.error)
@@ -9916,6 +10439,24 @@ function validateManifestDraft(contract, data) {
9916
10439
  issues: [firstIssue, ...remainingIssues]
9917
10440
  };
9918
10441
  }
10442
+ /** Public template bases may be incomplete until bindings resolve; only grid geometry is projected. */
10443
+ function validateManifestDraftForRendering(contract, data) {
10444
+ const state = contract[compiledManifestContentContract];
10445
+ const projected = projectLayoutGridPlansForRendering(state.plans, data);
10446
+ const parsed = state.renderDraftSchema.safeParse(projected);
10447
+ if (parsed.success) return {
10448
+ kind: "previewable",
10449
+ data: projected,
10450
+ issues: []
10451
+ };
10452
+ const [firstIssue, ...remainingIssues] = zodAdmissionIssues(parsed.error);
10453
+ if (!firstIssue) throw new Error("Render draft validation failed without an issue");
10454
+ return {
10455
+ kind: "invalid",
10456
+ data: projected,
10457
+ issues: [firstIssue, ...remainingIssues]
10458
+ };
10459
+ }
9919
10460
  /**
9920
10461
  * Checks whether a raw editor candidate is schema-ready for the effectful
9921
10462
  * media-resolution/write boundary without granting a stored proof.
@@ -9942,8 +10483,9 @@ function validateManifestWriteCandidate(contract, data) {
9942
10483
  * the resulting render shape before granting the renderable proof.
9943
10484
  */
9944
10485
  function deriveRenderableManifestContent(contract, stored, project) {
9945
- const projected = project(stored);
9946
- const parsed = contract[compiledManifestContentContract].renderSchema.safeParse(projected);
10486
+ const state = contract[compiledManifestContentContract];
10487
+ const projected = projectLayoutGridPlansForRendering(state.plans, normalizeLegacyStoredGroupContainers(state.plans, project(stored)));
10488
+ const parsed = state.renderSchema.safeParse(projected);
9947
10489
  return parsed.success ? {
9948
10490
  ok: true,
9949
10491
  value: projected
@@ -9958,33 +10500,50 @@ function deriveRenderableManifestContent(contract, stored, project) {
9958
10500
  * grants a stored proof, so hydrated media cannot flow back to persistence.
9959
10501
  */
9960
10502
  function admitTransportedRenderableManifestContent(contract, content) {
9961
- const parsed = contract[compiledManifestContentContract].renderSchema.safeParse(content);
10503
+ const state = contract[compiledManifestContentContract];
10504
+ const projected = projectLayoutGridPlansForRendering(state.plans, content);
10505
+ const parsed = state.renderSchema.safeParse(projected);
9962
10506
  return parsed.success ? {
9963
10507
  ok: true,
9964
- value: content
10508
+ value: projected
9965
10509
  } : {
9966
10510
  ok: false,
9967
10511
  error: zodAdmissionError(parsed.error)
9968
10512
  };
9969
10513
  }
9970
- function deriveNormalizedContentZodFromPlans(plans, options, normalize$4) {
10514
+ function deriveNormalizedContentZodFromPlans(plans, options, normalize$4, validateLayoutGrids = true) {
9971
10515
  const isDraft = options.mode === "draft";
9972
- const baseSchema = deriveContentZodFromPlans(plans, options);
10516
+ const baseSchema = deriveContentZodFromPlans(plans, options, validateLayoutGrids);
9973
10517
  if (isDraft) {
9974
10518
  return z.preprocess((content) => preprocessEmptyValues(normalize$4(plans, content, options)), baseSchema);
9975
10519
  }
9976
10520
  return z.preprocess((content) => normalize$4(plans, content, options), baseSchema);
9977
10521
  }
9978
- function deriveContentZodFromPlans(plans, options) {
9979
- const shape = {};
9980
- for (const plan of plans) {
9981
- shape[plan.fieldId] = fieldPlanToObjectPropertyZod(plan, options);
9982
- }
9983
- return z.object(shape).catchall(z.unknown()).passthrough();
10522
+ function deriveContentZodFromPlans(plans, options, validateLayoutGrids = true) {
10523
+ const shape = Object.fromEntries(plans.map((plan) => [plan.fieldId, fieldPlanToObjectPropertyZod(plan, options, validateLayoutGrids ? "validate" : "projected")]));
10524
+ return z.object(shape).catchall(z.unknown()).passthrough().superRefine((values, ctx) => {
10525
+ if (!validateLayoutGrids) return;
10526
+ validateLayoutGridPlans(plans, values).forEach((issue$1) => {
10527
+ ctx.addIssue({
10528
+ code: "custom",
10529
+ path: [...issue$1.path],
10530
+ message: issue$1.message
10531
+ });
10532
+ });
10533
+ });
9984
10534
  }
9985
10535
  function deriveStrictContentZodFromPlans(plans, mode = "strict") {
9986
- const shape = Object.fromEntries(plans.map((plan) => [plan.fieldId, fieldPlanToStrictObjectPropertyZod(plan, mode)]));
9987
- return z.object(shape).catchall(z.unknown()).passthrough();
10536
+ const shape = Object.fromEntries(plans.map((plan) => [plan.fieldId, fieldPlanToStrictObjectPropertyZod(plan, mode === "render" ? "render" : "strict", mode === "strict" ? "validate" : "projected")]));
10537
+ return z.object(shape).catchall(z.unknown()).passthrough().superRefine((values, ctx) => {
10538
+ if (mode !== "strict") return;
10539
+ validateLayoutGridPlans(plans, values).forEach((issue$1) => {
10540
+ ctx.addIssue({
10541
+ code: "custom",
10542
+ path: [...issue$1.path],
10543
+ message: issue$1.message
10544
+ });
10545
+ });
10546
+ });
9988
10547
  }
9989
10548
  function derivePlansFromManifestOrFields(manifestOrFields) {
9990
10549
  const fields$1 = Array.isArray(manifestOrFields) ? manifestOrFields : manifestOrFields.fields ?? [];
@@ -12306,6 +12865,7 @@ function styledSection(config$1) {
12306
12865
  baseClass,
12307
12866
  spacing,
12308
12867
  fullWidthPath,
12868
+ photoPaintsGround: renderBackgroundImage,
12309
12869
  skinSlot: config$1.skinSlot
12310
12870
  }), [renderedBackgroundNode, ...childrenArray]);
12311
12871
  }
@@ -12317,7 +12877,10 @@ function sectionAppearanceProps(input) {
12317
12877
  from: `${input.bindFrom}.background`,
12318
12878
  transforms: [{
12319
12879
  id: "sectionStyles.resolveColorsStyle",
12320
- options: { fallbackToken: input.background }
12880
+ options: {
12881
+ fallbackToken: input.background,
12882
+ photoPaintsGround: input.photoPaintsGround
12883
+ }
12321
12884
  }]
12322
12885
  } },
12323
12886
  className: { $bind: {
@@ -13487,70 +14050,6 @@ function applyAlpha(color, opacity) {
13487
14050
  return color;
13488
14051
  }
13489
14052
 
13490
- //#endregion
13491
- //#region ../blocks/src/system/fields/backgroundContent.ts
13492
- /**
13493
- * Replaces only the background medium while retaining presentation fields that
13494
- * are valid for the resulting image variant. Stale color/gradient tab state is
13495
- * never copied into the new value.
13496
- */
13497
- function replaceBackgroundWithImage(value, image, defaults = {}) {
13498
- const current = parseBackgroundContent(value);
13499
- let candidate;
13500
- if (current?.type === "image") {
13501
- const { type: _type, image: _image, ...presentation } = current;
13502
- candidate = {
13503
- ...defaults,
13504
- ...presentation,
13505
- type: "image",
13506
- image
13507
- };
13508
- } else if (current?.type === "video") {
13509
- const { type: _type, image: _image, showVideoOnMobile: _mobile, ...presentation } = current;
13510
- candidate = {
13511
- ...defaults,
13512
- ...presentation,
13513
- type: "image",
13514
- image
13515
- };
13516
- } else {
13517
- candidate = {
13518
- ...defaults,
13519
- ...commonPresentation(current),
13520
- type: "image",
13521
- image
13522
- };
13523
- }
13524
- const canonical$1 = boxBackgroundSchema.parse(candidate);
13525
- if (canonical$1.type !== "image") {
13526
- throw new Error("Image background construction produced a non-image variant");
13527
- }
13528
- return canonical$1;
13529
- }
13530
- function parseBackgroundContent(value) {
13531
- const result = boxBackgroundSchema.safeParse(value);
13532
- return result.success ? result.data : null;
13533
- }
13534
- function commonPresentation(background) {
13535
- return background ? {
13536
- contentOwner: background.contentOwner,
13537
- textColor: background.textColor,
13538
- headingColor: background.headingColor,
13539
- linkColor: background.linkColor,
13540
- supportingColor: background.supportingColor
13541
- } : {};
13542
- }
13543
- function parseBackgroundOverlay(value) {
13544
- const result = boxBackgroundOverlaySchema.safeParse(value);
13545
- return result.success ? result.data : undefined;
13546
- }
13547
- /** Rendering accepts explicit application sources separately from stored references. */
13548
- const runtimeBackgroundContentSchema = runtimeBoxBackgroundSchema;
13549
- function parseRuntimeBackgroundContent(value) {
13550
- const result = runtimeBackgroundContentSchema.safeParse(value);
13551
- return result.success ? result.data : null;
13552
- }
13553
-
13554
14053
  //#endregion
13555
14054
  //#region ../blocks/src/system/transforms/registry/_utils/themeColorResolution.ts
13556
14055
  function resolveBackgroundColorValue(value, context) {
@@ -13578,7 +14077,7 @@ function resolveSurfaceForegroundStyles(value, context, options) {
13578
14077
  const key = JSON.stringify([
13579
14078
  value,
13580
14079
  context.proseHoverCandidate,
13581
- options?.fallbackToken,
14080
+ options,
13582
14081
  context.data?._lookSlots !== undefined
13583
14082
  ]);
13584
14083
  const cache = sectionSurfaceCache.get(context.theme) ?? new Map();
@@ -13595,9 +14094,13 @@ function resolveSurfaceForegroundStyles(value, context, options) {
13595
14094
  }
13596
14095
  /** Cache only the owner's result; no foreground or button rules live here. */
13597
14096
  function resolveRuntimeSectionSurface(value, context, options) {
14097
+ const background = resolveSectionBackground(parseCanonicalRuntimeBackground({ _sectionStyles: { background: value } }), null, undefined, {
14098
+ paintsGround: options?.photoPaintsGround !== false,
14099
+ fallbackToken: options?.fallbackToken
14100
+ });
13598
14101
  return resolveSurface({
13599
14102
  kind: "section",
13600
- background: parseBackgroundContent(value)
14103
+ background: sectionForegroundBackground(background, context.theme.palette)
13601
14104
  }, {
13602
14105
  palette: context.theme.palette,
13603
14106
  buttonSystem: context.theme.buttons,
@@ -13622,6 +14125,70 @@ function surfaceInkStyles(foreground, palette) {
13622
14125
  };
13623
14126
  }
13624
14127
 
14128
+ //#endregion
14129
+ //#region ../blocks/src/system/fields/backgroundContent.ts
14130
+ /**
14131
+ * Replaces only the background medium while retaining presentation fields that
14132
+ * are valid for the resulting image variant. Stale color/gradient tab state is
14133
+ * never copied into the new value.
14134
+ */
14135
+ function replaceBackgroundWithImage(value, image, defaults = {}) {
14136
+ const current = parseBackgroundContent(value);
14137
+ let candidate;
14138
+ if (current?.type === "image") {
14139
+ const { type: _type, image: _image, ...presentation } = current;
14140
+ candidate = {
14141
+ ...defaults,
14142
+ ...presentation,
14143
+ type: "image",
14144
+ image
14145
+ };
14146
+ } else if (current?.type === "video") {
14147
+ const { type: _type, image: _image, showVideoOnMobile: _mobile, ...presentation } = current;
14148
+ candidate = {
14149
+ ...defaults,
14150
+ ...presentation,
14151
+ type: "image",
14152
+ image
14153
+ };
14154
+ } else {
14155
+ candidate = {
14156
+ ...defaults,
14157
+ ...commonPresentation(current),
14158
+ type: "image",
14159
+ image
14160
+ };
14161
+ }
14162
+ const canonical$1 = boxBackgroundSchema.parse(candidate);
14163
+ if (canonical$1.type !== "image") {
14164
+ throw new Error("Image background construction produced a non-image variant");
14165
+ }
14166
+ return canonical$1;
14167
+ }
14168
+ function parseBackgroundContent(value) {
14169
+ const result = boxBackgroundSchema.safeParse(value);
14170
+ return result.success ? result.data : null;
14171
+ }
14172
+ function commonPresentation(background) {
14173
+ return background ? {
14174
+ contentOwner: background.contentOwner,
14175
+ textColor: background.textColor,
14176
+ headingColor: background.headingColor,
14177
+ linkColor: background.linkColor,
14178
+ supportingColor: background.supportingColor
14179
+ } : {};
14180
+ }
14181
+ function parseBackgroundOverlay(value) {
14182
+ const result = boxBackgroundOverlaySchema.safeParse(value);
14183
+ return result.success ? result.data : undefined;
14184
+ }
14185
+ /** Rendering accepts explicit application sources separately from stored references. */
14186
+ const runtimeBackgroundContentSchema = runtimeBoxBackgroundSchema;
14187
+ function parseRuntimeBackgroundContent(value) {
14188
+ const result = runtimeBackgroundContentSchema.safeParse(value);
14189
+ return result.success ? result.data : null;
14190
+ }
14191
+
13625
14192
  //#endregion
13626
14193
  //#region ../blocks/src/system/transforms/registry/_utils/boxStyleClassNames.ts
13627
14194
  /**
@@ -17857,7 +18424,7 @@ function splitLayoutWith(children) {
17857
18424
  children
17858
18425
  }
17859
18426
  }),
17860
- renderBackgroundImage: false,
18427
+ renderBackgroundImage: heroPhotoPaintsGround("split"),
17861
18428
  baseClass: ""
17862
18429
  });
17863
18430
  }
@@ -17873,7 +18440,7 @@ const lookPhoto = el("figure", lookSlot("media"), [media({
17873
18440
  const lookCopy = el("div", lookSlot("copy"), heroContentNodes);
17874
18441
  const heroLookLayout = (children) => styledSection({
17875
18442
  baseClass: "",
17876
- renderBackgroundImage: false,
18443
+ renderBackgroundImage: heroPhotoPaintsGround("headline-row"),
17877
18444
  children: styledContainer({ children: el("div", lookSlot("frame"), children) })
17878
18445
  });
17879
18446
  const headlineRowLayout = heroLookLayout([
@@ -18921,8 +19488,24 @@ const blogListingBlockDefinition = {
18921
19488
  //#endregion
18922
19489
  //#region ../blocks/src/system/blocks/cta-full.ts
18923
19490
  const ctaComposition = composeFragments([{ fragment: ctaCopyFragment }, { fragment: ctaRowFragment }]);
19491
+ const figureLayout = styledSection({
19492
+ children: sectionContainer([el("div", lookSlot("layout"), [el("div", lookSlot("figure"), [text({
19493
+ as: "p",
19494
+ ...lookSlot("figure-value")
19495
+ }, when("content.figure"), bind("content.figure")), text({
19496
+ as: "p",
19497
+ ...lookSlot("figure-label")
19498
+ }, when("content.figureNote"), bind("content.figureNote"))], when("content.figure")), el("div", lookSlot("copy"), [...ctaComposition.layout])])], {
19499
+ gap: "md",
19500
+ className: "rb-relative"
19501
+ }),
19502
+ baseClass: ""
19503
+ });
18924
19504
  const ctaFullConfig = {
18925
- variantMeaning: { kind: "arrangement" },
19505
+ variantMeaning: {
19506
+ kind: "arrangement",
19507
+ lookOnlyKeys: ["figure"]
19508
+ },
18926
19509
  id: "block.cta-full",
18927
19510
  title: "Full-width CTA",
18928
19511
  category: "marketing",
@@ -18934,6 +19517,21 @@ const ctaFullConfig = {
18934
19517
  fragment: ctaRowFragment,
18935
19518
  fieldPriority: 2
18936
19519
  }],
19520
+ additionalFields: [parseFieldDefinition({
19521
+ id: "figure",
19522
+ type: "text",
19523
+ label: "Big figure",
19524
+ help: "A short number or price, like £12 or 6 weeks.",
19525
+ required: false,
19526
+ defaultValue: ""
19527
+ }), parseFieldDefinition({
19528
+ id: "figureNote",
19529
+ type: "text",
19530
+ label: "Under the figure",
19531
+ help: "A few words under it, like 'your first class'.",
19532
+ required: false,
19533
+ defaultValue: ""
19534
+ })],
18937
19535
  layout: styledSection({
18938
19536
  children: props(lookSlot("copy"))(sectionContainer(ctaComposition.layout, {
18939
19537
  gap: "md",
@@ -18941,6 +19539,7 @@ const ctaFullConfig = {
18941
19539
  })),
18942
19540
  baseClass: ""
18943
19541
  }),
19542
+ variants: { figure: figureLayout },
18944
19543
  description: "Centered call to action with optional background image.",
18945
19544
  tags: [
18946
19545
  "cta",
@@ -19786,6 +20385,21 @@ const splitHeadingColumnsLayout = styledSection({
19786
20385
  }),
19787
20386
  baseClass: ""
19788
20387
  });
20388
+ const openAnswer = el("div", lookSlot("item"), [text({
20389
+ as: "h3",
20390
+ ...lookSlot("item-title")
20391
+ }, bind("faqItem.question")), richText({ ...lookSlot("item-text") }, when("faqItem.answer"), bind("faqItem.answer"))], repeat("content.items", "faqItem", { indexName: "faqIndex" }));
20392
+ const openLayout = styledSection({
20393
+ children: styledContainer({
20394
+ baseClassName: "rb-relative rb-isolate",
20395
+ children: stack({
20396
+ gap: "lg",
20397
+ className: "rb-w-full",
20398
+ ...lookSlot("layout")
20399
+ }, [headingLayout, el("div", lookSlot("items"), [openAnswer])])
20400
+ }),
20401
+ baseClass: ""
20402
+ });
19789
20403
  const faqManifest = createBlockManifest({
19790
20404
  id: "block.faq",
19791
20405
  title: "FAQ",
@@ -19809,9 +20423,13 @@ const faqManifest = createBlockManifest({
19809
20423
  default: defaultLayout,
19810
20424
  "two-column": twoColumnLayout,
19811
20425
  "question-columns": questionColumnsLayout,
19812
- "split-heading-columns": splitHeadingColumnsLayout
20426
+ "split-heading-columns": splitHeadingColumnsLayout,
20427
+ open: openLayout
20428
+ },
20429
+ variantMeaning: {
20430
+ kind: "arrangement",
20431
+ lookOnlyKeys: ["open"]
19813
20432
  },
19814
- variantMeaning: { kind: "arrangement" },
19815
20433
  defaultVariant: "default",
19816
20434
  variantOptions: {
19817
20435
  default: {
@@ -40939,6 +41557,15 @@ const eventRegistrationManifest = createBlockManifest({
40939
41557
  ], { gap: "lg" })
40940
41558
  })]
40941
41559
  });
41560
+ const registrationEventScopeParams = {
41561
+ detailAdmission: { $bind: { from: "$root.subject.detailAdmission" } },
41562
+ seriesId: { $bind: { from: "$root.subject.eventSeriesId" } },
41563
+ seriesIds: { $bind: {
41564
+ from: "$root.subject.catalogueEventSeriesIds",
41565
+ transforms: [{ id: "value.csv" }]
41566
+ } },
41567
+ runSeriesId: { $bind: { from: "$root.entrySubrouteContext.run.seriesId" } }
41568
+ };
40942
41569
  const eventRegistrationBlockDefinition = {
40943
41570
  manifest: eventRegistrationManifest,
40944
41571
  runtime: eventRegistrationBlockRuntime,
@@ -40965,12 +41592,7 @@ const eventRegistrationBlockDefinition = {
40965
41592
  fallback: "published"
40966
41593
  } },
40967
41594
  readProfile: "registration",
40968
- seriesId: { $bind: { from: "$root.subject.eventSeriesId" } },
40969
- seriesIds: { $bind: {
40970
- from: "$root.subject.catalogueEventSeriesIds",
40971
- transforms: [{ id: "value.csv" }]
40972
- } },
40973
- runSeriesId: { $bind: { from: "$root.entrySubrouteContext.run.seriesId" } },
41595
+ ...registrationEventScopeParams,
40974
41596
  includeOccurrenceId: { $bind: { from: "$root.occurrenceContext.id" } }
40975
41597
  },
40976
41598
  mode: "server"
@@ -40990,12 +41612,7 @@ const eventRegistrationBlockDefinition = {
40990
41612
  fallback: "published"
40991
41613
  } },
40992
41614
  readProfile: "registration",
40993
- seriesId: { $bind: { from: "$root.subject.eventSeriesId" } },
40994
- seriesIds: { $bind: {
40995
- from: "$root.subject.catalogueEventSeriesIds",
40996
- transforms: [{ id: "value.csv" }]
40997
- } },
40998
- runSeriesId: { $bind: { from: "$root.entrySubrouteContext.run.seriesId" } }
41615
+ ...registrationEventScopeParams
40999
41616
  },
41000
41617
  mode: "server"
41001
41618
  },
@@ -41472,6 +42089,7 @@ const eventDetailsBlockDefinition = {
41472
42089
  } },
41473
42090
  limit: "100",
41474
42091
  readProfile: "details",
42092
+ detailAdmission: { $bind: { from: "$root.subject.detailAdmission" } },
41475
42093
  seriesId: { $bind: { from: "$root.subject.eventSeriesId" } },
41476
42094
  seriesIds: { $bind: {
41477
42095
  from: "$root.subject.catalogueEventSeriesIds",
@@ -53062,6 +53680,7 @@ function describeUnresolvedLocationMapLocation(resolvedLocation) {
53062
53680
  }
53063
53681
  }
53064
53682
  function normalizeLocationMapProps(input) {
53683
+ const parsedHours = parseWeeklyOpeningHours(input.openingHours);
53065
53684
  const resolvedLocation = resolveLocationMapLocation(input);
53066
53685
  const locationValue = resolvedLocation.kind === "unresolved" ? null : resolvedLocation.locationValue;
53067
53686
  const directionsLabel = normalizeLocationAddressText(input.directionsLabel) ?? LOCATION_MAP_DEFAULTS.directionsLabel;
@@ -53073,6 +53692,8 @@ function normalizeLocationMapProps(input) {
53073
53692
  location: toLocationCoordinates(locationValue),
53074
53693
  zoom: Math.min(LOCATION_MAP_LIMITS.zoom.max, Math.max(LOCATION_MAP_LIMITS.zoom.min, Math.round(normalizeNumber(input.zoom, LOCATION_MAP_DEFAULTS.zoom)))),
53075
53694
  heightPx: Math.min(LOCATION_MAP_LIMITS.heightPx.max, Math.max(LOCATION_MAP_LIMITS.heightPx.min, Math.round(normalizeNumber(input.heightPx, LOCATION_MAP_DEFAULTS.heightPx)))),
53695
+ openingHours: parsedHours.ok ? parsedHours.value : null,
53696
+ showOpeningHours: normalizeBoolean$1(input.showOpeningHours, true),
53076
53697
  showAddressText: normalizeBoolean$1(input.showAddressText, LOCATION_MAP_DEFAULTS.showAddressText),
53077
53698
  showDirections: normalizeBoolean$1(input.showDirections, LOCATION_MAP_DEFAULTS.showDirections),
53078
53699
  directionsLabel,
@@ -53085,6 +53706,8 @@ function toLocationMapHydrationProps(input) {
53085
53706
  location: normalized.locationValue,
53086
53707
  zoom: normalized.zoom,
53087
53708
  heightPx: normalized.heightPx,
53709
+ openingHours: normalized.openingHours,
53710
+ showOpeningHours: normalized.showOpeningHours,
53088
53711
  showAddressText: normalized.showAddressText,
53089
53712
  showDirections: normalized.showDirections,
53090
53713
  directionsLabel: normalized.directionsLabel,
@@ -53122,6 +53745,7 @@ function normalizeLocationMapContent(content) {
53122
53745
  heading: typeof record.heading === "string" ? record.heading : undefined,
53123
53746
  locationSource,
53124
53747
  ...authoredLocationMapSourceFields(locationSource, venueId, normalized.locationValue),
53748
+ showOpeningHours: record.showOpeningHours !== false,
53125
53749
  showAddressText: normalized.showAddressText,
53126
53750
  showDirections: normalized.showDirections,
53127
53751
  directionsLabel: normalized.directionsLabel,
@@ -53255,6 +53879,13 @@ const locationMapManifest = createBlockManifest({
53255
53879
  }
53256
53880
  ] }
53257
53881
  }),
53882
+ parseFieldDefinition({
53883
+ id: "showOpeningHours",
53884
+ type: "boolean",
53885
+ label: "Show opening hours",
53886
+ defaultValue: true,
53887
+ ui: { intent: "display" }
53888
+ }),
53258
53889
  parseFieldDefinition({
53259
53890
  id: "showAddressText",
53260
53891
  type: "boolean",
@@ -53316,6 +53947,14 @@ const locationMapManifest = createBlockManifest({
53316
53947
  baseClass: "",
53317
53948
  spacing: "comfortable",
53318
53949
  children: sectionContainer([text({ as: "h2" }, bind("content.heading"), when("content.heading")), el("location-map", {
53950
+ openingHours: { $bind: {
53951
+ from: "$root.openingHours",
53952
+ fallback: null
53953
+ } },
53954
+ showOpeningHours: { $bind: {
53955
+ from: "content.showOpeningHours",
53956
+ fallback: true
53957
+ } },
53319
53958
  location: { $bind: {
53320
53959
  from: "content.location",
53321
53960
  fallback: null
@@ -53368,6 +54007,7 @@ const locationMapBlockDefinition = {
53368
54007
  "venueId",
53369
54008
  "location",
53370
54009
  "showAddressText",
54010
+ "showOpeningHours",
53371
54011
  "showDirections",
53372
54012
  "directionsLabel",
53373
54013
  "zoom",
@@ -58504,6 +59144,7 @@ function validateAppointmentBookingFormSource(input) {
58504
59144
  }
58505
59145
  function normalizeAppointmentBookingServices(input) {
58506
59146
  return input.services.map((service) => ({
59147
+ ...service.image === undefined ? {} : { image: service.image },
58507
59148
  id: service.id,
58508
59149
  siteId: input.siteId,
58509
59150
  title: service.title,
@@ -58598,6 +59239,7 @@ const blockBookingPaymentTermsAvailabilitySchema = z.union([z.object({
58598
59239
  reason: z.literal("online_payment_configured_before_launch")
58599
59240
  })]);
58600
59241
  const bookingServiceSchema = z.object({
59242
+ image: runtimeMediaSchema.nullable().optional(),
58601
59243
  location: z.object({ mode: z.enum([
58602
59244
  "online",
58603
59245
  "in_person",
@@ -59284,7 +59926,7 @@ function scheduleLine(summary, event, now) {
59284
59926
  date: "short",
59285
59927
  year: now ? year(value) : "omit"
59286
59928
  });
59287
- const dates = (startsAt, endsAt) => formatDateRange({
59929
+ const dates = (startsAt, endsAt, order = "chronological") => formatDateRange({
59288
59930
  kind: "civil",
59289
59931
  value: getLocalScheduleDate(new Date(startsAt), context.timeZone)
59290
59932
  }, {
@@ -59292,6 +59934,7 @@ function scheduleLine(summary, event, now) {
59292
59934
  value: getLocalScheduleDate(new Date(endsAt), resolveBookingBusinessTimeZone(last.timeZone))
59293
59935
  }, context, {
59294
59936
  date: "short",
59937
+ order,
59295
59938
  year: now && year(startsAt) === "omit" && year(endsAt, resolveBookingBusinessTimeZone(last.timeZone)) === "omit" ? "omit" : now ? "numeric" : "omit"
59296
59939
  });
59297
59940
  switch (summary.kind) {
@@ -59305,8 +59948,8 @@ function scheduleLine(summary, event, now) {
59305
59948
  zone
59306
59949
  }) : date(summary.occurrence.startsAt);
59307
59950
  case "multi-day-span": return dates(summary.occurrence.startsAt, summary.occurrence.endsAt);
59308
- case "fixed-recurring-run": return `${dates(summary.first.startsAt, summary.last.startsAt)} · ${summary.recurrenceLabel}`;
59309
- case "irregular-multi-session-run": return `${dates(summary.first.startsAt, summary.last.startsAt)} · ${summary.sessionCount} dates`;
59951
+ case "fixed-recurring-run": return `${dates(summary.first.startsAt, summary.last.startsAt, "session")} · ${summary.recurrenceLabel}`;
59952
+ case "irregular-multi-session-run": return `${dates(summary.first.startsAt, summary.last.startsAt, "session")} · ${summary.sessionCount} dates`;
59310
59953
  }
59311
59954
  }
59312
59955
  function offeringAvailability(event) {
@@ -59945,16 +60588,44 @@ var event_details_server_default = EventDetailsSSR;
59945
60588
 
59946
60589
  //#endregion
59947
60590
  //#region ../blocks/src/system/runtime/nodes/location-map.view.tsx
59948
- function LocationMapView({ addressText, location, zoom, heightPx, showAddressText, showDirections, directionsLabel, unresolvedMessage, renderMap }) {
59949
- const directionsUrl = buildGoogleMapsDirectionsUrl({
60591
+ function OpeningHoursTable({ hours: hours$1 }) {
60592
+ if (!hours$1) return null;
60593
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("caption", { children: "Opening hours" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: formatOpeningHoursRows(hours$1).map((row$1) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", {
60594
+ "data-closed": row$1.kind === "closed" ? true : undefined,
60595
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
60596
+ scope: "row",
60597
+ children: row$1.label
60598
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: row$1.hours })]
60599
+ }, row$1.label)) })] }), hours$1.note ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: hours$1.note }) : null] });
60600
+ }
60601
+ function LocationMapDirections({ url, label }) {
60602
+ return url ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemedButtonLink, {
60603
+ href: url,
60604
+ target: "_blank",
60605
+ rel: "noreferrer",
60606
+ variant: "ghost",
60607
+ size: "sm",
60608
+ extraClassName: "rb-w-fit",
60609
+ label
60610
+ }) : null;
60611
+ }
60612
+ function LocationMapView(props$1) {
60613
+ const { addressText, location, renderMap, zoom, heightPx, showAddressText, directionsLabel } = props$1;
60614
+ const directionsUrl = props$1.showDirections ? buildGoogleMapsDirectionsUrl({
59950
60615
  addressText,
59951
60616
  location
59952
- });
59953
- if (!location && !addressText && !directionsUrl) {
60617
+ }) : null;
60618
+ const hours$1 = props$1.showOpeningHours === false ? null : props$1.openingHours ?? null;
60619
+ if (![
60620
+ location,
60621
+ addressText,
60622
+ directionsUrl,
60623
+ hours$1
60624
+ ].some(Boolean)) {
59954
60625
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
59955
60626
  className: "rb-caption",
59956
60627
  style: textColorStyle("mutedText"),
59957
- children: unresolvedMessage ?? "Location details coming soon."
60628
+ children: props$1.unresolvedMessage ?? "Location details coming soon."
59958
60629
  });
59959
60630
  }
59960
60631
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
@@ -59972,15 +60643,11 @@ function LocationMapView({ addressText, location, zoom, heightPx, showAddressTex
59972
60643
  style: textColorStyle("mutedText"),
59973
60644
  children: addressText
59974
60645
  }) : null,
59975
- showDirections && directionsUrl ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemedButtonLink, {
59976
- href: directionsUrl,
59977
- target: "_blank",
59978
- rel: "noreferrer",
59979
- variant: "ghost",
59980
- size: "sm",
59981
- extraClassName: "rb-w-fit",
60646
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OpeningHoursTable, { hours: hours$1 }),
60647
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LocationMapDirections, {
60648
+ url: directionsUrl,
59982
60649
  label: directionsLabel
59983
- }) : null
60650
+ })
59984
60651
  ]
59985
60652
  });
59986
60653
  }
@@ -59997,14 +60664,7 @@ function LocationMapSSR(props$1) {
59997
60664
  children: [
59998
60665
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IslandPropsScript, { value: hydrationProps }),
59999
60666
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LocationMapView, {
60000
- addressText: normalized.addressText,
60001
- location: normalized.location,
60002
- zoom: normalized.zoom,
60003
- heightPx: normalized.heightPx,
60004
- showAddressText: normalized.showAddressText,
60005
- showDirections: normalized.showDirections,
60006
- directionsLabel: normalized.directionsLabel,
60007
- unresolvedMessage: normalized.unresolvedMessage,
60667
+ ...normalized,
60008
60668
  renderMap: ({ addressText, className, height }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
60009
60669
  className,
60010
60670
  style: {
@@ -67189,7 +67849,8 @@ function arrangementContentFacts(content) {
67189
67849
  photo: photoCount > 0,
67190
67850
  facts: Array.isArray(content.facts) && content.facts.length > 0,
67191
67851
  featured,
67192
- outcome: typeof content.outcome === "string" && content.outcome.length > 0
67852
+ outcome: typeof content.outcome === "string" && content.outcome.length > 0,
67853
+ figure: typeof content.figure === "string" && content.figure.trim().length > 0
67193
67854
  }
67194
67855
  };
67195
67856
  }
@@ -67349,7 +68010,7 @@ function resolveArrangementRoleSurfaces(look, runtime, background) {
67349
68010
  }
67350
68011
  /** The arrangement key is resolved once by planBlock, including pinned Looks. */
67351
68012
  function heroPhotoGround(entry) {
67352
- return entry.kind === "renderable" && entry.block.kind === "block.hero" ? { paintsGround: entry.effectiveArrangementKey === "classic" } : undefined;
68013
+ return entry.kind === "renderable" && entry.block.kind === "block.hero" ? { paintsGround: heroPhotoPaintsGround(entry.effectiveArrangementKey ?? undefined) } : undefined;
67353
68014
  }
67354
68015
  /** Header consumers receive the opening that the public render plan actually admits. */
67355
68016
  function resolvePageOpeningSection(page, options = {}) {
@@ -68470,7 +69131,7 @@ function compilePagePushCommands(input) {
68470
69131
  });
68471
69132
  }
68472
69133
  function pageCommandRouting(diff) {
68473
- const changedPages = diff.pages.filter((pageDiff) => pageDiff.type !== "unchanged" || Boolean(pageDiff.statusChange));
69134
+ const changedPages = diff.pages.filter(hasPageOperations);
68474
69135
  if (changedPages.length === 0) return { kind: "no_page_changes" };
68475
69136
  return { kind: "command_backed" };
68476
69137
  }
@@ -68562,7 +69223,7 @@ function pageCommandReportToSyncResult(report) {
68562
69223
  });
68563
69224
  }
68564
69225
  function pageCommandsForDiff(input) {
68565
- if (input.pageDiff.type === "unchanged" && !input.pageDiff.statusChange) {
69226
+ if (!hasPageOperations(input.pageDiff)) {
68566
69227
  return ok(emptyPageCommandCompileFragment());
68567
69228
  }
68568
69229
  if (!input.localPage) {
@@ -68585,10 +69246,7 @@ function pageCommandsForDiff(input) {
68585
69246
  input: {
68586
69247
  identifier: localPageIdentifier,
68587
69248
  slug: localPageIdentifier,
68588
- title: input.localPage.title,
68589
- path: input.localPage.path,
68590
- ...input.localPage.seoTitle ? { seoTitle: input.localPage.seoTitle } : {},
68591
- ...input.localPage.seoDescription ? { seoDescription: input.localPage.seoDescription } : {},
69249
+ ...pageMetadataForUpsert(input.localPage),
68592
69250
  ...input.localPage.status ? { status: input.localPage.status } : {}
68593
69251
  }
68594
69252
  };
@@ -78049,7 +78707,7 @@ async function loadConfig(configPath, output) {
78049
78707
  return { errorMessage: "No config path provided." };
78050
78708
  }
78051
78709
  try {
78052
- const { loadConfigFile: loadConfigFile$1 } = await import("../load-config-Kg45qY-N.mjs");
78710
+ const { loadConfigFile: loadConfigFile$1 } = await import("../load-config-BRd0EUsF.mjs");
78053
78711
  const rawConfig = await loadConfigFile$1(configPath);
78054
78712
  const parsed = riverbankSiteConfigSchema.safeParse(rawConfig);
78055
78713
  if (!parsed.success) {