riverbankcms 0.4.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -0
- package/dist/acceptance/matrix.mjs +202 -3
- package/dist/cli/index.mjs +69232 -69439
- package/dist/{eventUpsertInputSchema-Dg60PKJI.mjs → eventUpsertInputSchema-7QFvBuxF.mjs} +4 -108
- package/dist/load-config-D88VsMlB.mjs +5 -0
- package/dist/{load-config-CCaUI4HN.mjs → load-config-DKJkcASy.mjs} +1 -1
- package/dist/{plan-CaWqAqfn.mjs → plan-DPS1XAby.mjs} +1 -1
- package/dist/scaffold/index.mjs +2 -2
- package/dist/{sdk-event-upsert-C6zsxDcf.mjs → sdk-event-upsert-Dy08IXYL.mjs} +82 -7
- package/dist/{sdk-peer-C6kOODEY.mjs → sdk-peer-sZF43pDS.mjs} +2 -2
- package/dist/{src-BRAF1Dee.mjs → src-DKm0bLEA.mjs} +40 -24
- package/dist/{style-qH8rqy9H.mjs → style-CeGQc8Oy.mjs} +2606 -992
- package/dist/testing.mjs +4 -4
- package/package.json +1 -1
- package/dist/load-config-CNhueqVc.mjs +0 -5
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { At as ok, B as parseUtcIsoDateTimeString, Ct as unsafeAsSiteScopedStoragePath, D as mapNonEmptyArray, Dt as unsafeAsUploadSessionId, E as fromArray, Et as unsafeAsStoragePath, O as hasText, Ot as unsafeAsUserId, St as unsafeAsSiteId, Tt as unsafeAsStorageBucketName, Z as assertNever, _ as parseFooterCompositionV1, _t as unsafeAsPositiveByteCount, a as parseVerticalPresetId, at as unsafeAsBookingRequirementId, bt as unsafeAsSafeDownloadFilename, c as EVENT_PRESET_VALUES, dt as unsafeAsEventOfferingId, f as createSecret, ft as unsafeAsEventSeriesId, g as footerNavigationLayouts, gt as unsafeAsOriginalFilename, h as stableJsonStringify, ht as unsafeAsOccurrenceId, i as parseVerticalId, k as isPresent, kt as err, l as ORDINARY_EVENT_PRESET_VALUES, lt as unsafeAsDetectedMimeType, m as parseManagementKey, mt as unsafeAsMediaAssetId, n as canonicalizeConfiguredBookingVerticalConfig, nt as parseSiteId, o as resolveSiteVerticalBookingConfig, ot as unsafeAsBookingSessionId, p as exposeSecret, pt as unsafeAsFileChecksumSha256, st as unsafeAsDeclaredMimeType, ut as unsafeAsEntitlementId, vt as unsafeAsPositiveSeconds, w as isCanonicalRichTextDoc, xt as unsafeAsSiteFontId, yt as unsafeAsProductId } from "./src-
|
|
3
|
-
import { a as classifySdkEventPricing, n as sdkEventOccurrenceInputSchema, o as classifySdkEventPaymentTerms } from "./sdk-event-upsert-
|
|
4
|
-
import { t as CliSdkPeerError } from "./sdk-peer-
|
|
2
|
+
import { At as ok, B as parseUtcIsoDateTimeString, Ct as unsafeAsSiteScopedStoragePath, D as mapNonEmptyArray, Dt as unsafeAsUploadSessionId, E as fromArray, Et as unsafeAsStoragePath, O as hasText, Ot as unsafeAsUserId, St as unsafeAsSiteId, Tt as unsafeAsStorageBucketName, Z as assertNever, _ as parseFooterCompositionV1, _t as unsafeAsPositiveByteCount, a as parseVerticalPresetId, at as unsafeAsBookingRequirementId, bt as unsafeAsSafeDownloadFilename, c as EVENT_PRESET_VALUES, dt as unsafeAsEventOfferingId, f as createSecret, ft as unsafeAsEventSeriesId, g as footerNavigationLayouts, gt as unsafeAsOriginalFilename, h as stableJsonStringify, ht as unsafeAsOccurrenceId, i as parseVerticalId, k as isPresent, kt as err, l as ORDINARY_EVENT_PRESET_VALUES, lt as unsafeAsDetectedMimeType, m as parseManagementKey, mt as unsafeAsMediaAssetId, n as canonicalizeConfiguredBookingVerticalConfig, nt as parseSiteId, o as resolveSiteVerticalBookingConfig, ot as unsafeAsBookingSessionId, p as exposeSecret, pt as unsafeAsFileChecksumSha256, st as unsafeAsDeclaredMimeType, ut as unsafeAsEntitlementId, vt as unsafeAsPositiveSeconds, w as isCanonicalRichTextDoc, xt as unsafeAsSiteFontId, yt as unsafeAsProductId } from "./src-DKm0bLEA.mjs";
|
|
3
|
+
import { a as classifySdkEventPricing, n as sdkEventOccurrenceInputSchema, o as classifySdkEventPaymentTerms } from "./sdk-event-upsert-Dy08IXYL.mjs";
|
|
4
|
+
import { t as CliSdkPeerError } from "./sdk-peer-sZF43pDS.mjs";
|
|
5
5
|
import "node:module";
|
|
6
6
|
import { Command } from "commander";
|
|
7
7
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
8
8
|
import * as fs from "fs/promises";
|
|
9
|
-
import * as path from "path";
|
|
9
|
+
import * as path$1 from "path";
|
|
10
10
|
import * as readline from "readline";
|
|
11
11
|
import { z } from "zod";
|
|
12
|
-
import {
|
|
12
|
+
import { constants } from "node:fs";
|
|
13
|
+
import { basename, dirname as dirname$1, join } from "node:path";
|
|
14
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
15
|
+
import { access, chmod, link, lstat, mkdir, open, rename, unlink as unlink$1 } from "node:fs/promises";
|
|
13
16
|
|
|
14
17
|
//#region rolldown:runtime
|
|
15
18
|
var __create = Object.create;
|
|
@@ -175,11 +178,11 @@ function sdkCommandApplicationResultSchema(value) {
|
|
|
175
178
|
|
|
176
179
|
//#endregion
|
|
177
180
|
//#region ../api/src/sdkOccurrenceResponseSchemas.ts
|
|
178
|
-
const identifierSchema = z.string().min(1);
|
|
181
|
+
const identifierSchema$1 = z.string().min(1);
|
|
179
182
|
const timestampSchema = z.string().min(1);
|
|
180
183
|
const sdkManagementEventOccurrenceSchema = z.object({
|
|
181
|
-
id: identifierSchema.transform(unsafeAsOccurrenceId),
|
|
182
|
-
seriesId: identifierSchema.transform(unsafeAsEventSeriesId),
|
|
184
|
+
id: identifierSchema$1.transform(unsafeAsOccurrenceId),
|
|
185
|
+
seriesId: identifierSchema$1.transform(unsafeAsEventSeriesId),
|
|
183
186
|
startsAt: timestampSchema,
|
|
184
187
|
endsAt: timestampSchema,
|
|
185
188
|
timeZone: z.string().min(1),
|
|
@@ -214,6 +217,125 @@ const sdkEventOccurrenceResponseSchema = z.object({ occurrence: sdkManagementEve
|
|
|
214
217
|
const sdkDeleteEventOccurrenceResponseSchema = z.object({ deleted: z.literal(true) });
|
|
215
218
|
const sdkReorderResponseSchema = z.object({ reordered: z.literal(true) });
|
|
216
219
|
|
|
220
|
+
//#endregion
|
|
221
|
+
//#region ../api/src/sdkMediaResponseSchemas.ts
|
|
222
|
+
const identifierSchema = z.string().min(1);
|
|
223
|
+
const nullablePathSchema = z.string().nullable();
|
|
224
|
+
const managementMediaAssetSchema = z.object({
|
|
225
|
+
id: identifierSchema,
|
|
226
|
+
siteId: identifierSchema,
|
|
227
|
+
identifier: identifierSchema,
|
|
228
|
+
storageBucket: identifierSchema,
|
|
229
|
+
storagePath: identifierSchema,
|
|
230
|
+
filename: identifierSchema,
|
|
231
|
+
mimeType: identifierSchema,
|
|
232
|
+
width: z.number().int().nonnegative().nullable(),
|
|
233
|
+
height: z.number().int().nonnegative().nullable(),
|
|
234
|
+
bytes: z.number().int().nonnegative().nullable(),
|
|
235
|
+
checksumSha256: z.string().nullable(),
|
|
236
|
+
createdAt: identifierSchema,
|
|
237
|
+
updatedAt: identifierSchema
|
|
238
|
+
});
|
|
239
|
+
const blockLocationFields = {
|
|
240
|
+
siteId: identifierSchema,
|
|
241
|
+
blockId: identifierSchema,
|
|
242
|
+
blockKind: identifierSchema,
|
|
243
|
+
blockIdentifier: z.string().nullable(),
|
|
244
|
+
stage: z.enum(["draft", "published"])
|
|
245
|
+
};
|
|
246
|
+
const mediaUsageLocationSchema = z.discriminatedUnion("kind", [
|
|
247
|
+
z.object({
|
|
248
|
+
kind: z.literal("pageBlock"),
|
|
249
|
+
...blockLocationFields,
|
|
250
|
+
pageId: identifierSchema,
|
|
251
|
+
pageTitle: z.string().nullable(),
|
|
252
|
+
pageDraftPath: nullablePathSchema,
|
|
253
|
+
pagePublishedPath: nullablePathSchema
|
|
254
|
+
}),
|
|
255
|
+
z.object({
|
|
256
|
+
kind: z.literal("entrySlotBlock"),
|
|
257
|
+
...blockLocationFields,
|
|
258
|
+
entryId: identifierSchema,
|
|
259
|
+
entryTitle: z.string(),
|
|
260
|
+
typeKey: identifierSchema,
|
|
261
|
+
routeDraftPath: nullablePathSchema,
|
|
262
|
+
routePublishedPath: nullablePathSchema,
|
|
263
|
+
slotKey: identifierSchema
|
|
264
|
+
}),
|
|
265
|
+
z.object({
|
|
266
|
+
kind: z.literal("surfaceSlotBlock"),
|
|
267
|
+
...blockLocationFields,
|
|
268
|
+
pageSurfaceId: identifierSchema,
|
|
269
|
+
surfaceSlotId: identifierSchema,
|
|
270
|
+
slotKey: identifierSchema
|
|
271
|
+
}),
|
|
272
|
+
z.object({
|
|
273
|
+
kind: z.literal("contentEntry"),
|
|
274
|
+
siteId: identifierSchema,
|
|
275
|
+
entryId: identifierSchema,
|
|
276
|
+
entryTitle: z.string(),
|
|
277
|
+
typeKey: identifierSchema,
|
|
278
|
+
routeDraftPath: nullablePathSchema,
|
|
279
|
+
routePublishedPath: nullablePathSchema,
|
|
280
|
+
stage: z.enum(["draft", "published"])
|
|
281
|
+
}),
|
|
282
|
+
z.object({
|
|
283
|
+
kind: z.literal("productFileDownload"),
|
|
284
|
+
siteId: identifierSchema,
|
|
285
|
+
productId: identifierSchema,
|
|
286
|
+
productTitle: z.string()
|
|
287
|
+
})
|
|
288
|
+
]);
|
|
289
|
+
const mediaUsageItemSchema = z.object({
|
|
290
|
+
assetId: identifierSchema,
|
|
291
|
+
filename: z.string().nullable(),
|
|
292
|
+
usedCount: z.number().int().nonnegative(),
|
|
293
|
+
locations: z.array(mediaUsageLocationSchema)
|
|
294
|
+
});
|
|
295
|
+
const mediaDeleteWarningSchema = z.discriminatedUnion("kind", [z.object({
|
|
296
|
+
kind: z.literal("ORPHAN_CLEANUP_FAILED"),
|
|
297
|
+
message: z.string(),
|
|
298
|
+
storageBucket: identifierSchema,
|
|
299
|
+
storagePath: identifierSchema
|
|
300
|
+
}), z.object({
|
|
301
|
+
kind: z.literal("LABEL_CLEANUP_FAILED"),
|
|
302
|
+
message: z.string()
|
|
303
|
+
})]);
|
|
304
|
+
const sdkMediaListResponseSchema = z.object({
|
|
305
|
+
items: z.array(managementMediaAssetSchema),
|
|
306
|
+
total: z.number().int().nonnegative(),
|
|
307
|
+
limit: z.number().int().positive(),
|
|
308
|
+
offset: z.number().int().nonnegative(),
|
|
309
|
+
nextOffset: z.number().int().nonnegative().optional()
|
|
310
|
+
});
|
|
311
|
+
const sdkMediaDownloadResponseSchema = z.object({
|
|
312
|
+
transferUrl: z.string().url(),
|
|
313
|
+
asset: managementMediaAssetSchema
|
|
314
|
+
});
|
|
315
|
+
const sdkMediaDeletePreviewResponseSchema = z.discriminatedUnion("kind", [
|
|
316
|
+
z.object({ kind: z.literal("missing") }),
|
|
317
|
+
z.object({
|
|
318
|
+
kind: z.literal("in_use"),
|
|
319
|
+
usage: mediaUsageItemSchema
|
|
320
|
+
}),
|
|
321
|
+
z.object({
|
|
322
|
+
kind: z.literal("deletable"),
|
|
323
|
+
assetId: identifierSchema
|
|
324
|
+
})
|
|
325
|
+
]);
|
|
326
|
+
const sdkMediaDeleteApplyResponseSchema = z.discriminatedUnion("kind", [
|
|
327
|
+
z.object({ kind: z.literal("missing") }),
|
|
328
|
+
z.object({
|
|
329
|
+
kind: z.literal("in_use"),
|
|
330
|
+
usage: mediaUsageItemSchema
|
|
331
|
+
}),
|
|
332
|
+
z.object({ kind: z.literal("deleted") }),
|
|
333
|
+
z.object({
|
|
334
|
+
kind: z.literal("deleted_with_warnings"),
|
|
335
|
+
warnings: z.tuple([mediaDeleteWarningSchema], mediaDeleteWarningSchema)
|
|
336
|
+
})
|
|
337
|
+
]);
|
|
338
|
+
|
|
217
339
|
//#endregion
|
|
218
340
|
//#region ../blocks/src/system/types/link.ts
|
|
219
341
|
const nullableString = z.union([z.string().min(1), z.null()]);
|
|
@@ -285,16 +407,16 @@ function visitLinkValueReferences(value, visitor) {
|
|
|
285
407
|
if (!parsed.success) {
|
|
286
408
|
return;
|
|
287
409
|
}
|
|
288
|
-
const link = parsed.data;
|
|
289
|
-
switch (link.kind) {
|
|
410
|
+
const link$1 = parsed.data;
|
|
411
|
+
switch (link$1.kind) {
|
|
290
412
|
case "internal":
|
|
291
|
-
visitor.routeIds.add(link.routeId);
|
|
413
|
+
visitor.routeIds.add(link$1.routeId);
|
|
292
414
|
return;
|
|
293
415
|
case "page":
|
|
294
|
-
visitor.pageIdentifiers.add(link.identifier);
|
|
416
|
+
visitor.pageIdentifiers.add(link$1.identifier);
|
|
295
417
|
return;
|
|
296
418
|
case "entry":
|
|
297
|
-
visitor.entryIdentifiers.add(link.contentType, link.identifier);
|
|
419
|
+
visitor.entryIdentifiers.add(link$1.contentType, link$1.identifier);
|
|
298
420
|
return;
|
|
299
421
|
case "external":
|
|
300
422
|
case "url": return;
|
|
@@ -9249,6 +9371,11 @@ const gradientConfigSchema = z.object({
|
|
|
9249
9371
|
position: z.string().optional(),
|
|
9250
9372
|
stops: z.array(gradientStopSchema).min(2)
|
|
9251
9373
|
});
|
|
9374
|
+
const gradientsSchema = z.object({
|
|
9375
|
+
button: z.string().min(1).nullable().optional(),
|
|
9376
|
+
hero: z.string().min(1).nullable().optional(),
|
|
9377
|
+
background: z.string().min(1).nullable().optional()
|
|
9378
|
+
});
|
|
9252
9379
|
|
|
9253
9380
|
//#endregion
|
|
9254
9381
|
//#region ../theme-core/src/interactive/baseSchema.ts
|
|
@@ -10254,12 +10381,8 @@ const legacyHeaderVariantValues = Object.keys(legacyHeaderVariants);
|
|
|
10254
10381
|
* `@riverbankcms/api` is `z.input<typeof themeSchema>`, so a widened input here
|
|
10255
10382
|
* would leak straight into a public type.
|
|
10256
10383
|
*
|
|
10257
|
-
*
|
|
10258
|
-
*
|
|
10259
|
-
* `apps/dashboard/src/server/services/sites/runtimeSiteResponse.ts` returns the
|
|
10260
|
-
* raw database JSON asserted as `Theme` with no parse, so a theme still
|
|
10261
|
-
* carrying a legacy spelling reaches the theme editor un-aliased. That gap
|
|
10262
|
-
* predates this alias and is harmless while persisted usage is zero.
|
|
10384
|
+
* Persisted runtime themes are admitted through `parseThemeForPersistence`
|
|
10385
|
+
* before reaching consumers, so legacy spellings normalize at that boundary.
|
|
10263
10386
|
*/
|
|
10264
10387
|
const headerVariantWithLegacyAliasSchema = z.enum([...headerVariantValues, ...legacyHeaderVariantValues]).transform((value) => value in legacyHeaderVariants ? legacyHeaderVariants[value] : value);
|
|
10265
10388
|
|
|
@@ -10478,12 +10601,33 @@ const headerContainerSchema = z.object({
|
|
|
10478
10601
|
|
|
10479
10602
|
//#endregion
|
|
10480
10603
|
//#region ../theme-core/src/header/interactionPalette.ts
|
|
10481
|
-
|
|
10604
|
+
/**
|
|
10605
|
+
* The palettes that fill the nav link with a colour on hover and focus. Each
|
|
10606
|
+
* one names a background/foreground token pair.
|
|
10607
|
+
*/
|
|
10608
|
+
const headerInteractionPillPaletteValues = [
|
|
10482
10609
|
"primary",
|
|
10483
10610
|
"secondary",
|
|
10484
10611
|
"accent"
|
|
10485
10612
|
];
|
|
10613
|
+
/**
|
|
10614
|
+
* "No pill": the nav link keeps whatever hover treatment its recipe compiled,
|
|
10615
|
+
* and the palette step writes nothing at all (#1318 Phase 4c decision 2).
|
|
10616
|
+
*
|
|
10617
|
+
* It exists because "a colour change on hover, no filled background" was not
|
|
10618
|
+
* expressible in the vocabulary, and it is what
|
|
10619
|
+
* `anna-raven-therapy-and-yoga` and `kirtan-lucy` have rendered since before
|
|
10620
|
+
* the palette existed. Without it those two rows can only be classified as a
|
|
10621
|
+
* conscious visual change.
|
|
10622
|
+
*/
|
|
10623
|
+
const headerInteractionQuietPalette = "quiet";
|
|
10624
|
+
const headerInteractionPaletteValues = [...headerInteractionPillPaletteValues, headerInteractionQuietPalette];
|
|
10486
10625
|
const headerInteractionPaletteSchema = z.enum(headerInteractionPaletteValues);
|
|
10626
|
+
/**
|
|
10627
|
+
* Narrowed to the pill palettes on purpose: `'quiet'` has no token pair, and a
|
|
10628
|
+
* caller that reaches this function without excluding it first is a type error
|
|
10629
|
+
* rather than a runtime surprise.
|
|
10630
|
+
*/
|
|
10487
10631
|
function headerInteractionPalettePair(palette) {
|
|
10488
10632
|
switch (palette) {
|
|
10489
10633
|
case "primary": return {
|
|
@@ -10883,11 +11027,12 @@ const inputStyle = z.object({
|
|
|
10883
11027
|
]),
|
|
10884
11028
|
label: z.enum(["inside", "above"])
|
|
10885
11029
|
});
|
|
10886
|
-
const
|
|
11030
|
+
const headerPositioningValues = [
|
|
10887
11031
|
"static",
|
|
10888
11032
|
"sticky",
|
|
10889
11033
|
"fixed"
|
|
10890
|
-
]
|
|
11034
|
+
];
|
|
11035
|
+
const headerPositioning = z.enum(headerPositioningValues);
|
|
10891
11036
|
const headerNavStyle = z.enum([
|
|
10892
11037
|
"minimal",
|
|
10893
11038
|
"underline",
|
|
@@ -10904,7 +11049,6 @@ const navFontWeight = z.enum([
|
|
|
10904
11049
|
"bold"
|
|
10905
11050
|
]);
|
|
10906
11051
|
const headerMaxWidth = z.enum(["container", "full"]);
|
|
10907
|
-
const headerBarFrame = z.enum(["full-bleed", "inset-shell"]);
|
|
10908
11052
|
const headerBorderSchema = componentBorderSchema.extend({ position: z.enum([
|
|
10909
11053
|
"bottom",
|
|
10910
11054
|
"top",
|
|
@@ -10933,12 +11077,6 @@ const headerLogoTextSchema = z.object({
|
|
|
10933
11077
|
]).optional(),
|
|
10934
11078
|
hideOnShrink: z.boolean().optional()
|
|
10935
11079
|
}).optional();
|
|
10936
|
-
const navEffectsSchema = z.object({
|
|
10937
|
-
underlineGradient: z.boolean().default(false),
|
|
10938
|
-
glow: z.boolean().default(false),
|
|
10939
|
-
glowColor: z.string().optional(),
|
|
10940
|
-
neumorphic: z.boolean().default(false)
|
|
10941
|
-
}).optional();
|
|
10942
11080
|
const dropdownStyleSchema = z.object({
|
|
10943
11081
|
background: themeColorTokenSchema.default("surface"),
|
|
10944
11082
|
textColor: themeColorTokenSchema.default("text"),
|
|
@@ -11029,10 +11167,8 @@ const headerSchema = z.object({
|
|
|
11029
11167
|
variant: headerVariantWithLegacyAliasSchema,
|
|
11030
11168
|
positioning: headerPositioning,
|
|
11031
11169
|
shrinkOnScroll: z.boolean(),
|
|
11032
|
-
maxWidth: headerMaxWidth,
|
|
11033
|
-
barFrame: headerBarFrame.optional(),
|
|
11170
|
+
maxWidth: headerMaxWidth.default("container"),
|
|
11034
11171
|
scrollTreatment: headerScrollTreatmentSchema,
|
|
11035
|
-
logoOverride: mediaSchema.nullable().optional(),
|
|
11036
11172
|
background: headerBackgroundSchema.default({
|
|
11037
11173
|
type: "color",
|
|
11038
11174
|
color: "surface"
|
|
@@ -11044,13 +11180,11 @@ const headerSchema = z.object({
|
|
|
11044
11180
|
navFontRole: z.enum(["body", "heading"]).optional(),
|
|
11045
11181
|
navTextSize: navLinkStyleSchema.shape.textSize,
|
|
11046
11182
|
interactionPalette: headerInteractionPaletteSchema.optional(),
|
|
11047
|
-
navPlacement: z.enum(["end", "center"]).optional(),
|
|
11048
11183
|
container: headerContainerSchema,
|
|
11049
11184
|
border: headerBorderSchema,
|
|
11050
11185
|
shadow: componentShadowSchema.optional(),
|
|
11051
11186
|
logo: logoStyleSchema,
|
|
11052
11187
|
logoText: headerLogoTextSchema,
|
|
11053
|
-
navEffects: navEffectsSchema,
|
|
11054
11188
|
navContainer: navContainerSchema,
|
|
11055
11189
|
navLinkStyle: navLinkStyleSchema.optional(),
|
|
11056
11190
|
ctaGap: headerCtaGapSchema.optional(),
|
|
@@ -11227,11 +11361,6 @@ const bodyTextSchema = z.object({ intro: z.object({
|
|
|
11227
11361
|
paragraphSize: bodyTextIntroTypographySizeSchema.optional(),
|
|
11228
11362
|
headingSize: bodyTextIntroTypographySizeSchema.optional()
|
|
11229
11363
|
}).optional() }).optional();
|
|
11230
|
-
const gradientsSchema = z.object({
|
|
11231
|
-
button: z.string().min(1).nullable().optional(),
|
|
11232
|
-
hero: z.string().min(1).nullable().optional(),
|
|
11233
|
-
background: z.string().min(1).nullable().optional()
|
|
11234
|
-
});
|
|
11235
11364
|
/**
|
|
11236
11365
|
* Testimonial quote-mark treatment, owned by the site style (analogous to the
|
|
11237
11366
|
* accordion icon). `style` is the coarse selector; the optional fields fine-tune
|
|
@@ -11847,7 +11976,7 @@ function resolveAccessibleSurface(input) {
|
|
|
11847
11976
|
threshold,
|
|
11848
11977
|
rejectedCandidates
|
|
11849
11978
|
});
|
|
11850
|
-
const link = resolveRequiredForeground({
|
|
11979
|
+
const link$1 = resolveRequiredForeground({
|
|
11851
11980
|
role: "link",
|
|
11852
11981
|
candidates: recipe.linkCandidates,
|
|
11853
11982
|
backgroundToken,
|
|
@@ -11856,7 +11985,7 @@ function resolveAccessibleSurface(input) {
|
|
|
11856
11985
|
threshold,
|
|
11857
11986
|
rejectedCandidates
|
|
11858
11987
|
});
|
|
11859
|
-
if (!heading || !text || !link) continue;
|
|
11988
|
+
if (!heading || !text || !link$1) continue;
|
|
11860
11989
|
const supporting = resolveSupportingForeground({
|
|
11861
11990
|
candidates: recipe.supportingCandidates,
|
|
11862
11991
|
backgroundToken,
|
|
@@ -11876,7 +12005,7 @@ function resolveAccessibleSurface(input) {
|
|
|
11876
12005
|
},
|
|
11877
12006
|
heading,
|
|
11878
12007
|
text,
|
|
11879
|
-
link,
|
|
12008
|
+
link: link$1,
|
|
11880
12009
|
supporting,
|
|
11881
12010
|
rejectedCandidates
|
|
11882
12011
|
};
|
|
@@ -12638,7 +12767,7 @@ const blockAppearancePresets = [
|
|
|
12638
12767
|
id: "schedule-details-brand-depth",
|
|
12639
12768
|
target: "events",
|
|
12640
12769
|
label: "Brand-depth schedule details",
|
|
12641
|
-
blockKinds: ["block.event-details"
|
|
12770
|
+
blockKinds: ["block.event-details"],
|
|
12642
12771
|
design: {
|
|
12643
12772
|
sectionSurface: "brand-depth-band",
|
|
12644
12773
|
contentFrame: plainFrame,
|
|
@@ -13318,6 +13447,32 @@ function replaceSiteStyleCompilationPlan(input) {
|
|
|
13318
13447
|
footerLookId: input.footerLookId ?? null
|
|
13319
13448
|
};
|
|
13320
13449
|
}
|
|
13450
|
+
/**
|
|
13451
|
+
* Writes the brand display / logo size preference onto the header's logo style.
|
|
13452
|
+
*
|
|
13453
|
+
* Moved here from preview-next in #1318 Phase 4a so the header preference
|
|
13454
|
+
* pipeline can be composed once, in theme-core, rather than half in the client.
|
|
13455
|
+
*/
|
|
13456
|
+
function withHeaderBrandThemePreferences(theme, preferences) {
|
|
13457
|
+
const currentLogo = theme.header.logo;
|
|
13458
|
+
if (!currentLogo && preferences.brandDisplay === defaultHeaderBrandPreferences.brandDisplay && preferences.logoSize === defaultHeaderBrandPreferences.logoSize) {
|
|
13459
|
+
return theme;
|
|
13460
|
+
}
|
|
13461
|
+
return {
|
|
13462
|
+
...theme,
|
|
13463
|
+
header: {
|
|
13464
|
+
...theme.header,
|
|
13465
|
+
logo: {
|
|
13466
|
+
fontFamily: currentLogo?.fontFamily ?? "heading",
|
|
13467
|
+
letterSpacing: currentLogo?.letterSpacing ?? "normal",
|
|
13468
|
+
gradient: currentLogo?.gradient ?? false,
|
|
13469
|
+
...currentLogo ?? {},
|
|
13470
|
+
brandDisplay: preferences.brandDisplay,
|
|
13471
|
+
size: preferences.logoSize
|
|
13472
|
+
}
|
|
13473
|
+
}
|
|
13474
|
+
};
|
|
13475
|
+
}
|
|
13321
13476
|
|
|
13322
13477
|
//#endregion
|
|
13323
13478
|
//#region ../theme-core/src/site-styles/schemaVersionedJson.ts
|
|
@@ -16360,90 +16515,6 @@ function deriveHeaderLogoShapeFromDimensions({ width, height }) {
|
|
|
16360
16515
|
}
|
|
16361
16516
|
const defaultHeaderLogoContext = { shape: "text-only" };
|
|
16362
16517
|
|
|
16363
|
-
//#endregion
|
|
16364
|
-
//#region ../theme-core/src/site-styles/headerInteractionPalette.ts
|
|
16365
|
-
const clearSplitInteractionPalette = defineLookOptionGroup({
|
|
16366
|
-
id: "interaction-palette",
|
|
16367
|
-
label: "Hover colour",
|
|
16368
|
-
defaultOptionId: "accent",
|
|
16369
|
-
options: [
|
|
16370
|
-
{
|
|
16371
|
-
id: "accent",
|
|
16372
|
-
label: "Accent"
|
|
16373
|
-
},
|
|
16374
|
-
{
|
|
16375
|
-
id: "primary",
|
|
16376
|
-
label: "Primary"
|
|
16377
|
-
},
|
|
16378
|
-
{
|
|
16379
|
-
id: "secondary",
|
|
16380
|
-
label: "Secondary"
|
|
16381
|
-
}
|
|
16382
|
-
]
|
|
16383
|
-
});
|
|
16384
|
-
function resolveHeaderInteractionPaletteControl(group, preference) {
|
|
16385
|
-
if (!group) return { kind: "unavailable" };
|
|
16386
|
-
if (preference === "recommended") {
|
|
16387
|
-
const resolution$1 = resolveLookOption(group, undefined);
|
|
16388
|
-
return availableControlState(group, preference, resolution$1.option.id, null);
|
|
16389
|
-
}
|
|
16390
|
-
const resolution = resolveHeaderInteractionPalette(group, preference);
|
|
16391
|
-
return availableControlState(group, preference, resolution.option.id, resolution.kind === "repaired" ? {
|
|
16392
|
-
requested: preference,
|
|
16393
|
-
fallback: resolution.option.id,
|
|
16394
|
-
reason: resolution.reason
|
|
16395
|
-
} : null);
|
|
16396
|
-
}
|
|
16397
|
-
function availableControlState(group, preference, effectivePalette, repair$1) {
|
|
16398
|
-
return {
|
|
16399
|
-
kind: "available",
|
|
16400
|
-
preference,
|
|
16401
|
-
effectivePalette,
|
|
16402
|
-
options: [{
|
|
16403
|
-
value: "recommended",
|
|
16404
|
-
label: `Recommended (${defaultOptionLabel(group)})`
|
|
16405
|
-
}, ...group.options.map((option) => ({
|
|
16406
|
-
value: option.id,
|
|
16407
|
-
label: option.label
|
|
16408
|
-
}))],
|
|
16409
|
-
repair: repair$1
|
|
16410
|
-
};
|
|
16411
|
-
}
|
|
16412
|
-
function applyHeaderInteractionPalette(header, group, preference) {
|
|
16413
|
-
if (!group) return header;
|
|
16414
|
-
if (!header.navLinkStyle) {
|
|
16415
|
-
throw new Error(`Header interaction palette "${group.id}" requires a nav link style.`);
|
|
16416
|
-
}
|
|
16417
|
-
const palette = resolveHeaderInteractionPalette(group, preference).option.id;
|
|
16418
|
-
const pair = headerInteractionPalettePair(palette);
|
|
16419
|
-
const pillEffect = {
|
|
16420
|
-
effectId: "background-on-hover",
|
|
16421
|
-
options: {
|
|
16422
|
-
colorToken: pair.backgroundToken,
|
|
16423
|
-
textColorToken: pair.foregroundToken
|
|
16424
|
-
}
|
|
16425
|
-
};
|
|
16426
|
-
return {
|
|
16427
|
-
...header,
|
|
16428
|
-
navLinkStyle: {
|
|
16429
|
-
...header.navLinkStyle,
|
|
16430
|
-
hoverColorToken: null,
|
|
16431
|
-
borderRadius: "full",
|
|
16432
|
-
effects: {
|
|
16433
|
-
...header.navLinkStyle.effects,
|
|
16434
|
-
hover: [pillEffect],
|
|
16435
|
-
focus: [pillEffect]
|
|
16436
|
-
}
|
|
16437
|
-
}
|
|
16438
|
-
};
|
|
16439
|
-
}
|
|
16440
|
-
function resolveHeaderInteractionPalette(group, preference) {
|
|
16441
|
-
return resolveLookOption(group, preference === "recommended" ? undefined : preference);
|
|
16442
|
-
}
|
|
16443
|
-
function defaultOptionLabel(group) {
|
|
16444
|
-
return resolveLookOption(group, undefined).option.label;
|
|
16445
|
-
}
|
|
16446
|
-
|
|
16447
16518
|
//#endregion
|
|
16448
16519
|
//#region ../theme-core/src/site-styles/headerLayoutCatalog.ts
|
|
16449
16520
|
const titleOnlyLogoAccommodation = {
|
|
@@ -16564,6 +16635,62 @@ const overlayLogoAccommodations = [
|
|
|
16564
16635
|
inlineMarkWithTitleLogoAccommodation,
|
|
16565
16636
|
unbackedOverlayLogoAccommodation
|
|
16566
16637
|
];
|
|
16638
|
+
const allCuratedLogoAccommodations = [
|
|
16639
|
+
...inlineClassicLogoAccommodations,
|
|
16640
|
+
...centeredLogoAccommodations,
|
|
16641
|
+
...overlayLogoAccommodations
|
|
16642
|
+
];
|
|
16643
|
+
/**
|
|
16644
|
+
* Whether an accommodation pattern draws its own backing behind the logo.
|
|
16645
|
+
* Derived from the catalogue above rather than restated, so a new backing
|
|
16646
|
+
* accommodation cannot drift out of sync with this answer. Patterns declared in
|
|
16647
|
+
* `LogoAccommodationPattern` but not offered by any layout resolve to `false`,
|
|
16648
|
+
* which is the honest answer: nothing can select them.
|
|
16649
|
+
*/
|
|
16650
|
+
const backingLogoAccommodationPatterns = new Set(allCuratedLogoAccommodations.filter((option) => option.providesBacking === true).map((option) => option.pattern));
|
|
16651
|
+
function logoAccommodationProvidesBacking(pattern) {
|
|
16652
|
+
return backingLogoAccommodationPatterns.has(pattern);
|
|
16653
|
+
}
|
|
16654
|
+
/**
|
|
16655
|
+
* The single answer to "what shape is this site's logo" (#1318 Phase 4b).
|
|
16656
|
+
*
|
|
16657
|
+
* The server and the public frontend each had their own copy of this, and they
|
|
16658
|
+
* disagreed on the one case that matters: a logo whose dimensions are unknown.
|
|
16659
|
+
* The server assumed `horizontal`; the frontend asked whether the persisted
|
|
16660
|
+
* accommodation brings its own backing and answered `square` if it does. The
|
|
16661
|
+
* richer rule is the correct one — a badge or masthead accommodation is
|
|
16662
|
+
* evidence about the asset that raw dimensions would otherwise supply — and it
|
|
16663
|
+
* reduces to `horizontal` whenever there is no backing, so it subsumes the
|
|
16664
|
+
* server's rule rather than contradicting it. The backing question goes through
|
|
16665
|
+
* `logoAccommodationProvidesBacking`, so the pattern list has one home.
|
|
16666
|
+
*/
|
|
16667
|
+
function deriveHeaderLogoShape({ asset, accommodation }) {
|
|
16668
|
+
if (!asset) return "text-only";
|
|
16669
|
+
if (!asset.width || !asset.height) {
|
|
16670
|
+
return accommodation !== undefined && logoAccommodationProvidesBacking(accommodation) ? "square" : "horizontal";
|
|
16671
|
+
}
|
|
16672
|
+
return deriveHeaderLogoShapeFromDimensions(asset);
|
|
16673
|
+
}
|
|
16674
|
+
/**
|
|
16675
|
+
* The site's logo as the header pipeline sees it, from the two database values
|
|
16676
|
+
* that describe it: the asset (or its absence) and the accommodation the row
|
|
16677
|
+
* currently persists, which is evidence about the asset when its dimensions are
|
|
16678
|
+
* missing.
|
|
16679
|
+
*
|
|
16680
|
+
* One definition, shared by every shell that has to answer it — the dashboard's
|
|
16681
|
+
* `loadHeaderLogoContextForSite`, the public frontend, and the Phase 4c repair
|
|
16682
|
+
* generator — because a second copy is a second answer to "what shape is this
|
|
16683
|
+
* site's logo", and the two former copies disagreed on exactly the case this
|
|
16684
|
+
* one is careful about (#1318 Phase 4b/4c).
|
|
16685
|
+
*/
|
|
16686
|
+
function headerLogoShapeContext(input) {
|
|
16687
|
+
return {
|
|
16688
|
+
kind: "shape",
|
|
16689
|
+
shape: deriveHeaderLogoShape(input),
|
|
16690
|
+
...input.brand?.brandDisplay === undefined ? {} : { brandDisplayMode: input.brand.brandDisplay },
|
|
16691
|
+
...input.brand?.logoSize === undefined ? {} : { sizePolicy: input.brand.logoSize }
|
|
16692
|
+
};
|
|
16693
|
+
}
|
|
16567
16694
|
function logoFactsForHeaderContext(logo) {
|
|
16568
16695
|
if (logo.kind === "facts") return logo.facts;
|
|
16569
16696
|
switch (logo.shape) {
|
|
@@ -16604,22 +16731,6 @@ function requireHeaderBrandPresentationSelection(plan) {
|
|
|
16604
16731
|
if (plan.kind === "selected") return plan;
|
|
16605
16732
|
throw new Error(`Selected header layout did not have a compatible logo accommodation: ${plan.reasons.join(", ")}`);
|
|
16606
16733
|
}
|
|
16607
|
-
/**
|
|
16608
|
-
* Starts each compilation from the generated theme's header branch, then
|
|
16609
|
-
* clears optional nested styling fields that would otherwise leak from a
|
|
16610
|
-
* different header selection.
|
|
16611
|
-
*/
|
|
16612
|
-
function baseHeader(theme) {
|
|
16613
|
-
const { navLinkStyle: _navLinkStyle, navFontRole: _navFontRole, navTextSize: _navTextSize, dropdownStyle: _dropdownStyle, ctaVariant: _ctaVariant, navContainer: _navContainer, container: _container, shadow: _shadow, logo: _logo, logoText: _logoText, border: _border, ...header } = theme.header;
|
|
16614
|
-
return {
|
|
16615
|
-
...header,
|
|
16616
|
-
navContainer: {
|
|
16617
|
-
type: "none",
|
|
16618
|
-
tint: null,
|
|
16619
|
-
opacity: .15
|
|
16620
|
-
}
|
|
16621
|
-
};
|
|
16622
|
-
}
|
|
16623
16734
|
const headerLayoutCatalog = defineExactCatalog()([
|
|
16624
16735
|
{
|
|
16625
16736
|
id: asHeaderLayoutId("classic"),
|
|
@@ -16660,43 +16771,37 @@ const headerStyleCatalog = defineExactCatalog()([
|
|
|
16660
16771
|
id: asHeaderStyleId("minimal"),
|
|
16661
16772
|
label: "Minimal",
|
|
16662
16773
|
description: "Quiet surface with simple color-change navigation.",
|
|
16663
|
-
supportedLayoutIds: supportedLayouts(["classic", "centered"])
|
|
16664
|
-
navTypography: "plain"
|
|
16774
|
+
supportedLayoutIds: supportedLayouts(["classic", "centered"])
|
|
16665
16775
|
},
|
|
16666
16776
|
{
|
|
16667
16777
|
id: asHeaderStyleId("fine-line"),
|
|
16668
16778
|
label: "Seamless",
|
|
16669
16779
|
description: "Borderless surface with underline hover for a quieter page transition.",
|
|
16670
|
-
supportedLayoutIds: supportedLayouts(["classic", "centered"])
|
|
16671
|
-
navTypography: "plain"
|
|
16780
|
+
supportedLayoutIds: supportedLayouts(["classic", "centered"])
|
|
16672
16781
|
},
|
|
16673
16782
|
{
|
|
16674
16783
|
id: asHeaderStyleId("soft-bar"),
|
|
16675
16784
|
label: "Soft Bar",
|
|
16676
16785
|
description: "Soft tinted surface with gentle pill hover states.",
|
|
16677
|
-
supportedLayoutIds: supportedLayouts(["classic", "centered"])
|
|
16678
|
-
navTypography: "plain"
|
|
16786
|
+
supportedLayoutIds: supportedLayouts(["classic", "centered"])
|
|
16679
16787
|
},
|
|
16680
16788
|
{
|
|
16681
16789
|
id: asHeaderStyleId("pill-nav"),
|
|
16682
16790
|
label: "Pill Nav",
|
|
16683
16791
|
description: "A calm pill navigation rail over a neutral header.",
|
|
16684
|
-
supportedLayoutIds: supportedLayouts(["classic", "centered"])
|
|
16685
|
-
navTypography: "plain"
|
|
16792
|
+
supportedLayoutIds: supportedLayouts(["classic", "centered"])
|
|
16686
16793
|
},
|
|
16687
16794
|
{
|
|
16688
16795
|
id: asHeaderStyleId("brand-bar"),
|
|
16689
16796
|
label: "Brand Bar",
|
|
16690
16797
|
description: "Solid brand surface with high-contrast navigation.",
|
|
16691
|
-
supportedLayoutIds: supportedLayouts(["classic", "centered"])
|
|
16692
|
-
navTypography: "brand-caps"
|
|
16798
|
+
supportedLayoutIds: supportedLayouts(["classic", "centered"])
|
|
16693
16799
|
},
|
|
16694
16800
|
{
|
|
16695
16801
|
id: asHeaderStyleId("brand-underline"),
|
|
16696
16802
|
label: "Brand Underline",
|
|
16697
16803
|
description: "Brand surface with underline-grow navigation.",
|
|
16698
|
-
supportedLayoutIds: supportedLayouts(["classic"])
|
|
16699
|
-
navTypography: "brand-caps"
|
|
16804
|
+
supportedLayoutIds: supportedLayouts(["classic"])
|
|
16700
16805
|
},
|
|
16701
16806
|
{
|
|
16702
16807
|
id: asHeaderStyleId("brand-pill"),
|
|
@@ -16706,69 +16811,55 @@ const headerStyleCatalog = defineExactCatalog()([
|
|
|
16706
16811
|
"classic",
|
|
16707
16812
|
"centered",
|
|
16708
16813
|
"floating"
|
|
16709
|
-
])
|
|
16710
|
-
navTypography: "brand-caps"
|
|
16814
|
+
])
|
|
16711
16815
|
},
|
|
16712
16816
|
{
|
|
16713
16817
|
id: asHeaderStyleId("brand-capsule"),
|
|
16714
16818
|
label: "Brand Capsule",
|
|
16715
16819
|
description: "Solid brand chrome designed as a floating capsule, with quiet-caps nav, a soft pill active state, and an inverted CTA pill.",
|
|
16716
|
-
supportedLayoutIds: supportedLayouts(["classic"])
|
|
16717
|
-
navTypography: "quiet-caps"
|
|
16820
|
+
supportedLayoutIds: supportedLayouts(["classic"])
|
|
16718
16821
|
},
|
|
16719
16822
|
{
|
|
16720
16823
|
id: asHeaderStyleId("serif-line"),
|
|
16721
16824
|
label: "Serif Line",
|
|
16722
16825
|
description: "Typographic logo treatment with understated underline navigation.",
|
|
16723
|
-
supportedLayoutIds: supportedLayouts(["classic", "centered"])
|
|
16724
|
-
navTypography: "heading"
|
|
16826
|
+
supportedLayoutIds: supportedLayouts(["classic", "centered"])
|
|
16725
16827
|
},
|
|
16726
16828
|
{
|
|
16727
16829
|
id: asHeaderStyleId("flat-tabs"),
|
|
16728
16830
|
label: "Flat Tabs",
|
|
16729
16831
|
description: "Flat block hover and active states with a Swiss utility feel.",
|
|
16730
|
-
supportedLayoutIds: supportedLayouts(["classic", "centered"])
|
|
16731
|
-
navTypography: "quiet-caps"
|
|
16832
|
+
supportedLayoutIds: supportedLayouts(["classic", "centered"])
|
|
16732
16833
|
},
|
|
16733
16834
|
{
|
|
16734
16835
|
id: asHeaderStyleId("glass"),
|
|
16735
16836
|
label: "Glass",
|
|
16736
16837
|
description: "Translucent chrome with frosted navigation backing.",
|
|
16737
|
-
supportedLayoutIds: supportedLayouts(["floating"])
|
|
16738
|
-
navTypography: "plain"
|
|
16838
|
+
supportedLayoutIds: supportedLayouts(["floating"])
|
|
16739
16839
|
},
|
|
16740
16840
|
{
|
|
16741
16841
|
id: asHeaderStyleId("minimal-surface"),
|
|
16742
16842
|
label: "Clear Split",
|
|
16743
16843
|
description: "Floating logo with transparent navigation and no shared shell.",
|
|
16744
|
-
supportedLayoutIds: supportedLayouts(["floating"])
|
|
16745
|
-
navTypography: "plain",
|
|
16746
|
-
openingForegroundTarget: "all",
|
|
16747
|
-
scrollTreatment: "contrast-glass",
|
|
16748
|
-
interactionPalette: clearSplitInteractionPalette
|
|
16844
|
+
supportedLayoutIds: supportedLayouts(["floating"])
|
|
16749
16845
|
},
|
|
16750
16846
|
{
|
|
16751
16847
|
id: asHeaderStyleId("soft-shell"),
|
|
16752
16848
|
label: "Soft Shell",
|
|
16753
16849
|
description: "Floating rounded shell with soft pill navigation states.",
|
|
16754
|
-
supportedLayoutIds: supportedLayouts(["floating"])
|
|
16755
|
-
navTypography: "plain"
|
|
16850
|
+
supportedLayoutIds: supportedLayouts(["floating"])
|
|
16756
16851
|
},
|
|
16757
16852
|
{
|
|
16758
16853
|
id: asHeaderStyleId("split-glass"),
|
|
16759
16854
|
label: "Split Glass",
|
|
16760
16855
|
description: "Logo floats independently while the navigation sits in a glass rail.",
|
|
16761
|
-
supportedLayoutIds: supportedLayouts(["floating"])
|
|
16762
|
-
navTypography: "quiet-caps",
|
|
16763
|
-
openingForegroundTarget: "brand-only"
|
|
16856
|
+
supportedLayoutIds: supportedLayouts(["floating"])
|
|
16764
16857
|
},
|
|
16765
16858
|
{
|
|
16766
16859
|
id: asHeaderStyleId("split-pill"),
|
|
16767
16860
|
label: "Split Pill",
|
|
16768
16861
|
description: "Logo floats independently with a soft pill-backed navigation rail.",
|
|
16769
|
-
supportedLayoutIds: supportedLayouts(["floating"])
|
|
16770
|
-
navTypography: "heading",
|
|
16771
|
-
openingForegroundTarget: "brand-only"
|
|
16862
|
+
supportedLayoutIds: supportedLayouts(["floating"])
|
|
16772
16863
|
}
|
|
16773
16864
|
]);
|
|
16774
16865
|
const headerLayoutsById = new Map(headerLayoutCatalog.map((layout) => [layout.id, layout]));
|
|
@@ -16955,6 +17046,551 @@ function resolveHeaderLayoutStyleSelection(selection) {
|
|
|
16955
17046
|
};
|
|
16956
17047
|
}
|
|
16957
17048
|
|
|
17049
|
+
//#endregion
|
|
17050
|
+
//#region ../theme-core/src/site-styles/headerInteractionPalette.ts
|
|
17051
|
+
const clearSplitInteractionPalette = defineLookOptionGroup({
|
|
17052
|
+
id: "interaction-palette",
|
|
17053
|
+
label: "Hover colour",
|
|
17054
|
+
defaultOptionId: "accent",
|
|
17055
|
+
options: [
|
|
17056
|
+
{
|
|
17057
|
+
id: "accent",
|
|
17058
|
+
label: "Accent"
|
|
17059
|
+
},
|
|
17060
|
+
{
|
|
17061
|
+
id: "primary",
|
|
17062
|
+
label: "Primary"
|
|
17063
|
+
},
|
|
17064
|
+
{
|
|
17065
|
+
id: "secondary",
|
|
17066
|
+
label: "Secondary"
|
|
17067
|
+
},
|
|
17068
|
+
{
|
|
17069
|
+
id: headerInteractionQuietPalette,
|
|
17070
|
+
label: "Text only"
|
|
17071
|
+
}
|
|
17072
|
+
]
|
|
17073
|
+
});
|
|
17074
|
+
function resolveHeaderInteractionPaletteControl(group, preference) {
|
|
17075
|
+
if (!group) return { kind: "unavailable" };
|
|
17076
|
+
if (preference === "recommended") {
|
|
17077
|
+
const resolution$1 = resolveLookOption(group, undefined);
|
|
17078
|
+
return availableControlState(group, preference, resolution$1.option.id, null);
|
|
17079
|
+
}
|
|
17080
|
+
const resolution = resolveHeaderInteractionPalette(group, preference);
|
|
17081
|
+
return availableControlState(group, preference, resolution.option.id, resolution.kind === "repaired" ? {
|
|
17082
|
+
requested: preference,
|
|
17083
|
+
fallback: resolution.option.id,
|
|
17084
|
+
reason: resolution.reason
|
|
17085
|
+
} : null);
|
|
17086
|
+
}
|
|
17087
|
+
function availableControlState(group, preference, effectivePalette, repair$1) {
|
|
17088
|
+
return {
|
|
17089
|
+
kind: "available",
|
|
17090
|
+
preference,
|
|
17091
|
+
effectivePalette,
|
|
17092
|
+
options: [{
|
|
17093
|
+
value: "recommended",
|
|
17094
|
+
label: `Recommended (${defaultOptionLabel(group)})`
|
|
17095
|
+
}, ...group.options.map((option) => ({
|
|
17096
|
+
value: option.id,
|
|
17097
|
+
label: option.label
|
|
17098
|
+
}))],
|
|
17099
|
+
repair: repair$1
|
|
17100
|
+
};
|
|
17101
|
+
}
|
|
17102
|
+
function applyHeaderInteractionPalette(header, group, preference) {
|
|
17103
|
+
if (!group) return header;
|
|
17104
|
+
const palette = resolveHeaderInteractionPalette(group, preference).option.id;
|
|
17105
|
+
if (palette === headerInteractionQuietPalette) return header;
|
|
17106
|
+
if (!header.navLinkStyle) {
|
|
17107
|
+
throw new Error(`Header interaction palette "${group.id}" requires a nav link style.`);
|
|
17108
|
+
}
|
|
17109
|
+
const pair = headerInteractionPalettePair(palette);
|
|
17110
|
+
const pillEffect = {
|
|
17111
|
+
effectId: "background-on-hover",
|
|
17112
|
+
options: {
|
|
17113
|
+
colorToken: pair.backgroundToken,
|
|
17114
|
+
textColorToken: pair.foregroundToken
|
|
17115
|
+
}
|
|
17116
|
+
};
|
|
17117
|
+
return {
|
|
17118
|
+
...header,
|
|
17119
|
+
navLinkStyle: {
|
|
17120
|
+
...header.navLinkStyle,
|
|
17121
|
+
hoverColorToken: null,
|
|
17122
|
+
borderRadius: "full",
|
|
17123
|
+
effects: {
|
|
17124
|
+
...header.navLinkStyle.effects,
|
|
17125
|
+
hover: [pillEffect],
|
|
17126
|
+
focus: [pillEffect]
|
|
17127
|
+
}
|
|
17128
|
+
}
|
|
17129
|
+
};
|
|
17130
|
+
}
|
|
17131
|
+
function resolveHeaderInteractionPalette(group, preference) {
|
|
17132
|
+
return resolveLookOption(group, preference === "recommended" ? undefined : preference);
|
|
17133
|
+
}
|
|
17134
|
+
function defaultOptionLabel(group) {
|
|
17135
|
+
return resolveLookOption(group, undefined).option.label;
|
|
17136
|
+
}
|
|
17137
|
+
/**
|
|
17138
|
+
* The four fields `applyHeaderInteractionPalette` writes, normalised so the
|
|
17139
|
+
* comparison below can be made against bytes that came back out of `jsonb`.
|
|
17140
|
+
*
|
|
17141
|
+
* `hoverColorToken` collapses absent to `null` because the palette writes an
|
|
17142
|
+
* explicit `null` where a pre-palette row simply has no key, and the two mean
|
|
17143
|
+
* the same thing.
|
|
17144
|
+
*/
|
|
17145
|
+
function headerInteractionPaletteFields(header) {
|
|
17146
|
+
return {
|
|
17147
|
+
hoverColorToken: header.navLinkStyle?.hoverColorToken ?? null,
|
|
17148
|
+
borderRadius: header.navLinkStyle?.borderRadius,
|
|
17149
|
+
hover: header.navLinkStyle?.effects?.hover,
|
|
17150
|
+
focus: header.navLinkStyle?.effects?.focus
|
|
17151
|
+
};
|
|
17152
|
+
}
|
|
17153
|
+
/**
|
|
17154
|
+
* Key-sorted serialisation, because one side of the comparison is built here
|
|
17155
|
+
* and the other comes back out of `jsonb` in whatever order it was stored.
|
|
17156
|
+
* `JSON.stringify` alone reported two identical effect lists as different,
|
|
17157
|
+
* which is the same key-order trap the `logoText` derivation hit in Phase 4a.
|
|
17158
|
+
*/
|
|
17159
|
+
function canonicalJson(value) {
|
|
17160
|
+
if (Array.isArray(value)) {
|
|
17161
|
+
return `[${value.map(canonicalJson).join(",")}]`;
|
|
17162
|
+
}
|
|
17163
|
+
if (typeof value === "object" && value !== null) {
|
|
17164
|
+
const entries = Object.entries(value).filter(([, member]) => member !== undefined).sort(([left], [right]) => left.localeCompare(right));
|
|
17165
|
+
return `{${entries.map(([key, member]) => `${JSON.stringify(key)}:${canonicalJson(member)}`).join(",")}}`;
|
|
17166
|
+
}
|
|
17167
|
+
return value === undefined ? "undefined" : JSON.stringify(value);
|
|
17168
|
+
}
|
|
17169
|
+
/**
|
|
17170
|
+
* The effect id the palette writes. A nav link carrying it is "palette-shaped":
|
|
17171
|
+
* its hover treatment is a filled pill rather than the plain colour change a
|
|
17172
|
+
* pre-palette recipe compiles.
|
|
17173
|
+
*/
|
|
17174
|
+
const paletteEffectId = "background-on-hover";
|
|
17175
|
+
function carriesPaletteShapedEffect(header) {
|
|
17176
|
+
const effects = header.navLinkStyle?.effects;
|
|
17177
|
+
return [effects?.hover, effects?.focus].some((list) => (list ?? []).some((effect) => effect.effectId === paletteEffectId));
|
|
17178
|
+
}
|
|
17179
|
+
/**
|
|
17180
|
+
* Which option of `group` a persisted header's nav link style is the output of
|
|
17181
|
+
* (#1318 Phase 4c decision 2).
|
|
17182
|
+
*
|
|
17183
|
+
* This is a baseline comparison rather than a pattern match on effect bytes.
|
|
17184
|
+
* The palette overwrites its four fields unconditionally, so applying an option
|
|
17185
|
+
* to a header leaves that header unchanged exactly when the header is already
|
|
17186
|
+
* that option's output — which makes "which option produced these bytes" a
|
|
17187
|
+
* fixed-point search over the closed option list.
|
|
17188
|
+
*
|
|
17189
|
+
* Order is load-bearing twice over:
|
|
17190
|
+
*
|
|
17191
|
+
* - `'recommended'` is tried first, so a row compiled from the recommendation
|
|
17192
|
+
* derives the sentinel rather than the default option's own id. As with the
|
|
17193
|
+
* structure rule, an explicit choice that happens to equal the default
|
|
17194
|
+
* collapses to the sentinel; nothing in the bytes distinguishes the two.
|
|
17195
|
+
* - `'quiet'` is tried last, because it writes nothing and is therefore a fixed
|
|
17196
|
+
* point of every header. Reaching it means no pill option produced these
|
|
17197
|
+
* bytes — which is what "this row renders no pill" means, *provided the row
|
|
17198
|
+
* carries no pill*. `carriesPaletteShapedEffect` is that proviso: a
|
|
17199
|
+
* palette-shaped nav link that no option reproduces is `'unrecognised'`, not
|
|
17200
|
+
* `'quiet'`.
|
|
17201
|
+
*/
|
|
17202
|
+
function classifyHeaderInteractionPaletteBytes(header, group) {
|
|
17203
|
+
if (!group) return { kind: "no-palette" };
|
|
17204
|
+
if (!header.navLinkStyle) return { kind: "no-palette" };
|
|
17205
|
+
const persisted = canonicalJson(headerInteractionPaletteFields(header));
|
|
17206
|
+
const match = orderedPaletteCandidates(group).find((candidate$1) => canonicalJson(headerInteractionPaletteFields(applyHeaderInteractionPalette(header, group, candidate$1))) === persisted);
|
|
17207
|
+
if (match === undefined) return { kind: "unrecognised" };
|
|
17208
|
+
if (match === headerInteractionQuietPalette && carriesPaletteShapedEffect(header)) {
|
|
17209
|
+
return { kind: "unrecognised" };
|
|
17210
|
+
}
|
|
17211
|
+
return {
|
|
17212
|
+
kind: "derived",
|
|
17213
|
+
preference: match
|
|
17214
|
+
};
|
|
17215
|
+
}
|
|
17216
|
+
/**
|
|
17217
|
+
* The preference `classifyHeaderInteractionPaletteBytes` derived, or `null`
|
|
17218
|
+
* when the bytes express none — either because the style offers no palette or
|
|
17219
|
+
* because they match no option it does offer. Callers that need to tell those
|
|
17220
|
+
* two apart use the classification directly.
|
|
17221
|
+
*/
|
|
17222
|
+
function readHeaderInteractionPaletteFromBytes(header, group) {
|
|
17223
|
+
const classified = classifyHeaderInteractionPaletteBytes(header, group);
|
|
17224
|
+
return classified.kind === "derived" ? classified.preference : null;
|
|
17225
|
+
}
|
|
17226
|
+
function orderedPaletteCandidates(group) {
|
|
17227
|
+
const ids = group.options.map((option) => option.id);
|
|
17228
|
+
return [
|
|
17229
|
+
"recommended",
|
|
17230
|
+
...ids.filter((id) => id !== headerInteractionQuietPalette),
|
|
17231
|
+
...ids.filter((id) => id === headerInteractionQuietPalette)
|
|
17232
|
+
];
|
|
17233
|
+
}
|
|
17234
|
+
|
|
17235
|
+
//#endregion
|
|
17236
|
+
//#region ../theme-core/src/site-styles/headerStyleDefaults.ts
|
|
17237
|
+
/**
|
|
17238
|
+
* A style either takes the default recipe or authors a complete one. This is
|
|
17239
|
+
* deliberately not a deep merge: the same "declare the variant or fall back"
|
|
17240
|
+
* rule cards use, so a half-authored recipe cannot exist.
|
|
17241
|
+
*/
|
|
17242
|
+
function resolveHeaderStyleRecipe(siteStyle, styleId) {
|
|
17243
|
+
return siteStyle?.headerStyleRecipes?.[styleId] ?? defaultHeaderStyleRecipes[styleId];
|
|
17244
|
+
}
|
|
17245
|
+
/**
|
|
17246
|
+
* The default header appearance for every catalogue style id (#1318).
|
|
17247
|
+
*
|
|
17248
|
+
* This is the only id-keyed structure in the header appearance pipeline, and it
|
|
17249
|
+
* is data rather than dispatch — the same shape `cards/siteStyleDefaults.ts`
|
|
17250
|
+
* uses. A site style that wants a different header takes over an entry here
|
|
17251
|
+
* with its own recipe; it does not add a branch anywhere.
|
|
17252
|
+
*
|
|
17253
|
+
* `navWeight` (the bar) and `navLinkWeight` (the link text) are separate fields
|
|
17254
|
+
* and they disagree on five styles. That disagreement is real and pre-existing;
|
|
17255
|
+
* it is spelled out here rather than left implicit in a helper default. See the
|
|
17256
|
+
* note on `HeaderStyleRecipeCore.navLinkWeight`.
|
|
17257
|
+
*/
|
|
17258
|
+
const defaultHeaderStyleRecipes = {
|
|
17259
|
+
minimal: {
|
|
17260
|
+
tone: "neutral",
|
|
17261
|
+
background: {
|
|
17262
|
+
kind: "token",
|
|
17263
|
+
token: "surface"
|
|
17264
|
+
},
|
|
17265
|
+
nav: {
|
|
17266
|
+
kind: "minimal",
|
|
17267
|
+
hoverColorToken: "primary"
|
|
17268
|
+
},
|
|
17269
|
+
navWeight: "medium",
|
|
17270
|
+
navLinkWeight: "medium",
|
|
17271
|
+
navTypography: "plain",
|
|
17272
|
+
border: "none",
|
|
17273
|
+
shadow: { kind: "absent" },
|
|
17274
|
+
frame: { kind: "none" },
|
|
17275
|
+
cta: { treatment: "default" }
|
|
17276
|
+
},
|
|
17277
|
+
"fine-line": {
|
|
17278
|
+
tone: "neutral",
|
|
17279
|
+
background: {
|
|
17280
|
+
kind: "token",
|
|
17281
|
+
token: "surface"
|
|
17282
|
+
},
|
|
17283
|
+
nav: {
|
|
17284
|
+
kind: "underline",
|
|
17285
|
+
hover: "underline"
|
|
17286
|
+
},
|
|
17287
|
+
navWeight: "medium",
|
|
17288
|
+
navLinkWeight: "medium",
|
|
17289
|
+
navTypography: "plain",
|
|
17290
|
+
border: "none",
|
|
17291
|
+
shadow: { kind: "absent" },
|
|
17292
|
+
frame: { kind: "none" },
|
|
17293
|
+
cta: { treatment: "outline" }
|
|
17294
|
+
},
|
|
17295
|
+
"soft-bar": {
|
|
17296
|
+
tone: "neutral",
|
|
17297
|
+
background: {
|
|
17298
|
+
kind: "token",
|
|
17299
|
+
token: "surfaceAlt"
|
|
17300
|
+
},
|
|
17301
|
+
nav: {
|
|
17302
|
+
kind: "pill",
|
|
17303
|
+
backing: "capsule",
|
|
17304
|
+
hoverColorToken: "primary",
|
|
17305
|
+
hoverAlpha: 8
|
|
17306
|
+
},
|
|
17307
|
+
navWeight: "medium",
|
|
17308
|
+
navLinkWeight: "medium",
|
|
17309
|
+
navTypography: "plain",
|
|
17310
|
+
border: "bottom",
|
|
17311
|
+
shadow: { kind: "absent" },
|
|
17312
|
+
frame: { kind: "none" },
|
|
17313
|
+
cta: { treatment: "default" }
|
|
17314
|
+
},
|
|
17315
|
+
"pill-nav": {
|
|
17316
|
+
tone: "neutral",
|
|
17317
|
+
background: {
|
|
17318
|
+
kind: "token",
|
|
17319
|
+
token: "surface"
|
|
17320
|
+
},
|
|
17321
|
+
nav: {
|
|
17322
|
+
kind: "pill",
|
|
17323
|
+
backing: "capsule",
|
|
17324
|
+
hoverColorToken: "primary",
|
|
17325
|
+
hoverAlpha: 10
|
|
17326
|
+
},
|
|
17327
|
+
navWeight: "medium",
|
|
17328
|
+
navLinkWeight: "medium",
|
|
17329
|
+
navTypography: "plain",
|
|
17330
|
+
border: "none",
|
|
17331
|
+
shadow: { kind: "absent" },
|
|
17332
|
+
frame: {
|
|
17333
|
+
kind: "nav-container",
|
|
17334
|
+
type: "pill",
|
|
17335
|
+
tint: "surfaceAlt",
|
|
17336
|
+
opacity: .7
|
|
17337
|
+
},
|
|
17338
|
+
cta: { treatment: "default" }
|
|
17339
|
+
},
|
|
17340
|
+
"brand-bar": {
|
|
17341
|
+
tone: "brand",
|
|
17342
|
+
background: {
|
|
17343
|
+
kind: "token",
|
|
17344
|
+
token: "primary"
|
|
17345
|
+
},
|
|
17346
|
+
nav: {
|
|
17347
|
+
kind: "minimal",
|
|
17348
|
+
hoverColorToken: "secondary"
|
|
17349
|
+
},
|
|
17350
|
+
navWeight: "semibold",
|
|
17351
|
+
navLinkWeight: "medium",
|
|
17352
|
+
navTypography: "brand-caps",
|
|
17353
|
+
border: "none",
|
|
17354
|
+
shadow: { kind: "absent" },
|
|
17355
|
+
frame: { kind: "none" },
|
|
17356
|
+
cta: { treatment: "inverted" }
|
|
17357
|
+
},
|
|
17358
|
+
"brand-underline": {
|
|
17359
|
+
tone: "brand",
|
|
17360
|
+
background: {
|
|
17361
|
+
kind: "token",
|
|
17362
|
+
token: "primary"
|
|
17363
|
+
},
|
|
17364
|
+
nav: {
|
|
17365
|
+
kind: "underline",
|
|
17366
|
+
hover: "both",
|
|
17367
|
+
hoverColorToken: "secondary",
|
|
17368
|
+
underlineColorToken: "secondary"
|
|
17369
|
+
},
|
|
17370
|
+
navWeight: "bold",
|
|
17371
|
+
navLinkWeight: "bold",
|
|
17372
|
+
navTypography: "brand-caps",
|
|
17373
|
+
border: "none",
|
|
17374
|
+
shadow: { kind: "absent" },
|
|
17375
|
+
frame: { kind: "none" },
|
|
17376
|
+
cta: {
|
|
17377
|
+
treatment: "inverted",
|
|
17378
|
+
gap: "compact"
|
|
17379
|
+
}
|
|
17380
|
+
},
|
|
17381
|
+
"brand-pill": {
|
|
17382
|
+
tone: "brand",
|
|
17383
|
+
background: {
|
|
17384
|
+
kind: "token",
|
|
17385
|
+
token: "primary"
|
|
17386
|
+
},
|
|
17387
|
+
nav: {
|
|
17388
|
+
kind: "pill",
|
|
17389
|
+
backing: "capsule",
|
|
17390
|
+
hoverColorToken: "primaryForeground",
|
|
17391
|
+
hoverAlpha: 14
|
|
17392
|
+
},
|
|
17393
|
+
navWeight: "semibold",
|
|
17394
|
+
navLinkWeight: "medium",
|
|
17395
|
+
navTypography: "brand-caps",
|
|
17396
|
+
border: "none",
|
|
17397
|
+
shadow: { kind: "absent" },
|
|
17398
|
+
frame: { kind: "none" },
|
|
17399
|
+
cta: { treatment: "inverted" },
|
|
17400
|
+
onFloating: {
|
|
17401
|
+
background: { kind: "transparent" },
|
|
17402
|
+
frame: {
|
|
17403
|
+
kind: "container",
|
|
17404
|
+
rounded: "2xl",
|
|
17405
|
+
padding: "sm",
|
|
17406
|
+
tint: "primary",
|
|
17407
|
+
opacity: .95
|
|
17408
|
+
}
|
|
17409
|
+
}
|
|
17410
|
+
},
|
|
17411
|
+
"brand-capsule": {
|
|
17412
|
+
tone: "brand",
|
|
17413
|
+
background: {
|
|
17414
|
+
kind: "token",
|
|
17415
|
+
token: "primary"
|
|
17416
|
+
},
|
|
17417
|
+
nav: {
|
|
17418
|
+
kind: "pill",
|
|
17419
|
+
backing: "capsule",
|
|
17420
|
+
hoverColorToken: "primaryForeground",
|
|
17421
|
+
hoverAlpha: 16
|
|
17422
|
+
},
|
|
17423
|
+
navWeight: "semibold",
|
|
17424
|
+
navLinkWeight: "medium",
|
|
17425
|
+
navTypography: "quiet-caps",
|
|
17426
|
+
border: "none",
|
|
17427
|
+
shadow: {
|
|
17428
|
+
kind: "fixed",
|
|
17429
|
+
elevation: "md"
|
|
17430
|
+
},
|
|
17431
|
+
frame: { kind: "none" },
|
|
17432
|
+
cta: { treatment: "inverted" }
|
|
17433
|
+
},
|
|
17434
|
+
"serif-line": {
|
|
17435
|
+
tone: "neutral",
|
|
17436
|
+
background: {
|
|
17437
|
+
kind: "token",
|
|
17438
|
+
token: "background"
|
|
17439
|
+
},
|
|
17440
|
+
nav: {
|
|
17441
|
+
kind: "underline",
|
|
17442
|
+
hover: "underline"
|
|
17443
|
+
},
|
|
17444
|
+
navWeight: "medium",
|
|
17445
|
+
navLinkWeight: "medium",
|
|
17446
|
+
navTypography: "heading",
|
|
17447
|
+
border: "none",
|
|
17448
|
+
shadow: { kind: "explicit-none" },
|
|
17449
|
+
frame: { kind: "none" },
|
|
17450
|
+
cta: {
|
|
17451
|
+
treatment: "outline",
|
|
17452
|
+
gap: "relaxed"
|
|
17453
|
+
},
|
|
17454
|
+
logo: {
|
|
17455
|
+
fontFamily: "heading",
|
|
17456
|
+
letterSpacing: "wide",
|
|
17457
|
+
gradient: false
|
|
17458
|
+
}
|
|
17459
|
+
},
|
|
17460
|
+
"flat-tabs": {
|
|
17461
|
+
tone: "neutral",
|
|
17462
|
+
background: {
|
|
17463
|
+
kind: "token",
|
|
17464
|
+
token: "surface"
|
|
17465
|
+
},
|
|
17466
|
+
nav: { kind: "flat-tab" },
|
|
17467
|
+
navWeight: "semibold",
|
|
17468
|
+
navLinkWeight: "semibold",
|
|
17469
|
+
navTypography: "quiet-caps",
|
|
17470
|
+
border: "bottom",
|
|
17471
|
+
shadow: { kind: "themed" },
|
|
17472
|
+
frame: { kind: "none" },
|
|
17473
|
+
cta: {
|
|
17474
|
+
treatment: "primary",
|
|
17475
|
+
gap: "compact"
|
|
17476
|
+
}
|
|
17477
|
+
},
|
|
17478
|
+
glass: {
|
|
17479
|
+
tone: "neutral",
|
|
17480
|
+
background: { kind: "transparent" },
|
|
17481
|
+
nav: {
|
|
17482
|
+
kind: "pill",
|
|
17483
|
+
backing: "frosted",
|
|
17484
|
+
hoverColorToken: "primary",
|
|
17485
|
+
hoverAlpha: 8
|
|
17486
|
+
},
|
|
17487
|
+
navWeight: "semibold",
|
|
17488
|
+
navLinkWeight: "medium",
|
|
17489
|
+
navTypography: "plain",
|
|
17490
|
+
border: "none",
|
|
17491
|
+
shadow: { kind: "absent" },
|
|
17492
|
+
frame: {
|
|
17493
|
+
kind: "container",
|
|
17494
|
+
rounded: "2xl",
|
|
17495
|
+
padding: "sm",
|
|
17496
|
+
tint: "surface",
|
|
17497
|
+
opacity: .4
|
|
17498
|
+
},
|
|
17499
|
+
cta: { treatment: "default" }
|
|
17500
|
+
},
|
|
17501
|
+
"minimal-surface": {
|
|
17502
|
+
tone: "neutral",
|
|
17503
|
+
background: { kind: "transparent" },
|
|
17504
|
+
nav: {
|
|
17505
|
+
kind: "minimal",
|
|
17506
|
+
hoverColorToken: "primary"
|
|
17507
|
+
},
|
|
17508
|
+
navWeight: "medium",
|
|
17509
|
+
navLinkWeight: "medium",
|
|
17510
|
+
navTypography: "plain",
|
|
17511
|
+
border: "none",
|
|
17512
|
+
shadow: { kind: "absent" },
|
|
17513
|
+
frame: { kind: "none" },
|
|
17514
|
+
cta: { treatment: "default" },
|
|
17515
|
+
navLinkExtras: {
|
|
17516
|
+
textSize: "lg",
|
|
17517
|
+
contrastTreatment: "soft-glow"
|
|
17518
|
+
},
|
|
17519
|
+
openingForeground: "all",
|
|
17520
|
+
scrollTreatment: "contrast-glass",
|
|
17521
|
+
interactionPalette: clearSplitInteractionPalette
|
|
17522
|
+
},
|
|
17523
|
+
"soft-shell": {
|
|
17524
|
+
tone: "neutral",
|
|
17525
|
+
background: { kind: "transparent" },
|
|
17526
|
+
nav: {
|
|
17527
|
+
kind: "pill",
|
|
17528
|
+
backing: "capsule",
|
|
17529
|
+
hoverColorToken: "primary",
|
|
17530
|
+
hoverAlpha: 8
|
|
17531
|
+
},
|
|
17532
|
+
navWeight: "medium",
|
|
17533
|
+
navLinkWeight: "medium",
|
|
17534
|
+
navTypography: "plain",
|
|
17535
|
+
border: "none",
|
|
17536
|
+
shadow: { kind: "absent" },
|
|
17537
|
+
frame: {
|
|
17538
|
+
kind: "container",
|
|
17539
|
+
rounded: "2xl",
|
|
17540
|
+
padding: "md",
|
|
17541
|
+
tint: "surfaceAlt",
|
|
17542
|
+
opacity: .95
|
|
17543
|
+
},
|
|
17544
|
+
cta: { treatment: "default" }
|
|
17545
|
+
},
|
|
17546
|
+
"split-glass": {
|
|
17547
|
+
tone: "neutral",
|
|
17548
|
+
background: { kind: "transparent" },
|
|
17549
|
+
nav: {
|
|
17550
|
+
kind: "pill",
|
|
17551
|
+
backing: "frosted",
|
|
17552
|
+
hoverColorToken: "primary",
|
|
17553
|
+
hoverAlpha: 8
|
|
17554
|
+
},
|
|
17555
|
+
navWeight: "semibold",
|
|
17556
|
+
navLinkWeight: "medium",
|
|
17557
|
+
navTypography: "quiet-caps",
|
|
17558
|
+
border: "none",
|
|
17559
|
+
shadow: { kind: "absent" },
|
|
17560
|
+
frame: {
|
|
17561
|
+
kind: "nav-container",
|
|
17562
|
+
type: "glass",
|
|
17563
|
+
tint: "surface",
|
|
17564
|
+
opacity: .4
|
|
17565
|
+
},
|
|
17566
|
+
cta: { treatment: "default" },
|
|
17567
|
+
openingForeground: "brand-only"
|
|
17568
|
+
},
|
|
17569
|
+
"split-pill": {
|
|
17570
|
+
tone: "neutral",
|
|
17571
|
+
background: { kind: "transparent" },
|
|
17572
|
+
nav: {
|
|
17573
|
+
kind: "pill",
|
|
17574
|
+
backing: "capsule",
|
|
17575
|
+
hoverColorToken: "primary",
|
|
17576
|
+
hoverAlpha: 10
|
|
17577
|
+
},
|
|
17578
|
+
navWeight: "medium",
|
|
17579
|
+
navLinkWeight: "medium",
|
|
17580
|
+
navTypography: "heading",
|
|
17581
|
+
border: "none",
|
|
17582
|
+
shadow: { kind: "absent" },
|
|
17583
|
+
frame: {
|
|
17584
|
+
kind: "nav-container",
|
|
17585
|
+
type: "pill",
|
|
17586
|
+
tint: "surface",
|
|
17587
|
+
opacity: .94
|
|
17588
|
+
},
|
|
17589
|
+
cta: { treatment: "default" },
|
|
17590
|
+
openingForeground: "brand-only"
|
|
17591
|
+
}
|
|
17592
|
+
};
|
|
17593
|
+
|
|
16958
17594
|
//#endregion
|
|
16959
17595
|
//#region ../theme-core/src/site-styles/headerNavStyles.ts
|
|
16960
17596
|
function applyNavTypography(style, typography) {
|
|
@@ -17078,95 +17714,133 @@ function flatTabNavLinkStyle(colorToken, typography = "plain") {
|
|
|
17078
17714
|
}
|
|
17079
17715
|
|
|
17080
17716
|
//#endregion
|
|
17081
|
-
//#region ../theme-core/src/site-styles/
|
|
17082
|
-
function
|
|
17083
|
-
return {
|
|
17084
|
-
...
|
|
17085
|
-
|
|
17086
|
-
|
|
17087
|
-
shrinkOnScroll: layout.shrinkOnScroll,
|
|
17088
|
-
ctaGap: layout.id === asHeaderLayoutId("floating") ? "compact" : "default",
|
|
17089
|
-
logoText: layout.id === asHeaderLayoutId("classic") ? {
|
|
17090
|
-
mobileMaxWidth: "sm",
|
|
17091
|
-
mobileWrapLines: 1,
|
|
17092
|
-
hideOnShrink: false
|
|
17093
|
-
} : undefined
|
|
17094
|
-
};
|
|
17717
|
+
//#region ../theme-core/src/site-styles/headerStyleRecipe.ts
|
|
17718
|
+
function effectiveRecipe(layout, recipe) {
|
|
17719
|
+
return layout.id === asHeaderLayoutId("floating") && recipe.onFloating ? {
|
|
17720
|
+
...recipe,
|
|
17721
|
+
...recipe.onFloating
|
|
17722
|
+
} : recipe;
|
|
17095
17723
|
}
|
|
17096
|
-
|
|
17097
|
-
|
|
17098
|
-
|
|
17099
|
-
|
|
17100
|
-
|
|
17724
|
+
/**
|
|
17725
|
+
* One token drives the header text, the nav text and the nav link colour: they
|
|
17726
|
+
* are the same decision, and splitting them was how they could disagree.
|
|
17727
|
+
*/
|
|
17728
|
+
function toneColorToken(tone) {
|
|
17729
|
+
switch (tone) {
|
|
17730
|
+
case "neutral": return "text";
|
|
17731
|
+
case "brand": return "primaryForeground";
|
|
17732
|
+
default: return assertNever$5(tone, "Unhandled header tone recipe");
|
|
17733
|
+
}
|
|
17101
17734
|
}
|
|
17102
|
-
function
|
|
17103
|
-
|
|
17104
|
-
|
|
17105
|
-
|
|
17106
|
-
|
|
17107
|
-
|
|
17108
|
-
|
|
17109
|
-
|
|
17110
|
-
|
|
17111
|
-
|
|
17735
|
+
function toneDropdownStyle(tone) {
|
|
17736
|
+
switch (tone) {
|
|
17737
|
+
case "neutral": return {
|
|
17738
|
+
background: "surface",
|
|
17739
|
+
textColor: "text",
|
|
17740
|
+
borderColor: "border",
|
|
17741
|
+
shadow: "md",
|
|
17742
|
+
borderRadius: "md",
|
|
17743
|
+
textTransform: "none",
|
|
17744
|
+
letterSpacing: "normal"
|
|
17745
|
+
};
|
|
17746
|
+
case "brand": return {
|
|
17747
|
+
background: "primary-700",
|
|
17748
|
+
textColor: "primaryForeground",
|
|
17749
|
+
borderColor: null,
|
|
17750
|
+
shadow: "md",
|
|
17751
|
+
borderRadius: "md",
|
|
17752
|
+
hoverBackground: "primary-900",
|
|
17753
|
+
hoverTextColor: "secondary",
|
|
17754
|
+
textTransform: "uppercase",
|
|
17755
|
+
letterSpacing: "wider",
|
|
17756
|
+
fontWeight: "bold",
|
|
17757
|
+
textSize: "sm"
|
|
17758
|
+
};
|
|
17759
|
+
default: return assertNever$5(tone, "Unhandled header tone recipe");
|
|
17760
|
+
}
|
|
17112
17761
|
}
|
|
17113
|
-
function
|
|
17114
|
-
|
|
17115
|
-
|
|
17116
|
-
|
|
17117
|
-
|
|
17118
|
-
|
|
17119
|
-
|
|
17120
|
-
|
|
17121
|
-
|
|
17122
|
-
|
|
17123
|
-
|
|
17124
|
-
|
|
17125
|
-
textSize: "sm"
|
|
17126
|
-
};
|
|
17762
|
+
function recipeBackground(background) {
|
|
17763
|
+
switch (background.kind) {
|
|
17764
|
+
case "token": return {
|
|
17765
|
+
type: "color",
|
|
17766
|
+
color: background.token
|
|
17767
|
+
};
|
|
17768
|
+
case "transparent": return {
|
|
17769
|
+
type: "color",
|
|
17770
|
+
color: "transparent"
|
|
17771
|
+
};
|
|
17772
|
+
default: return assertNever$5(background, "Unhandled header background recipe");
|
|
17773
|
+
}
|
|
17127
17774
|
}
|
|
17128
|
-
function
|
|
17129
|
-
|
|
17130
|
-
|
|
17131
|
-
|
|
17132
|
-
|
|
17133
|
-
|
|
17775
|
+
function recipeBorder(border$1) {
|
|
17776
|
+
switch (border$1) {
|
|
17777
|
+
case "none": return {
|
|
17778
|
+
style: "solid",
|
|
17779
|
+
width: "none",
|
|
17780
|
+
position: "none"
|
|
17781
|
+
};
|
|
17782
|
+
case "bottom": return {
|
|
17783
|
+
style: "solid",
|
|
17784
|
+
width: "thin",
|
|
17785
|
+
position: "bottom",
|
|
17786
|
+
colorToken: "border"
|
|
17787
|
+
};
|
|
17788
|
+
default: return assertNever$5(border$1, "Unhandled header border recipe");
|
|
17789
|
+
}
|
|
17134
17790
|
}
|
|
17135
|
-
function
|
|
17136
|
-
|
|
17137
|
-
|
|
17138
|
-
|
|
17139
|
-
|
|
17140
|
-
|
|
17141
|
-
|
|
17791
|
+
function recipeNavStyle(nav) {
|
|
17792
|
+
switch (nav.kind) {
|
|
17793
|
+
case "minimal": return "minimal";
|
|
17794
|
+
case "underline": return "underline-grow";
|
|
17795
|
+
case "pill": return nav.backing;
|
|
17796
|
+
case "flat-tab": return "solid";
|
|
17797
|
+
default: return assertNever$5(nav, "Unhandled header nav recipe");
|
|
17798
|
+
}
|
|
17142
17799
|
}
|
|
17143
|
-
function
|
|
17144
|
-
|
|
17145
|
-
|
|
17146
|
-
|
|
17800
|
+
function underlineHoverEffects(nav) {
|
|
17801
|
+
const underline = {
|
|
17802
|
+
effectId: "nav-underline",
|
|
17803
|
+
options: {
|
|
17804
|
+
style: "grow",
|
|
17805
|
+
...nav.underlineColorToken ? { colorToken: nav.underlineColorToken } : {}
|
|
17806
|
+
}
|
|
17147
17807
|
};
|
|
17808
|
+
switch (nav.hover) {
|
|
17809
|
+
case "underline": return [underline];
|
|
17810
|
+
case "colour": return [{
|
|
17811
|
+
effectId: "text-color-change",
|
|
17812
|
+
options: { hoverColorToken: nav.hoverColorToken }
|
|
17813
|
+
}];
|
|
17814
|
+
case "both": return [{
|
|
17815
|
+
effectId: "text-color-change",
|
|
17816
|
+
options: { hoverColorToken: nav.hoverColorToken }
|
|
17817
|
+
}, underline];
|
|
17818
|
+
default: return assertNever$5(nav, "Unhandled underline nav hover affordance");
|
|
17819
|
+
}
|
|
17148
17820
|
}
|
|
17149
|
-
function
|
|
17821
|
+
function underlineNavLink(nav, colorToken, typography) {
|
|
17822
|
+
const base = underlineNavLinkStyle(colorToken, nav.underlineColorToken, typography);
|
|
17150
17823
|
return {
|
|
17151
|
-
|
|
17152
|
-
|
|
17153
|
-
|
|
17824
|
+
...base,
|
|
17825
|
+
...nav.hover === "underline" ? {} : { hoverColorToken: nav.hoverColorToken },
|
|
17826
|
+
effects: {
|
|
17827
|
+
...base.effects,
|
|
17828
|
+
hover: underlineHoverEffects(nav)
|
|
17829
|
+
}
|
|
17154
17830
|
};
|
|
17155
17831
|
}
|
|
17156
|
-
function
|
|
17157
|
-
|
|
17158
|
-
|
|
17159
|
-
|
|
17160
|
-
|
|
17161
|
-
|
|
17162
|
-
|
|
17163
|
-
|
|
17164
|
-
|
|
17165
|
-
|
|
17166
|
-
opacity: input.opacity
|
|
17167
|
-
};
|
|
17832
|
+
function recipeNavLinkStyle(recipe) {
|
|
17833
|
+
const colorToken = toneColorToken(recipe.tone);
|
|
17834
|
+
const nav = recipe.nav;
|
|
17835
|
+
switch (nav.kind) {
|
|
17836
|
+
case "minimal": return minimalNavLinkStyle(colorToken, nav.hoverColorToken, recipe.navTypography);
|
|
17837
|
+
case "underline": return underlineNavLink(nav, colorToken, recipe.navTypography);
|
|
17838
|
+
case "pill": return pillNavLinkStyle(colorToken, nav.hoverColorToken, nav.hoverAlpha, recipe.navTypography);
|
|
17839
|
+
case "flat-tab": return flatTabNavLinkStyle(colorToken, recipe.navTypography);
|
|
17840
|
+
default: return assertNever$5(nav, "Unhandled header nav recipe");
|
|
17841
|
+
}
|
|
17168
17842
|
}
|
|
17169
|
-
function
|
|
17843
|
+
function themedShadow(theme) {
|
|
17170
17844
|
switch (theme.shadow.elevation) {
|
|
17171
17845
|
case "none": return undefined;
|
|
17172
17846
|
case "low": return { elevation: "sm" };
|
|
@@ -17175,248 +17849,162 @@ function themedHeaderShadow(theme) {
|
|
|
17175
17849
|
default: return assertNever$5(theme.shadow.elevation, "Unhandled theme shadow elevation");
|
|
17176
17850
|
}
|
|
17177
17851
|
}
|
|
17178
|
-
function
|
|
17179
|
-
|
|
17852
|
+
function recipeShadow(shadow$1, theme) {
|
|
17853
|
+
switch (shadow$1.kind) {
|
|
17854
|
+
case "absent": return {};
|
|
17855
|
+
case "explicit-none": return { shadow: { elevation: "none" } };
|
|
17856
|
+
case "fixed": return { shadow: { elevation: shadow$1.elevation } };
|
|
17857
|
+
case "themed": {
|
|
17858
|
+
const resolved = themedShadow(theme);
|
|
17859
|
+
return resolved ? { shadow: resolved } : {};
|
|
17860
|
+
}
|
|
17861
|
+
default: return assertNever$5(shadow$1, "Unhandled header shadow recipe");
|
|
17862
|
+
}
|
|
17863
|
+
}
|
|
17864
|
+
/**
|
|
17865
|
+
* `{kind: 'none'}` writes nothing: the base header already carries the
|
|
17866
|
+
* `{type: 'none', tint: null, opacity: 0.15}` nav container default, and
|
|
17867
|
+
* omitting the key would be a different JSON.
|
|
17868
|
+
*/
|
|
17869
|
+
function recipeFrame(frame) {
|
|
17870
|
+
switch (frame.kind) {
|
|
17871
|
+
case "none": return {};
|
|
17872
|
+
case "container": return { container: {
|
|
17873
|
+
rounded: frame.rounded,
|
|
17874
|
+
border: true,
|
|
17875
|
+
shadow: true,
|
|
17876
|
+
padding: frame.padding,
|
|
17877
|
+
tint: frame.tint,
|
|
17878
|
+
opacity: frame.opacity
|
|
17879
|
+
} };
|
|
17880
|
+
case "nav-container": return { navContainer: {
|
|
17881
|
+
type: frame.type,
|
|
17882
|
+
tint: frame.tint,
|
|
17883
|
+
opacity: frame.opacity
|
|
17884
|
+
} };
|
|
17885
|
+
default: return assertNever$5(frame, "Unhandled header frame recipe");
|
|
17886
|
+
}
|
|
17887
|
+
}
|
|
17888
|
+
/**
|
|
17889
|
+
* `ctaVariant` is spread conditionally everywhere, which is byte-safe for the
|
|
17890
|
+
* nine `'default'` styles only because `resolveHeaderCtaVariant(theme,
|
|
17891
|
+
* 'default')` returns `undefined`. The compiler used to mix a conditional
|
|
17892
|
+
* spread with three direct assignments; the difference was invisible in
|
|
17893
|
+
* persisted JSON and visible to an in-memory `toEqual`, so it is normalised
|
|
17894
|
+
* here (ledger D1).
|
|
17895
|
+
*/
|
|
17896
|
+
function recipeCta(cta, theme) {
|
|
17897
|
+
const variant = resolveHeaderCtaVariant(theme, cta.treatment);
|
|
17180
17898
|
return {
|
|
17181
|
-
|
|
17182
|
-
...
|
|
17183
|
-
|
|
17184
|
-
dropdownStyle: brandDropdownStyle(),
|
|
17185
|
-
border: noHeaderBorder(),
|
|
17186
|
-
ctaTreatment: "inverted",
|
|
17187
|
-
...ctaVariant ? { ctaVariant } : {}
|
|
17899
|
+
ctaTreatment: cta.treatment,
|
|
17900
|
+
...variant ? { ctaVariant: variant } : {},
|
|
17901
|
+
...cta.gap ? { ctaGap: cta.gap } : {}
|
|
17188
17902
|
};
|
|
17189
17903
|
}
|
|
17190
|
-
|
|
17904
|
+
/**
|
|
17905
|
+
* Layout-derived structure. The only part of the header a recipe cannot reach.
|
|
17906
|
+
*
|
|
17907
|
+
* It used to start from `baseHeader(theme)` — the previous header with the
|
|
17908
|
+
* style-owned keys stripped — which is what let a value hand-edited once
|
|
17909
|
+
* survive every later recompile. Phase 4e deleted that (#1318): the layout
|
|
17910
|
+
* writes its own fields and nothing else arrives from the theme's header.
|
|
17911
|
+
*
|
|
17912
|
+
* Three fields moved here as a result, each with a named producer rather than
|
|
17913
|
+
* an inherited value:
|
|
17914
|
+
*
|
|
17915
|
+
* - `maxWidth` is the constant `'container'` (D16). It is layout-owned with one
|
|
17916
|
+
* value; `headerSchema` makes it required on the *output* type, so a
|
|
17917
|
+
* `.default()` at the parse boundary would not do — that would be a hidden
|
|
17918
|
+
* authority, which is the thing this slice removes.
|
|
17919
|
+
* - `navContainer` is `baseHeader`'s old default, which a `frame: {kind:'none'}`
|
|
17920
|
+
* recipe still relies on being present rather than omitted.
|
|
17921
|
+
* - `scrollTreatment` and `interactionPalette` are deliberately **absent**.
|
|
17922
|
+
* Neither is compiled here: `scrollTreatment` is written at render time by
|
|
17923
|
+
* `applyOpeningForeground` from the page's own context, and the interaction
|
|
17924
|
+
* palette's marker field is written by `applyHeaderPreferences` from the
|
|
17925
|
+
* record. Both used to reach the output only by surviving `baseHeader`.
|
|
17926
|
+
*/
|
|
17927
|
+
function headerForLayout(layout) {
|
|
17191
17928
|
return {
|
|
17192
|
-
|
|
17193
|
-
|
|
17194
|
-
|
|
17195
|
-
|
|
17196
|
-
|
|
17197
|
-
|
|
17198
|
-
|
|
17199
|
-
|
|
17200
|
-
|
|
17201
|
-
|
|
17202
|
-
|
|
17203
|
-
|
|
17204
|
-
|
|
17205
|
-
|
|
17206
|
-
|
|
17207
|
-
|
|
17208
|
-
};
|
|
17209
|
-
case "fine-line": return {
|
|
17210
|
-
...header,
|
|
17211
|
-
...neutralHeaderBase(),
|
|
17212
|
-
background: colorHeaderBackground("surface"),
|
|
17213
|
-
navStyle: "underline-grow",
|
|
17214
|
-
navWeight: "medium",
|
|
17215
|
-
navLinkStyle: underlineNavLinkStyle("text", undefined, style.navTypography),
|
|
17216
|
-
border: noHeaderBorder(),
|
|
17217
|
-
ctaTreatment: "outline",
|
|
17218
|
-
ctaVariant: resolveHeaderCtaVariant(theme, "outline")
|
|
17219
|
-
};
|
|
17220
|
-
case "soft-bar": return {
|
|
17221
|
-
...header,
|
|
17222
|
-
...neutralHeaderBase(),
|
|
17223
|
-
background: colorHeaderBackground("surfaceAlt"),
|
|
17224
|
-
navStyle: "capsule",
|
|
17225
|
-
navWeight: "medium",
|
|
17226
|
-
navLinkStyle: pillNavLinkStyle("text", "primary", 8, style.navTypography),
|
|
17227
|
-
border: bottomHeaderBorder(),
|
|
17228
|
-
ctaTreatment: "default"
|
|
17229
|
-
};
|
|
17230
|
-
case "pill-nav": return {
|
|
17231
|
-
...header,
|
|
17232
|
-
...neutralHeaderBase(),
|
|
17233
|
-
background: colorHeaderBackground("surface"),
|
|
17234
|
-
navStyle: "capsule",
|
|
17235
|
-
navWeight: "medium",
|
|
17236
|
-
navLinkStyle: pillNavLinkStyle("text", "primary", 10, style.navTypography),
|
|
17237
|
-
navContainer: {
|
|
17238
|
-
type: "pill",
|
|
17239
|
-
tint: "surfaceAlt",
|
|
17240
|
-
opacity: .7
|
|
17241
|
-
},
|
|
17242
|
-
border: noHeaderBorder(),
|
|
17243
|
-
ctaTreatment: "default"
|
|
17244
|
-
};
|
|
17245
|
-
default: return assertNever$5(id, "Unhandled basic header style id");
|
|
17246
|
-
}
|
|
17247
|
-
}
|
|
17248
|
-
function applyBrandHeaderStyle(id, theme, header, style) {
|
|
17249
|
-
switch (id) {
|
|
17250
|
-
case "brand-bar": return {
|
|
17251
|
-
...brandSurfaceBase(theme, header),
|
|
17252
|
-
navStyle: "minimal",
|
|
17253
|
-
navWeight: "semibold",
|
|
17254
|
-
navLinkStyle: minimalNavLinkStyle("primaryForeground", "secondary", style.navTypography)
|
|
17255
|
-
};
|
|
17256
|
-
case "brand-underline": return {
|
|
17257
|
-
...brandSurfaceBase(theme, header),
|
|
17258
|
-
navStyle: "underline-grow",
|
|
17259
|
-
navWeight: "bold",
|
|
17260
|
-
navLinkStyle: underlineNavLinkStyle("primaryForeground", "secondary", style.navTypography)
|
|
17261
|
-
};
|
|
17262
|
-
case "brand-pill": return {
|
|
17263
|
-
...brandSurfaceBase(theme, header),
|
|
17264
|
-
...header.variant === "floating" ? {
|
|
17265
|
-
background: transparentHeaderBackground(),
|
|
17266
|
-
container: floatingContainer({
|
|
17267
|
-
rounded: "2xl",
|
|
17268
|
-
padding: "sm",
|
|
17269
|
-
tint: "primary",
|
|
17270
|
-
opacity: .95
|
|
17271
|
-
})
|
|
17272
|
-
} : {},
|
|
17273
|
-
navStyle: "capsule",
|
|
17274
|
-
navWeight: "semibold",
|
|
17275
|
-
navLinkStyle: pillNavLinkStyle("primaryForeground", "primaryForeground", 14, style.navTypography)
|
|
17276
|
-
};
|
|
17277
|
-
case "brand-capsule": return {
|
|
17278
|
-
...brandSurfaceBase(theme, header),
|
|
17279
|
-
navStyle: "capsule",
|
|
17280
|
-
navWeight: "semibold",
|
|
17281
|
-
navLinkStyle: pillNavLinkStyle("primaryForeground", "primaryForeground", 16, style.navTypography),
|
|
17282
|
-
shadow: { elevation: "md" }
|
|
17283
|
-
};
|
|
17284
|
-
default: return assertNever$5(id, "Unhandled brand header style id");
|
|
17285
|
-
}
|
|
17286
|
-
}
|
|
17287
|
-
function applyEditorialHeaderStyle(id, theme, header, style) {
|
|
17288
|
-
switch (id) {
|
|
17289
|
-
case "serif-line": return {
|
|
17290
|
-
...header,
|
|
17291
|
-
...neutralHeaderBase(),
|
|
17292
|
-
background: colorHeaderBackground("background"),
|
|
17293
|
-
navStyle: "underline-grow",
|
|
17294
|
-
navWeight: "medium",
|
|
17295
|
-
navLinkStyle: underlineNavLinkStyle("text", undefined, style.navTypography),
|
|
17296
|
-
border: noHeaderBorder(),
|
|
17297
|
-
shadow: { elevation: "none" },
|
|
17298
|
-
logo: {
|
|
17299
|
-
fontFamily: "heading",
|
|
17300
|
-
letterSpacing: "wide",
|
|
17301
|
-
gradient: false
|
|
17302
|
-
},
|
|
17303
|
-
ctaTreatment: "outline",
|
|
17304
|
-
ctaVariant: resolveHeaderCtaVariant(theme, "outline"),
|
|
17305
|
-
ctaGap: "relaxed"
|
|
17306
|
-
};
|
|
17307
|
-
case "flat-tabs": return {
|
|
17308
|
-
...header,
|
|
17309
|
-
...neutralHeaderBase(),
|
|
17310
|
-
background: colorHeaderBackground("surface"),
|
|
17311
|
-
navStyle: "solid",
|
|
17312
|
-
navWeight: "semibold",
|
|
17313
|
-
navLinkStyle: flatTabNavLinkStyle("text", style.navTypography),
|
|
17314
|
-
border: bottomHeaderBorder(),
|
|
17315
|
-
shadow: themedHeaderShadow(theme),
|
|
17316
|
-
ctaTreatment: "primary",
|
|
17317
|
-
ctaVariant: resolveHeaderCtaVariant(theme, "primary"),
|
|
17318
|
-
ctaGap: "compact"
|
|
17319
|
-
};
|
|
17320
|
-
default: return assertNever$5(id, "Unhandled editorial header style id");
|
|
17321
|
-
}
|
|
17322
|
-
}
|
|
17323
|
-
function applyFloatingHeaderStyle(id, header, style) {
|
|
17324
|
-
switch (id) {
|
|
17325
|
-
case "glass": return {
|
|
17326
|
-
...floatingSurfaceBase(header),
|
|
17327
|
-
...neutralHeaderBase(),
|
|
17328
|
-
navStyle: "frosted",
|
|
17329
|
-
navWeight: "semibold",
|
|
17330
|
-
navLinkStyle: pillNavLinkStyle("text", "primary", 8, style.navTypography),
|
|
17331
|
-
container: floatingContainer({
|
|
17332
|
-
rounded: "2xl",
|
|
17333
|
-
padding: "sm",
|
|
17334
|
-
tint: "surface",
|
|
17335
|
-
opacity: .4
|
|
17336
|
-
}),
|
|
17337
|
-
ctaTreatment: "default"
|
|
17338
|
-
};
|
|
17339
|
-
case "minimal-surface": return {
|
|
17340
|
-
...floatingSurfaceBase(header),
|
|
17341
|
-
...neutralHeaderBase(),
|
|
17342
|
-
navStyle: "minimal",
|
|
17343
|
-
navWeight: "medium",
|
|
17344
|
-
navLinkStyle: {
|
|
17345
|
-
...minimalNavLinkStyle("text", "primary", style.navTypography),
|
|
17346
|
-
textSize: "lg",
|
|
17347
|
-
contrastTreatment: "soft-glow"
|
|
17348
|
-
},
|
|
17349
|
-
ctaTreatment: "default"
|
|
17350
|
-
};
|
|
17351
|
-
case "soft-shell": return {
|
|
17352
|
-
...floatingSurfaceBase(header),
|
|
17353
|
-
...neutralHeaderBase(),
|
|
17354
|
-
navStyle: "capsule",
|
|
17355
|
-
navWeight: "medium",
|
|
17356
|
-
navLinkStyle: pillNavLinkStyle("text", "primary", 8, style.navTypography),
|
|
17357
|
-
container: floatingContainer({
|
|
17358
|
-
rounded: "2xl",
|
|
17359
|
-
padding: "md",
|
|
17360
|
-
tint: "surfaceAlt",
|
|
17361
|
-
opacity: .95
|
|
17362
|
-
}),
|
|
17363
|
-
ctaTreatment: "default"
|
|
17364
|
-
};
|
|
17365
|
-
case "split-glass": return {
|
|
17366
|
-
...floatingSurfaceBase(header),
|
|
17367
|
-
...neutralHeaderBase(),
|
|
17368
|
-
navStyle: "frosted",
|
|
17369
|
-
navWeight: "semibold",
|
|
17370
|
-
navLinkStyle: pillNavLinkStyle("text", "primary", 8, style.navTypography),
|
|
17371
|
-
navContainer: {
|
|
17372
|
-
type: "glass",
|
|
17373
|
-
tint: "surface",
|
|
17374
|
-
opacity: .4
|
|
17375
|
-
},
|
|
17376
|
-
ctaTreatment: "default"
|
|
17377
|
-
};
|
|
17378
|
-
case "split-pill": return {
|
|
17379
|
-
...floatingSurfaceBase(header),
|
|
17380
|
-
...neutralHeaderBase(),
|
|
17381
|
-
navStyle: "capsule",
|
|
17382
|
-
navWeight: "medium",
|
|
17383
|
-
navLinkStyle: pillNavLinkStyle("text", "primary", 10, style.navTypography),
|
|
17384
|
-
navContainer: {
|
|
17385
|
-
type: "pill",
|
|
17386
|
-
tint: "surface",
|
|
17387
|
-
opacity: .94
|
|
17388
|
-
},
|
|
17389
|
-
ctaTreatment: "default"
|
|
17390
|
-
};
|
|
17391
|
-
default: return assertNever$5(id, "Unhandled floating header style id");
|
|
17392
|
-
}
|
|
17393
|
-
}
|
|
17394
|
-
const headerStyleCompilers = {
|
|
17395
|
-
minimal: (theme, header, style) => applyBasicHeaderStyle("minimal", theme, header, style),
|
|
17396
|
-
"fine-line": (theme, header, style) => applyBasicHeaderStyle("fine-line", theme, header, style),
|
|
17397
|
-
"soft-bar": (theme, header, style) => applyBasicHeaderStyle("soft-bar", theme, header, style),
|
|
17398
|
-
"pill-nav": (theme, header, style) => applyBasicHeaderStyle("pill-nav", theme, header, style),
|
|
17399
|
-
"brand-bar": (theme, header, style) => applyBrandHeaderStyle("brand-bar", theme, header, style),
|
|
17400
|
-
"brand-underline": (theme, header, style) => applyBrandHeaderStyle("brand-underline", theme, header, style),
|
|
17401
|
-
"brand-pill": (theme, header, style) => applyBrandHeaderStyle("brand-pill", theme, header, style),
|
|
17402
|
-
"brand-capsule": (theme, header, style) => applyBrandHeaderStyle("brand-capsule", theme, header, style),
|
|
17403
|
-
"serif-line": (theme, header, style) => applyEditorialHeaderStyle("serif-line", theme, header, style),
|
|
17404
|
-
"flat-tabs": (theme, header, style) => applyEditorialHeaderStyle("flat-tabs", theme, header, style),
|
|
17405
|
-
glass: (_theme, header, style) => applyFloatingHeaderStyle("glass", header, style),
|
|
17406
|
-
"minimal-surface": (_theme, header, style) => applyFloatingHeaderStyle("minimal-surface", header, style),
|
|
17407
|
-
"soft-shell": (_theme, header, style) => applyFloatingHeaderStyle("soft-shell", header, style),
|
|
17408
|
-
"split-glass": (_theme, header, style) => applyFloatingHeaderStyle("split-glass", header, style),
|
|
17409
|
-
"split-pill": (_theme, header, style) => applyFloatingHeaderStyle("split-pill", header, style)
|
|
17410
|
-
};
|
|
17411
|
-
function applyHeaderStyle(theme, header, style) {
|
|
17412
|
-
return headerStyleCompilers[headerStyleIdValue(style.id)](theme, header, style);
|
|
17929
|
+
maxWidth: "container",
|
|
17930
|
+
navContainer: {
|
|
17931
|
+
type: "none",
|
|
17932
|
+
tint: null,
|
|
17933
|
+
opacity: .15
|
|
17934
|
+
},
|
|
17935
|
+
variant: layout.variant,
|
|
17936
|
+
positioning: layout.defaultPositioning,
|
|
17937
|
+
shrinkOnScroll: layout.shrinkOnScroll,
|
|
17938
|
+
ctaGap: layout.id === asHeaderLayoutId("floating") ? "compact" : "default",
|
|
17939
|
+
logoText: layout.id === asHeaderLayoutId("classic") ? {
|
|
17940
|
+
mobileMaxWidth: "sm",
|
|
17941
|
+
mobileWrapLines: 1,
|
|
17942
|
+
hideOnShrink: false
|
|
17943
|
+
} : undefined
|
|
17944
|
+
};
|
|
17413
17945
|
}
|
|
17414
|
-
|
|
17415
|
-
|
|
17946
|
+
/**
|
|
17947
|
+
* The single header interpreter. Pure and total: same `(theme, layout, recipe)`
|
|
17948
|
+
* in, same object out, no throws of its own.
|
|
17949
|
+
*
|
|
17950
|
+
* It **constructs** a `ThemeHeader` rather than starting from one (#1318 Phase
|
|
17951
|
+
* 4e). `theme` is read for the palette-dependent facets only — the themed
|
|
17952
|
+
* shadow and the CTA variant — never for `theme.header`, so no value can
|
|
17953
|
+
* survive a recompile by accident. Every required field of `headerSchema` is
|
|
17954
|
+
* produced by a named branch here or in `headerForLayout`; the type is the
|
|
17955
|
+
* check.
|
|
17956
|
+
*/
|
|
17957
|
+
function compileHeaderRecipe(theme, layout, recipe) {
|
|
17958
|
+
const effective = effectiveRecipe(layout, recipe);
|
|
17959
|
+
const navLinkStyle = recipeNavLinkStyle(effective);
|
|
17960
|
+
return {
|
|
17961
|
+
...headerForLayout(layout),
|
|
17962
|
+
textColor: toneColorToken(effective.tone),
|
|
17963
|
+
navColor: toneColorToken(effective.tone),
|
|
17964
|
+
dropdownStyle: toneDropdownStyle(effective.tone),
|
|
17965
|
+
background: recipeBackground(effective.background),
|
|
17966
|
+
border: recipeBorder(effective.border),
|
|
17967
|
+
navStyle: recipeNavStyle(effective.nav),
|
|
17968
|
+
navWeight: effective.navWeight,
|
|
17969
|
+
navLinkStyle: {
|
|
17970
|
+
...navLinkStyle,
|
|
17971
|
+
fontWeight: effective.navLinkWeight,
|
|
17972
|
+
...effective.navLinkExtras ?? {}
|
|
17973
|
+
},
|
|
17974
|
+
...recipeShadow(effective.shadow, theme),
|
|
17975
|
+
...recipeFrame(effective.frame),
|
|
17976
|
+
...effective.logo ? { logo: effective.logo } : {},
|
|
17977
|
+
...recipeCta(effective.cta, theme)
|
|
17978
|
+
};
|
|
17416
17979
|
}
|
|
17417
|
-
|
|
17418
|
-
|
|
17419
|
-
|
|
17980
|
+
|
|
17981
|
+
//#endregion
|
|
17982
|
+
//#region ../theme-core/src/site-styles/headerStyleCompiler.ts
|
|
17983
|
+
/**
|
|
17984
|
+
* The header compilation pipeline: pick the recipe, interpret it, then apply
|
|
17985
|
+
* the user's interaction-palette preference.
|
|
17986
|
+
*
|
|
17987
|
+
* Appearance decisions live in `headerStyleDefaults.ts` (data), optionally
|
|
17988
|
+
* taken over per site style, and `headerStyleRecipe.ts` (one pure interpreter).
|
|
17989
|
+
* Nothing here switches on a header style id.
|
|
17990
|
+
*
|
|
17991
|
+
* `siteStyle` is required rather than optional: a caller that has no resolved
|
|
17992
|
+
* site style has to say so, because silently falling back to the catalogue
|
|
17993
|
+
* default is exactly the ownership leak this slice closes. Pass `null` when
|
|
17994
|
+
* there genuinely is no site style (initial site provisioning).
|
|
17995
|
+
*
|
|
17996
|
+
* `interactionPalette` is a parameter rather than a read off
|
|
17997
|
+
* `theme.header.interactionPalette` (#1318 Phase 4c). The marker field on the
|
|
17998
|
+
* header is where the preference is *persisted*, but taking it from there made
|
|
17999
|
+
* the palette the one preference the compiler could not be told about — which
|
|
18000
|
+
* is invisible while the marker always equals what was compiled and wrong the
|
|
18001
|
+
* moment a backfill supplies a different one. The caller that owns the
|
|
18002
|
+
* preference record now says which palette to compile.
|
|
18003
|
+
*/
|
|
18004
|
+
function compileHeaderLayoutStyleSelection(theme, resolution, siteStyle, interactionPalette) {
|
|
18005
|
+
const recipe = resolveHeaderStyleRecipe(siteStyle, resolution.style.id);
|
|
18006
|
+
const header = compileHeaderRecipe(theme, resolution.layout, recipe);
|
|
18007
|
+
return applyHeaderInteractionPalette(header, recipe.interactionPalette, interactionPalette);
|
|
17420
18008
|
}
|
|
17421
18009
|
|
|
17422
18010
|
//#endregion
|
|
@@ -18228,7 +18816,6 @@ const modernMinimalTheme = {
|
|
|
18228
18816
|
positioning: "static",
|
|
18229
18817
|
shrinkOnScroll: false,
|
|
18230
18818
|
maxWidth: "container",
|
|
18231
|
-
logoOverride: null,
|
|
18232
18819
|
background: {
|
|
18233
18820
|
type: "color",
|
|
18234
18821
|
color: "surface"
|
|
@@ -19933,10 +20520,22 @@ function defaultPaletteForSiteStyle(siteStyle) {
|
|
|
19933
20520
|
return defaultCuratedChoice(siteStyle.paletteRecommendations);
|
|
19934
20521
|
}
|
|
19935
20522
|
function defaultHeaderSelectionForSiteStyle(siteStyle) {
|
|
20523
|
+
return defaultHeaderCurationForSiteStyle(siteStyle).selection;
|
|
20524
|
+
}
|
|
20525
|
+
/**
|
|
20526
|
+
* The curated default selection together with the style choices that belong to
|
|
20527
|
+
* it. Carrying the styles alongside the ids is what lets a caller fall back to
|
|
20528
|
+
* the default layout without a second, nullable lookup that can never actually
|
|
20529
|
+
* miss — the source of an unreachable throw before #1318 Phase 4a.
|
|
20530
|
+
*/
|
|
20531
|
+
function defaultHeaderCurationForSiteStyle(siteStyle) {
|
|
19936
20532
|
const [{ layout, styles }] = siteStyle.headerCuration;
|
|
19937
20533
|
return {
|
|
19938
|
-
|
|
19939
|
-
|
|
20534
|
+
selection: {
|
|
20535
|
+
layoutId: layout.id,
|
|
20536
|
+
styleId: styles[0].id
|
|
20537
|
+
},
|
|
20538
|
+
styles
|
|
19940
20539
|
};
|
|
19941
20540
|
}
|
|
19942
20541
|
function headerLayoutChoicesForSiteStyle(siteStyle) {
|
|
@@ -21085,6 +21684,19 @@ const curatedSiteStyles = [
|
|
|
21085
21684
|
})
|
|
21086
21685
|
];
|
|
21087
21686
|
const curatedSiteStylesById = new Map(curatedSiteStyles.map((style) => [style.id, style]));
|
|
21687
|
+
/**
|
|
21688
|
+
* The header appearance a persisted theme actually renders: the catalogue
|
|
21689
|
+
* recipe unless the theme's own site style has taken that id over (#1318).
|
|
21690
|
+
*
|
|
21691
|
+
* Consumers that hold a theme and a selected style id — the public frontend and
|
|
21692
|
+
* the chrome customiser — go through here rather than reading the style
|
|
21693
|
+
* definition, so a site-style override is visible to every reader rather than
|
|
21694
|
+
* only to the compiler.
|
|
21695
|
+
*/
|
|
21696
|
+
function headerStyleRecipeForTheme(theme, styleId) {
|
|
21697
|
+
const siteStyle = theme?.siteStyleId ? getCuratedSiteStyle(theme.siteStyleId) : null;
|
|
21698
|
+
return resolveHeaderStyleRecipe(siteStyle, styleId);
|
|
21699
|
+
}
|
|
21088
21700
|
function getCuratedSiteStyle(id) {
|
|
21089
21701
|
return curatedSiteStylesById.get(id) ?? null;
|
|
21090
21702
|
}
|
|
@@ -22478,6 +23090,878 @@ function reinterpretForSiteStyle(input) {
|
|
|
22478
23090
|
};
|
|
22479
23091
|
}
|
|
22480
23092
|
|
|
23093
|
+
//#endregion
|
|
23094
|
+
//#region ../theme-core/src/site-styles/siteChromeBackgrounds.ts
|
|
23095
|
+
const siteChromeBackgroundColorTokens = toSiteChromeBackgroundColorTokens(backgroundAppearanceColorTokenNames);
|
|
23096
|
+
const quickSiteChromeBackgroundColorTokens = toSiteChromeBackgroundColorTokens(backgroundAppearanceQuickColorTokenNames);
|
|
23097
|
+
const advancedSiteChromeBackgroundColorTokens = siteChromeBackgroundColorTokens.filter((token) => !quickSiteChromeBackgroundColorTokens.includes(token));
|
|
23098
|
+
const defaultSiteChromeBackgroundOverride = { kind: "default" };
|
|
23099
|
+
function defaultSiteChromeBackgroundPreferences() {
|
|
23100
|
+
return {
|
|
23101
|
+
header: defaultSiteChromeBackgroundOverride,
|
|
23102
|
+
footer: defaultSiteChromeBackgroundOverride
|
|
23103
|
+
};
|
|
23104
|
+
}
|
|
23105
|
+
function parseSiteChromeBackgroundColorToken(value) {
|
|
23106
|
+
const token = parseBackgroundAppearanceColorToken(value);
|
|
23107
|
+
return token ? token : null;
|
|
23108
|
+
}
|
|
23109
|
+
function siteChromeBackgroundPreferencesFromTheme(theme) {
|
|
23110
|
+
if (!theme) return defaultSiteChromeBackgroundPreferences();
|
|
23111
|
+
return {
|
|
23112
|
+
header: theme.header ? siteChromeBackgroundOverrideFromHeader(theme.header) : defaultSiteChromeBackgroundOverride,
|
|
23113
|
+
footer: theme.footer ? siteChromeBackgroundOverrideFromFooter(theme.footer) : defaultSiteChromeBackgroundOverride
|
|
23114
|
+
};
|
|
23115
|
+
}
|
|
23116
|
+
function applySiteChromeBackgroundPreferences(theme, preferences) {
|
|
23117
|
+
return {
|
|
23118
|
+
...theme,
|
|
23119
|
+
header: applyHeaderBackgroundOverride(theme, theme.header, preferences.header),
|
|
23120
|
+
footer: applyFooterBackgroundOverride(theme, theme.footer, preferences.footer)
|
|
23121
|
+
};
|
|
23122
|
+
}
|
|
23123
|
+
function canCustomizeSiteChromeBackground(theme, surface$1) {
|
|
23124
|
+
if (surface$1 === "footer") return true;
|
|
23125
|
+
const header = theme?.header;
|
|
23126
|
+
if (!header) return false;
|
|
23127
|
+
const navContainerType = header.navContainer?.type ?? "none";
|
|
23128
|
+
return header.variant !== "floating" && navContainerType !== "glass" && navContainerType !== "pill";
|
|
23129
|
+
}
|
|
23130
|
+
function siteChromeBackgroundOverrideLabel(override) {
|
|
23131
|
+
switch (override.kind) {
|
|
23132
|
+
case "default": return "Default";
|
|
23133
|
+
case "color": return formatSiteChromeBackgroundTokenLabel(override.token);
|
|
23134
|
+
}
|
|
23135
|
+
}
|
|
23136
|
+
function formatSiteChromeBackgroundTokenLabel(token) {
|
|
23137
|
+
return String(token).replace(/-/g, " ").replace(/\b\w/g, (letter) => letter.toUpperCase());
|
|
23138
|
+
}
|
|
23139
|
+
function siteChromeBackgroundOverrideEquals(left, right) {
|
|
23140
|
+
return left.kind === right.kind && (left.kind === "default" || right.kind === "color" && left.token === right.token);
|
|
23141
|
+
}
|
|
23142
|
+
function cycleSiteChromeBackgroundOverride(current, direction) {
|
|
23143
|
+
const fallback = direction === "next" ? quickSiteChromeBackgroundColorTokens[0] : quickSiteChromeBackgroundColorTokens[quickSiteChromeBackgroundColorTokens.length - 1];
|
|
23144
|
+
if (!fallback) return defaultSiteChromeBackgroundOverride;
|
|
23145
|
+
if (current.kind === "default") {
|
|
23146
|
+
return {
|
|
23147
|
+
kind: "color",
|
|
23148
|
+
token: fallback
|
|
23149
|
+
};
|
|
23150
|
+
}
|
|
23151
|
+
const currentIndex = quickSiteChromeBackgroundColorTokens.indexOf(current.token);
|
|
23152
|
+
if (currentIndex === -1) {
|
|
23153
|
+
return {
|
|
23154
|
+
kind: "color",
|
|
23155
|
+
token: fallback
|
|
23156
|
+
};
|
|
23157
|
+
}
|
|
23158
|
+
const nextIndex = direction === "next" ? (currentIndex + 1) % quickSiteChromeBackgroundColorTokens.length : currentIndex === 0 ? quickSiteChromeBackgroundColorTokens.length - 1 : currentIndex - 1;
|
|
23159
|
+
const token = requiredQuickSiteChromeBackgroundColorToken(nextIndex);
|
|
23160
|
+
return {
|
|
23161
|
+
kind: "color",
|
|
23162
|
+
token
|
|
23163
|
+
};
|
|
23164
|
+
}
|
|
23165
|
+
/**
|
|
23166
|
+
* Reads the header background override back out of persisted bytes. Exported
|
|
23167
|
+
* for `readHeaderPreferences`, which needs the header half on its own.
|
|
23168
|
+
*/
|
|
23169
|
+
function siteChromeBackgroundOverrideFromHeader(header) {
|
|
23170
|
+
return siteChromeBackgroundOverrideFromBackground(header.background);
|
|
23171
|
+
}
|
|
23172
|
+
function siteChromeBackgroundOverrideFromFooter(footer) {
|
|
23173
|
+
return siteChromeBackgroundOverrideFromBackground(footer.background);
|
|
23174
|
+
}
|
|
23175
|
+
function siteChromeBackgroundOverrideFromBackground(background) {
|
|
23176
|
+
if (background?.contentOwner !== siteChromeBackgroundOverrideOwner || background.type !== "color") {
|
|
23177
|
+
return defaultSiteChromeBackgroundOverride;
|
|
23178
|
+
}
|
|
23179
|
+
const token = parseSiteChromeBackgroundColorToken(background.color);
|
|
23180
|
+
return token ? {
|
|
23181
|
+
kind: "color",
|
|
23182
|
+
token
|
|
23183
|
+
} : defaultSiteChromeBackgroundOverride;
|
|
23184
|
+
}
|
|
23185
|
+
/**
|
|
23186
|
+
* Header-only background applier. Exported so the header preference pipeline
|
|
23187
|
+
* can apply the header override without going through
|
|
23188
|
+
* `applySiteChromeBackgroundPreferences`, which also writes the footer and
|
|
23189
|
+
* would clobber footer intent (#1318 Correction 20).
|
|
23190
|
+
*/
|
|
23191
|
+
function applyHeaderBackgroundOverride(theme, header, override) {
|
|
23192
|
+
if (override.kind === "default") return header;
|
|
23193
|
+
const background = siteChromeColorBackground(override.token);
|
|
23194
|
+
const foreground = resolveChromeForegroundToken(theme, background);
|
|
23195
|
+
return {
|
|
23196
|
+
...header,
|
|
23197
|
+
background: {
|
|
23198
|
+
...background,
|
|
23199
|
+
textColor: foreground
|
|
23200
|
+
},
|
|
23201
|
+
textColor: foreground,
|
|
23202
|
+
navColor: foreground,
|
|
23203
|
+
...header.navLinkStyle ? { navLinkStyle: {
|
|
23204
|
+
...header.navLinkStyle,
|
|
23205
|
+
colorToken: foreground
|
|
23206
|
+
} } : {}
|
|
23207
|
+
};
|
|
23208
|
+
}
|
|
23209
|
+
/**
|
|
23210
|
+
* Footer-only background applier, the counterpart of
|
|
23211
|
+
* `applyHeaderBackgroundOverride`. Exported so a caller that owns one surface
|
|
23212
|
+
* can write that surface rather than passing a no-op override for the other
|
|
23213
|
+
* (#1318 Correction 20).
|
|
23214
|
+
*/
|
|
23215
|
+
function applyFooterBackgroundOverride(theme, footer, override) {
|
|
23216
|
+
if (override.kind === "default") return footer;
|
|
23217
|
+
const background = siteChromeColorBackground(override.token);
|
|
23218
|
+
const foreground = resolveChromeForegroundToken(theme, background);
|
|
23219
|
+
return {
|
|
23220
|
+
...footer,
|
|
23221
|
+
background: {
|
|
23222
|
+
...background,
|
|
23223
|
+
textColor: foreground
|
|
23224
|
+
},
|
|
23225
|
+
navColor: foreground,
|
|
23226
|
+
...footer.navLinkStyle ? { navLinkStyle: {
|
|
23227
|
+
...footer.navLinkStyle,
|
|
23228
|
+
colorToken: foreground
|
|
23229
|
+
} } : {},
|
|
23230
|
+
...footer.bottomTextLinkStyle ? { bottomTextLinkStyle: {
|
|
23231
|
+
...footer.bottomTextLinkStyle,
|
|
23232
|
+
colorToken: foreground
|
|
23233
|
+
} } : {},
|
|
23234
|
+
...footer.bottomBar ? { bottomBar: {
|
|
23235
|
+
...footer.bottomBar,
|
|
23236
|
+
textColor: foreground
|
|
23237
|
+
} } : {}
|
|
23238
|
+
};
|
|
23239
|
+
}
|
|
23240
|
+
function resolveChromeForegroundToken(theme, background) {
|
|
23241
|
+
const resolved = resolveSurfaceForeground(background, expandPalette(theme.palette));
|
|
23242
|
+
if (resolved.kind === "unknown") {
|
|
23243
|
+
throw new Error(`Site chrome background "${background?.type === "color" ? background.color ?? "unknown" : "unknown"}" has no resolvable AA foreground; the theme palette is incomplete`);
|
|
23244
|
+
}
|
|
23245
|
+
return resolved.roles.text;
|
|
23246
|
+
}
|
|
23247
|
+
function siteChromeColorBackground(color) {
|
|
23248
|
+
return {
|
|
23249
|
+
type: "color",
|
|
23250
|
+
contentOwner: siteChromeBackgroundOverrideOwner,
|
|
23251
|
+
color
|
|
23252
|
+
};
|
|
23253
|
+
}
|
|
23254
|
+
function toSiteChromeBackgroundColorTokens(tokenNames) {
|
|
23255
|
+
return tokenNames.flatMap((name) => {
|
|
23256
|
+
const token = parseSiteChromeBackgroundColorToken(name);
|
|
23257
|
+
return token ? [token] : [];
|
|
23258
|
+
});
|
|
23259
|
+
}
|
|
23260
|
+
function requiredQuickSiteChromeBackgroundColorToken(index) {
|
|
23261
|
+
const token = quickSiteChromeBackgroundColorTokens[index];
|
|
23262
|
+
if (!token) {
|
|
23263
|
+
throw new Error(`Missing quick site chrome background token at index ${index}`);
|
|
23264
|
+
}
|
|
23265
|
+
return token;
|
|
23266
|
+
}
|
|
23267
|
+
|
|
23268
|
+
//#endregion
|
|
23269
|
+
//#region ../theme-core/src/site-styles/siteChromeNavPreferences.ts
|
|
23270
|
+
const headerNavTypographyPreferenceValues = [
|
|
23271
|
+
"inherit",
|
|
23272
|
+
"body",
|
|
23273
|
+
"heading"
|
|
23274
|
+
];
|
|
23275
|
+
const headerNavFontSizePreferenceValues = [
|
|
23276
|
+
"inherit",
|
|
23277
|
+
"sm",
|
|
23278
|
+
"base",
|
|
23279
|
+
"lg",
|
|
23280
|
+
"xl"
|
|
23281
|
+
];
|
|
23282
|
+
const headerNavTypographyOptions = [
|
|
23283
|
+
{
|
|
23284
|
+
value: "inherit",
|
|
23285
|
+
label: "Default"
|
|
23286
|
+
},
|
|
23287
|
+
{
|
|
23288
|
+
value: "body",
|
|
23289
|
+
label: "Body"
|
|
23290
|
+
},
|
|
23291
|
+
{
|
|
23292
|
+
value: "heading",
|
|
23293
|
+
label: "Heading"
|
|
23294
|
+
}
|
|
23295
|
+
];
|
|
23296
|
+
const headerNavFontSizeOptions = [
|
|
23297
|
+
{
|
|
23298
|
+
value: "inherit",
|
|
23299
|
+
label: "Default"
|
|
23300
|
+
},
|
|
23301
|
+
{
|
|
23302
|
+
value: "sm",
|
|
23303
|
+
label: "Small"
|
|
23304
|
+
},
|
|
23305
|
+
{
|
|
23306
|
+
value: "base",
|
|
23307
|
+
label: "Regular"
|
|
23308
|
+
},
|
|
23309
|
+
{
|
|
23310
|
+
value: "lg",
|
|
23311
|
+
label: "Large"
|
|
23312
|
+
},
|
|
23313
|
+
{
|
|
23314
|
+
value: "xl",
|
|
23315
|
+
label: "Huge"
|
|
23316
|
+
}
|
|
23317
|
+
];
|
|
23318
|
+
const defaultHeaderNavPreferences = {
|
|
23319
|
+
typography: "inherit",
|
|
23320
|
+
fontSize: "inherit",
|
|
23321
|
+
interactionPalette: "recommended"
|
|
23322
|
+
};
|
|
23323
|
+
function readHeaderNavPreferences(theme) {
|
|
23324
|
+
const typography = theme?.header?.navFontRole;
|
|
23325
|
+
const fontSize = theme?.header?.navTextSize;
|
|
23326
|
+
const interactionPalette = readHeaderInteractionPalettePreference(theme?.header?.interactionPalette);
|
|
23327
|
+
return typography === "body" || typography === "heading" ? {
|
|
23328
|
+
typography,
|
|
23329
|
+
fontSize: readHeaderNavFontSizePreference(fontSize),
|
|
23330
|
+
interactionPalette
|
|
23331
|
+
} : {
|
|
23332
|
+
...defaultHeaderNavPreferences,
|
|
23333
|
+
fontSize: readHeaderNavFontSizePreference(fontSize),
|
|
23334
|
+
interactionPalette
|
|
23335
|
+
};
|
|
23336
|
+
}
|
|
23337
|
+
/**
|
|
23338
|
+
* Closes over the palette vocabulary rather than trusting the stored value: a
|
|
23339
|
+
* persisted header is `jsonb` and can carry anything, and a bare `?? ` would
|
|
23340
|
+
* pass a malformed value straight through as if it were a preference.
|
|
23341
|
+
*/
|
|
23342
|
+
function readHeaderInteractionPalettePreference(value) {
|
|
23343
|
+
return headerInteractionPaletteValues.find((palette) => palette === value) ?? "recommended";
|
|
23344
|
+
}
|
|
23345
|
+
function headerNavPreferencesEqual(left, right) {
|
|
23346
|
+
return left.typography === right.typography && left.fontSize === right.fontSize && left.interactionPalette === right.interactionPalette;
|
|
23347
|
+
}
|
|
23348
|
+
function withHeaderNavThemePreferences(theme, preferences) {
|
|
23349
|
+
const { navFontRole: _navFontRole, navTextSize: _navTextSize, interactionPalette: _interactionPalette, ...headerWithoutPreference } = theme.header;
|
|
23350
|
+
const nextHeader = {
|
|
23351
|
+
...headerWithoutPreference,
|
|
23352
|
+
...preferences.typography === "inherit" ? {} : { navFontRole: preferences.typography },
|
|
23353
|
+
...preferences.fontSize === "inherit" ? {} : { navTextSize: preferences.fontSize },
|
|
23354
|
+
...preferences.interactionPalette === "recommended" ? {} : { interactionPalette: preferences.interactionPalette }
|
|
23355
|
+
};
|
|
23356
|
+
return {
|
|
23357
|
+
...theme,
|
|
23358
|
+
header: nextHeader
|
|
23359
|
+
};
|
|
23360
|
+
}
|
|
23361
|
+
/**
|
|
23362
|
+
* Applies the user's nav font-role/size overrides on top of a style-resolved
|
|
23363
|
+
* nav link style. This is where the precedence contract lands:
|
|
23364
|
+
*
|
|
23365
|
+
* - The nav typography TREATMENT (case/tracking identity: plain / heading /
|
|
23366
|
+
* quiet-caps / brand-caps) is ALWAYS owned by the active header style and is
|
|
23367
|
+
* baked into `style` before this call (see `applyNavTypography` in
|
|
23368
|
+
* headerLayoutStyles.ts).
|
|
23369
|
+
* - Each treatment resolves through a DEFAULT font role: `heading` resolves
|
|
23370
|
+
* through the heading role; `plain` / `quiet-caps` / `brand-caps` resolve
|
|
23371
|
+
* through the body role.
|
|
23372
|
+
* - The user's `navFontRole` (surfaced here as `preferences.typography`) only
|
|
23373
|
+
* overrides which font ROLE the treatment resolves through. It never changes
|
|
23374
|
+
* the treatment. `'inherit'` keeps the treatment's default role.
|
|
23375
|
+
*/
|
|
23376
|
+
function mergeHeaderNavTypographyPreference(style, preferences) {
|
|
23377
|
+
return preferences.typography === "inherit" && preferences.fontSize === "inherit" ? style : {
|
|
23378
|
+
...style,
|
|
23379
|
+
...preferences.typography === "inherit" ? {} : { typography: preferences.typography },
|
|
23380
|
+
...preferences.fontSize === "inherit" ? {} : { textSize: preferences.fontSize }
|
|
23381
|
+
};
|
|
23382
|
+
}
|
|
23383
|
+
function readHeaderNavFontSizePreference(value) {
|
|
23384
|
+
return value === "sm" || value === "base" || value === "lg" || value === "xl" ? value : "inherit";
|
|
23385
|
+
}
|
|
23386
|
+
/**
|
|
23387
|
+
* The nav size a header actually renders at, from either of the two places it
|
|
23388
|
+
* can be written.
|
|
23389
|
+
*
|
|
23390
|
+
* `generateHeaderCss` merges `header.navTextSize` over `navLinkStyle.textSize`
|
|
23391
|
+
* at render time (`mergeHeaderNavTypographyPreference`), so the marker field
|
|
23392
|
+
* wins when it is present and the compiled nav link style supplies the size
|
|
23393
|
+
* otherwise. Reading only the marker — which is what
|
|
23394
|
+
* `readHeaderNavPreferences` does, and correctly, because it answers "what does
|
|
23395
|
+
* the control show" — misses a size that reached the row through the compiled
|
|
23396
|
+
* bytes instead.
|
|
23397
|
+
*/
|
|
23398
|
+
function headerNavEffectiveFontSize(header) {
|
|
23399
|
+
const marker = readHeaderNavFontSizePreference(header.navTextSize);
|
|
23400
|
+
return marker === "inherit" ? readHeaderNavFontSizePreference(header.navLinkStyle?.textSize) : marker;
|
|
23401
|
+
}
|
|
23402
|
+
/**
|
|
23403
|
+
* `navTextSize` derived as a preference by baseline comparison (#1318 Phase 4c
|
|
23404
|
+
* decision 1) — the same rule the structure fields already use: compare the
|
|
23405
|
+
* persisted value against what the row's own (layout, recipe) compiles, emit
|
|
23406
|
+
* `'inherit'` when they agree and the explicit value when they do not.
|
|
23407
|
+
*
|
|
23408
|
+
* The reason it needs the rule at all is that nav size is one of the fields a
|
|
23409
|
+
* pre-preferences row could only express by carrying it in the compiled bytes:
|
|
23410
|
+
* `heart-led-yoga` and `therese-millar-yoga` persist `navLinkStyle.textSize:
|
|
23411
|
+
* 'lg'` under a `serif-line` recipe that compiles no size, which is a user's
|
|
23412
|
+
* choice sitting in the wrong place. Reading only `header.navTextSize` derives
|
|
23413
|
+
* `'inherit'` there and a recompile would shrink their nav.
|
|
23414
|
+
*
|
|
23415
|
+
* Backfilling the derived value is pixel-preserving because the CSS merge above
|
|
23416
|
+
* makes `{navTextSize: 'lg'}` over a size-less nav link style render exactly
|
|
23417
|
+
* what `{navLinkStyle: {textSize: 'lg'}}` renders.
|
|
23418
|
+
*
|
|
23419
|
+
* **Two cases lose information, and both are accepted rather than accidental.**
|
|
23420
|
+
* The preference vocabulary is `inherit | sm | base | lg | xl` — there is no
|
|
23421
|
+
* value meaning "no size at all" — so `'inherit'` has to carry both of them:
|
|
23422
|
+
*
|
|
23423
|
+
* 1. *Persisted size equals the baseline.* Nothing in the bytes distinguishes
|
|
23424
|
+
* "chose the size the recipe already compiles" from "never chose", so the
|
|
23425
|
+
* marker field is dropped on recompile. **Pixel-neutral**: the recipe puts
|
|
23426
|
+
* the same size back.
|
|
23427
|
+
* 2. *Persisted has no size and the baseline compiles one.* The row renders at
|
|
23428
|
+
* the browser default and the recipe wants, say, `'sm'`; `'inherit'` is the
|
|
23429
|
+
* only representable answer, so **the recompile adopts the baseline size and
|
|
23430
|
+
* the row's sizeless-ness is lost**. This one is *not* pixel-neutral, and
|
|
23431
|
+
* representing it properly would mean adding a `'none'` arm to the
|
|
23432
|
+
* preference the way `HeaderStructurePreferences.logoText` has one.
|
|
23433
|
+
*
|
|
23434
|
+
* Case 2 is not silent. Adopting the baseline size is a byte change, so such a
|
|
23435
|
+
* row shows up as divergent in the Phase 4c report (`navLinkStyle.textSize
|
|
23436
|
+
* undefined -> "sm"`) and cannot reach Phase 4e unclassified — the report's
|
|
23437
|
+
* zero-rows gate is the safety net for exactly this. No row in either corpus is
|
|
23438
|
+
* in that state today; if one appears, it is a classification decision (widen
|
|
23439
|
+
* the vocabulary, or accept the size) rather than something this function may
|
|
23440
|
+
* guess at.
|
|
23441
|
+
*/
|
|
23442
|
+
function readHeaderNavFontSizeAgainstBaseline(persisted, compiled) {
|
|
23443
|
+
const size = headerNavEffectiveFontSize(persisted);
|
|
23444
|
+
return size === headerNavEffectiveFontSize(compiled) ? "inherit" : size;
|
|
23445
|
+
}
|
|
23446
|
+
|
|
23447
|
+
//#endregion
|
|
23448
|
+
//#region ../theme-core/src/site-styles/headerBrandPresentation.ts
|
|
23449
|
+
/**
|
|
23450
|
+
* Whether the header wears a brand presentation at all (#1318 Phase 4c).
|
|
23451
|
+
*
|
|
23452
|
+
* `applyHeaderBrandPresentation` is unconditional today: given a logo context
|
|
23453
|
+
* it always writes a `logo` object carrying a resolved display, size and
|
|
23454
|
+
* accommodation. That makes "this header has no brand presentation block" —
|
|
23455
|
+
* which is what several live sites persist, including one whose site *does*
|
|
23456
|
+
* have a logo asset — a state the preference record could not express, so a
|
|
23457
|
+
* recompile would silently give the site a logo treatment it has never
|
|
23458
|
+
* rendered.
|
|
23459
|
+
*
|
|
23460
|
+
* This is the third field in this phase with that shape, after the interaction
|
|
23461
|
+
* palette and the nav text size: the persisted bytes carry intent the reader
|
|
23462
|
+
* cannot say, so the recompile "corrects" it. The fix is the same one — widen
|
|
23463
|
+
* the closed vocabulary with a named member and derive it by baseline
|
|
23464
|
+
* comparison — and it is a *widening*, not a restyle: `'recommended'` is the
|
|
23465
|
+
* default and reproduces today's behaviour exactly, so no row whose bytes carry
|
|
23466
|
+
* a presentation compiles differently for `'plain'` existing.
|
|
23467
|
+
*
|
|
23468
|
+
* `'plain'` is deliberately not "preserve whatever bytes are there". It is a
|
|
23469
|
+
* constructive instruction — *write no presentation block* — so Phase 4e's
|
|
23470
|
+
* compiler, which builds the header from nothing, can produce it.
|
|
23471
|
+
*/
|
|
23472
|
+
const headerBrandPresentationValues = ["recommended", "plain"];
|
|
23473
|
+
const defaultHeaderBrandPresentation = "recommended";
|
|
23474
|
+
/**
|
|
23475
|
+
* Key-order-safe structural equality over the persisted `logo` object.
|
|
23476
|
+
*
|
|
23477
|
+
* The persisted value comes back from `jsonb`, whose key order is not the order
|
|
23478
|
+
* the applier builds; a `JSON.stringify` comparison would read two equal logo
|
|
23479
|
+
* blocks as different and quietly classify a `'recommended'` row as
|
|
23480
|
+
* unrecognised — the same trap the interaction-palette transcription test
|
|
23481
|
+
* exists to catch.
|
|
23482
|
+
*/
|
|
23483
|
+
function jsonEquals(left, right) {
|
|
23484
|
+
if (left === right) return true;
|
|
23485
|
+
if (left === null || right === null) return false;
|
|
23486
|
+
if (typeof left !== "object" || typeof right !== "object") return false;
|
|
23487
|
+
if (Array.isArray(left) !== Array.isArray(right)) return false;
|
|
23488
|
+
if (Array.isArray(left) && Array.isArray(right)) {
|
|
23489
|
+
return left.length === right.length && left.every((value, index) => jsonEquals(value, right[index]));
|
|
23490
|
+
}
|
|
23491
|
+
const leftRecord = left;
|
|
23492
|
+
const rightRecord = right;
|
|
23493
|
+
const keys = new Set([...Object.keys(leftRecord), ...Object.keys(rightRecord)]);
|
|
23494
|
+
for (const key of keys) {
|
|
23495
|
+
if (!jsonEquals(leftRecord[key], rightRecord[key])) return false;
|
|
23496
|
+
}
|
|
23497
|
+
return true;
|
|
23498
|
+
}
|
|
23499
|
+
/**
|
|
23500
|
+
* The two keys the brand appliers write with a documented `'auto'` default, so
|
|
23501
|
+
* a persisted block that omits them means the same thing as one that spells
|
|
23502
|
+
* them out. This is the "implicit brand defaults made explicit" class the 4a
|
|
23503
|
+
* sweep pinned; treating it as a difference here would classify two live rows
|
|
23504
|
+
* as unrecognised over a value nobody chose and nothing renders differently.
|
|
23505
|
+
*/
|
|
23506
|
+
function withImplicitBrandDefaults(logo) {
|
|
23507
|
+
return {
|
|
23508
|
+
...logo,
|
|
23509
|
+
brandDisplay: logo.brandDisplay ?? "auto",
|
|
23510
|
+
size: logo.size ?? "auto"
|
|
23511
|
+
};
|
|
23512
|
+
}
|
|
23513
|
+
/**
|
|
23514
|
+
* Equal modulo the implicit brand defaults, and `null`-tolerant on both sides:
|
|
23515
|
+
* an absent block and an absent block are the same answer.
|
|
23516
|
+
*/
|
|
23517
|
+
function logoBytesEqual(left, right) {
|
|
23518
|
+
const leftAbsent = left === undefined || left === null;
|
|
23519
|
+
const rightAbsent = right === undefined || right === null;
|
|
23520
|
+
if (leftAbsent || rightAbsent) return leftAbsent && rightAbsent;
|
|
23521
|
+
return jsonEquals(withImplicitBrandDefaults(left), withImplicitBrandDefaults(right));
|
|
23522
|
+
}
|
|
23523
|
+
/**
|
|
23524
|
+
* Which mode reproduces this row's persisted logo bytes.
|
|
23525
|
+
*
|
|
23526
|
+
* Both candidates are supplied rather than reconstructed here, because both are
|
|
23527
|
+
* the compilation's own output and rebuilding either would be a second
|
|
23528
|
+
* authority on what the modes mean. `'recommended'` is tested first so that a
|
|
23529
|
+
* row the resolver already agrees with keeps the live mode; `'plain'` is the
|
|
23530
|
+
* answer for the rows that carry no presentation — an absent block, or a block
|
|
23531
|
+
* holding only the recipe's own brand typography.
|
|
23532
|
+
*/
|
|
23533
|
+
function classifyHeaderBrandPresentationBytes(persisted, recommended, plain) {
|
|
23534
|
+
if (logoBytesEqual(persisted.logo, recommended.logo)) {
|
|
23535
|
+
return { kind: "recommended" };
|
|
23536
|
+
}
|
|
23537
|
+
if (logoBytesEqual(persisted.logo, plain.logo)) return { kind: "plain" };
|
|
23538
|
+
return { kind: "unrecognised" };
|
|
23539
|
+
}
|
|
23540
|
+
/**
|
|
23541
|
+
* The preference the bytes express, or `null` when they express none the record
|
|
23542
|
+
* can carry. `null` is the caller's cue to fall back to the default and say so.
|
|
23543
|
+
*/
|
|
23544
|
+
function readHeaderBrandPresentationFromBytes(persisted, recommended, plain) {
|
|
23545
|
+
const classification = classifyHeaderBrandPresentationBytes(persisted, recommended, plain);
|
|
23546
|
+
return classification.kind === "unrecognised" ? null : classification.kind;
|
|
23547
|
+
}
|
|
23548
|
+
|
|
23549
|
+
//#endregion
|
|
23550
|
+
//#region ../theme-core/src/site-styles/headerPreferences.ts
|
|
23551
|
+
const defaultHeaderStructurePreferences = {
|
|
23552
|
+
positioning: "inherit",
|
|
23553
|
+
shrinkOnScroll: "inherit",
|
|
23554
|
+
logoText: "inherit"
|
|
23555
|
+
};
|
|
23556
|
+
const defaultHeaderPreferences = {
|
|
23557
|
+
brandPresentation: defaultHeaderBrandPresentation,
|
|
23558
|
+
brandDisplay: defaultHeaderBrandPreferences.brandDisplay,
|
|
23559
|
+
logoSize: defaultHeaderBrandPreferences.logoSize,
|
|
23560
|
+
navFontRole: defaultHeaderNavPreferences.typography,
|
|
23561
|
+
navTextSize: defaultHeaderNavPreferences.fontSize,
|
|
23562
|
+
interactionPalette: defaultHeaderNavPreferences.interactionPalette,
|
|
23563
|
+
background: { kind: "default" },
|
|
23564
|
+
structure: defaultHeaderStructurePreferences
|
|
23565
|
+
};
|
|
23566
|
+
/**
|
|
23567
|
+
* Field-wise rather than `JSON.stringify`: the persisted value comes back from
|
|
23568
|
+
* `jsonb` and the compiled one is built by the interpreter, so their key order
|
|
23569
|
+
* is not guaranteed to match and a string comparison would report two equal
|
|
23570
|
+
* records as different.
|
|
23571
|
+
*
|
|
23572
|
+
* The destructure is exhaustive on purpose — adding a field to
|
|
23573
|
+
* `headerLogoTextSchema` without deciding whether it participates in this
|
|
23574
|
+
* comparison breaks the build here rather than silently collapsing two
|
|
23575
|
+
* different records to `'inherit'`.
|
|
23576
|
+
*
|
|
23577
|
+
* Note the asymmetry this leaves at the edge: a persisted `{}` against a
|
|
23578
|
+
* compiled `undefined` is *not* equal, so it derives to an explicit `{}`
|
|
23579
|
+
* rather than to `'none'`. That is deliberate — the row does carry a logo-text
|
|
23580
|
+
* record, it just carries an empty one — but it means `'none'` means "no
|
|
23581
|
+
* record at all", never "a record with nothing in it".
|
|
23582
|
+
*/
|
|
23583
|
+
function headerLogoTextEquals(left, right) {
|
|
23584
|
+
if (left === undefined || right === undefined) return left === right;
|
|
23585
|
+
const comparisons = {
|
|
23586
|
+
mobileWrapLines: left.mobileWrapLines === right.mobileWrapLines,
|
|
23587
|
+
mobileMaxWidth: left.mobileMaxWidth === right.mobileMaxWidth,
|
|
23588
|
+
hideOnShrink: left.hideOnShrink === right.hideOnShrink
|
|
23589
|
+
};
|
|
23590
|
+
return Object.values(comparisons).every((equal) => equal);
|
|
23591
|
+
}
|
|
23592
|
+
/**
|
|
23593
|
+
* Brand presentation runs *after* the structure preferences and forces
|
|
23594
|
+
* `shrinkOnScroll: true` for a display-size logo on a backing accommodation
|
|
23595
|
+
* over a non-static header (`applyHeaderBrandPresentation` below). On such a
|
|
23596
|
+
* row the persisted `true` therefore carries no intent — it would be written
|
|
23597
|
+
* whatever the user had chosen — so the field is not derivable there and
|
|
23598
|
+
* collapses to `'inherit'`.
|
|
23599
|
+
*
|
|
23600
|
+
* This reads the row's own persisted logo rather than a live logo context, so
|
|
23601
|
+
* it holds for callers that have no context to supply (the divergence report,
|
|
23602
|
+
* and 4c's backfill). A caller that *does* supply the presentation gets the
|
|
23603
|
+
* same answer through the baseline, which already carries the forced value.
|
|
23604
|
+
*/
|
|
23605
|
+
function headerBrandPresentationForcesShrink(header) {
|
|
23606
|
+
const accommodation = header.logo?.accommodation;
|
|
23607
|
+
return header.logo?.size === "display" && accommodation !== undefined && logoAccommodationProvidesBacking(accommodation) && header.positioning !== "static";
|
|
23608
|
+
}
|
|
23609
|
+
/**
|
|
23610
|
+
* The three structure fields are read back out of `jsonb`, so each one is
|
|
23611
|
+
* validated against its own vocabulary rather than trusted. A malformed value
|
|
23612
|
+
* expresses no intent, so it derives to `'inherit'` — never to a default that
|
|
23613
|
+
* would then be persisted as if the user had chosen it.
|
|
23614
|
+
*/
|
|
23615
|
+
function readPersistedPositioning(value) {
|
|
23616
|
+
return headerPositioningValues.find((candidate$1) => candidate$1 === value) ?? null;
|
|
23617
|
+
}
|
|
23618
|
+
function readPersistedShrinkOnScroll(value) {
|
|
23619
|
+
return typeof value === "boolean" ? value : null;
|
|
23620
|
+
}
|
|
23621
|
+
/** "Present, but not a value this field's vocabulary accepts." */
|
|
23622
|
+
const invalidField = Symbol("invalid persisted field");
|
|
23623
|
+
const logoTextWrapLines = [1, 2];
|
|
23624
|
+
const logoTextMobileMaxWidths = [
|
|
23625
|
+
"xs",
|
|
23626
|
+
"sm",
|
|
23627
|
+
"md"
|
|
23628
|
+
];
|
|
23629
|
+
function readOptionalMember(value, allowed) {
|
|
23630
|
+
if (value === undefined) return undefined;
|
|
23631
|
+
return allowed.find((candidate$1) => candidate$1 === value) ?? invalidField;
|
|
23632
|
+
}
|
|
23633
|
+
function readOptionalBoolean(value) {
|
|
23634
|
+
if (value === undefined) return undefined;
|
|
23635
|
+
return typeof value === "boolean" ? value : invalidField;
|
|
23636
|
+
}
|
|
23637
|
+
function readPersistedLogoText(value) {
|
|
23638
|
+
if (value === undefined) return { kind: "absent" };
|
|
23639
|
+
if (typeof value !== "object" || value === null) return { kind: "invalid" };
|
|
23640
|
+
const wrapLines = readOptionalMember(value.mobileWrapLines, logoTextWrapLines);
|
|
23641
|
+
const maxWidth = readOptionalMember(value.mobileMaxWidth, logoTextMobileMaxWidths);
|
|
23642
|
+
const hide = readOptionalBoolean(value.hideOnShrink);
|
|
23643
|
+
if (wrapLines === invalidField || maxWidth === invalidField || hide === invalidField) {
|
|
23644
|
+
return { kind: "invalid" };
|
|
23645
|
+
}
|
|
23646
|
+
return {
|
|
23647
|
+
kind: "present",
|
|
23648
|
+
value: {
|
|
23649
|
+
...wrapLines === undefined ? {} : { mobileWrapLines: wrapLines },
|
|
23650
|
+
...maxWidth === undefined ? {} : { mobileMaxWidth: maxWidth },
|
|
23651
|
+
...hide === undefined ? {} : { hideOnShrink: hide }
|
|
23652
|
+
}
|
|
23653
|
+
};
|
|
23654
|
+
}
|
|
23655
|
+
function deriveLogoTextPreference(persisted, compiled) {
|
|
23656
|
+
const read = readPersistedLogoText(persisted);
|
|
23657
|
+
switch (read.kind) {
|
|
23658
|
+
case "invalid": return "inherit";
|
|
23659
|
+
case "absent": return compiled === undefined ? "inherit" : "none";
|
|
23660
|
+
case "present": return headerLogoTextEquals(read.value, compiled) ? "inherit" : read.value;
|
|
23661
|
+
}
|
|
23662
|
+
}
|
|
23663
|
+
/**
|
|
23664
|
+
* The written-down backfill rule (#1318 Phase 4a): compare the persisted value
|
|
23665
|
+
* against the value the row's own (layout, recipe) compiles, and emit
|
|
23666
|
+
* `'inherit'` when they are equal, the explicit value when they are not.
|
|
23667
|
+
*
|
|
23668
|
+
* Equal-to-default therefore collapses to `'inherit'`. Nothing in the data
|
|
23669
|
+
* distinguishes a deliberate "sticky, same as the default" from an untouched
|
|
23670
|
+
* row, so the backfill does not pretend to recover it; going forward the editor
|
|
23671
|
+
* records what the user picks and the distinction is real for new intent.
|
|
23672
|
+
*
|
|
23673
|
+
* Two qualifications on "the value it compiles", both of which keep the rule
|
|
23674
|
+
* from inventing intent:
|
|
23675
|
+
*
|
|
23676
|
+
* 1. A value that no vocabulary accepts expresses nothing, so it derives to
|
|
23677
|
+
* `'inherit'` rather than to whatever a fallback would have picked.
|
|
23678
|
+
* 2. A field a later pipeline step overwrites unconditionally cannot carry
|
|
23679
|
+
* intent at all. That is `shrinkOnScroll` on a display-logo row — see
|
|
23680
|
+
* `headerBrandPresentationForcesShrink`.
|
|
23681
|
+
*/
|
|
23682
|
+
function readHeaderStructurePreferences(persisted, compiled) {
|
|
23683
|
+
const positioning = readPersistedPositioning(persisted.positioning);
|
|
23684
|
+
const shrinkOnScroll = readPersistedShrinkOnScroll(persisted.shrinkOnScroll);
|
|
23685
|
+
return {
|
|
23686
|
+
positioning: positioning === null || positioning === compiled.positioning ? "inherit" : positioning,
|
|
23687
|
+
shrinkOnScroll: shrinkOnScroll === null || shrinkOnScroll === compiled.shrinkOnScroll || headerBrandPresentationForcesShrink(persisted) ? "inherit" : shrinkOnScroll,
|
|
23688
|
+
logoText: deriveLogoTextPreference(persisted.logoText, compiled.logoText)
|
|
23689
|
+
};
|
|
23690
|
+
}
|
|
23691
|
+
/**
|
|
23692
|
+
* Total over any persisted theme, including a malformed one: every field goes
|
|
23693
|
+
* through a reader that closes over the vocabulary rather than trusting the
|
|
23694
|
+
* stored value.
|
|
23695
|
+
*
|
|
23696
|
+
* `compiledHeader` is the structure baseline — the header this row's own
|
|
23697
|
+
* (layout, recipe) compiles. It is a required parameter rather than something
|
|
23698
|
+
* derived inside, because a `Theme` alone does not carry the selection ids the
|
|
23699
|
+
* baseline needs; asking the caller for it keeps the rule single-valued.
|
|
23700
|
+
*
|
|
23701
|
+
* `navTextSize` is derived against that same baseline rather than read off
|
|
23702
|
+
* `header.navTextSize` (#1318 Phase 4c decision 1). It is the one nav field a
|
|
23703
|
+
* pre-preferences row could express only through the compiled bytes, so
|
|
23704
|
+
* reading the marker alone loses it — see
|
|
23705
|
+
* `readHeaderNavFontSizeAgainstBaseline`. `navFontRole` stays a marker read:
|
|
23706
|
+
* unlike size, `navLinkStyle.typography` is written by the recipe's own
|
|
23707
|
+
* treatment on every row, so a baseline comparison there would report the
|
|
23708
|
+
* treatment's default role as user intent.
|
|
23709
|
+
*
|
|
23710
|
+
* `interactionPaletteGroup` is the palette the row's resolved recipe offers,
|
|
23711
|
+
* and the same reasoning applies to it (#1318 Phase 4c decision 2): a row that
|
|
23712
|
+
* predates the palette carries its hover treatment in the compiled bytes and
|
|
23713
|
+
* nothing in `header.interactionPalette`. `undefined` means the caller does not
|
|
23714
|
+
* know which palette applies — it is not "the style has none" — and the marker
|
|
23715
|
+
* read stands.
|
|
23716
|
+
*
|
|
23717
|
+
* `brandPresentationCandidates` are the two headers the two presentation modes
|
|
23718
|
+
* produce for this row. The mode is derived by comparing the persisted `logo`
|
|
23719
|
+
* against both, so a row that carries no presentation block keeps having none
|
|
23720
|
+
* instead of being given one it has never rendered (#1318 Phase 4c decision
|
|
23721
|
+
* 5b). They are supplied rather than rebuilt here because they are the
|
|
23722
|
+
* compilation's own output, and a second construction of them would be a second
|
|
23723
|
+
* authority on what the modes mean — `headerBrandPresentationCandidates` builds
|
|
23724
|
+
* them with the real appliers.
|
|
23725
|
+
*/
|
|
23726
|
+
function readHeaderPreferences(theme, compiledHeader, interactionPaletteGroup, brandPresentationCandidates) {
|
|
23727
|
+
const nav = readHeaderNavPreferences(theme);
|
|
23728
|
+
const brand = headerBrandPreferencesFromTheme(theme);
|
|
23729
|
+
const brandPresentation = readHeaderBrandPresentationFromBytes(theme.header, brandPresentationCandidates.recommended, brandPresentationCandidates.plain) ?? defaultHeaderBrandPresentation;
|
|
23730
|
+
return {
|
|
23731
|
+
brandPresentation,
|
|
23732
|
+
brandDisplay: brand.brandDisplay,
|
|
23733
|
+
logoSize: brand.logoSize,
|
|
23734
|
+
navFontRole: nav.typography,
|
|
23735
|
+
navTextSize: readHeaderNavFontSizeAgainstBaseline(theme.header, compiledHeader),
|
|
23736
|
+
interactionPalette: readHeaderInteractionPaletteFromBytes(theme.header, interactionPaletteGroup) ?? nav.interactionPalette,
|
|
23737
|
+
background: siteChromeBackgroundOverrideFromHeader(theme.header),
|
|
23738
|
+
structure: readHeaderStructurePreferences(theme.header, brandPresentationCandidates[brandPresentation])
|
|
23739
|
+
};
|
|
23740
|
+
}
|
|
23741
|
+
/**
|
|
23742
|
+
* Both candidates, built with the same two appliers the pipeline uses.
|
|
23743
|
+
*
|
|
23744
|
+
* There is no circularity to resolve: the only record fields that reach
|
|
23745
|
+
* `header.logo` are `brandDisplay` and `logoSize`, and those are read straight
|
|
23746
|
+
* off the persisted theme rather than derived. The structure preferences run
|
|
23747
|
+
* before the presentation in the named order but touch `positioning`,
|
|
23748
|
+
* `shrinkOnScroll` and `logoText` — never `logo` — so leaving them out here
|
|
23749
|
+
* cannot change either answer.
|
|
23750
|
+
*
|
|
23751
|
+
* One known over-specification, recorded rather than fixed because it changes
|
|
23752
|
+
* no bytes. `applyHeaderBrandPresentation` reads `positioning` when it decides
|
|
23753
|
+
* whether to force `shrinkOnScroll` for a display logo, and it sees the
|
|
23754
|
+
* *structure-adjusted* value at apply time while it sees the layout default
|
|
23755
|
+
* here. On a `'recommended'` row that overrides positioning to `'static'` the
|
|
23756
|
+
* candidate can therefore carry a forced `shrinkOnScroll` the applied header
|
|
23757
|
+
* will not, which makes `readHeaderStructurePreferences` emit an explicit
|
|
23758
|
+
* `shrinkOnScroll` where `'inherit'` would have done. The record is more
|
|
23759
|
+
* specific than it needs to be; the compiled bytes are identical either way,
|
|
23760
|
+
* because the explicit value it writes is the one the row already has. Fixing
|
|
23761
|
+
* it means deriving `positioning` before the candidates, which is a second
|
|
23762
|
+
* derivation pass for no byte change (#1318 Phase 4c review round 2, item 10).
|
|
23763
|
+
*/
|
|
23764
|
+
function headerBrandPresentationCandidates(theme, compiledBaseline, presentation) {
|
|
23765
|
+
const brand = headerBrandPreferencesFromTheme(theme);
|
|
23766
|
+
const withBrand = (header) => withHeaderBrandThemePreferences({
|
|
23767
|
+
...theme,
|
|
23768
|
+
header
|
|
23769
|
+
}, brand).header;
|
|
23770
|
+
return {
|
|
23771
|
+
recommended: withBrand(presentation === null ? compiledBaseline : applyHeaderBrandPresentation(compiledBaseline, presentation)),
|
|
23772
|
+
plain: withBrand(compiledBaseline)
|
|
23773
|
+
};
|
|
23774
|
+
}
|
|
23775
|
+
function applyHeaderStructurePreferences(header, preferences) {
|
|
23776
|
+
const { logoText: currentLogoText, ...headerWithoutLogoText } = header;
|
|
23777
|
+
return {
|
|
23778
|
+
...headerWithoutLogoText,
|
|
23779
|
+
...preferences.positioning === "inherit" ? {} : { positioning: preferences.positioning },
|
|
23780
|
+
...preferences.shrinkOnScroll === "inherit" ? {} : { shrinkOnScroll: preferences.shrinkOnScroll },
|
|
23781
|
+
...logoTextField(currentLogoText, preferences.logoText)
|
|
23782
|
+
};
|
|
23783
|
+
}
|
|
23784
|
+
function logoTextField(current, preference) {
|
|
23785
|
+
if (preference === "inherit") {
|
|
23786
|
+
return current === undefined ? {} : { logoText: current };
|
|
23787
|
+
}
|
|
23788
|
+
if (preference === "none") return {};
|
|
23789
|
+
return { logoText: preference };
|
|
23790
|
+
}
|
|
23791
|
+
/**
|
|
23792
|
+
* The brand presentation step, which sits between the structure preferences and
|
|
23793
|
+
* the nav/logo/background ones because it reads `positioning` and can force
|
|
23794
|
+
* `shrinkOnScroll` for a display logo (`previewCompiler.ts`).
|
|
23795
|
+
*/
|
|
23796
|
+
/**
|
|
23797
|
+
* Writes a resolved brand presentation onto the header. Lives here rather than
|
|
23798
|
+
* in `previewCompiler` so the pipeline step has one definition: it sits between
|
|
23799
|
+
* the structure preferences and the nav/logo/background ones, and it is the
|
|
23800
|
+
* reason that position is load-bearing — it reads `positioning` and can force
|
|
23801
|
+
* `shrinkOnScroll` for a display logo that brings its own backing.
|
|
23802
|
+
*/
|
|
23803
|
+
function applyHeaderBrandPresentation(header, presentation) {
|
|
23804
|
+
const shouldShrinkDisplayLogo = presentation.size === "display" && presentation.accommodation.providesBacking === true && header.positioning !== "static";
|
|
23805
|
+
return {
|
|
23806
|
+
...header,
|
|
23807
|
+
shrinkOnScroll: shouldShrinkDisplayLogo ? true : header.shrinkOnScroll,
|
|
23808
|
+
logo: {
|
|
23809
|
+
fontFamily: header.logo?.fontFamily ?? "heading",
|
|
23810
|
+
letterSpacing: header.logo?.letterSpacing ?? "normal",
|
|
23811
|
+
gradient: header.logo?.gradient ?? false,
|
|
23812
|
+
brandDisplay: presentation.display,
|
|
23813
|
+
size: presentation.size,
|
|
23814
|
+
accommodation: presentation.accommodation.pattern
|
|
23815
|
+
}
|
|
23816
|
+
};
|
|
23817
|
+
}
|
|
23818
|
+
/**
|
|
23819
|
+
* The preference half of the header pipeline, composed once so that no caller
|
|
23820
|
+
* assembles its own order. Named order, tail of the recompile:
|
|
23821
|
+
*
|
|
23822
|
+
* structure preferences → brand presentation → brand display/logo size
|
|
23823
|
+
* → nav preferences → header background override
|
|
23824
|
+
*
|
|
23825
|
+
* `brandPresentation` is data rather than a callback so the step is inspectable
|
|
23826
|
+
* and cannot vary by caller; `null` is "this caller has no logo context".
|
|
23827
|
+
*
|
|
23828
|
+
* The background applier is the header-only one on purpose: the header+footer
|
|
23829
|
+
* pair function would clobber footer intent (#1318 Correction 20).
|
|
23830
|
+
*/
|
|
23831
|
+
function applyHeaderPreferences(theme, preferences, brandPresentation) {
|
|
23832
|
+
const structured = applyHeaderStructurePreferences(theme.header, preferences.structure);
|
|
23833
|
+
const presented = brandPresentation === null || preferences.brandPresentation === "plain" ? structured : applyHeaderBrandPresentation(structured, brandPresentation);
|
|
23834
|
+
const branded = withHeaderBrandThemePreferences({
|
|
23835
|
+
...theme,
|
|
23836
|
+
header: presented
|
|
23837
|
+
}, {
|
|
23838
|
+
brandDisplay: preferences.brandDisplay,
|
|
23839
|
+
logoSize: preferences.logoSize
|
|
23840
|
+
});
|
|
23841
|
+
const navved = withHeaderNavThemePreferences(branded, {
|
|
23842
|
+
typography: preferences.navFontRole,
|
|
23843
|
+
fontSize: preferences.navTextSize,
|
|
23844
|
+
interactionPalette: preferences.interactionPalette
|
|
23845
|
+
});
|
|
23846
|
+
return {
|
|
23847
|
+
...navved,
|
|
23848
|
+
header: applyHeaderBackgroundOverride(navved, navved.header, preferences.background)
|
|
23849
|
+
};
|
|
23850
|
+
}
|
|
23851
|
+
|
|
23852
|
+
//#endregion
|
|
23853
|
+
//#region ../theme-core/src/site-styles/headerRecompile.ts
|
|
23854
|
+
/**
|
|
23855
|
+
* Curated repair followed by catalogue repair — the first two steps of the
|
|
23856
|
+
* header compilation pipeline, in one place so no caller re-derives them.
|
|
23857
|
+
*/
|
|
23858
|
+
function resolveCuratedHeaderSelection(siteStyle, requested) {
|
|
23859
|
+
const requestedLayoutChoices = headerStyleChoicesForLayout(siteStyle, requested.layoutId);
|
|
23860
|
+
const curatedDefault = defaultHeaderCurationForSiteStyle(siteStyle);
|
|
23861
|
+
const resolvedLayoutId = requestedLayoutChoices ? requested.layoutId : curatedDefault.selection.layoutId;
|
|
23862
|
+
const curatedChoices = requestedLayoutChoices ?? curatedDefault.styles;
|
|
23863
|
+
const resolution = resolveHeaderLayoutStyleSelection({
|
|
23864
|
+
layoutId: resolvedLayoutId,
|
|
23865
|
+
styleId: deriveNearestCompatibleHeaderStyle(requested.styleId, resolvedLayoutId, curatedChoices.map((choice) => choice.id))
|
|
23866
|
+
});
|
|
23867
|
+
return {
|
|
23868
|
+
resolution,
|
|
23869
|
+
repair: resolution.selection.layoutId === requested.layoutId && resolution.selection.styleId === requested.styleId ? { kind: "exact" } : {
|
|
23870
|
+
kind: "repaired",
|
|
23871
|
+
reason: requestedLayoutChoices ? "style-not-curated-for-layout" : "layout-not-curated-for-site-style"
|
|
23872
|
+
}
|
|
23873
|
+
};
|
|
23874
|
+
}
|
|
23875
|
+
/**
|
|
23876
|
+
* Curated repair → catalogue repair → recipe → interaction palette → brand
|
|
23877
|
+
* presentation. Everything that runs before the preference record, in one
|
|
23878
|
+
* place: `previewCompiler.compileHeaderDirective` and the recompile below are
|
|
23879
|
+
* both callers, so the middle of the pipeline has a single composition.
|
|
23880
|
+
*
|
|
23881
|
+
* `baseline` and `header` are both returned because they answer different
|
|
23882
|
+
* questions. `header` is what a preview wants to render. `baseline` is what
|
|
23883
|
+
* `readHeaderPreferences` compares the persisted bytes against for every field
|
|
23884
|
+
* the presentation does not touch.
|
|
23885
|
+
*
|
|
23886
|
+
* The interaction-palette preference is threaded in through `theme.header`
|
|
23887
|
+
* because that is where `compileHeaderLayoutStyleSelection` reads it from
|
|
23888
|
+
* today; 4b can pass it explicitly once the preferences column exists.
|
|
23889
|
+
*/
|
|
23890
|
+
function compileHeaderForCuratedSelection(theme, siteStyle, requested, requestedBrandPresentation, requestedInteractionPalette) {
|
|
23891
|
+
const { resolution, repair: repair$1 } = resolveCuratedHeaderSelection(siteStyle, requested);
|
|
23892
|
+
const interactionPalette = resolveHeaderStyleRecipe(siteStyle, resolution.style.id).interactionPalette;
|
|
23893
|
+
const baseline = compileHeaderLayoutStyleSelection(theme, resolution, siteStyle, resolveInteractionPaletteRequest(theme, interactionPalette, requestedInteractionPalette));
|
|
23894
|
+
const brandPresentation = requestedBrandPresentation.kind === "apply-logo-context" ? resolveHeaderBrandPresentationForLayout({
|
|
23895
|
+
layout: resolution.layout,
|
|
23896
|
+
logo: requestedBrandPresentation.logo
|
|
23897
|
+
}) : null;
|
|
23898
|
+
return {
|
|
23899
|
+
baseline,
|
|
23900
|
+
header: brandPresentation === null ? baseline : applyHeaderBrandPresentation(baseline, brandPresentation),
|
|
23901
|
+
brandPresentation,
|
|
23902
|
+
interactionPalette,
|
|
23903
|
+
resolution,
|
|
23904
|
+
repair: repair$1
|
|
23905
|
+
};
|
|
23906
|
+
}
|
|
23907
|
+
/**
|
|
23908
|
+
* The bytes-derived answer wins when the recipe offers a palette at all;
|
|
23909
|
+
* otherwise the marker field on the theme stands, which is what every caller
|
|
23910
|
+
* that holds no preference record has always used.
|
|
23911
|
+
*/
|
|
23912
|
+
function resolveInteractionPaletteRequest(theme, group, request) {
|
|
23913
|
+
if (request.kind === "preference") return request.preference;
|
|
23914
|
+
return readHeaderInteractionPaletteFromBytes(theme.header, group) ?? readHeaderNavPreferences(theme).interactionPalette;
|
|
23915
|
+
}
|
|
23916
|
+
/**
|
|
23917
|
+
* The whole header pipeline, in one named function so 4b, 4c and 4d cannot
|
|
23918
|
+
* drift into three orders (#1318 Phase 4):
|
|
23919
|
+
*
|
|
23920
|
+
* curated repair → catalogue repair → recipe → layout defaults
|
|
23921
|
+
* → interaction palette → structure preferences → brand presentation
|
|
23922
|
+
* → nav/logo/background preferences
|
|
23923
|
+
*/
|
|
23924
|
+
function recompileSiteChromeHeader(input) {
|
|
23925
|
+
return applyPreferencesToCompiledHeader(input.theme, compileHeaderForCuratedSelection(input.theme, input.siteStyle, input.requested, input.brandPresentation, {
|
|
23926
|
+
kind: "preference",
|
|
23927
|
+
preference: input.preferences.interactionPalette
|
|
23928
|
+
}), input.preferences);
|
|
23929
|
+
}
|
|
23930
|
+
/**
|
|
23931
|
+
* The preferences run against `baseline`, not `header`: the brand presentation
|
|
23932
|
+
* belongs *after* the structure preferences in the named order, and
|
|
23933
|
+
* `applyHeaderPreferences` applies it there. Starting from `header` would apply
|
|
23934
|
+
* it twice and put the earlier copy in the wrong place.
|
|
23935
|
+
*/
|
|
23936
|
+
function applyPreferencesToCompiledHeader(theme, compiled, preferences) {
|
|
23937
|
+
const applied = applyHeaderPreferences({
|
|
23938
|
+
...theme,
|
|
23939
|
+
header: compiled.baseline
|
|
23940
|
+
}, preferences, compiled.brandPresentation);
|
|
23941
|
+
return {
|
|
23942
|
+
header: applied.header,
|
|
23943
|
+
resolution: compiled.resolution,
|
|
23944
|
+
repair: compiled.repair
|
|
23945
|
+
};
|
|
23946
|
+
}
|
|
23947
|
+
/**
|
|
23948
|
+
* The read-then-recompile round trip: derive the row's intent from its own
|
|
23949
|
+
* persisted bytes, then rebuild the header from that intent. On a row whose
|
|
23950
|
+
* bytes are compiler output this returns the persisted header unchanged, which
|
|
23951
|
+
* is what the Phase 4c divergence report measures.
|
|
23952
|
+
*/
|
|
23953
|
+
function recompileSiteChromeHeaderFromPersistedIntent(theme, siteStyle, requested, brandPresentation) {
|
|
23954
|
+
const compiled = compileHeaderForCuratedSelection(theme, siteStyle, requested, brandPresentation, { kind: "derive-from-persisted-bytes" });
|
|
23955
|
+
const candidates = headerBrandPresentationCandidates(theme, compiled.baseline, compiled.brandPresentation);
|
|
23956
|
+
const preferences = readHeaderPreferences(theme, compiled.header, compiled.interactionPalette, candidates);
|
|
23957
|
+
return {
|
|
23958
|
+
recompiled: applyPreferencesToCompiledHeader(theme, compiled, preferences),
|
|
23959
|
+
preferences,
|
|
23960
|
+
interactionPaletteBytes: classifyHeaderInteractionPaletteBytes(theme.header, compiled.interactionPalette),
|
|
23961
|
+
brandPresentationBytes: classifyHeaderBrandPresentationBytes(theme.header, candidates.recommended, candidates.plain)
|
|
23962
|
+
};
|
|
23963
|
+
}
|
|
23964
|
+
|
|
22481
23965
|
//#endregion
|
|
22482
23966
|
//#region ../theme-core/src/site-styles/runtimeBackgroundResolution.ts
|
|
22483
23967
|
function parseCanonicalRuntimeBackground(content) {
|
|
@@ -22764,43 +24248,56 @@ function isCycleableDesignOption(option) {
|
|
|
22764
24248
|
//#endregion
|
|
22765
24249
|
//#region ../theme-core/src/site-styles/previewCompiler.ts
|
|
22766
24250
|
const previewCustomizePurpose = "preview-customize";
|
|
24251
|
+
/**
|
|
24252
|
+
* "Leave the header as the user last chose it" (#1318 Phase 4d).
|
|
24253
|
+
*
|
|
24254
|
+
* Before 4d this returned the caller's bytes verbatim, which was a fourth path
|
|
24255
|
+
* by which a hand-written header reached persistence. It now recompiles from
|
|
24256
|
+
* the row's stored selection and preferences — the stored record when the
|
|
24257
|
+
* caller holds one, and the row's own bytes read back through the Phase 4a
|
|
24258
|
+
* reader while the Phase 4c backfill is outstanding. On a row whose bytes are
|
|
24259
|
+
* compiler output this returns the persisted header unchanged, which is what
|
|
24260
|
+
* the divergence report measures; on a row whose bytes diverge, the intent is
|
|
24261
|
+
* what survives, which is the point of the phase.
|
|
24262
|
+
*/
|
|
24263
|
+
function compilePreservedHeaderDirective(directive, input) {
|
|
24264
|
+
const recompiled = directive.preferences === null ? recompileSiteChromeHeaderFromPersistedIntent(directive.persistedTheme, input.siteStyle, input.currentSelection, directive.brandPresentation).recompiled : recompileSiteChromeHeader({
|
|
24265
|
+
theme: directive.persistedTheme,
|
|
24266
|
+
siteStyle: input.siteStyle,
|
|
24267
|
+
requested: input.currentSelection,
|
|
24268
|
+
preferences: directive.preferences,
|
|
24269
|
+
brandPresentation: directive.brandPresentation
|
|
24270
|
+
});
|
|
24271
|
+
return {
|
|
24272
|
+
header: recompiled.header,
|
|
24273
|
+
brandPresentation: null,
|
|
24274
|
+
resolvedHeaderSelection: recompiled.resolution.selection,
|
|
24275
|
+
warning: recompiled.repair.kind === "exact" ? null : {
|
|
24276
|
+
kind: "repaired-header-selection",
|
|
24277
|
+
requested: input.currentSelection,
|
|
24278
|
+
resolved: recompiled.resolution.selection,
|
|
24279
|
+
reason: recompiled.repair.reason
|
|
24280
|
+
}
|
|
24281
|
+
};
|
|
24282
|
+
}
|
|
22767
24283
|
function compileHeaderDirective(input) {
|
|
22768
24284
|
if (input.directive.kind === "preserve") {
|
|
22769
|
-
return
|
|
22770
|
-
header: input.directive.header,
|
|
22771
|
-
brandPresentation: null,
|
|
22772
|
-
resolvedHeaderSelection: input.currentSelection,
|
|
22773
|
-
warning: null
|
|
22774
|
-
};
|
|
24285
|
+
return compilePreservedHeaderDirective(input.directive, input);
|
|
22775
24286
|
}
|
|
22776
24287
|
const requested = input.directive.selection;
|
|
22777
|
-
const
|
|
22778
|
-
|
|
22779
|
-
|
|
22780
|
-
const curatedChoices = requestedLayoutChoices ?? headerStyleChoicesForLayout(input.siteStyle, curatedDefault.layoutId);
|
|
22781
|
-
if (!curatedChoices) {
|
|
22782
|
-
throw new Error(`Site style ${input.siteStyle.id} has invalid header curation`);
|
|
22783
|
-
}
|
|
22784
|
-
const resolvedStyleId = deriveNearestCompatibleHeaderStyle(requested.styleId, resolvedLayoutId, curatedChoices.map((choice) => choice.id));
|
|
22785
|
-
const resolution = resolveHeaderLayoutStyleSelection({
|
|
22786
|
-
layoutId: resolvedLayoutId,
|
|
22787
|
-
styleId: resolvedStyleId
|
|
24288
|
+
const { header, brandPresentation, resolution, repair: repair$1 } = compileHeaderForCuratedSelection(input.theme, input.siteStyle, requested, input.directive.brandPresentation, {
|
|
24289
|
+
kind: "preference",
|
|
24290
|
+
preference: readHeaderNavPreferences(input.theme).interactionPalette
|
|
22788
24291
|
});
|
|
22789
|
-
const header = compileHeaderLayoutStyleSelection(input.theme, resolution);
|
|
22790
|
-
const brandPresentation = input.directive.brandPresentation.kind === "apply-logo-context" ? resolveHeaderBrandPresentationForLayout({
|
|
22791
|
-
layout: resolution.layout,
|
|
22792
|
-
logo: input.directive.brandPresentation.logo
|
|
22793
|
-
}) : null;
|
|
22794
|
-
const headerWithBrandPresentation = brandPresentation !== null ? applyHeaderBrandPresentation(header, brandPresentation) : header;
|
|
22795
24292
|
return {
|
|
22796
|
-
header
|
|
24293
|
+
header,
|
|
22797
24294
|
brandPresentation,
|
|
22798
24295
|
resolvedHeaderSelection: resolution.selection,
|
|
22799
|
-
warning:
|
|
24296
|
+
warning: repair$1.kind === "exact" ? null : {
|
|
22800
24297
|
kind: "repaired-header-selection",
|
|
22801
24298
|
requested,
|
|
22802
24299
|
resolved: resolution.selection,
|
|
22803
|
-
reason:
|
|
24300
|
+
reason: repair$1.reason
|
|
22804
24301
|
}
|
|
22805
24302
|
};
|
|
22806
24303
|
}
|
|
@@ -22834,21 +24331,6 @@ function compileIncrementalFooter(input) {
|
|
|
22834
24331
|
resolvedFooterLookId: result.selectedLookId
|
|
22835
24332
|
};
|
|
22836
24333
|
}
|
|
22837
|
-
function applyHeaderBrandPresentation(header, presentation) {
|
|
22838
|
-
const shouldShrinkDisplayLogo = presentation.size === "display" && presentation.accommodation.providesBacking === true && header.positioning !== "static";
|
|
22839
|
-
return {
|
|
22840
|
-
...header,
|
|
22841
|
-
shrinkOnScroll: shouldShrinkDisplayLogo ? true : header.shrinkOnScroll,
|
|
22842
|
-
logo: {
|
|
22843
|
-
fontFamily: header.logo?.fontFamily ?? "heading",
|
|
22844
|
-
letterSpacing: header.logo?.letterSpacing ?? "normal",
|
|
22845
|
-
gradient: header.logo?.gradient ?? false,
|
|
22846
|
-
brandDisplay: presentation.display,
|
|
22847
|
-
size: presentation.size,
|
|
22848
|
-
accommodation: presentation.accommodation.pattern
|
|
22849
|
-
}
|
|
22850
|
-
};
|
|
22851
|
-
}
|
|
22852
24334
|
function applyFooterBrandPresentation(footer, presentation) {
|
|
22853
24335
|
const size = footerLogoSizeFromBrandPresentation(presentation.size);
|
|
22854
24336
|
return {
|
|
@@ -23924,10 +25406,127 @@ function runGeneratedPageDesignPipeline(input) {
|
|
|
23924
25406
|
};
|
|
23925
25407
|
}
|
|
23926
25408
|
|
|
25409
|
+
//#endregion
|
|
25410
|
+
//#region ../theme-core/src/site-styles/headerPreferencesPersistence.ts
|
|
25411
|
+
const backgroundColorTokenSchema = z.unknown().transform((value, ctx) => {
|
|
25412
|
+
const token = parseSiteChromeBackgroundColorToken(value);
|
|
25413
|
+
if (token === null) {
|
|
25414
|
+
ctx.addIssue({
|
|
25415
|
+
code: "custom",
|
|
25416
|
+
message: "Unknown site chrome background colour token"
|
|
25417
|
+
});
|
|
25418
|
+
return z.NEVER;
|
|
25419
|
+
}
|
|
25420
|
+
return token;
|
|
25421
|
+
});
|
|
25422
|
+
const backgroundOverrideSchema = z.discriminatedUnion("kind", [z.object({ kind: z.literal("default") }).strict(), z.object({
|
|
25423
|
+
kind: z.literal("color"),
|
|
25424
|
+
token: backgroundColorTokenSchema
|
|
25425
|
+
}).strict()]);
|
|
25426
|
+
const logoTextIntentSchema = z.object({
|
|
25427
|
+
mobileMaxWidth: z.enum([
|
|
25428
|
+
"xs",
|
|
25429
|
+
"sm",
|
|
25430
|
+
"md"
|
|
25431
|
+
]).optional(),
|
|
25432
|
+
mobileWrapLines: z.union([z.literal(1), z.literal(2)]).optional(),
|
|
25433
|
+
hideOnShrink: z.boolean().optional()
|
|
25434
|
+
}).strict();
|
|
25435
|
+
const structureSchema = z.object({
|
|
25436
|
+
positioning: z.union([z.literal("inherit"), z.enum(headerPositioningValues)]),
|
|
25437
|
+
shrinkOnScroll: z.union([z.literal("inherit"), z.boolean()]),
|
|
25438
|
+
logoText: z.union([
|
|
25439
|
+
z.literal("inherit"),
|
|
25440
|
+
z.literal("none"),
|
|
25441
|
+
logoTextIntentSchema
|
|
25442
|
+
])
|
|
25443
|
+
}).strict();
|
|
25444
|
+
const headerPreferencesShape = {
|
|
25445
|
+
brandPresentation: z.enum(headerBrandPresentationValues),
|
|
25446
|
+
brandDisplay: z.enum(brandDisplayModeValues),
|
|
25447
|
+
logoSize: z.enum(logoSizePolicyValues),
|
|
25448
|
+
navFontRole: z.enum(headerNavTypographyPreferenceValues),
|
|
25449
|
+
navTextSize: z.enum(headerNavFontSizePreferenceValues),
|
|
25450
|
+
interactionPalette: z.union([z.literal("recommended"), z.enum(headerInteractionPaletteValues)]),
|
|
25451
|
+
background: backgroundOverrideSchema,
|
|
25452
|
+
structure: structureSchema
|
|
25453
|
+
};
|
|
25454
|
+
/**
|
|
25455
|
+
* The persisted shape of `HeaderPreferences`, closed at every field.
|
|
25456
|
+
*
|
|
25457
|
+
* `.strict()` throughout is deliberate: an unknown key in the stored blob means
|
|
25458
|
+
* the writer knew about a field this reader does not, and silently dropping it
|
|
25459
|
+
* would let a save round-trip away someone else's intent. Every field is
|
|
25460
|
+
* required for the same reason a default would be a hidden authority: a stored
|
|
25461
|
+
* record that omits one is not a record this reader may complete.
|
|
25462
|
+
*/
|
|
25463
|
+
const headerPreferencesSchema = z.object(headerPreferencesShape).strict();
|
|
25464
|
+
/** Only controls the client can edit may cross the write boundary. */
|
|
25465
|
+
const headerPreferencesWireSchema = z.object(headerPreferencesShape).omit({
|
|
25466
|
+
brandPresentation: true,
|
|
25467
|
+
structure: true
|
|
25468
|
+
}).strict();
|
|
25469
|
+
function parsePersistedHeaderPreferences(value) {
|
|
25470
|
+
if (value === null || value === undefined) return { kind: "absent" };
|
|
25471
|
+
const parsed = headerPreferencesSchema.safeParse(value);
|
|
25472
|
+
if (parsed.success) return {
|
|
25473
|
+
kind: "present",
|
|
25474
|
+
preferences: parsed.data
|
|
25475
|
+
};
|
|
25476
|
+
return {
|
|
25477
|
+
kind: "invalid",
|
|
25478
|
+
issues: parsed.error.issues.map((issue) => `${issue.path.join(".") || "<root>"}: ${issue.message}`)
|
|
25479
|
+
};
|
|
25480
|
+
}
|
|
25481
|
+
|
|
25482
|
+
//#endregion
|
|
25483
|
+
//#region ../theme-core/src/site-styles/headerBytesComparison.ts
|
|
25484
|
+
function isPlainObject(value) {
|
|
25485
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
25486
|
+
}
|
|
25487
|
+
/**
|
|
25488
|
+
* Structural diff by JSON path. Values are compared through
|
|
25489
|
+
* `JSON.stringify`, so a key present with the value `undefined` and an absent
|
|
25490
|
+
* key read as the same thing — which is what `jsonb` does to them anyway
|
|
25491
|
+
* (#1318 Correction 5).
|
|
25492
|
+
*/
|
|
25493
|
+
function diffJsonPaths(left, right, path$2 = "") {
|
|
25494
|
+
if (isPlainObject(left) && isPlainObject(right)) {
|
|
25495
|
+
const keys = [...new Set([...Object.keys(left), ...Object.keys(right)])].sort();
|
|
25496
|
+
return keys.flatMap((key) => diffJsonPaths(left[key], right[key], path$2 ? `${path$2}.${key}` : key));
|
|
25497
|
+
}
|
|
25498
|
+
if (JSON.stringify(left) === JSON.stringify(right)) return [];
|
|
25499
|
+
return [{
|
|
25500
|
+
path: path$2,
|
|
25501
|
+
left,
|
|
25502
|
+
right
|
|
25503
|
+
}];
|
|
25504
|
+
}
|
|
25505
|
+
function roundTrip(header) {
|
|
25506
|
+
return JSON.parse(JSON.stringify(header));
|
|
25507
|
+
}
|
|
25508
|
+
/**
|
|
25509
|
+
* The Phase 4b equality assertion: does the header a client compiled match the
|
|
25510
|
+
* one the server compiles from the same `(selection, preferences)`?
|
|
25511
|
+
*
|
|
25512
|
+
* Both sides go through a JSON round trip first, because the persisted form is
|
|
25513
|
+
* `jsonb` and an in-memory `ctaVariant: undefined` is not a difference the
|
|
25514
|
+
* database could ever store (#1318 Correction 5). Comparing the in-memory
|
|
25515
|
+
* objects directly would report a mismatch that no save could produce.
|
|
25516
|
+
*/
|
|
25517
|
+
function compareCompiledHeaders(left, right) {
|
|
25518
|
+
const diffs = diffJsonPaths(roundTrip(left), roundTrip(right));
|
|
25519
|
+
return diffs.length === 0 ? { kind: "equal" } : {
|
|
25520
|
+
kind: "different",
|
|
25521
|
+
diffs,
|
|
25522
|
+
differingPaths: diffs.map((diff) => diff.path)
|
|
25523
|
+
};
|
|
25524
|
+
}
|
|
25525
|
+
|
|
23927
25526
|
//#endregion
|
|
23928
25527
|
//#region ../theme-core/src/site-styles/headerOpeningForeground.ts
|
|
23929
|
-
function headerStyleAvailability(
|
|
23930
|
-
if (!
|
|
25528
|
+
function headerStyleAvailability(recipe, context) {
|
|
25529
|
+
if (!recipe.openingForeground) return { kind: "available" };
|
|
23931
25530
|
return context?.openingForeground?.kind === "known" ? { kind: "available" } : {
|
|
23932
25531
|
kind: "unavailable",
|
|
23933
25532
|
reason: "requires-known-opening-foreground"
|
|
@@ -23939,16 +25538,16 @@ function headerStyleAvailabilityReasonLabel(reason) {
|
|
|
23939
25538
|
default: return assertNever$5(reason, "Unhandled header style availability reason");
|
|
23940
25539
|
}
|
|
23941
25540
|
}
|
|
23942
|
-
function applyOpeningForeground(theme, header,
|
|
23943
|
-
if (!
|
|
25541
|
+
function applyOpeningForeground(theme, header, recipe, context) {
|
|
25542
|
+
if (!recipe.openingForeground) return header;
|
|
23944
25543
|
const foreground = context?.openingForeground ?? { kind: "unknown" };
|
|
23945
25544
|
if (foreground.kind === "unknown") return applyUnknownOpeningForegroundFallback(header);
|
|
23946
25545
|
const withBrandForeground = {
|
|
23947
25546
|
...header,
|
|
23948
25547
|
textColor: foreground.roles.text
|
|
23949
25548
|
};
|
|
23950
|
-
if (
|
|
23951
|
-
const scrollTreatment = compileHeaderScrollTreatment(theme,
|
|
25549
|
+
if (recipe.openingForeground === "brand-only") return withBrandForeground;
|
|
25550
|
+
const scrollTreatment = compileHeaderScrollTreatment(theme, recipe, foreground.roles.text);
|
|
23952
25551
|
return {
|
|
23953
25552
|
...withBrandForeground,
|
|
23954
25553
|
navColor: foreground.roles.text,
|
|
@@ -23965,8 +25564,8 @@ function applyOpeningForeground(theme, header, style, context) {
|
|
|
23965
25564
|
...scrollTreatment ? { scrollTreatment } : {}
|
|
23966
25565
|
};
|
|
23967
25566
|
}
|
|
23968
|
-
function compileHeaderScrollTreatment(theme,
|
|
23969
|
-
if (
|
|
25567
|
+
function compileHeaderScrollTreatment(theme, recipe, foregroundToken) {
|
|
25568
|
+
if (recipe.scrollTreatment !== "contrast-glass") return undefined;
|
|
23970
25569
|
const tokenParts = parseColorTokenParts(foregroundToken);
|
|
23971
25570
|
if (tokenParts.kind !== "token") return undefined;
|
|
23972
25571
|
const foregroundHex = expandPalette(theme.palette)[tokenParts.token];
|
|
@@ -24003,11 +25602,11 @@ function applyUnknownOpeningForegroundFallback(header) {
|
|
|
24003
25602
|
container: UNKNOWN_OPENING_FOREGROUND_CONTAINER
|
|
24004
25603
|
};
|
|
24005
25604
|
}
|
|
24006
|
-
function applyPageOpeningForegroundToTheme(theme,
|
|
24007
|
-
if (!
|
|
25605
|
+
function applyPageOpeningForegroundToTheme(theme, recipe, context) {
|
|
25606
|
+
if (!recipe?.openingForeground) return theme;
|
|
24008
25607
|
return {
|
|
24009
25608
|
...theme,
|
|
24010
|
-
header: applyOpeningForeground(theme, theme.header,
|
|
25609
|
+
header: applyOpeningForeground(theme, theme.header, recipe, context)
|
|
24011
25610
|
};
|
|
24012
25611
|
}
|
|
24013
25612
|
|
|
@@ -24363,276 +25962,6 @@ function boundaryPinsEqual(left, right) {
|
|
|
24363
25962
|
return left.boundaryId === right.boundaryId && left.previousBlockId === right.previousBlockId && left.nextBlockId === right.nextBlockId && boundaryOptionIdForTreatment(left.choice.value) === boundaryOptionIdForTreatment(right.choice.value);
|
|
24364
25963
|
}
|
|
24365
25964
|
|
|
24366
|
-
//#endregion
|
|
24367
|
-
//#region ../theme-core/src/site-styles/siteChromeBackgrounds.ts
|
|
24368
|
-
const siteChromeBackgroundColorTokens = toSiteChromeBackgroundColorTokens(backgroundAppearanceColorTokenNames);
|
|
24369
|
-
const quickSiteChromeBackgroundColorTokens = toSiteChromeBackgroundColorTokens(backgroundAppearanceQuickColorTokenNames);
|
|
24370
|
-
const advancedSiteChromeBackgroundColorTokens = siteChromeBackgroundColorTokens.filter((token) => !quickSiteChromeBackgroundColorTokens.includes(token));
|
|
24371
|
-
const defaultSiteChromeBackgroundOverride = { kind: "default" };
|
|
24372
|
-
function defaultSiteChromeBackgroundPreferences() {
|
|
24373
|
-
return {
|
|
24374
|
-
header: defaultSiteChromeBackgroundOverride,
|
|
24375
|
-
footer: defaultSiteChromeBackgroundOverride
|
|
24376
|
-
};
|
|
24377
|
-
}
|
|
24378
|
-
function parseSiteChromeBackgroundColorToken(value) {
|
|
24379
|
-
const token = parseBackgroundAppearanceColorToken(value);
|
|
24380
|
-
return token ? token : null;
|
|
24381
|
-
}
|
|
24382
|
-
function siteChromeBackgroundPreferencesFromTheme(theme) {
|
|
24383
|
-
if (!theme) return defaultSiteChromeBackgroundPreferences();
|
|
24384
|
-
return {
|
|
24385
|
-
header: theme.header ? siteChromeBackgroundOverrideFromHeader(theme.header) : defaultSiteChromeBackgroundOverride,
|
|
24386
|
-
footer: theme.footer ? siteChromeBackgroundOverrideFromFooter(theme.footer) : defaultSiteChromeBackgroundOverride
|
|
24387
|
-
};
|
|
24388
|
-
}
|
|
24389
|
-
function applySiteChromeBackgroundPreferences(theme, preferences) {
|
|
24390
|
-
return {
|
|
24391
|
-
...theme,
|
|
24392
|
-
header: applyHeaderBackgroundOverride(theme, theme.header, preferences.header),
|
|
24393
|
-
footer: applyFooterBackgroundOverride(theme, theme.footer, preferences.footer)
|
|
24394
|
-
};
|
|
24395
|
-
}
|
|
24396
|
-
function canCustomizeSiteChromeBackground(theme, surface$1) {
|
|
24397
|
-
if (surface$1 === "footer") return true;
|
|
24398
|
-
const header = theme?.header;
|
|
24399
|
-
if (!header) return false;
|
|
24400
|
-
const navContainerType = header.navContainer?.type ?? "none";
|
|
24401
|
-
return header.variant !== "floating" && navContainerType !== "glass" && navContainerType !== "pill";
|
|
24402
|
-
}
|
|
24403
|
-
function siteChromeBackgroundOverrideLabel(override) {
|
|
24404
|
-
switch (override.kind) {
|
|
24405
|
-
case "default": return "Default";
|
|
24406
|
-
case "color": return formatSiteChromeBackgroundTokenLabel(override.token);
|
|
24407
|
-
}
|
|
24408
|
-
}
|
|
24409
|
-
function formatSiteChromeBackgroundTokenLabel(token) {
|
|
24410
|
-
return String(token).replace(/-/g, " ").replace(/\b\w/g, (letter) => letter.toUpperCase());
|
|
24411
|
-
}
|
|
24412
|
-
function siteChromeBackgroundOverrideEquals(left, right) {
|
|
24413
|
-
return left.kind === right.kind && (left.kind === "default" || right.kind === "color" && left.token === right.token);
|
|
24414
|
-
}
|
|
24415
|
-
function cycleSiteChromeBackgroundOverride(current, direction) {
|
|
24416
|
-
const fallback = direction === "next" ? quickSiteChromeBackgroundColorTokens[0] : quickSiteChromeBackgroundColorTokens[quickSiteChromeBackgroundColorTokens.length - 1];
|
|
24417
|
-
if (!fallback) return defaultSiteChromeBackgroundOverride;
|
|
24418
|
-
if (current.kind === "default") {
|
|
24419
|
-
return {
|
|
24420
|
-
kind: "color",
|
|
24421
|
-
token: fallback
|
|
24422
|
-
};
|
|
24423
|
-
}
|
|
24424
|
-
const currentIndex = quickSiteChromeBackgroundColorTokens.indexOf(current.token);
|
|
24425
|
-
if (currentIndex === -1) {
|
|
24426
|
-
return {
|
|
24427
|
-
kind: "color",
|
|
24428
|
-
token: fallback
|
|
24429
|
-
};
|
|
24430
|
-
}
|
|
24431
|
-
const nextIndex = direction === "next" ? (currentIndex + 1) % quickSiteChromeBackgroundColorTokens.length : currentIndex === 0 ? quickSiteChromeBackgroundColorTokens.length - 1 : currentIndex - 1;
|
|
24432
|
-
const token = requiredQuickSiteChromeBackgroundColorToken(nextIndex);
|
|
24433
|
-
return {
|
|
24434
|
-
kind: "color",
|
|
24435
|
-
token
|
|
24436
|
-
};
|
|
24437
|
-
}
|
|
24438
|
-
function siteChromeBackgroundOverrideFromHeader(header) {
|
|
24439
|
-
return siteChromeBackgroundOverrideFromBackground(header.background);
|
|
24440
|
-
}
|
|
24441
|
-
function siteChromeBackgroundOverrideFromFooter(footer) {
|
|
24442
|
-
return siteChromeBackgroundOverrideFromBackground(footer.background);
|
|
24443
|
-
}
|
|
24444
|
-
function siteChromeBackgroundOverrideFromBackground(background) {
|
|
24445
|
-
if (background?.contentOwner !== siteChromeBackgroundOverrideOwner || background.type !== "color") {
|
|
24446
|
-
return defaultSiteChromeBackgroundOverride;
|
|
24447
|
-
}
|
|
24448
|
-
const token = parseSiteChromeBackgroundColorToken(background.color);
|
|
24449
|
-
return token ? {
|
|
24450
|
-
kind: "color",
|
|
24451
|
-
token
|
|
24452
|
-
} : defaultSiteChromeBackgroundOverride;
|
|
24453
|
-
}
|
|
24454
|
-
function applyHeaderBackgroundOverride(theme, header, override) {
|
|
24455
|
-
if (override.kind === "default") return header;
|
|
24456
|
-
const background = siteChromeColorBackground(override.token);
|
|
24457
|
-
const foreground = resolveChromeForegroundToken(theme, background);
|
|
24458
|
-
return {
|
|
24459
|
-
...header,
|
|
24460
|
-
background: {
|
|
24461
|
-
...background,
|
|
24462
|
-
textColor: foreground
|
|
24463
|
-
},
|
|
24464
|
-
textColor: foreground,
|
|
24465
|
-
navColor: foreground,
|
|
24466
|
-
...header.navLinkStyle ? { navLinkStyle: {
|
|
24467
|
-
...header.navLinkStyle,
|
|
24468
|
-
colorToken: foreground
|
|
24469
|
-
} } : {}
|
|
24470
|
-
};
|
|
24471
|
-
}
|
|
24472
|
-
function applyFooterBackgroundOverride(theme, footer, override) {
|
|
24473
|
-
if (override.kind === "default") return footer;
|
|
24474
|
-
const background = siteChromeColorBackground(override.token);
|
|
24475
|
-
const foreground = resolveChromeForegroundToken(theme, background);
|
|
24476
|
-
return {
|
|
24477
|
-
...footer,
|
|
24478
|
-
background: {
|
|
24479
|
-
...background,
|
|
24480
|
-
textColor: foreground
|
|
24481
|
-
},
|
|
24482
|
-
navColor: foreground,
|
|
24483
|
-
...footer.navLinkStyle ? { navLinkStyle: {
|
|
24484
|
-
...footer.navLinkStyle,
|
|
24485
|
-
colorToken: foreground
|
|
24486
|
-
} } : {},
|
|
24487
|
-
...footer.bottomTextLinkStyle ? { bottomTextLinkStyle: {
|
|
24488
|
-
...footer.bottomTextLinkStyle,
|
|
24489
|
-
colorToken: foreground
|
|
24490
|
-
} } : {},
|
|
24491
|
-
...footer.bottomBar ? { bottomBar: {
|
|
24492
|
-
...footer.bottomBar,
|
|
24493
|
-
textColor: foreground
|
|
24494
|
-
} } : {}
|
|
24495
|
-
};
|
|
24496
|
-
}
|
|
24497
|
-
function resolveChromeForegroundToken(theme, background) {
|
|
24498
|
-
const resolved = resolveSurfaceForeground(background, expandPalette(theme.palette));
|
|
24499
|
-
if (resolved.kind === "unknown") {
|
|
24500
|
-
throw new Error(`Site chrome background "${background?.type === "color" ? background.color ?? "unknown" : "unknown"}" has no resolvable AA foreground; the theme palette is incomplete`);
|
|
24501
|
-
}
|
|
24502
|
-
return resolved.roles.text;
|
|
24503
|
-
}
|
|
24504
|
-
function siteChromeColorBackground(color) {
|
|
24505
|
-
return {
|
|
24506
|
-
type: "color",
|
|
24507
|
-
contentOwner: siteChromeBackgroundOverrideOwner,
|
|
24508
|
-
color
|
|
24509
|
-
};
|
|
24510
|
-
}
|
|
24511
|
-
function toSiteChromeBackgroundColorTokens(tokenNames) {
|
|
24512
|
-
return tokenNames.flatMap((name) => {
|
|
24513
|
-
const token = parseSiteChromeBackgroundColorToken(name);
|
|
24514
|
-
return token ? [token] : [];
|
|
24515
|
-
});
|
|
24516
|
-
}
|
|
24517
|
-
function requiredQuickSiteChromeBackgroundColorToken(index) {
|
|
24518
|
-
const token = quickSiteChromeBackgroundColorTokens[index];
|
|
24519
|
-
if (!token) {
|
|
24520
|
-
throw new Error(`Missing quick site chrome background token at index ${index}`);
|
|
24521
|
-
}
|
|
24522
|
-
return token;
|
|
24523
|
-
}
|
|
24524
|
-
|
|
24525
|
-
//#endregion
|
|
24526
|
-
//#region ../theme-core/src/site-styles/siteChromeNavPreferences.ts
|
|
24527
|
-
const headerNavTypographyPreferenceValues = [
|
|
24528
|
-
"inherit",
|
|
24529
|
-
"body",
|
|
24530
|
-
"heading"
|
|
24531
|
-
];
|
|
24532
|
-
const headerNavFontSizePreferenceValues = [
|
|
24533
|
-
"inherit",
|
|
24534
|
-
"sm",
|
|
24535
|
-
"base",
|
|
24536
|
-
"lg",
|
|
24537
|
-
"xl"
|
|
24538
|
-
];
|
|
24539
|
-
const headerNavTypographyOptions = [
|
|
24540
|
-
{
|
|
24541
|
-
value: "inherit",
|
|
24542
|
-
label: "Default"
|
|
24543
|
-
},
|
|
24544
|
-
{
|
|
24545
|
-
value: "body",
|
|
24546
|
-
label: "Body"
|
|
24547
|
-
},
|
|
24548
|
-
{
|
|
24549
|
-
value: "heading",
|
|
24550
|
-
label: "Heading"
|
|
24551
|
-
}
|
|
24552
|
-
];
|
|
24553
|
-
const headerNavFontSizeOptions = [
|
|
24554
|
-
{
|
|
24555
|
-
value: "inherit",
|
|
24556
|
-
label: "Default"
|
|
24557
|
-
},
|
|
24558
|
-
{
|
|
24559
|
-
value: "sm",
|
|
24560
|
-
label: "Small"
|
|
24561
|
-
},
|
|
24562
|
-
{
|
|
24563
|
-
value: "base",
|
|
24564
|
-
label: "Regular"
|
|
24565
|
-
},
|
|
24566
|
-
{
|
|
24567
|
-
value: "lg",
|
|
24568
|
-
label: "Large"
|
|
24569
|
-
},
|
|
24570
|
-
{
|
|
24571
|
-
value: "xl",
|
|
24572
|
-
label: "Huge"
|
|
24573
|
-
}
|
|
24574
|
-
];
|
|
24575
|
-
const defaultHeaderNavPreferences = {
|
|
24576
|
-
typography: "inherit",
|
|
24577
|
-
fontSize: "inherit",
|
|
24578
|
-
interactionPalette: "recommended"
|
|
24579
|
-
};
|
|
24580
|
-
function readHeaderNavPreferences(theme) {
|
|
24581
|
-
const typography = theme?.header?.navFontRole;
|
|
24582
|
-
const fontSize = theme?.header?.navTextSize;
|
|
24583
|
-
const interactionPalette = theme?.header?.interactionPalette ?? "recommended";
|
|
24584
|
-
return typography === "body" || typography === "heading" ? {
|
|
24585
|
-
typography,
|
|
24586
|
-
fontSize: readHeaderNavFontSizePreference(fontSize),
|
|
24587
|
-
interactionPalette
|
|
24588
|
-
} : {
|
|
24589
|
-
...defaultHeaderNavPreferences,
|
|
24590
|
-
fontSize: readHeaderNavFontSizePreference(fontSize),
|
|
24591
|
-
interactionPalette
|
|
24592
|
-
};
|
|
24593
|
-
}
|
|
24594
|
-
function headerNavPreferencesEqual(left, right) {
|
|
24595
|
-
return left.typography === right.typography && left.fontSize === right.fontSize && left.interactionPalette === right.interactionPalette;
|
|
24596
|
-
}
|
|
24597
|
-
function withHeaderNavThemePreferences(theme, preferences) {
|
|
24598
|
-
const { navFontRole: _navFontRole, navTextSize: _navTextSize, interactionPalette: _interactionPalette, ...headerWithoutPreference } = theme.header;
|
|
24599
|
-
const nextHeader = {
|
|
24600
|
-
...headerWithoutPreference,
|
|
24601
|
-
...preferences.typography === "inherit" ? {} : { navFontRole: preferences.typography },
|
|
24602
|
-
...preferences.fontSize === "inherit" ? {} : { navTextSize: preferences.fontSize },
|
|
24603
|
-
...preferences.interactionPalette === "recommended" ? {} : { interactionPalette: preferences.interactionPalette }
|
|
24604
|
-
};
|
|
24605
|
-
return {
|
|
24606
|
-
...theme,
|
|
24607
|
-
header: nextHeader
|
|
24608
|
-
};
|
|
24609
|
-
}
|
|
24610
|
-
/**
|
|
24611
|
-
* Applies the user's nav font-role/size overrides on top of a style-resolved
|
|
24612
|
-
* nav link style. This is where the precedence contract lands:
|
|
24613
|
-
*
|
|
24614
|
-
* - The nav typography TREATMENT (case/tracking identity: plain / heading /
|
|
24615
|
-
* quiet-caps / brand-caps) is ALWAYS owned by the active header style and is
|
|
24616
|
-
* baked into `style` before this call (see `applyNavTypography` in
|
|
24617
|
-
* headerLayoutStyles.ts).
|
|
24618
|
-
* - Each treatment resolves through a DEFAULT font role: `heading` resolves
|
|
24619
|
-
* through the heading role; `plain` / `quiet-caps` / `brand-caps` resolve
|
|
24620
|
-
* through the body role.
|
|
24621
|
-
* - The user's `navFontRole` (surfaced here as `preferences.typography`) only
|
|
24622
|
-
* overrides which font ROLE the treatment resolves through. It never changes
|
|
24623
|
-
* the treatment. `'inherit'` keeps the treatment's default role.
|
|
24624
|
-
*/
|
|
24625
|
-
function mergeHeaderNavTypographyPreference(style, preferences) {
|
|
24626
|
-
return preferences.typography === "inherit" && preferences.fontSize === "inherit" ? style : {
|
|
24627
|
-
...style,
|
|
24628
|
-
...preferences.typography === "inherit" ? {} : { typography: preferences.typography },
|
|
24629
|
-
...preferences.fontSize === "inherit" ? {} : { textSize: preferences.fontSize }
|
|
24630
|
-
};
|
|
24631
|
-
}
|
|
24632
|
-
function readHeaderNavFontSizePreference(value) {
|
|
24633
|
-
return value === "sm" || value === "base" || value === "lg" || value === "xl" ? value : "inherit";
|
|
24634
|
-
}
|
|
24635
|
-
|
|
24636
25965
|
//#endregion
|
|
24637
25966
|
//#region ../theme-core/src/site-styles/tokenRecipes.ts
|
|
24638
25967
|
const themeTokenRecipeOptions = {
|
|
@@ -24748,24 +26077,24 @@ function normalizeHex$1(value) {
|
|
|
24748
26077
|
const blue = triplet.charAt(2);
|
|
24749
26078
|
return `#${red}${red}${green}${green}${blue}${blue}`;
|
|
24750
26079
|
}
|
|
24751
|
-
function collectDifferences(before, after, path$
|
|
26080
|
+
function collectDifferences(before, after, path$2, differences) {
|
|
24752
26081
|
if (Object.is(before, after)) return;
|
|
24753
26082
|
if (Array.isArray(before) && Array.isArray(after)) {
|
|
24754
26083
|
const sharedLength = Math.min(before.length, after.length);
|
|
24755
26084
|
for (let index = 0; index < sharedLength; index += 1) {
|
|
24756
|
-
collectDifferences(before[index], after[index], `${path$
|
|
26085
|
+
collectDifferences(before[index], after[index], `${path$2}[${index}]`, differences);
|
|
24757
26086
|
}
|
|
24758
26087
|
for (let index = sharedLength; index < before.length; index += 1) {
|
|
24759
26088
|
differences.push({
|
|
24760
26089
|
kind: "missing",
|
|
24761
|
-
path: `${path$
|
|
26090
|
+
path: `${path$2}[${index}]`,
|
|
24762
26091
|
before: before[index]
|
|
24763
26092
|
});
|
|
24764
26093
|
}
|
|
24765
26094
|
for (let index = sharedLength; index < after.length; index += 1) {
|
|
24766
26095
|
differences.push({
|
|
24767
26096
|
kind: "extra",
|
|
24768
|
-
path: `${path$
|
|
26097
|
+
path: `${path$2}[${index}]`,
|
|
24769
26098
|
after: after[index]
|
|
24770
26099
|
});
|
|
24771
26100
|
}
|
|
@@ -24776,7 +26105,7 @@ function collectDifferences(before, after, path$1, differences) {
|
|
|
24776
26105
|
const afterKeys = new Set(Object.keys(after));
|
|
24777
26106
|
const keys = [...new Set([...beforeKeys, ...afterKeys])].sort();
|
|
24778
26107
|
for (const key of keys) {
|
|
24779
|
-
const childPath = `${path$
|
|
26108
|
+
const childPath = `${path$2}.${key}`;
|
|
24780
26109
|
if (!afterKeys.has(key)) {
|
|
24781
26110
|
differences.push({
|
|
24782
26111
|
kind: "missing",
|
|
@@ -24799,7 +26128,7 @@ function collectDifferences(before, after, path$1, differences) {
|
|
|
24799
26128
|
}
|
|
24800
26129
|
differences.push({
|
|
24801
26130
|
kind: "changed",
|
|
24802
|
-
path: path$
|
|
26131
|
+
path: path$2,
|
|
24803
26132
|
before,
|
|
24804
26133
|
after
|
|
24805
26134
|
});
|
|
@@ -24970,8 +26299,8 @@ const sdkConfigureBookingVerticalResponseSchema = sdkCommandApplicationResultSch
|
|
|
24970
26299
|
function isRecord$8(value) {
|
|
24971
26300
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
24972
26301
|
}
|
|
24973
|
-
function pullSchemaForRequest(path$
|
|
24974
|
-
switch (path$
|
|
26302
|
+
function pullSchemaForRequest(path$2) {
|
|
26303
|
+
switch (path$2) {
|
|
24975
26304
|
case "/pull/entries/all": return sdkPullEntriesAllResponseSchema;
|
|
24976
26305
|
case "/pull/entries": return sdkPullEntriesResponseSchema;
|
|
24977
26306
|
case "/pull/pages": return sdkPullPagesResponseSchema;
|
|
@@ -24983,12 +26312,12 @@ function pullSchemaForRequest(path$1) {
|
|
|
24983
26312
|
case "/pull/venues": return sdkPullVenuesResponseSchema;
|
|
24984
26313
|
case "/pull/event-categories": return sdkPullEventCategoriesResponseSchema;
|
|
24985
26314
|
case "/pull/events": return sdkPullEventsResponseSchema;
|
|
24986
|
-
default: return /^\/pull\/entries\/[^/]+$/.test(path$
|
|
26315
|
+
default: return /^\/pull\/entries\/[^/]+$/.test(path$2) ? sdkPullEntriesByTypeResponseSchema : undefined;
|
|
24987
26316
|
}
|
|
24988
26317
|
}
|
|
24989
|
-
function rootMutationSchema(method, path$
|
|
26318
|
+
function rootMutationSchema(method, path$2, body) {
|
|
24990
26319
|
if (method === "POST") {
|
|
24991
|
-
switch (path$
|
|
26320
|
+
switch (path$2) {
|
|
24992
26321
|
case "/entries": return isRecord$8(body) && body.publication === "publish" ? sdkUpsertEntryPublishResponseSchema : sdkUpsertEntryDraftResponseSchema;
|
|
24993
26322
|
case "/pages": return isRecord$8(body) && body.publication === "publish" ? sdkUpsertPagePublishResponseSchema : sdkUpsertPageDraftResponseSchema;
|
|
24994
26323
|
case "/navigation": return sdkUpsertNavigationMenuResponseSchema;
|
|
@@ -25001,11 +26330,11 @@ function rootMutationSchema(method, path$1, body) {
|
|
|
25001
26330
|
case "/footer": return sdkUpsertFooterResponseSchema;
|
|
25002
26331
|
}
|
|
25003
26332
|
}
|
|
25004
|
-
if (method === "PATCH" && path$
|
|
25005
|
-
if (method === "POST" && path$
|
|
26333
|
+
if (method === "PATCH" && path$2 === "/settings") return sdkUpsertSettingsResponseSchema;
|
|
26334
|
+
if (method === "POST" && path$2 === "/settings/booking/vertical") return sdkConfigureBookingVerticalResponseSchema;
|
|
25006
26335
|
return undefined;
|
|
25007
26336
|
}
|
|
25008
|
-
function isDeleteMutationPath(path$
|
|
26337
|
+
function isDeleteMutationPath(path$2) {
|
|
25009
26338
|
return [
|
|
25010
26339
|
/^\/entries\/[^/]+\/[^/]+$/,
|
|
25011
26340
|
/^\/pages\/[^/]+$/,
|
|
@@ -25015,35 +26344,42 @@ function isDeleteMutationPath(path$1) {
|
|
|
25015
26344
|
/^\/venues\/[^/]+$/,
|
|
25016
26345
|
/^\/event-categories\/[^/]+$/,
|
|
25017
26346
|
/^\/events\/[^/]+$/
|
|
25018
|
-
].some((pattern) => pattern.test(path$
|
|
26347
|
+
].some((pattern) => pattern.test(path$2));
|
|
25019
26348
|
}
|
|
25020
|
-
function nestedMutationSchema(method, path$
|
|
25021
|
-
if (method === "POST" && /^\/pages\/[^/]+\/blocks$/.test(path$
|
|
25022
|
-
if (method === "POST" && /^\/pages\/[^/]+\/blocks\/reorder$/.test(path$
|
|
25023
|
-
if (method === "PATCH" && /^\/pages\/[^/]+\/blocks\/[^/]+$/.test(path$
|
|
25024
|
-
if (method === "POST" && /^\/entries\/[^/]+\/[^/]+\/(publish|unpublish)$/.test(path$
|
|
26349
|
+
function nestedMutationSchema(method, path$2) {
|
|
26350
|
+
if (method === "POST" && /^\/pages\/[^/]+\/blocks$/.test(path$2)) return sdkUpsertBlockResponseSchema;
|
|
26351
|
+
if (method === "POST" && /^\/pages\/[^/]+\/blocks\/reorder$/.test(path$2)) return sdkReorderResponseSchema;
|
|
26352
|
+
if (method === "PATCH" && /^\/pages\/[^/]+\/blocks\/[^/]+$/.test(path$2)) return sdkPatchPageBlockResponseSchema;
|
|
26353
|
+
if (method === "POST" && /^\/entries\/[^/]+\/[^/]+\/(publish|unpublish)$/.test(path$2)) {
|
|
25025
26354
|
return sdkCommandApplicationResultSchema(sdkUpsertEntryDraftResponseSchema.shape.value);
|
|
25026
26355
|
}
|
|
25027
|
-
if (method === "POST" && /^\/pages\/[^/]+\/(publish|unpublish)$/.test(path$
|
|
26356
|
+
if (method === "POST" && /^\/pages\/[^/]+\/(publish|unpublish)$/.test(path$2)) {
|
|
25028
26357
|
return sdkCommandApplicationResultSchema(sdkUpsertPageDraftResponseSchema.shape.value);
|
|
25029
26358
|
}
|
|
25030
|
-
if (method === "DELETE" && isDeleteMutationPath(path$
|
|
26359
|
+
if (method === "DELETE" && isDeleteMutationPath(path$2)) return sdkDeleteResponseSchema;
|
|
25031
26360
|
return undefined;
|
|
25032
26361
|
}
|
|
25033
|
-
function occurrenceResponseSchema(method, path$
|
|
26362
|
+
function occurrenceResponseSchema(method, path$2) {
|
|
25034
26363
|
const collection = /^\/events\/[^/]+\/runs\/[^/]+\/occurrences$/;
|
|
25035
26364
|
const member = /^\/events\/[^/]+\/runs\/[^/]+\/occurrences\/[^/]+$/;
|
|
25036
|
-
if (method === "GET" && collection.test(path$
|
|
25037
|
-
if (method === "POST" && collection.test(path$
|
|
25038
|
-
if (method === "PATCH" && member.test(path$
|
|
25039
|
-
if (method === "DELETE" && member.test(path$
|
|
25040
|
-
return method === "POST" && /^\/events\/[^/]+\/runs\/[^/]+\/occurrences\/[^/]+\/cancel$/.test(path$
|
|
26365
|
+
if (method === "GET" && collection.test(path$2)) return sdkListEventOccurrencesResponseSchema;
|
|
26366
|
+
if (method === "POST" && collection.test(path$2)) return sdkEventOccurrenceResponseSchema;
|
|
26367
|
+
if (method === "PATCH" && member.test(path$2)) return sdkEventOccurrenceResponseSchema;
|
|
26368
|
+
if (method === "DELETE" && member.test(path$2)) return sdkDeleteEventOccurrenceResponseSchema;
|
|
26369
|
+
return method === "POST" && /^\/events\/[^/]+\/runs\/[^/]+\/occurrences\/[^/]+\/cancel$/.test(path$2) ? sdkEventOccurrenceResponseSchema : undefined;
|
|
26370
|
+
}
|
|
26371
|
+
function mediaResponseSchema(method, path$2) {
|
|
26372
|
+
if (method === "GET" && path$2 === "/media") return sdkMediaListResponseSchema;
|
|
26373
|
+
if (method === "GET" && /^\/media\/[^/]+\/download$/.test(path$2)) return sdkMediaDownloadResponseSchema;
|
|
26374
|
+
if (method === "POST" && /^\/media\/[^/]+\/delete-preview$/.test(path$2)) return sdkMediaDeletePreviewResponseSchema;
|
|
26375
|
+
if (method === "DELETE" && /^\/media\/[^/]+$/.test(path$2)) return sdkMediaDeleteApplyResponseSchema;
|
|
26376
|
+
return undefined;
|
|
25041
26377
|
}
|
|
25042
|
-
function schemaForManagementRequest(method, path$
|
|
26378
|
+
function schemaForManagementRequest(method, path$2, body) {
|
|
25043
26379
|
if (method === "GET") {
|
|
25044
|
-
return pullSchemaForRequest(path$
|
|
26380
|
+
return pullSchemaForRequest(path$2) ?? occurrenceResponseSchema(method, path$2) ?? mediaResponseSchema(method, path$2);
|
|
25045
26381
|
}
|
|
25046
|
-
return rootMutationSchema(method, path$
|
|
26382
|
+
return rootMutationSchema(method, path$2, body) ?? occurrenceResponseSchema(method, path$2) ?? mediaResponseSchema(method, path$2) ?? nestedMutationSchema(method, path$2);
|
|
25047
26383
|
}
|
|
25048
26384
|
function validateManagementResponse(value, schema) {
|
|
25049
26385
|
const parsed = schema.safeParse(value);
|
|
@@ -25198,10 +26534,10 @@ async function readApiResponse(response, requestInfo, options = {}) {
|
|
|
25198
26534
|
}
|
|
25199
26535
|
return envelope;
|
|
25200
26536
|
}
|
|
25201
|
-
function requestUrl(baseUrl, path$
|
|
25202
|
-
if (!params || Object.keys(params).length === 0) return `${baseUrl}${path$
|
|
26537
|
+
function requestUrl(baseUrl, path$2, params) {
|
|
26538
|
+
if (!params || Object.keys(params).length === 0) return `${baseUrl}${path$2}`;
|
|
25203
26539
|
const query = new URLSearchParams(params).toString();
|
|
25204
|
-
return `${baseUrl}${path$
|
|
26540
|
+
return `${baseUrl}${path$2}?${query}`;
|
|
25205
26541
|
}
|
|
25206
26542
|
function requestFetchOptions(config, method, body) {
|
|
25207
26543
|
const options = {
|
|
@@ -25215,10 +26551,10 @@ function requestFetchOptions(config, method, body) {
|
|
|
25215
26551
|
if (body !== undefined && method !== "GET") options.body = JSON.stringify(body);
|
|
25216
26552
|
return options;
|
|
25217
26553
|
}
|
|
25218
|
-
async function fetchManagementResponse(config, method, path$
|
|
26554
|
+
async function fetchManagementResponse(config, method, path$2, url, baseUrl, options) {
|
|
25219
26555
|
const requestInfo = buildRequestInfo({
|
|
25220
26556
|
method,
|
|
25221
|
-
path: path$
|
|
26557
|
+
path: path$2,
|
|
25222
26558
|
url,
|
|
25223
26559
|
baseUrl,
|
|
25224
26560
|
dashboardUrl: config.dashboardUrl,
|
|
@@ -25244,12 +26580,12 @@ function throwApiError(response, json, requestInfo) {
|
|
|
25244
26580
|
}
|
|
25245
26581
|
throw new ManagementApiError(`Request failed with status ${response.status}`, "sdk:http-error", undefined, response.status, requestInfo);
|
|
25246
26582
|
}
|
|
25247
|
-
async function requestWithConfig(config, method, path$
|
|
26583
|
+
async function requestWithConfig(config, method, path$2, options) {
|
|
25248
26584
|
const baseUrl = `${config.dashboardUrl}/api/sdk/${config.siteId}`;
|
|
25249
|
-
const url = requestUrl(baseUrl, path$
|
|
26585
|
+
const url = requestUrl(baseUrl, path$2, options?.params);
|
|
25250
26586
|
const fetchOptions = requestFetchOptions(config, method, options?.body);
|
|
25251
|
-
const { response, requestInfo } = await fetchManagementResponse(config, method, path$
|
|
25252
|
-
const responseSchema = options?.responseSchema ?? schemaForManagementRequest(method, path$
|
|
26587
|
+
const { response, requestInfo } = await fetchManagementResponse(config, method, path$2, url, baseUrl, fetchOptions);
|
|
26588
|
+
const responseSchema = options?.responseSchema ?? schemaForManagementRequest(method, path$2, options?.body);
|
|
25253
26589
|
const json = responseSchema ? await readApiResponse(response, requestInfo, { invalidOkResponse: true }) : await readApiResponse(response, requestInfo, { allowNonJsonOk: true });
|
|
25254
26590
|
if (!json) return undefined;
|
|
25255
26591
|
throwApiError(response, json, requestInfo);
|
|
@@ -25257,38 +26593,38 @@ async function requestWithConfig(config, method, path$1, options) {
|
|
|
25257
26593
|
}
|
|
25258
26594
|
function createHttpClient(config) {
|
|
25259
26595
|
return {
|
|
25260
|
-
async get(path$
|
|
25261
|
-
return requestWithConfig(config, "GET", path$
|
|
26596
|
+
async get(path$2, params) {
|
|
26597
|
+
return requestWithConfig(config, "GET", path$2, { params });
|
|
25262
26598
|
},
|
|
25263
|
-
async post(path$
|
|
25264
|
-
return requestWithConfig(config, "POST", path$
|
|
26599
|
+
async post(path$2, body) {
|
|
26600
|
+
return requestWithConfig(config, "POST", path$2, { body });
|
|
25265
26601
|
},
|
|
25266
|
-
async patch(path$
|
|
25267
|
-
return requestWithConfig(config, "PATCH", path$
|
|
26602
|
+
async patch(path$2, body) {
|
|
26603
|
+
return requestWithConfig(config, "PATCH", path$2, { body });
|
|
25268
26604
|
},
|
|
25269
|
-
async delete(path$
|
|
25270
|
-
return requestWithConfig(config, "DELETE", path$
|
|
26605
|
+
async delete(path$2, body) {
|
|
26606
|
+
return requestWithConfig(config, "DELETE", path$2, { body });
|
|
25271
26607
|
},
|
|
25272
|
-
async getValidated(path$
|
|
25273
|
-
return requestWithConfig(config, "GET", path$
|
|
26608
|
+
async getValidated(path$2, schema, params) {
|
|
26609
|
+
return requestWithConfig(config, "GET", path$2, {
|
|
25274
26610
|
params,
|
|
25275
26611
|
responseSchema: schema
|
|
25276
26612
|
});
|
|
25277
26613
|
},
|
|
25278
|
-
async postValidated(path$
|
|
25279
|
-
return requestWithConfig(config, "POST", path$
|
|
26614
|
+
async postValidated(path$2, schema, body) {
|
|
26615
|
+
return requestWithConfig(config, "POST", path$2, {
|
|
25280
26616
|
body,
|
|
25281
26617
|
responseSchema: schema
|
|
25282
26618
|
});
|
|
25283
26619
|
},
|
|
25284
|
-
async patchValidated(path$
|
|
25285
|
-
return requestWithConfig(config, "PATCH", path$
|
|
26620
|
+
async patchValidated(path$2, schema, body) {
|
|
26621
|
+
return requestWithConfig(config, "PATCH", path$2, {
|
|
25286
26622
|
body,
|
|
25287
26623
|
responseSchema: schema
|
|
25288
26624
|
});
|
|
25289
26625
|
},
|
|
25290
|
-
async deleteValidated(path$
|
|
25291
|
-
return requestWithConfig(config, "DELETE", path$
|
|
26626
|
+
async deleteValidated(path$2, schema, body) {
|
|
26627
|
+
return requestWithConfig(config, "DELETE", path$2, {
|
|
25292
26628
|
body,
|
|
25293
26629
|
responseSchema: schema
|
|
25294
26630
|
});
|
|
@@ -25903,17 +27239,51 @@ function createIdentifiersOperations(http) {
|
|
|
25903
27239
|
|
|
25904
27240
|
//#endregion
|
|
25905
27241
|
//#region ../management-client/src/media.ts
|
|
27242
|
+
const productionMediaOperationsDeps = {
|
|
27243
|
+
createHttpClient,
|
|
27244
|
+
fetch
|
|
27245
|
+
};
|
|
25906
27246
|
/**
|
|
25907
27247
|
* Create media operations for the management client.
|
|
25908
27248
|
* Uses direct-to-storage signed upload URLs for file uploads.
|
|
25909
27249
|
*/
|
|
25910
|
-
function createMediaOperations(config) {
|
|
27250
|
+
function createMediaOperations(config, deps = productionMediaOperationsDeps) {
|
|
25911
27251
|
const baseUrl = `${config.dashboardUrl}/api/sdk/${config.siteId}`;
|
|
25912
27252
|
const uploadTimeout = config.timeout ?? 10 * 60 * 1e3;
|
|
25913
|
-
const http = createHttpClient(config);
|
|
27253
|
+
const http = deps.createHttpClient(config);
|
|
25914
27254
|
return {
|
|
27255
|
+
async list(options = {}) {
|
|
27256
|
+
const params = {};
|
|
27257
|
+
if (options.search !== undefined) params.search = options.search;
|
|
27258
|
+
if (options.limit !== undefined) params.limit = String(options.limit);
|
|
27259
|
+
if (options.offset !== undefined) params.offset = String(options.offset);
|
|
27260
|
+
return http.get("/media", params);
|
|
27261
|
+
},
|
|
27262
|
+
async download(identifier, maxBytes) {
|
|
27263
|
+
const path$2 = `/media/${encodeURIComponent(identifier)}/download`;
|
|
27264
|
+
const transfer = await http.get(path$2);
|
|
27265
|
+
const requestInfo = buildRequestInfo({
|
|
27266
|
+
method: "GET",
|
|
27267
|
+
path: path$2,
|
|
27268
|
+
url: `${baseUrl}${path$2}`,
|
|
27269
|
+
baseUrl,
|
|
27270
|
+
dashboardUrl: config.dashboardUrl,
|
|
27271
|
+
siteId: config.siteId
|
|
27272
|
+
});
|
|
27273
|
+
const response = await getMediaFile(transfer.transferUrl, uploadTimeout, requestInfo, deps.fetch);
|
|
27274
|
+
return {
|
|
27275
|
+
data: await readBoundedResponseBody(response, maxBytes),
|
|
27276
|
+
asset: transfer.asset
|
|
27277
|
+
};
|
|
27278
|
+
},
|
|
27279
|
+
async previewDelete(identifier) {
|
|
27280
|
+
return http.post(`/media/${encodeURIComponent(identifier)}/delete-preview`);
|
|
27281
|
+
},
|
|
27282
|
+
async delete(identifier) {
|
|
27283
|
+
return http.delete(`/media/${encodeURIComponent(identifier)}`);
|
|
27284
|
+
},
|
|
25915
27285
|
async upload(file) {
|
|
25916
|
-
return uploadMediaFile(config, http, baseUrl, uploadTimeout, file);
|
|
27286
|
+
return uploadMediaFile(config, http, baseUrl, uploadTimeout, file, deps.fetch);
|
|
25917
27287
|
},
|
|
25918
27288
|
async exists(storagePath) {
|
|
25919
27289
|
const result = await http.post("/media/exists", { storagePath });
|
|
@@ -25928,7 +27298,48 @@ function createMediaOperations(config) {
|
|
|
25928
27298
|
}
|
|
25929
27299
|
};
|
|
25930
27300
|
}
|
|
25931
|
-
async function
|
|
27301
|
+
async function readBoundedResponseBody(response, maxBytes) {
|
|
27302
|
+
const declaredLength = response.headers.get("content-length");
|
|
27303
|
+
if (declaredLength !== null && Number(declaredLength) > maxBytes) {
|
|
27304
|
+
throw new ManagementApiError(`Download exceeds the ${maxBytes}-byte limit`, "sdk:download-too-large");
|
|
27305
|
+
}
|
|
27306
|
+
if (!response.body) return Buffer.alloc(0);
|
|
27307
|
+
const chunks = [];
|
|
27308
|
+
let totalBytes = 0;
|
|
27309
|
+
const reader = response.body.getReader();
|
|
27310
|
+
try {
|
|
27311
|
+
for (;;) {
|
|
27312
|
+
const chunk = await reader.read();
|
|
27313
|
+
if (chunk.done) break;
|
|
27314
|
+
totalBytes += chunk.value.byteLength;
|
|
27315
|
+
if (totalBytes > maxBytes) {
|
|
27316
|
+
await reader.cancel();
|
|
27317
|
+
throw new ManagementApiError(`Download exceeds the ${maxBytes}-byte limit`, "sdk:download-too-large");
|
|
27318
|
+
}
|
|
27319
|
+
chunks.push(chunk.value);
|
|
27320
|
+
}
|
|
27321
|
+
} finally {
|
|
27322
|
+
reader.releaseLock();
|
|
27323
|
+
}
|
|
27324
|
+
return Buffer.concat(chunks.map((chunk) => Buffer.from(chunk)), totalBytes);
|
|
27325
|
+
}
|
|
27326
|
+
async function getMediaFile(transferUrl, timeout, requestInfo, fetchImpl) {
|
|
27327
|
+
try {
|
|
27328
|
+
const response = await fetchImpl(transferUrl, {
|
|
27329
|
+
method: "GET",
|
|
27330
|
+
signal: AbortSignal.timeout(timeout)
|
|
27331
|
+
});
|
|
27332
|
+
if (!response.ok) {
|
|
27333
|
+
throw new ManagementApiError(`Download failed with status ${response.status}`, "sdk:http-error", undefined, response.status, requestInfo);
|
|
27334
|
+
}
|
|
27335
|
+
return response;
|
|
27336
|
+
} catch (error) {
|
|
27337
|
+
if (error instanceof ManagementApiError) throw error;
|
|
27338
|
+
const timedOut = error instanceof Error && error.name === "TimeoutError";
|
|
27339
|
+
throw new ManagementApiError(timedOut ? `Download timed out after ${timeout}ms` : error instanceof Error ? error.message : "Network request failed", timedOut ? "sdk:timeout" : "sdk:network-error", undefined, undefined, requestInfo);
|
|
27340
|
+
}
|
|
27341
|
+
}
|
|
27342
|
+
async function uploadMediaFile(config, http, baseUrl, uploadTimeout, file, fetchImpl) {
|
|
25932
27343
|
const filename = file.filename || "upload";
|
|
25933
27344
|
const mimeType = file.contentType || "application/octet-stream";
|
|
25934
27345
|
const { signedUrl, storagePath, bucket } = await http.post("/media/upload-url", {
|
|
@@ -25946,7 +27357,7 @@ async function uploadMediaFile(config, http, baseUrl, uploadTimeout, file) {
|
|
|
25946
27357
|
dashboardUrl: config.dashboardUrl,
|
|
25947
27358
|
siteId: config.siteId
|
|
25948
27359
|
});
|
|
25949
|
-
const response = await putMediaFile(signedUrl, file.data, mimeType, uploadTimeout, requestInfo);
|
|
27360
|
+
const response = await putMediaFile(signedUrl, file.data, mimeType, uploadTimeout, requestInfo, fetchImpl);
|
|
25950
27361
|
if (!response.ok && response.status !== 409) {
|
|
25951
27362
|
throw new ManagementApiError(`Upload failed with status ${response.status}`, "sdk:http-error", undefined, response.status, requestInfo);
|
|
25952
27363
|
}
|
|
@@ -25960,9 +27371,9 @@ async function uploadMediaFile(config, http, baseUrl, uploadTimeout, file) {
|
|
|
25960
27371
|
identifier: file.identifier
|
|
25961
27372
|
});
|
|
25962
27373
|
}
|
|
25963
|
-
async function putMediaFile(signedUrl, data, mimeType, timeout, requestInfo) {
|
|
27374
|
+
async function putMediaFile(signedUrl, data, mimeType, timeout, requestInfo, fetchImpl) {
|
|
25964
27375
|
try {
|
|
25965
|
-
return await
|
|
27376
|
+
return await fetchImpl(signedUrl, {
|
|
25966
27377
|
method: "PUT",
|
|
25967
27378
|
headers: { "Content-Type": mimeType },
|
|
25968
27379
|
body: new Uint8Array(data),
|
|
@@ -26705,8 +28116,8 @@ async function withRiverbankEnv(target, run) {
|
|
|
26705
28116
|
}
|
|
26706
28117
|
const previous = envScopeQueue;
|
|
26707
28118
|
let release;
|
|
26708
|
-
envScopeQueue = new Promise((resolve$
|
|
26709
|
-
release = resolve$
|
|
28119
|
+
envScopeQueue = new Promise((resolve$2) => {
|
|
28120
|
+
release = resolve$2;
|
|
26710
28121
|
});
|
|
26711
28122
|
await previous;
|
|
26712
28123
|
const previousEnv = process.env.RIVERBANK_ENV;
|
|
@@ -26888,7 +28299,7 @@ function shouldConfirm(plan, prepared) {
|
|
|
26888
28299
|
return readyTargets(prepared).length > 0 && !hasPreparationBarrierFailure(plan, prepared);
|
|
26889
28300
|
}
|
|
26890
28301
|
function hasPreparationBarrierFailure(plan, prepared) {
|
|
26891
|
-
return plan.targets.length > 1 && plan.policy.confirmation === "command" && prepared.some((entry) => entry.kind === "failed");
|
|
28302
|
+
return plan.targets.length > 1 && (plan.policy.confirmation === "command" || plan.policy.preparationBarrier === "all-target") && prepared.some((entry) => entry.kind === "failed");
|
|
26892
28303
|
}
|
|
26893
28304
|
async function confirmPrepared(plan, prepared, deps) {
|
|
26894
28305
|
if (!shouldConfirm(plan, prepared)) return true;
|
|
@@ -27196,7 +28607,7 @@ function formatDateShort(isoDate) {
|
|
|
27196
28607
|
*/
|
|
27197
28608
|
async function parseJsonData(options) {
|
|
27198
28609
|
if (options.file) {
|
|
27199
|
-
const filePath = path.resolve(options.file);
|
|
28610
|
+
const filePath = path$1.resolve(options.file);
|
|
27200
28611
|
const content = await fs.readFile(filePath, "utf-8");
|
|
27201
28612
|
return JSON.parse(content);
|
|
27202
28613
|
}
|
|
@@ -27250,12 +28661,12 @@ async function confirmAction(message, options, isRemote) {
|
|
|
27250
28661
|
input: process.stdin,
|
|
27251
28662
|
output: process.stderr
|
|
27252
28663
|
});
|
|
27253
|
-
return new Promise((resolve$
|
|
28664
|
+
return new Promise((resolve$2) => {
|
|
27254
28665
|
let settled = false;
|
|
27255
28666
|
const finish = (confirmed) => {
|
|
27256
28667
|
if (settled) return;
|
|
27257
28668
|
settled = true;
|
|
27258
|
-
resolve$
|
|
28669
|
+
resolve$2(confirmed);
|
|
27259
28670
|
};
|
|
27260
28671
|
rl.once("close", () => finish(false));
|
|
27261
28672
|
rl.question(`${message} (y/N): `, (answer) => {
|
|
@@ -27708,7 +29119,7 @@ const resourceKinds = [
|
|
|
27708
29119
|
"customer-pass",
|
|
27709
29120
|
"customer-membership",
|
|
27710
29121
|
"appointment",
|
|
27711
|
-
"
|
|
29122
|
+
"event-series-registration",
|
|
27712
29123
|
"event-registration",
|
|
27713
29124
|
"pass-redemption",
|
|
27714
29125
|
"order",
|
|
@@ -27857,7 +29268,7 @@ const appointmentBookingStatuses = [
|
|
|
27857
29268
|
"cancelled",
|
|
27858
29269
|
"no_show"
|
|
27859
29270
|
];
|
|
27860
|
-
const
|
|
29271
|
+
const eventSeriesRegistrationStatuses = ["enrolled"];
|
|
27861
29272
|
const eventRegistrationStatuses = ["confirmed"];
|
|
27862
29273
|
|
|
27863
29274
|
//#endregion
|
|
@@ -28011,7 +29422,6 @@ const classicProfessionalTheme = {
|
|
|
28011
29422
|
positioning: "static",
|
|
28012
29423
|
shrinkOnScroll: false,
|
|
28013
29424
|
maxWidth: "container",
|
|
28014
|
-
logoOverride: null,
|
|
28015
29425
|
background: {
|
|
28016
29426
|
type: "color",
|
|
28017
29427
|
color: "surface"
|
|
@@ -28168,7 +29578,6 @@ const vibrantCreativeTheme = {
|
|
|
28168
29578
|
positioning: "sticky",
|
|
28169
29579
|
shrinkOnScroll: false,
|
|
28170
29580
|
maxWidth: "container",
|
|
28171
|
-
logoOverride: null,
|
|
28172
29581
|
background: {
|
|
28173
29582
|
type: "color",
|
|
28174
29583
|
color: "surface"
|
|
@@ -30380,19 +31789,19 @@ var require_react_development = /* @__PURE__ */ __commonJSMin(((exports, module)
|
|
|
30380
31789
|
prevActScopeDepth !== actScopeDepth - 1 && console.error("You seem to have overlapping act() calls, this is not supported. Be sure to await previous act() calls before making a new one. ");
|
|
30381
31790
|
actScopeDepth = prevActScopeDepth;
|
|
30382
31791
|
}
|
|
30383
|
-
function recursivelyFlushAsyncActWork(returnValue, resolve$
|
|
31792
|
+
function recursivelyFlushAsyncActWork(returnValue, resolve$2, reject) {
|
|
30384
31793
|
var queue = ReactSharedInternals$1.actQueue;
|
|
30385
31794
|
if (null !== queue) if (0 !== queue.length) try {
|
|
30386
31795
|
flushActQueue(queue);
|
|
30387
31796
|
enqueueTask(function() {
|
|
30388
|
-
return recursivelyFlushAsyncActWork(returnValue, resolve$
|
|
31797
|
+
return recursivelyFlushAsyncActWork(returnValue, resolve$2, reject);
|
|
30389
31798
|
});
|
|
30390
31799
|
return;
|
|
30391
31800
|
} catch (error) {
|
|
30392
31801
|
ReactSharedInternals$1.thrownErrors.push(error);
|
|
30393
31802
|
}
|
|
30394
31803
|
else ReactSharedInternals$1.actQueue = null;
|
|
30395
|
-
0 < ReactSharedInternals$1.thrownErrors.length ? (queue = aggregateErrors(ReactSharedInternals$1.thrownErrors), ReactSharedInternals$1.thrownErrors.length = 0, reject(queue)) : resolve$
|
|
31804
|
+
0 < ReactSharedInternals$1.thrownErrors.length ? (queue = aggregateErrors(ReactSharedInternals$1.thrownErrors), ReactSharedInternals$1.thrownErrors.length = 0, reject(queue)) : resolve$2(returnValue);
|
|
30396
31805
|
}
|
|
30397
31806
|
function flushActQueue(queue) {
|
|
30398
31807
|
if (!isFlushing) {
|
|
@@ -30553,14 +31962,14 @@ var require_react_development = /* @__PURE__ */ __commonJSMin(((exports, module)
|
|
|
30553
31962
|
queueSeveralMicrotasks(function() {
|
|
30554
31963
|
didAwaitActCall || didWarnNoAwaitAct || (didWarnNoAwaitAct = !0, console.error("You called act(async () => ...) without await. This could lead to unexpected testing behaviour, interleaving multiple act calls and mixing their scopes. You should - await act(async () => ...);"));
|
|
30555
31964
|
});
|
|
30556
|
-
return { then: function(resolve$
|
|
31965
|
+
return { then: function(resolve$2, reject) {
|
|
30557
31966
|
didAwaitActCall = !0;
|
|
30558
31967
|
thenable.then(function(returnValue) {
|
|
30559
31968
|
popActScope(prevActQueue, prevActScopeDepth);
|
|
30560
31969
|
if (0 === prevActScopeDepth) {
|
|
30561
31970
|
try {
|
|
30562
31971
|
flushActQueue(queue), enqueueTask(function() {
|
|
30563
|
-
return recursivelyFlushAsyncActWork(returnValue, resolve$
|
|
31972
|
+
return recursivelyFlushAsyncActWork(returnValue, resolve$2, reject);
|
|
30564
31973
|
});
|
|
30565
31974
|
} catch (error$0) {
|
|
30566
31975
|
ReactSharedInternals$1.thrownErrors.push(error$0);
|
|
@@ -30570,7 +31979,7 @@ var require_react_development = /* @__PURE__ */ __commonJSMin(((exports, module)
|
|
|
30570
31979
|
ReactSharedInternals$1.thrownErrors.length = 0;
|
|
30571
31980
|
reject(_thrownError);
|
|
30572
31981
|
}
|
|
30573
|
-
} else resolve$
|
|
31982
|
+
} else resolve$2(returnValue);
|
|
30574
31983
|
}, function(error) {
|
|
30575
31984
|
popActScope(prevActQueue, prevActScopeDepth);
|
|
30576
31985
|
0 < ReactSharedInternals$1.thrownErrors.length ? (error = aggregateErrors(ReactSharedInternals$1.thrownErrors), ReactSharedInternals$1.thrownErrors.length = 0, reject(error)) : reject(error);
|
|
@@ -30583,11 +31992,11 @@ var require_react_development = /* @__PURE__ */ __commonJSMin(((exports, module)
|
|
|
30583
31992
|
didAwaitActCall || didWarnNoAwaitAct || (didWarnNoAwaitAct = !0, console.error("A component suspended inside an `act` scope, but the `act` call was not awaited. When testing React components that depend on asynchronous data, you must await the result:\n\nawait act(() => ...)"));
|
|
30584
31993
|
}), ReactSharedInternals$1.actQueue = null);
|
|
30585
31994
|
if (0 < ReactSharedInternals$1.thrownErrors.length) throw callback = aggregateErrors(ReactSharedInternals$1.thrownErrors), ReactSharedInternals$1.thrownErrors.length = 0, callback;
|
|
30586
|
-
return { then: function(resolve$
|
|
31995
|
+
return { then: function(resolve$2, reject) {
|
|
30587
31996
|
didAwaitActCall = !0;
|
|
30588
31997
|
0 === prevActScopeDepth ? (ReactSharedInternals$1.actQueue = queue, enqueueTask(function() {
|
|
30589
|
-
return recursivelyFlushAsyncActWork(returnValue$jscomp$0, resolve$
|
|
30590
|
-
})) : resolve$
|
|
31998
|
+
return recursivelyFlushAsyncActWork(returnValue$jscomp$0, resolve$2, reject);
|
|
31999
|
+
})) : resolve$2(returnValue$jscomp$0);
|
|
30591
32000
|
} };
|
|
30592
32001
|
};
|
|
30593
32002
|
exports.cache = function(fn) {
|
|
@@ -31363,7 +32772,7 @@ const appointmentBookingRefSchema = resourceRefSchema("appointment");
|
|
|
31363
32772
|
const appointmentResourceRefSchema = resourceRefSchema("appointment-resource");
|
|
31364
32773
|
const appointmentServiceRefSchema = resourceRefSchema("appointment-service");
|
|
31365
32774
|
const bookingCustomerRefSchema = resourceRefSchema("customer");
|
|
31366
|
-
const
|
|
32775
|
+
const eventSeriesRegistrationRefSchema = resourceRefSchema("event-series-registration");
|
|
31367
32776
|
const customerMembershipRefSchema = resourceRefSchema("customer-membership");
|
|
31368
32777
|
const customerPassRefSchema = resourceRefSchema("customer-pass");
|
|
31369
32778
|
const eventRegistrationRefSchema = resourceRefSchema("event-registration");
|
|
@@ -31544,13 +32953,13 @@ const seedAppointmentBookingCommandSchema = z.object({
|
|
|
31544
32953
|
});
|
|
31545
32954
|
})
|
|
31546
32955
|
}).strict();
|
|
31547
|
-
const
|
|
31548
|
-
type: z.literal("
|
|
31549
|
-
ref:
|
|
32956
|
+
const seedEventSeriesRegistrationCommandSchema = z.object({
|
|
32957
|
+
type: z.literal("seedEventSeriesRegistration"),
|
|
32958
|
+
ref: eventSeriesRegistrationRefSchema,
|
|
31550
32959
|
input: z.object({
|
|
31551
32960
|
customerRef: bookingCustomerRefSchema,
|
|
31552
32961
|
eventRef: eventSeriesRefSchema$1,
|
|
31553
|
-
status: z.enum(
|
|
32962
|
+
status: z.enum(eventSeriesRegistrationStatuses).default("enrolled"),
|
|
31554
32963
|
paymentMethod: z.literal("free").default("free"),
|
|
31555
32964
|
paymentStatus: z.literal("not_required").default("not_required"),
|
|
31556
32965
|
formSubmission: unknownRecordSchema.default({}),
|
|
@@ -31592,8 +33001,8 @@ const portalOrderSubjectSchema = z.discriminatedUnion("kind", [z.object({
|
|
|
31592
33001
|
kind: z.literal("event_registration"),
|
|
31593
33002
|
eventRegistrationRef: eventRegistrationRefSchema
|
|
31594
33003
|
}).strict(), z.object({
|
|
31595
|
-
kind: z.literal("
|
|
31596
|
-
|
|
33004
|
+
kind: z.literal("event_series_registration"),
|
|
33005
|
+
eventSeriesRegistrationRef: eventSeriesRegistrationRefSchema
|
|
31597
33006
|
}).strict()]);
|
|
31598
33007
|
const seedPortalOrderCommandSchema = z.object({
|
|
31599
33008
|
type: z.literal("seedPortalOrder"),
|
|
@@ -32253,7 +33662,7 @@ const siteCommandSchema = z.discriminatedUnion("type", [
|
|
|
32253
33662
|
grantCustomerPassCommandSchema,
|
|
32254
33663
|
grantCustomerMembershipCommandSchema,
|
|
32255
33664
|
seedAppointmentBookingCommandSchema,
|
|
32256
|
-
|
|
33665
|
+
seedEventSeriesRegistrationCommandSchema,
|
|
32257
33666
|
seedEventRegistrationCommandSchema,
|
|
32258
33667
|
seedPassRedemptionCommandSchema,
|
|
32259
33668
|
seedPortalOrderCommandSchema,
|
|
@@ -32377,7 +33786,7 @@ const commandScopes = [
|
|
|
32377
33786
|
"customer_pass",
|
|
32378
33787
|
"customer_membership",
|
|
32379
33788
|
"appointment",
|
|
32380
|
-
"
|
|
33789
|
+
"event_series_registration",
|
|
32381
33790
|
"event_registration",
|
|
32382
33791
|
"pass_redemption",
|
|
32383
33792
|
"order",
|
|
@@ -32972,8 +34381,8 @@ const siteCommandMetadata = {
|
|
|
32972
34381
|
capability: "bookings.appointments.write",
|
|
32973
34382
|
support: "supported"
|
|
32974
34383
|
},
|
|
32975
|
-
|
|
32976
|
-
scope: "
|
|
34384
|
+
seedEventSeriesRegistration: {
|
|
34385
|
+
scope: "event_series_registration",
|
|
32977
34386
|
risk: "medium",
|
|
32978
34387
|
writeSemantics: "upsert",
|
|
32979
34388
|
lifecycleIntent: "seed",
|
|
@@ -32982,7 +34391,7 @@ const siteCommandMetadata = {
|
|
|
32982
34391
|
previewability: "dry_run_only",
|
|
32983
34392
|
allowedRuntimes: allRuntimes,
|
|
32984
34393
|
intendedExposure: recipeExposure,
|
|
32985
|
-
capability: "bookings.
|
|
34394
|
+
capability: "bookings.event_series_registrations.write",
|
|
32986
34395
|
support: "supported"
|
|
32987
34396
|
},
|
|
32988
34397
|
seedEventRegistration: {
|
|
@@ -33199,7 +34608,7 @@ const bookingRefCommandTypeValues = [
|
|
|
33199
34608
|
"grantCustomerPass",
|
|
33200
34609
|
"grantCustomerMembership",
|
|
33201
34610
|
"seedAppointmentBooking",
|
|
33202
|
-
"
|
|
34611
|
+
"seedEventSeriesRegistration",
|
|
33203
34612
|
"seedEventRegistration",
|
|
33204
34613
|
"seedPassRedemption",
|
|
33205
34614
|
"seedPortalOrder",
|
|
@@ -33253,7 +34662,7 @@ function bookingCommandRefContributions(command) {
|
|
|
33253
34662
|
field: "input.resourceRef"
|
|
33254
34663
|
}
|
|
33255
34664
|
]);
|
|
33256
|
-
case "
|
|
34665
|
+
case "seedEventSeriesRegistration": return providedWithRequirements(command.ref, [{
|
|
33257
34666
|
ref: command.input.customerRef,
|
|
33258
34667
|
field: "input.customerRef"
|
|
33259
34668
|
}, {
|
|
@@ -33329,9 +34738,9 @@ function portalOrderSubjectRefContribution(subject) {
|
|
|
33329
34738
|
ref: subject.eventRegistrationRef,
|
|
33330
34739
|
field: "input.subject.eventRegistrationRef"
|
|
33331
34740
|
};
|
|
33332
|
-
case "
|
|
33333
|
-
ref: subject.
|
|
33334
|
-
field: "input.subject.
|
|
34741
|
+
case "event_series_registration": return {
|
|
34742
|
+
ref: subject.eventSeriesRegistrationRef,
|
|
34743
|
+
field: "input.subject.eventSeriesRegistrationRef"
|
|
33335
34744
|
};
|
|
33336
34745
|
default: return assertNever(subject);
|
|
33337
34746
|
}
|
|
@@ -33533,13 +34942,13 @@ function optionalRef(ref, field) {
|
|
|
33533
34942
|
field
|
|
33534
34943
|
}] : [];
|
|
33535
34944
|
}
|
|
33536
|
-
function collectVenueRefsFromIntervals(intervals, path$
|
|
33537
|
-
return intervals.flatMap((interval, index) => optionalRef(interval.venueRef, `${path$
|
|
34945
|
+
function collectVenueRefsFromIntervals(intervals, path$2) {
|
|
34946
|
+
return intervals.flatMap((interval, index) => optionalRef(interval.venueRef, `${path$2}[${index}].venueRef`));
|
|
33538
34947
|
}
|
|
33539
|
-
function collectNavigationRefs(items, path$
|
|
34948
|
+
function collectNavigationRefs(items, path$2) {
|
|
33540
34949
|
return items.flatMap((item, index) => {
|
|
33541
34950
|
if (!isRecord$5(item)) return [];
|
|
33542
|
-
const itemPath = `${path$
|
|
34951
|
+
const itemPath = `${path$2}[${index}]`;
|
|
33543
34952
|
const refs = [];
|
|
33544
34953
|
if (typeof item.pageRef === "string") {
|
|
33545
34954
|
refs.push({
|
|
@@ -33559,26 +34968,26 @@ function collectNavigationRefs(items, path$1) {
|
|
|
33559
34968
|
return refs;
|
|
33560
34969
|
});
|
|
33561
34970
|
}
|
|
33562
|
-
function collectBlockRefs(blocks, path$
|
|
34971
|
+
function collectBlockRefs(blocks, path$2) {
|
|
33563
34972
|
return blocks.flatMap((block, index) => {
|
|
33564
34973
|
if (!isRecord$5(block)) return [];
|
|
33565
|
-
const blockPath = `${path$
|
|
34974
|
+
const blockPath = `${path$2}[${index}]`;
|
|
33566
34975
|
return [...collectNestedRefs(block.props, `${blockPath}.props`), ...collectNestedRefs(block.children, `${blockPath}.children`)];
|
|
33567
34976
|
});
|
|
33568
34977
|
}
|
|
33569
|
-
function collectNestedRefs(value, path$
|
|
34978
|
+
function collectNestedRefs(value, path$2) {
|
|
33570
34979
|
if (typeof value === "string") {
|
|
33571
34980
|
const parsed = parseResourceRef(value);
|
|
33572
34981
|
return parsed.ok ? [{
|
|
33573
34982
|
ref: value,
|
|
33574
|
-
field: path$
|
|
34983
|
+
field: path$2
|
|
33575
34984
|
}] : [];
|
|
33576
34985
|
}
|
|
33577
34986
|
if (Array.isArray(value)) {
|
|
33578
|
-
return value.flatMap((item, index) => collectNestedRefs(item, `${path$
|
|
34987
|
+
return value.flatMap((item, index) => collectNestedRefs(item, `${path$2}[${index}]`));
|
|
33579
34988
|
}
|
|
33580
34989
|
if (!isRecord$5(value)) return [];
|
|
33581
|
-
return Object.entries(value).flatMap(([key, child]) => collectNestedRefs(child, `${path$
|
|
34990
|
+
return Object.entries(value).flatMap(([key, child]) => collectNestedRefs(child, `${path$2}.${key}`));
|
|
33582
34991
|
}
|
|
33583
34992
|
|
|
33584
34993
|
//#endregion
|
|
@@ -33765,7 +35174,7 @@ function matchSiteCommand(command, match) {
|
|
|
33765
35174
|
case "grantCustomerPass": return match(command);
|
|
33766
35175
|
case "grantCustomerMembership": return match(command);
|
|
33767
35176
|
case "seedAppointmentBooking": return match(command);
|
|
33768
|
-
case "
|
|
35177
|
+
case "seedEventSeriesRegistration": return match(command);
|
|
33769
35178
|
case "seedEventRegistration": return match(command);
|
|
33770
35179
|
case "seedPassRedemption": return match(command);
|
|
33771
35180
|
case "seedPortalOrder": return match(command);
|
|
@@ -33844,8 +35253,8 @@ function invalidPayloadError(input, issue) {
|
|
|
33844
35253
|
message: `${field}: ${issue.message}`
|
|
33845
35254
|
};
|
|
33846
35255
|
}
|
|
33847
|
-
function commandIndexForIssuePath(path$
|
|
33848
|
-
return path$
|
|
35256
|
+
function commandIndexForIssuePath(path$2) {
|
|
35257
|
+
return path$2[0] === "commands" && typeof path$2[1] === "number" ? path$2[1] : -1;
|
|
33849
35258
|
}
|
|
33850
35259
|
function commandTypeForIssue(input, commandIndex) {
|
|
33851
35260
|
if (!isRecord$5(input) || !isUnknownArray(input.commands)) return null;
|
|
@@ -33855,9 +35264,9 @@ function commandTypeForIssue(input, commandIndex) {
|
|
|
33855
35264
|
function isUnknownArray(value) {
|
|
33856
35265
|
return Array.isArray(value);
|
|
33857
35266
|
}
|
|
33858
|
-
function formatIssuePath(path$
|
|
33859
|
-
if (path$
|
|
33860
|
-
return path$
|
|
35267
|
+
function formatIssuePath(path$2) {
|
|
35268
|
+
if (path$2.length === 0) return "<root>";
|
|
35269
|
+
return path$2.reduce((formatted, segment) => {
|
|
33861
35270
|
if (typeof segment === "number") return `${formatted}[${segment}]`;
|
|
33862
35271
|
if (formatted.length === 0) return String(segment);
|
|
33863
35272
|
return `${formatted}.${String(segment)}`;
|
|
@@ -34022,7 +35431,7 @@ function checkPortalOrdersHaveUniqueSubjects(batch) {
|
|
|
34022
35431
|
function portalOrderSubjectKey(subject) {
|
|
34023
35432
|
switch (subject.kind) {
|
|
34024
35433
|
case "event_registration": return `event_registration:${subject.eventRegistrationRef}`;
|
|
34025
|
-
case "
|
|
35434
|
+
case "event_series_registration": return `event_series_registration:${subject.eventSeriesRegistrationRef}`;
|
|
34026
35435
|
}
|
|
34027
35436
|
}
|
|
34028
35437
|
function indexOfferingCommands(batch) {
|
|
@@ -34071,7 +35480,7 @@ function indexOfferingCommands(batch) {
|
|
|
34071
35480
|
case "grantCustomerPass":
|
|
34072
35481
|
case "grantCustomerMembership":
|
|
34073
35482
|
case "seedAppointmentBooking":
|
|
34074
|
-
case "
|
|
35483
|
+
case "seedEventSeriesRegistration":
|
|
34075
35484
|
case "seedEventRegistration":
|
|
34076
35485
|
case "seedPassRedemption":
|
|
34077
35486
|
case "seedPortalOrder":
|
|
@@ -34590,6 +35999,8 @@ const FILE_SIZE_LIMITS = {
|
|
|
34590
35999
|
instagramZip: unsafeAsPositiveByteCount(100 * 1024 * 1024),
|
|
34591
36000
|
bookingEvidence: unsafeAsPositiveByteCount(25 * 1024 * 1024)
|
|
34592
36001
|
};
|
|
36002
|
+
/** Shared admission ceiling for SDK-management media uploads before intent-specific finalization. */
|
|
36003
|
+
const MAX_MANAGEMENT_MEDIA_UPLOAD_BYTES = unsafeAsPositiveByteCount(250 * 1024 * 1024);
|
|
34593
36004
|
const DEFAULT_PRIVATE_FILE_SIGNED_URL_EXPIRY_SECONDS = unsafeAsPositiveSeconds(5 * 60);
|
|
34594
36005
|
const MAX_SAFE_DOWNLOAD_FILENAME_LENGTH = 180;
|
|
34595
36006
|
function asPositiveByteCount(value) {
|
|
@@ -34853,6 +36264,16 @@ function classifyKnownMimeType(mimeType) {
|
|
|
34853
36264
|
default: return err("unsupported-mime-type");
|
|
34854
36265
|
}
|
|
34855
36266
|
}
|
|
36267
|
+
function managementMediaLibraryUploadLimitForMimeType(mimeType) {
|
|
36268
|
+
const kind = classifyKnownMimeType(mimeType);
|
|
36269
|
+
if (!kind.ok) return kind;
|
|
36270
|
+
const renderUse = kind.value.tag;
|
|
36271
|
+
if (renderUse !== "image" && renderUse !== "video" && renderUse !== "audio") {
|
|
36272
|
+
return err("unsupported-mime-type");
|
|
36273
|
+
}
|
|
36274
|
+
const libraryLimit = mediaLibraryMaxBytes([renderUse]);
|
|
36275
|
+
return ok(unsafeAsPositiveByteCount(Math.min(libraryLimit, MAX_MANAGEMENT_MEDIA_UPLOAD_BYTES)));
|
|
36276
|
+
}
|
|
34856
36277
|
function isMimeTypeAllowedForPolicy(mimeType, policy) {
|
|
34857
36278
|
const kindResult = classifyKnownMimeType(mimeType);
|
|
34858
36279
|
return kindResult.ok && isFileKindAllowed(kindResult.value, policy);
|
|
@@ -35420,9 +36841,9 @@ function buildDurableFileStoragePath(input) {
|
|
|
35420
36841
|
input.fileAssetId,
|
|
35421
36842
|
input.safeDownloadFilename
|
|
35422
36843
|
];
|
|
35423
|
-
const path$
|
|
35424
|
-
if (!path$
|
|
35425
|
-
return ok(unsafeAsSiteScopedStoragePath(path$
|
|
36844
|
+
const path$2 = joinStoragePathSegments(segments);
|
|
36845
|
+
if (!path$2.ok) return path$2;
|
|
36846
|
+
return ok(unsafeAsSiteScopedStoragePath(path$2.value));
|
|
35426
36847
|
}
|
|
35427
36848
|
function parseStoragePath(value) {
|
|
35428
36849
|
return joinStoragePathSegments(value.split("/"));
|
|
@@ -36847,10 +38268,10 @@ function emptyStatePatch(patch) {
|
|
|
36847
38268
|
rejection_reason: patch.rejection_reason ?? null
|
|
36848
38269
|
};
|
|
36849
38270
|
}
|
|
36850
|
-
function traverse(items, parse) {
|
|
38271
|
+
function traverse(items, parse$1) {
|
|
36851
38272
|
const values = [];
|
|
36852
38273
|
for (const item of items) {
|
|
36853
|
-
const parsed = parse(item);
|
|
38274
|
+
const parsed = parse$1(item);
|
|
36854
38275
|
if (!parsed.ok) return parsed;
|
|
36855
38276
|
values.push(parsed.value);
|
|
36856
38277
|
}
|
|
@@ -37531,18 +38952,18 @@ function resolveFileDownload(input) {
|
|
|
37531
38952
|
assetKind: input.asset.kind
|
|
37532
38953
|
});
|
|
37533
38954
|
}
|
|
37534
|
-
const access = authorizeFileAccess(input.actor, input.asset.accessPolicy);
|
|
37535
|
-
if (!access.ok) {
|
|
38955
|
+
const access$1 = authorizeFileAccess(input.actor, input.asset.accessPolicy);
|
|
38956
|
+
if (!access$1.ok) {
|
|
37536
38957
|
return err({
|
|
37537
38958
|
tag: "access-denied",
|
|
37538
|
-
reason: access.error
|
|
38959
|
+
reason: access$1.error
|
|
37539
38960
|
});
|
|
37540
38961
|
}
|
|
37541
38962
|
return ok(fileAccessGrantToDownloadPlan({
|
|
37542
38963
|
asset: input.asset,
|
|
37543
38964
|
requestedUse: input.requestedUse,
|
|
37544
38965
|
disposition: input.disposition ?? defaultDispositionForFileUse(input.requestedUse),
|
|
37545
|
-
grant: access.value
|
|
38966
|
+
grant: access$1.value
|
|
37546
38967
|
}));
|
|
37547
38968
|
}
|
|
37548
38969
|
function fileAccessGrantToDownloadPlan(input) {
|
|
@@ -38073,9 +39494,9 @@ const EXTENSIONS_BY_MIME_TYPE = {
|
|
|
38073
39494
|
};
|
|
38074
39495
|
function fileExtension(filename) {
|
|
38075
39496
|
const pathSegments = filename.replace(/\\/g, "/").split("/");
|
|
38076
|
-
const basename = pathSegments[pathSegments.length - 1] ?? filename;
|
|
38077
|
-
const dotIndex = basename.lastIndexOf(".");
|
|
38078
|
-
return dotIndex <= 0 ? "" : basename.slice(dotIndex).toLowerCase();
|
|
39497
|
+
const basename$1 = pathSegments[pathSegments.length - 1] ?? filename;
|
|
39498
|
+
const dotIndex = basename$1.lastIndexOf(".");
|
|
39499
|
+
return dotIndex <= 0 ? "" : basename$1.slice(dotIndex).toLowerCase();
|
|
38079
39500
|
}
|
|
38080
39501
|
function getMimeType(filename) {
|
|
38081
39502
|
return MIME_TYPES_BY_EXTENSION[fileExtension(filename)] ?? "application/octet-stream";
|
|
@@ -38623,20 +40044,20 @@ function formatMediaUsageStage(stage) {
|
|
|
38623
40044
|
function mediaUsageLocationLabel(location$1) {
|
|
38624
40045
|
switch (location$1.kind) {
|
|
38625
40046
|
case "pageBlock": {
|
|
38626
|
-
const path$
|
|
40047
|
+
const path$2 = location$1.pageDraftPath ?? location$1.pagePublishedPath ?? null;
|
|
38627
40048
|
const title = location$1.pageTitle ?? "Untitled page";
|
|
38628
|
-
const where = path$
|
|
40049
|
+
const where = path$2 ? `${title} (${path$2})` : title;
|
|
38629
40050
|
return `${where} — ${formatMediaUsageStage(location$1.stage)} — Block: ${location$1.blockKind}`;
|
|
38630
40051
|
}
|
|
38631
40052
|
case "entrySlotBlock": {
|
|
38632
|
-
const path$
|
|
38633
|
-
const where = path$
|
|
40053
|
+
const path$2 = location$1.routeDraftPath ?? location$1.routePublishedPath ?? null;
|
|
40054
|
+
const where = path$2 ? `${location$1.entryTitle} (${path$2})` : location$1.entryTitle;
|
|
38634
40055
|
return `${where} — Page Content: ${location$1.slotKey} — ${formatMediaUsageStage(location$1.stage)} — Block: ${location$1.blockKind}`;
|
|
38635
40056
|
}
|
|
38636
40057
|
case "surfaceSlotBlock": return `Page surface ${location$1.pageSurfaceId} — Page Content: ${location$1.slotKey} — ${formatMediaUsageStage(location$1.stage)} — Block: ${location$1.blockKind}`;
|
|
38637
40058
|
case "contentEntry": {
|
|
38638
|
-
const path$
|
|
38639
|
-
const where = path$
|
|
40059
|
+
const path$2 = location$1.routeDraftPath ?? location$1.routePublishedPath ?? null;
|
|
40060
|
+
const where = path$2 ? `${location$1.entryTitle} (${path$2})` : location$1.entryTitle;
|
|
38640
40061
|
return `${where} — ${formatMediaUsageStage(location$1.stage)}`;
|
|
38641
40062
|
}
|
|
38642
40063
|
case "productFileDownload": return `${location$1.productTitle} — Product download`;
|
|
@@ -38666,10 +40087,17 @@ function hasMediaUsage(response) {
|
|
|
38666
40087
|
return response.items.some((item) => item.usedCount > 0);
|
|
38667
40088
|
}
|
|
38668
40089
|
function planBulkDeleteCompletionToast(result) {
|
|
40090
|
+
const hasWarnings = (result.warnings?.length ?? 0) > 0;
|
|
38669
40091
|
if (result.failed > 0) {
|
|
38670
40092
|
return {
|
|
38671
40093
|
kind: "error",
|
|
38672
|
-
message: `Deleted ${result.deleted}. Failed to delete ${result.failed}.`
|
|
40094
|
+
message: hasWarnings ? `Deleted ${result.deleted}. Failed to delete ${result.failed}. Some cleanup could not be completed.` : `Deleted ${result.deleted}. Failed to delete ${result.failed}.`
|
|
40095
|
+
};
|
|
40096
|
+
}
|
|
40097
|
+
if (hasWarnings) {
|
|
40098
|
+
return {
|
|
40099
|
+
kind: "warning",
|
|
40100
|
+
message: result.deleted === 1 ? "Deleted 1 media item, but some cleanup could not be completed." : `Deleted ${result.deleted} media items, but some cleanup could not be completed.`
|
|
38673
40101
|
};
|
|
38674
40102
|
}
|
|
38675
40103
|
return {
|
|
@@ -38934,7 +40362,7 @@ const ORDER = [
|
|
|
38934
40362
|
];
|
|
38935
40363
|
const getImageUrl = (asset, params = {}, basePath = DEFAULT_BASE_PATH) => {
|
|
38936
40364
|
const trimmedBase = basePath.endsWith("/") ? basePath.slice(0, Math.max(basePath.length - 1, 0)) : basePath;
|
|
38937
|
-
const path$
|
|
40365
|
+
const path$2 = `${trimmedBase}/${encodeURIComponent(asset.id)}`;
|
|
38938
40366
|
const searchParams = new URLSearchParams();
|
|
38939
40367
|
if (params.w !== undefined) searchParams.set("w", formatNumber(params.w));
|
|
38940
40368
|
if (params.h !== undefined) searchParams.set("h", formatNumber(params.h));
|
|
@@ -38953,7 +40381,7 @@ const getImageUrl = (asset, params = {}, basePath = DEFAULT_BASE_PATH) => {
|
|
|
38953
40381
|
if (value !== null) sorted.set(key, value);
|
|
38954
40382
|
});
|
|
38955
40383
|
const query = sorted.toString();
|
|
38956
|
-
return query ? `${path$
|
|
40384
|
+
return query ? `${path$2}?${query}` : path$2;
|
|
38957
40385
|
};
|
|
38958
40386
|
|
|
38959
40387
|
//#endregion
|
|
@@ -39723,8 +41151,8 @@ function mediaIdentifierOrFallback(current, fallback) {
|
|
|
39723
41151
|
function isRecord$4(value) {
|
|
39724
41152
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
39725
41153
|
}
|
|
39726
|
-
function formatContentFieldPath(path$
|
|
39727
|
-
return path$
|
|
41154
|
+
function formatContentFieldPath(path$2) {
|
|
41155
|
+
return path$2.map((segment, index) => typeof segment === "number" ? `[${segment}]` : index === 0 ? segment : `.${segment}`).join("");
|
|
39728
41156
|
}
|
|
39729
41157
|
function summarizeContentMediaValue(value) {
|
|
39730
41158
|
if (typeof value === "string") {
|
|
@@ -39737,7 +41165,7 @@ function summarizeContentMediaValue(value) {
|
|
|
39737
41165
|
if (isRecord$4(value)) return `object(${Object.keys(value).sort().join(",")})`;
|
|
39738
41166
|
return typeof value;
|
|
39739
41167
|
}
|
|
39740
|
-
function auditMediaValue(context, path$
|
|
41168
|
+
function auditMediaValue(context, path$2, allowedMediaTypes$1, value) {
|
|
39741
41169
|
const expectedMediaType = allowedMediaTypes$1[0];
|
|
39742
41170
|
if (value === undefined || value === null) return [];
|
|
39743
41171
|
if (typeof value === "string") {
|
|
@@ -39746,7 +41174,7 @@ function auditMediaValue(context, path$1, allowedMediaTypes$1, value) {
|
|
|
39746
41174
|
return [{
|
|
39747
41175
|
tag: "blank-string",
|
|
39748
41176
|
context,
|
|
39749
|
-
path: path$
|
|
41177
|
+
path: path$2,
|
|
39750
41178
|
valueSummary: summarizeContentMediaValue(value),
|
|
39751
41179
|
expectedMediaType
|
|
39752
41180
|
}];
|
|
@@ -39754,7 +41182,7 @@ function auditMediaValue(context, path$1, allowedMediaTypes$1, value) {
|
|
|
39754
41182
|
return [{
|
|
39755
41183
|
tag: "legacy-string",
|
|
39756
41184
|
context,
|
|
39757
|
-
path: path$
|
|
41185
|
+
path: path$2,
|
|
39758
41186
|
valueSummary: summarizeContentMediaValue(value),
|
|
39759
41187
|
originalValue: value,
|
|
39760
41188
|
expectedMediaType,
|
|
@@ -39770,7 +41198,7 @@ function auditMediaValue(context, path$1, allowedMediaTypes$1, value) {
|
|
|
39770
41198
|
return [{
|
|
39771
41199
|
tag: "malformed-object",
|
|
39772
41200
|
context,
|
|
39773
|
-
path: path$
|
|
41201
|
+
path: path$2,
|
|
39774
41202
|
valueSummary: summarizeContentMediaValue(value),
|
|
39775
41203
|
expectedMediaType
|
|
39776
41204
|
}];
|
|
@@ -40173,8 +41601,8 @@ function mediaTypeFromAsset(row) {
|
|
|
40173
41601
|
}
|
|
40174
41602
|
function legacyStorageValues(siteId, storagePath) {
|
|
40175
41603
|
const sitePrefix = `sites/${siteId}/`;
|
|
40176
|
-
const relative = storagePath.startsWith(sitePrefix) ? storagePath.slice(sitePrefix.length) : storagePath;
|
|
40177
|
-
return [...new Set([storagePath, `/${relative}`])];
|
|
41604
|
+
const relative$1 = storagePath.startsWith(sitePrefix) ? storagePath.slice(sitePrefix.length) : storagePath;
|
|
41605
|
+
return [...new Set([storagePath, `/${relative$1}`])];
|
|
40178
41606
|
}
|
|
40179
41607
|
function assetCandidates(rows) {
|
|
40180
41608
|
return rows.flatMap((row) => {
|
|
@@ -40562,10 +41990,10 @@ var BrowserImage = class extends ImageBase {
|
|
|
40562
41990
|
return Promise.reject(new Error(`Cannot load buffer as an image in browser`));
|
|
40563
41991
|
}
|
|
40564
41992
|
this.image = img;
|
|
40565
|
-
return new Promise((resolve$
|
|
41993
|
+
return new Promise((resolve$2, reject) => {
|
|
40566
41994
|
const onImageLoad = () => {
|
|
40567
41995
|
this._initCanvas();
|
|
40568
|
-
resolve$
|
|
41996
|
+
resolve$2(this);
|
|
40569
41997
|
};
|
|
40570
41998
|
if (img.complete) {
|
|
40571
41999
|
onImageLoad();
|
|
@@ -42151,7 +43579,7 @@ const isMediaValue = (value) => isRecordValue(value) && !("attrs" in value) && t
|
|
|
42151
43579
|
//#endregion
|
|
42152
43580
|
//#region src/content/media-manifest.ts
|
|
42153
43581
|
const MANIFEST_VERSION = 2;
|
|
42154
|
-
const getManifestPath = (contentDir) => path.join(contentDir, "media", ".manifest.json");
|
|
43582
|
+
const getManifestPath = (contentDir) => path$1.join(contentDir, "media", ".manifest.json");
|
|
42155
43583
|
const ensureDir = async (dirPath) => {
|
|
42156
43584
|
try {
|
|
42157
43585
|
await fs.mkdir(dirPath, { recursive: true });
|
|
@@ -42243,7 +43671,7 @@ async function readMediaManifest(contentDir) {
|
|
|
42243
43671
|
return createEmptyMediaManifest();
|
|
42244
43672
|
}
|
|
42245
43673
|
async function writeMediaManifest(contentDir, manifest) {
|
|
42246
|
-
const mediaDir = path.join(contentDir, "media");
|
|
43674
|
+
const mediaDir = path$1.join(contentDir, "media");
|
|
42247
43675
|
await ensureDir(mediaDir);
|
|
42248
43676
|
const updatedManifest = {
|
|
42249
43677
|
...manifest,
|
|
@@ -42379,7 +43807,7 @@ const DEFAULT_RETRY_CONFIG = {
|
|
|
42379
43807
|
* Sleep for a given number of milliseconds.
|
|
42380
43808
|
*/
|
|
42381
43809
|
function sleep(ms) {
|
|
42382
|
-
return new Promise((resolve$
|
|
43810
|
+
return new Promise((resolve$2) => setTimeout(resolve$2, ms));
|
|
42383
43811
|
}
|
|
42384
43812
|
/**
|
|
42385
43813
|
* Calculate exponential backoff delay with jitter.
|
|
@@ -42488,8 +43916,8 @@ const TRANSFORMABLE_EXTENSIONS = [
|
|
|
42488
43916
|
* Check if a file path is an image that can be transformed by Supabase.
|
|
42489
43917
|
* @param path - The file path or storage path
|
|
42490
43918
|
*/
|
|
42491
|
-
function isTransformableImage(path$
|
|
42492
|
-
const ext = path$
|
|
43919
|
+
function isTransformableImage(path$2) {
|
|
43920
|
+
const ext = path$2.split(".").pop()?.toLowerCase();
|
|
42493
43921
|
return TRANSFORMABLE_EXTENSIONS.includes(ext ?? "");
|
|
42494
43922
|
}
|
|
42495
43923
|
/**
|
|
@@ -42800,7 +44228,7 @@ function collectRichTextImageAttrs(value, out = [], location$1 = "") {
|
|
|
42800
44228
|
//#endregion
|
|
42801
44229
|
//#region src/media/pull-manifest.ts
|
|
42802
44230
|
const buildLocalFilename = (identifier, asset) => {
|
|
42803
|
-
const extensionFromFilename = path.extname(asset.filename ?? "");
|
|
44231
|
+
const extensionFromFilename = path$1.extname(asset.filename ?? "");
|
|
42804
44232
|
const extension = extensionFromFilename || getExtensionForMimeType(asset.mimeType) || "";
|
|
42805
44233
|
return extension ? `${identifier}${extension}` : identifier;
|
|
42806
44234
|
};
|
|
@@ -43122,6 +44550,165 @@ function sha256(buffer) {
|
|
|
43122
44550
|
return createHash("sha256").update(buffer).digest("hex");
|
|
43123
44551
|
}
|
|
43124
44552
|
|
|
44553
|
+
//#endregion
|
|
44554
|
+
//#region src/files/stable-binary-file.ts
|
|
44555
|
+
function isNodeError(error) {
|
|
44556
|
+
return error instanceof Error && "code" in error;
|
|
44557
|
+
}
|
|
44558
|
+
function observationsMatch(left, right) {
|
|
44559
|
+
return left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeMs === right.mtimeMs && left.ctimeMs === right.ctimeMs;
|
|
44560
|
+
}
|
|
44561
|
+
function admitStableBinaryRead(stats, maxBytes) {
|
|
44562
|
+
if (!stats.isFile()) return err({ tag: "not_a_regular_file" });
|
|
44563
|
+
return stats.size > maxBytes ? err({
|
|
44564
|
+
tag: "file_too_large",
|
|
44565
|
+
maxBytes,
|
|
44566
|
+
actualBytes: stats.size
|
|
44567
|
+
}) : ok(undefined);
|
|
44568
|
+
}
|
|
44569
|
+
function mapStableBinaryReadFailure(error) {
|
|
44570
|
+
if (!isNodeError(error)) return { tag: "read_failed" };
|
|
44571
|
+
if (error.code === "ENOENT") return { tag: "file_not_found" };
|
|
44572
|
+
return error.code === "ELOOP" || error.code === "EISDIR" ? { tag: "not_a_regular_file" } : { tag: "read_failed" };
|
|
44573
|
+
}
|
|
44574
|
+
async function readObservedByteLength(handle, byteLength) {
|
|
44575
|
+
const data = Buffer.allocUnsafe(byteLength);
|
|
44576
|
+
let offset = 0;
|
|
44577
|
+
while (offset < byteLength) {
|
|
44578
|
+
const read = await handle.read(data, offset, byteLength - offset, offset);
|
|
44579
|
+
if (read.bytesRead === 0) return null;
|
|
44580
|
+
offset += read.bytesRead;
|
|
44581
|
+
}
|
|
44582
|
+
const growthProbe = Buffer.allocUnsafe(1);
|
|
44583
|
+
const extra = await handle.read(growthProbe, 0, 1, byteLength);
|
|
44584
|
+
return extra.bytesRead === 0 ? data : null;
|
|
44585
|
+
}
|
|
44586
|
+
async function readStableBinaryFile(filePath, maxBytes) {
|
|
44587
|
+
let handle;
|
|
44588
|
+
try {
|
|
44589
|
+
const noFollow = constants.O_NOFOLLOW ?? 0;
|
|
44590
|
+
handle = await open(filePath, constants.O_RDONLY | noFollow);
|
|
44591
|
+
const before = await handle.stat();
|
|
44592
|
+
const admission = admitStableBinaryRead(before, maxBytes);
|
|
44593
|
+
if (!admission.ok) return admission;
|
|
44594
|
+
const data = await readObservedByteLength(handle, before.size);
|
|
44595
|
+
const after = await handle.stat();
|
|
44596
|
+
if (data === null || !observationsMatch(before, after)) {
|
|
44597
|
+
return err({ tag: "file_changed_during_read" });
|
|
44598
|
+
}
|
|
44599
|
+
return ok(data);
|
|
44600
|
+
} catch (error) {
|
|
44601
|
+
return err(mapStableBinaryReadFailure(error));
|
|
44602
|
+
} finally {
|
|
44603
|
+
await handle?.close().catch(() => undefined);
|
|
44604
|
+
}
|
|
44605
|
+
}
|
|
44606
|
+
async function inspectDestination(filePath, overwrite) {
|
|
44607
|
+
try {
|
|
44608
|
+
const stats = await lstat(filePath);
|
|
44609
|
+
if (overwrite === "refuse") return err({ tag: "destination_exists" });
|
|
44610
|
+
return stats.isFile() ? ok(undefined) : err({ tag: "unsafe_destination" });
|
|
44611
|
+
} catch (error) {
|
|
44612
|
+
if (isNodeError(error) && error.code === "ENOENT") return ok(undefined);
|
|
44613
|
+
return err({ tag: "write_failed" });
|
|
44614
|
+
}
|
|
44615
|
+
}
|
|
44616
|
+
/**
|
|
44617
|
+
* Admit an output path before a remote operation while preserving the atomic
|
|
44618
|
+
* writer's second check at finalization time to close the race window.
|
|
44619
|
+
*/
|
|
44620
|
+
async function admitBinaryWriteDestination(filePath, overwrite) {
|
|
44621
|
+
const destination = await inspectDestination(filePath, overwrite);
|
|
44622
|
+
if (!destination.ok) return destination;
|
|
44623
|
+
try {
|
|
44624
|
+
const parent = dirname$1(filePath);
|
|
44625
|
+
const parentStats = await lstat(parent);
|
|
44626
|
+
if (!parentStats.isDirectory()) return err({ tag: "unsafe_destination" });
|
|
44627
|
+
await access(parent, constants.W_OK);
|
|
44628
|
+
return ok(undefined);
|
|
44629
|
+
} catch {
|
|
44630
|
+
return err({ tag: "write_failed" });
|
|
44631
|
+
}
|
|
44632
|
+
}
|
|
44633
|
+
async function syncDirectory(directory) {
|
|
44634
|
+
if (process.platform === "win32") return true;
|
|
44635
|
+
let handle;
|
|
44636
|
+
try {
|
|
44637
|
+
handle = await open(directory, "r");
|
|
44638
|
+
await handle.sync();
|
|
44639
|
+
return true;
|
|
44640
|
+
} catch {
|
|
44641
|
+
return false;
|
|
44642
|
+
} finally {
|
|
44643
|
+
await handle?.close().catch(() => undefined);
|
|
44644
|
+
}
|
|
44645
|
+
}
|
|
44646
|
+
function mapAtomicBinaryWriteFailure(error) {
|
|
44647
|
+
return isNodeError(error) && (error.code === "ELOOP" || error.code === "EISDIR" || error.code === "ENOTDIR") ? { tag: "unsafe_destination" } : { tag: "write_failed" };
|
|
44648
|
+
}
|
|
44649
|
+
async function writeTemporaryBinaryFile(temporaryPath, data, mode) {
|
|
44650
|
+
let handle;
|
|
44651
|
+
try {
|
|
44652
|
+
handle = await open(temporaryPath, "wx", mode);
|
|
44653
|
+
await handle.writeFile(data);
|
|
44654
|
+
await handle.sync();
|
|
44655
|
+
await handle.close();
|
|
44656
|
+
handle = undefined;
|
|
44657
|
+
await chmod(temporaryPath, mode);
|
|
44658
|
+
} finally {
|
|
44659
|
+
await handle?.close().catch(() => undefined);
|
|
44660
|
+
}
|
|
44661
|
+
}
|
|
44662
|
+
async function finalizeTemporaryBinaryFile(temporaryPath, filePath, overwrite) {
|
|
44663
|
+
if (overwrite === "allow") {
|
|
44664
|
+
await rename(temporaryPath, filePath);
|
|
44665
|
+
return ok([]);
|
|
44666
|
+
}
|
|
44667
|
+
try {
|
|
44668
|
+
await link(temporaryPath, filePath);
|
|
44669
|
+
} catch (error) {
|
|
44670
|
+
return isNodeError(error) && error.code === "EEXIST" ? err({ tag: "destination_exists" }) : err(mapAtomicBinaryWriteFailure(error));
|
|
44671
|
+
}
|
|
44672
|
+
try {
|
|
44673
|
+
await unlink$1(temporaryPath);
|
|
44674
|
+
return ok([]);
|
|
44675
|
+
} catch {
|
|
44676
|
+
return ok([{
|
|
44677
|
+
tag: "temporary_cleanup_failed",
|
|
44678
|
+
temporaryPath
|
|
44679
|
+
}]);
|
|
44680
|
+
}
|
|
44681
|
+
}
|
|
44682
|
+
function toAtomicBinaryWriteOutcome(warnings) {
|
|
44683
|
+
const [first, ...rest] = warnings;
|
|
44684
|
+
return first === undefined ? { tag: "written" } : {
|
|
44685
|
+
tag: "written_with_warnings",
|
|
44686
|
+
warnings: [first, ...rest]
|
|
44687
|
+
};
|
|
44688
|
+
}
|
|
44689
|
+
async function writeBinaryFileAtomic(filePath, data, options) {
|
|
44690
|
+
const parent = dirname$1(filePath);
|
|
44691
|
+
const mode = options.mode ?? 384;
|
|
44692
|
+
const suffix = options.randomSuffix ?? (() => `${process.pid}-${Date.now()}-${randomUUID()}`);
|
|
44693
|
+
let temporaryPath;
|
|
44694
|
+
try {
|
|
44695
|
+
if (options.ensureParent === true) await mkdir(parent, { recursive: true });
|
|
44696
|
+
const admitted = await inspectDestination(filePath, options.overwrite);
|
|
44697
|
+
if (!admitted.ok) return admitted;
|
|
44698
|
+
temporaryPath = join(parent, `.${basename(filePath)}.${suffix()}.tmp`);
|
|
44699
|
+
await writeTemporaryBinaryFile(temporaryPath, data, mode);
|
|
44700
|
+
const finalized = await finalizeTemporaryBinaryFile(temporaryPath, filePath, options.overwrite);
|
|
44701
|
+
if (!finalized.ok) return finalized;
|
|
44702
|
+
temporaryPath = undefined;
|
|
44703
|
+
const warnings = await syncDirectory(parent) ? finalized.value : [...finalized.value, { tag: "directory_sync_failed" }];
|
|
44704
|
+
return ok(toAtomicBinaryWriteOutcome(warnings));
|
|
44705
|
+
} catch (error) {
|
|
44706
|
+
return err(mapAtomicBinaryWriteFailure(error));
|
|
44707
|
+
} finally {
|
|
44708
|
+
if (temporaryPath) await unlink$1(temporaryPath).catch(() => undefined);
|
|
44709
|
+
}
|
|
44710
|
+
}
|
|
44711
|
+
|
|
43125
44712
|
//#endregion
|
|
43126
44713
|
//#region src/media/local-media.ts
|
|
43127
44714
|
const MEDIA_DIR_NAME = "media";
|
|
@@ -43143,10 +44730,10 @@ const listMediaDirFiles = async (mediaDir) => {
|
|
|
43143
44730
|
}
|
|
43144
44731
|
};
|
|
43145
44732
|
const findMatchingFilename = async (contentDir, identifier, manifest) => {
|
|
43146
|
-
const mediaDir = path.join(contentDir, MEDIA_DIR_NAME);
|
|
44733
|
+
const mediaDir = path$1.join(contentDir, MEDIA_DIR_NAME);
|
|
43147
44734
|
const manifestEntry = manifest.assets[identifier];
|
|
43148
44735
|
if (manifestEntry?.filename) {
|
|
43149
|
-
const candidatePath = path.join(mediaDir, manifestEntry.filename);
|
|
44736
|
+
const candidatePath = path$1.join(mediaDir, manifestEntry.filename);
|
|
43150
44737
|
if (await fileExists(candidatePath)) {
|
|
43151
44738
|
return manifestEntry.filename;
|
|
43152
44739
|
}
|
|
@@ -43154,7 +44741,7 @@ const findMatchingFilename = async (contentDir, identifier, manifest) => {
|
|
|
43154
44741
|
const files = await listMediaDirFiles(mediaDir);
|
|
43155
44742
|
const matches = files.filter((name) => {
|
|
43156
44743
|
if (name.startsWith(".")) return false;
|
|
43157
|
-
const parsed = path.parse(name);
|
|
44744
|
+
const parsed = path$1.parse(name);
|
|
43158
44745
|
return parsed.name === identifier;
|
|
43159
44746
|
});
|
|
43160
44747
|
if (matches.length > 1) {
|
|
@@ -43165,9 +44752,13 @@ const findMatchingFilename = async (contentDir, identifier, manifest) => {
|
|
|
43165
44752
|
async function loadLocalMediaFile(contentDir, identifier, manifest) {
|
|
43166
44753
|
const filename = await findMatchingFilename(contentDir, identifier, manifest);
|
|
43167
44754
|
if (!filename) return null;
|
|
43168
|
-
const mediaDir = path.join(contentDir, MEDIA_DIR_NAME);
|
|
43169
|
-
const filePath = path.join(mediaDir, filename);
|
|
43170
|
-
const
|
|
44755
|
+
const mediaDir = path$1.join(contentDir, MEDIA_DIR_NAME);
|
|
44756
|
+
const filePath = path$1.join(mediaDir, filename);
|
|
44757
|
+
const read = await readStableBinaryFile(filePath, MAX_MANAGEMENT_MEDIA_UPLOAD_BYTES);
|
|
44758
|
+
if (!read.ok) {
|
|
44759
|
+
throw new Error(`Failed to read media file ${filePath}: ${read.error.tag}`);
|
|
44760
|
+
}
|
|
44761
|
+
const data = read.value;
|
|
43171
44762
|
const checksum = sha256(data);
|
|
43172
44763
|
const mimeType = getMimeType(filename);
|
|
43173
44764
|
manifest.assets[identifier] = {
|
|
@@ -43186,15 +44777,31 @@ async function loadLocalMediaFile(contentDir, identifier, manifest) {
|
|
|
43186
44777
|
};
|
|
43187
44778
|
}
|
|
43188
44779
|
async function writeLocalMediaFile(contentDir, filename, data) {
|
|
43189
|
-
const mediaDir = path.join(contentDir, MEDIA_DIR_NAME);
|
|
44780
|
+
const mediaDir = path$1.join(contentDir, MEDIA_DIR_NAME);
|
|
43190
44781
|
await fs.mkdir(mediaDir, { recursive: true });
|
|
43191
|
-
const filePath = path.join(mediaDir, filename);
|
|
43192
|
-
await
|
|
43193
|
-
|
|
44782
|
+
const filePath = path$1.join(mediaDir, filename);
|
|
44783
|
+
const written = await writeBinaryFileAtomic(filePath, data, {
|
|
44784
|
+
overwrite: "allow",
|
|
44785
|
+
mode: 420
|
|
44786
|
+
});
|
|
44787
|
+
if (!written.ok) {
|
|
44788
|
+
throw new Error(`Failed to write media file ${filePath}: ${written.error.tag}`);
|
|
44789
|
+
}
|
|
44790
|
+
return written.value.tag === "written" ? {
|
|
44791
|
+
kind: "written",
|
|
44792
|
+
filePath
|
|
44793
|
+
} : {
|
|
44794
|
+
kind: "written_with_warnings",
|
|
44795
|
+
filePath,
|
|
44796
|
+
warnings: written.value.warnings
|
|
44797
|
+
};
|
|
43194
44798
|
}
|
|
43195
|
-
async function readLocalMediaChecksum(filePath) {
|
|
43196
|
-
const
|
|
43197
|
-
|
|
44799
|
+
async function readLocalMediaChecksum(filePath, maxBytes) {
|
|
44800
|
+
const read = await readStableBinaryFile(filePath, maxBytes);
|
|
44801
|
+
if (!read.ok) {
|
|
44802
|
+
throw new Error(`Failed to read media file ${filePath}: ${read.error.tag}`);
|
|
44803
|
+
}
|
|
44804
|
+
return sha256(read.value);
|
|
43198
44805
|
}
|
|
43199
44806
|
async function localMediaFileExists(filePath) {
|
|
43200
44807
|
return fileExists(filePath);
|
|
@@ -43259,7 +44866,7 @@ const parseFilenameFromUrl = (src) => {
|
|
|
43259
44866
|
}
|
|
43260
44867
|
};
|
|
43261
44868
|
const ensureFilenameExtension = (filename, mimeType) => {
|
|
43262
|
-
if (path.extname(filename)) return filename;
|
|
44869
|
+
if (path$1.extname(filename)) return filename;
|
|
43263
44870
|
const ext = getExtensionForMimeType(mimeType);
|
|
43264
44871
|
return ext ? `${filename}${ext}` : filename;
|
|
43265
44872
|
};
|
|
@@ -44436,7 +46043,7 @@ const removeStaleLocalFile = async (options) => {
|
|
|
44436
46043
|
if (options.dryRun || !options.previousFilename || options.previousFilename === options.filename) {
|
|
44437
46044
|
return;
|
|
44438
46045
|
}
|
|
44439
|
-
const previousPath = path.join(options.contentDir, "media", options.previousFilename);
|
|
46046
|
+
const previousPath = path$1.join(options.contentDir, "media", options.previousFilename);
|
|
44440
46047
|
if (await localMediaFileExists(previousPath)) {
|
|
44441
46048
|
await fs.unlink(previousPath).catch(() => {});
|
|
44442
46049
|
}
|
|
@@ -44467,7 +46074,8 @@ const applyDownloadDecision = async (decision, context) => {
|
|
|
44467
46074
|
context.warnings.push(`Failed to download media for "${decision.identifier}" from ${urlResult.value}.`);
|
|
44468
46075
|
return;
|
|
44469
46076
|
}
|
|
44470
|
-
await writeLocalMediaFile(context.contentDir, decision.filename, downloaded.data);
|
|
46077
|
+
const written = await writeLocalMediaFile(context.contentDir, decision.filename, downloaded.data);
|
|
46078
|
+
context.warnings.push(...localMediaWriteWarningMessages(decision.identifier, written));
|
|
44471
46079
|
context.manifest.assets[decision.identifier] = {
|
|
44472
46080
|
checksum: sha256(downloaded.data),
|
|
44473
46081
|
filename: decision.filename,
|
|
@@ -44484,7 +46092,7 @@ const syncPullMediaDownload = async (planned, indexes, context) => {
|
|
|
44484
46092
|
return;
|
|
44485
46093
|
}
|
|
44486
46094
|
const filename = buildLocalFilename(identifier, asset);
|
|
44487
|
-
const localFilePath = path.join(context.contentDir, "media", filename);
|
|
46095
|
+
const localFilePath = path$1.join(context.contentDir, "media", filename);
|
|
44488
46096
|
const existingEntry = context.manifest.assets[identifier];
|
|
44489
46097
|
await removeStaleLocalFile({
|
|
44490
46098
|
contentDir: context.contentDir,
|
|
@@ -44522,10 +46130,16 @@ const syncPullMediaDownloads = async (options) => {
|
|
|
44522
46130
|
}
|
|
44523
46131
|
if (!options.dryRun) options.summary.pendingDownloads = 0;
|
|
44524
46132
|
};
|
|
44525
|
-
const sha256FromFile =
|
|
44526
|
-
|
|
44527
|
-
|
|
44528
|
-
|
|
46133
|
+
const sha256FromFile = (filePath) => readLocalMediaChecksum(filePath, FILE_SIZE_LIMITS.rawVideoBeforeCompression);
|
|
46134
|
+
function localMediaWriteWarningMessages(identifier, outcome) {
|
|
46135
|
+
if (outcome.kind === "written") return [];
|
|
46136
|
+
return outcome.warnings.map((warning) => {
|
|
46137
|
+
switch (warning.tag) {
|
|
46138
|
+
case "temporary_cleanup_failed": return `Downloaded media for "${identifier}", but could not remove temporary file ${warning.temporaryPath}.`;
|
|
46139
|
+
case "directory_sync_failed": return `Downloaded media for "${identifier}", but could not confirm the destination directory was synced.`;
|
|
46140
|
+
}
|
|
46141
|
+
});
|
|
46142
|
+
}
|
|
44529
46143
|
|
|
44530
46144
|
//#endregion
|
|
44531
46145
|
//#region src/media/pull-workflow.ts
|
|
@@ -44945,4 +46559,4 @@ Examples:
|
|
|
44945
46559
|
}
|
|
44946
46560
|
|
|
44947
46561
|
//#endregion
|
|
44948
|
-
export {
|
|
46562
|
+
export { mapEventCommandContent as $, headingDecorationColorTokenValues as $n, generateEffectsCSS as $r, requireEnvironmentWriteAuthorization as $t, isExplicitRectCrop as A, customLinkSchema$1 as Ai, boxBackgroundSchema as An, normalizeCardRole as Ar, createCommandContext as At, resolveVideoSelectionIntentForMediaField as B, sdkConfiguredBookingVerticalConfigSchema as Bi, MQ_MD_DOWN as Bn, resolveContextualButtonRole as Br, parseJsonArray as Bt, attachRenderableMediaAssetFields as C, parseBlockColorToken as Ci, parseBackgroundAppearanceColorToken as Cn, ICON_SIZE_PX as Cr, createPageRef as Ct, looksLikeUuid as D, persistableMediaSchema as Di, sdkThemeWireSchema as Dn, generateShadowValue as Dr, slugPart as Dt, deleteEnvSpecificKeys as E, mediaTypeDiscriminants as Ei, sdkSettingsUpdateSchema as En, generateCardCss as Er, refSlug as Et, getImageUrl as F, internalRouteOnlyLinkSchema as Fi, BREAKPOINT_LG as Fn, resolvedButtonColourDeclarations as Fr, formatDateShort as Ft, classifyKnownMimeType as G, pageDesignEditorBackgroundOverrideOwner as Gn, backgroundColorStyle as Gr, withCommandContext as Gt, asRecord as H, MQ_SM_UP as Hn, resolveOnColorForThemeToken as Hr, parsePaginationOptions as Ht, isSiteScopedDirectImageStoragePath as I, isInternalResolvedLinkValue as Ii, BREAKPOINT_MD as In, resolvedButtonFocusDeclarations as Ir, getOutputContext as It, venueManagementFields as J, defineFrameCapability as Jn, mergeStyles as Jr, withOutputOnlyErrorHandling as Jt, managementMediaLibraryUploadLimitForMimeType as K, assertNever$5 as Kn, borderColorStyle as Kr, withConfirmation as Kt, buildMediaIdentifierFromFilename as L, isResolvedInternalLinkEntityType as Li, BREAKPOINT_SM as Ln, contextualButtonSurfaceResetStyles as Lr, getOutputOnlyContext as Lt, resolveMediaTransformForAspect as M, externalLinkSchema$1 as Mi, generateMobileOverlayBackdropFilterFix as Mn, AFFORDANCE_SUPPRESS_CLASS as Mr, createListCommand$1 as Mt, getDirectImageUrlForRelativePath as N, internalLinkSchema as Ni, getHeaderPaintTargets as Nn, generateButtonCss as Nr, createOutputForCommand as Nt, rotateNormalizedPoint as O, linkPayloadSchema as Oi, themeSchema as On, cardRoleSchema as Or, confirmAction as Ot, getDirectImageUrlForSiteScopedPath as P, internalResolvedLinkSchema as Pi, renderHeaderPaintTargetRules as Pn, RESOLVED_BUTTON_DISABLED_DECLARATIONS as Pr, currentInvocationEnvelope as Pt, parseAndPlanCommandBatchForTypes as Q, featureCardCompositionTreatmentValues as Qn, generateDefaultButtonSystem as Qr, planEnvironmentWriteAuthorization as Qt, parseMediaIdentifier as R, linkSchema as Ri, BREAKPOINT_XL as Rn, contextualButtonSurfaceStyles as Rr, handleCommandError as Rt, isMediaValue as S, unsafeAsOpaqueButtonColourToken as Si, backgroundAppearanceQuickColorTokenNames as Sn, ICON_CONFIG as Sr, createNavigationRef as St, walkAndCollectImageAttrs as T, mediaSchema as Ti, defineBlockDesignMetadata as Tn, getDefaultCardVariants as Tr, createVenueRef as Tt, parseRelativeStoragePath as U, MQ_TABLET_ONLY as Un, resolveThemeColorReferenceValue as Ur, resolveCommandPath as Ut, getMimeType as V, __toESM as Vi, MQ_SM_DOWN as Vn, isCssColorLiteral as Vr, parseJsonData as Vt, FILE_SIZE_LIMITS as W, headerVariantValues as Wn, resolveTokenOrCssColorValue as Wr, runWithInvocation as Wt, stableStringify as X, eventListTreatmentValues as Xn, textColorStyle as Xr, parseEnvironmentSelection as Xt, sortRecord as Y, defineNoBlockDesignCapability as Yn, rgbColorValue as Yr, executeEnvironmentPlan as Yt, createDryRunReport as Z, eyebrowTreatmentValues as Zn, resolveSurfaceForeground as Zr, planEnvironmentExecution as Zt, extractMediaIdentifiers as _, ORDINARY_TEXT_MIN_CONTRAST_RATIO as _i, GRADIENT_PRESET_IDS as _n, LABEL_FONT_SIZE_MAP as _r, createContentEntryRef as _t, syncLocalMediaForPull as a, parseThemeForPersistence as ai, getEnvironmentDebugInfo as an, getComponentBorderWidthPx as ar, entryIdentifierForCommand as at, hasOwnSettingsField as b, VARIANT_ROLES as bi, boundaryRuntimeMetricsForTransition as bn, ACCORDION_TRIGGER_FONT_SCALE as br, createFooterRef as bt, admitBinaryWriteDestination as c, generateShadowValue$1 as ci, loadSdkEnvironment as cn, parseCustomCssRules as cr, pagePathForCommand as ct, sha256 as d, surfaceForegroundStyle as di, ManagementApiError as dn, NAV_LINK_PADDING_MAP as dr, require_react as dt, generateBaseInteractiveCSS as ei, withRiverbankEnv as en, introCompositionTreatmentValues as er, evaluateSiteCommandBatchExposure as et, normalizeMediaIdentifierInput as f, surfaceForegroundValue as fi, retargetCompiledBoundaryRuntimeForContentAdjacency as fn, NAV_LINK_PADDING_X_MAP as fr, pickShadeTowardBackground as ft, downloadMedia as g, getTextColorForBackground as gi, gradientToCss as gn, INPUT_SPACING_MAP as gr, createCommandRef as gt, buildStorageUrl as h, onColorTokenName as hi, getCuratedSiteStyle as hn, INPUT_FONT_SIZE_MAP as hr, createBlocksRef as ht, syncLocalMediaForPush as i, themeButtonClassName as ii, getEnvVarName as in, visibleHeadingDecorationFamilyValues as ir, blockOrderIdentifiersForCommand as it, parseAspectRatioValue as j, entryLinkSchema$1 as ji, generateHeaderScrollTreatmentStyles as jn, PUBLIC_BUTTON_ROLES as jr, createGetCommand as jt, normalizeWidthCandidates as k, RESOLVED_INTERNAL_LINK_ENTITY_TYPES as ki, boxBackgroundOverlaySchema as kn, cardVariantRoleValues as kr, createClientForEnv as kt, readStableBinaryFile as l, DEFAULT_SURFACE_TEXT_COLOR as li, createManagementClient as ln, NAV_LINK_BORDER_RADIUS_MAP as lr, hasConfiguredBookingVertical as lt, walkLocalContentMedia as m, isOnColorBackgroundCandidateToken as mi, readHeaderNavFontSizePreference as mn, FONT_WEIGHT_MAP as mr, createBlockRef as mt, createStyleCommand as n, TokenResolver as ni, CliExit as nn, testimonialsCompositionTreatmentValues as nr, getSiteCommandMetadata as nt, applyLocalMediaPush as o, compileButtonColourStates as oi, isLocalConfigured as on, getComponentShadowVar as or, pageIdentifierForBlockCommand as ot, normalizeMediaValueToIdentifierOnly as p, expandPalette as pi, mergeHeaderNavTypographyPreference as pn, NAV_LINK_TEXT_SIZE_MAP as pr, generateCssVars as pt, parseSafeDownloadFilename as q, defineBlockDesignCapability as qn, cssVarValue as qr, withErrorHandling as qt, revalidateLocalMediaPushPlan as r, parseThemeButtonVariantId as ri, getEnvPrefix as rn, tileTreatmentValues as rr, blockIdentifierForCommand as rt, prepareLocalMediaForPush as s, formatButtonColourContractError as si, isRemoteConfigured as sn, parseBlockCustomCssRules as sr, pageIdentifierForCommand as st, buildStyleSelectionInitializeCommandFromOptions as t, generateInteractiveChildInheritanceCSS as ti, formatError as tn, offeringsPreviewCompositionTreatmentValues as tr, evaluateSiteCommandExposure as tt, writeBinaryFileAtomic as u, surfaceForegroundDeclarations as ui, formatIgnoredDocumentSyncSectionWarning as un, NAV_LINK_LETTER_SPACING_MAP as ur, require_jsx_runtime as ut, SETTINGS_FILE_FIELDS as v, featuresFromHex as vi, getGradientPreset as vn, ACCORDION_OPEN_ACCENT_THICKNESS as vr, createEventCategoryRef as vt, buildRenderableMediaAssetIndex as w, mediaIdentifierSchema as wi, defaultThemeTokens as wn, generateCardCoreCss as wr, createSiteCommandSiteRef as wt, writeMediaManifest as x, buttonVariantIdSchema as xi, backgroundAppearanceColorTokenNames as xn, ACCORDION_TRIGGER_LETTER_SPACING as xr, createFormRef as xt, buildSettingsUpdateInput as y, hexToRgb$1 as yi, parseGradientPresetId as yn, ACCORDION_SPACING_MAP as yr, createEventSeriesRef as yt, deriveVideoSlotPolicy as z, pageLinkSchema$1 as zi, MQ_LG_UP as zn, deriveButtonSurfaceContext as zr, mapEntryForOutput as zt };
|