@anthusai/papyrus 1.2.0 → 1.4.0-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/amplify/functions/model-attachment-upload/handler.ts +1 -0
- package/app/newsroom/[section]/page.tsx +6 -0
- package/app/newsroom/articles/page.tsx +3 -0
- package/components/news-desk-client-provider.tsx +17 -2
- package/components/news-desk-taxonomy-client.ts +133 -2
- package/components/newspaper.tsx +4 -4
- package/components/newsroom-board-view.tsx +74 -50
- package/components/newsroom-client-shell.tsx +8 -1
- package/components/newsroom-organizer-shell.tsx +12 -7
- package/components/newsroom-organizer.css +4 -3
- package/components/newsroom-references-view.tsx +18 -0
- package/components/reference-cyclotron-priming.tsx +85 -0
- package/components/topic-steering-workspace.tsx +35 -1
- package/lib/article-flow.ts +95 -0
- package/lib/article-frame-solver.ts +519 -0
- package/lib/chrome-text.ts +504 -0
- package/lib/content-item-lifecycle.ts +25 -26
- package/lib/front-mosaic.ts +1132 -0
- package/lib/furniture.ts +385 -0
- package/lib/labels.ts +37 -0
- package/lib/layout-config.ts +237 -0
- package/lib/layout-plan.ts +37 -9
- package/lib/layout-scenarios.ts +21 -11
- package/lib/layout-types.ts +457 -0
- package/lib/local-preview-seed.ts +97 -0
- package/lib/local-preview.ts +3 -0
- package/lib/newspaper-layout.ts +17 -3410
- package/lib/newsroom-board.ts +63 -62
- package/lib/newsroom-organizer-nav.ts +3 -4
- package/lib/pretext-layout.ts +22 -0
- package/lib/relevance-status.ts +73 -0
- package/lib/site-brand.ts +15 -1
- package/lib/text-columns.ts +251 -0
- package/package.json +13 -2
- package/renderers/pretext/presentation-shell.tsx +1 -1
- package/routes.manifest.json +1 -9
- package/app/newsroom/work/page.tsx +0 -10
package/lib/layout-plan.ts
CHANGED
|
@@ -469,7 +469,12 @@ export type LayoutRegionSpec = z.infer<typeof LayoutRegionSchema>;
|
|
|
469
469
|
export type LayoutPageSpec = z.infer<typeof LayoutPageSchema>;
|
|
470
470
|
export type EditionLayoutPlan = z.infer<typeof EditionLayoutPlanSchema>;
|
|
471
471
|
|
|
472
|
-
export
|
|
472
|
+
export type ArticleCutPolicy = ArticleFrameBlockSpec["cutPolicy"];
|
|
473
|
+
|
|
474
|
+
export function createDefaultEditionLayoutPlan(
|
|
475
|
+
itemIds: string[],
|
|
476
|
+
cutPoliciesByItemId: Record<string, ArticleCutPolicy> = {},
|
|
477
|
+
): EditionLayoutPlan {
|
|
473
478
|
const frontItemIds = getDefaultFrontItemOrder(itemIds);
|
|
474
479
|
const frontBlocks = frontItemIds.map((itemId, index) => ({
|
|
475
480
|
id: `front-${itemId}`,
|
|
@@ -563,7 +568,7 @@ export function createDefaultEditionLayoutPlan(itemIds: string[]): EditionLayout
|
|
|
563
568
|
],
|
|
564
569
|
}
|
|
565
570
|
: undefined,
|
|
566
|
-
cutPolicy:
|
|
571
|
+
cutPolicy: cutPoliciesByItemId[itemId],
|
|
567
572
|
}));
|
|
568
573
|
|
|
569
574
|
const plan: EditionLayoutPlan = normalizeEditionLayoutPlan({
|
|
@@ -812,9 +817,39 @@ export function validateEditionLayoutPlanForItems(
|
|
|
812
817
|
}
|
|
813
818
|
}
|
|
814
819
|
|
|
820
|
+
validateJumpTargetsHaveContinuationFrames(layoutPlan, label);
|
|
821
|
+
|
|
815
822
|
return layoutPlan;
|
|
816
823
|
}
|
|
817
824
|
|
|
825
|
+
function validateJumpTargetsHaveContinuationFrames(layoutPlan: EditionLayoutPlan, label: string): void {
|
|
826
|
+
const continuationFlowKeysByPageNumber = new Map<number, Set<string>>();
|
|
827
|
+
for (const page of layoutPlan.pages) {
|
|
828
|
+
const flowKeys = new Set<string>();
|
|
829
|
+
for (const region of page.regions) {
|
|
830
|
+
for (const block of region.blocks) {
|
|
831
|
+
if (block.type === "articleFrame" && block.startCursor === "current") flowKeys.add(block.flowKey ?? block.itemId);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
continuationFlowKeysByPageNumber.set(page.pageNumber, flowKeys);
|
|
835
|
+
}
|
|
836
|
+
for (const page of layoutPlan.pages) {
|
|
837
|
+
for (const region of page.regions) {
|
|
838
|
+
for (const block of region.blocks) {
|
|
839
|
+
if (block.type !== "articleFrame") continue;
|
|
840
|
+
const jumpTargetPage = block.cutPolicy?.jumpTargetPage;
|
|
841
|
+
if (!jumpTargetPage) continue;
|
|
842
|
+
const flowKey = block.flowKey ?? block.itemId;
|
|
843
|
+
if (!continuationFlowKeysByPageNumber.get(jumpTargetPage)?.has(flowKey)) {
|
|
844
|
+
throw new Error(
|
|
845
|
+
`${label}.pages[${page.pageNumber}].regions.${region.id}.${block.id} advertises page ${jumpTargetPage} but that page has no startCursor "current" frame for flow ${flowKey}`,
|
|
846
|
+
);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
|
|
818
853
|
function validateBlock(
|
|
819
854
|
block: LayoutBlockSpec,
|
|
820
855
|
itemsBySlug: Map<string, PublicationItem>,
|
|
@@ -954,13 +989,6 @@ function getDefaultFrontItemOrder(itemIds: string[]): string[] {
|
|
|
954
989
|
return [itemIds[1], itemIds[0], itemIds[2], ...itemIds.slice(3)];
|
|
955
990
|
}
|
|
956
991
|
|
|
957
|
-
function getDefaultCutPolicy(itemId: string): ArticleFrameBlockSpec["cutPolicy"] | undefined {
|
|
958
|
-
if (itemId === "agent-procedure-patterns") return { maxBodyLines: 22, jumpTargetPage: 2 };
|
|
959
|
-
if (itemId === "schools-reading-lab") return { maxBodyLines: 16, jumpTargetPage: 3 };
|
|
960
|
-
if (itemId === "market-hall") return { maxBodyLines: 14, jumpTargetPage: 3 };
|
|
961
|
-
return undefined;
|
|
962
|
-
}
|
|
963
|
-
|
|
964
992
|
function parseMaybeJson(value: unknown, label: string): unknown {
|
|
965
993
|
if (typeof value !== "string") {
|
|
966
994
|
if (value === undefined || value === null) throw new Error(`${label} is required`);
|
package/lib/layout-scenarios.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type Article, type ArticleVideoAsset, articles, editionDate } from "./articles";
|
|
2
2
|
import type { EditionContent, NewsDeskAppendix } from "./content-types";
|
|
3
3
|
import { createEditionSectionPlan } from "./edition-sections";
|
|
4
|
-
import { createDefaultEditionLayoutPlan, type EditionLayoutPlan } from "./layout-plan";
|
|
4
|
+
import { createDefaultEditionLayoutPlan, type ArticleCutPolicy, type EditionLayoutPlan } from "./layout-plan";
|
|
5
5
|
import { articleToPublicationItem, cloneArticle } from "./publication-items";
|
|
6
6
|
import { PILOBOL_SAMPLE_SCENARIO_ID, createPilobolSampleEditionContent } from "./pilobol-sample";
|
|
7
7
|
import { SITE_BRAND } from "./site-brand";
|
|
@@ -17,6 +17,16 @@ type RawLayoutScenario = Omit<LayoutScenario, "sections"> & {
|
|
|
17
17
|
sections?: LayoutScenario["sections"];
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
+
const fixtureCutPoliciesByItemId: Record<string, ArticleCutPolicy> = {
|
|
21
|
+
"agent-procedure-patterns": { maxBodyLines: 22, jumpTargetPage: 2 },
|
|
22
|
+
"schools-reading-lab": { maxBodyLines: 16, jumpTargetPage: 3 },
|
|
23
|
+
"market-hall": { maxBodyLines: 14, jumpTargetPage: 3 },
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export function createFixtureEditionLayoutPlan(itemIds: string[]): EditionLayoutPlan {
|
|
27
|
+
return createDefaultEditionLayoutPlan(itemIds, fixtureCutPoliciesByItemId);
|
|
28
|
+
}
|
|
29
|
+
|
|
20
30
|
const defaultScenarioSeed = getDefaultScenarioSeed();
|
|
21
31
|
|
|
22
32
|
const rawLayoutScenarios: RawLayoutScenario[] = [
|
|
@@ -27,7 +37,7 @@ const rawLayoutScenarios: RawLayoutScenario[] = [
|
|
|
27
37
|
editionDate: defaultScenarioSeed.editionDate,
|
|
28
38
|
scenarioId: DEFAULT_LAYOUT_SCENARIO_ID,
|
|
29
39
|
description: defaultScenarioSeed.description,
|
|
30
|
-
layoutPlan:
|
|
40
|
+
layoutPlan: createFixtureEditionLayoutPlan(defaultScenarioSeed.articles.map((article) => article.slug)),
|
|
31
41
|
items: cloneArticles(defaultScenarioSeed.articles).map(articleToPublicationItem),
|
|
32
42
|
suppressNewsDeskAppendix: defaultScenarioSeed.suppressNewsDeskAppendix,
|
|
33
43
|
editionVideo: defaultScenarioSeed.editionVideo,
|
|
@@ -40,7 +50,7 @@ const rawLayoutScenarios: RawLayoutScenario[] = [
|
|
|
40
50
|
editionDate,
|
|
41
51
|
scenarioId: "blank-edition-title",
|
|
42
52
|
description: "A fixture edition with a blank title used to verify masthead fallback labeling.",
|
|
43
|
-
layoutPlan:
|
|
53
|
+
layoutPlan: createFixtureEditionLayoutPlan(articles.map((article) => article.slug)),
|
|
44
54
|
items: cloneArticles(articles).map(articleToPublicationItem),
|
|
45
55
|
},
|
|
46
56
|
{
|
|
@@ -51,7 +61,7 @@ const rawLayoutScenarios: RawLayoutScenario[] = [
|
|
|
51
61
|
scenarioId: "shared-blank-column-pressure",
|
|
52
62
|
description:
|
|
53
63
|
"Shorter shared continuation tails with images and pull quotes, used to prove that empty columns are repaired with solved furniture.",
|
|
54
|
-
layoutPlan:
|
|
64
|
+
layoutPlan: createFixtureEditionLayoutPlan(articles.map((article) => article.slug)),
|
|
55
65
|
items: createSharedBlankColumnPressureArticles().map(articleToPublicationItem),
|
|
56
66
|
},
|
|
57
67
|
{
|
|
@@ -62,7 +72,7 @@ const rawLayoutScenarios: RawLayoutScenario[] = [
|
|
|
62
72
|
scenarioId: "shared-continuation-no-pull-quotes",
|
|
63
73
|
description:
|
|
64
74
|
"The shared continuation page with editorial pull quotes removed, used to prove pull quotes are optional display furniture.",
|
|
65
|
-
layoutPlan:
|
|
75
|
+
layoutPlan: createFixtureEditionLayoutPlan(articles.map((article) => article.slug)),
|
|
66
76
|
items: createNoPullQuoteArticles().map(articleToPublicationItem),
|
|
67
77
|
},
|
|
68
78
|
{
|
|
@@ -106,7 +116,7 @@ const rawLayoutScenarios: RawLayoutScenario[] = [
|
|
|
106
116
|
scenarioId: "long-image-caption",
|
|
107
117
|
description:
|
|
108
118
|
"A continuation image with a long caption proves image furniture reserves enough rhythm rows for complete caption text.",
|
|
109
|
-
layoutPlan:
|
|
119
|
+
layoutPlan: createFixtureEditionLayoutPlan(articles.map((article) => article.slug)),
|
|
110
120
|
items: createLongImageCaptionArticles().map(articleToPublicationItem),
|
|
111
121
|
},
|
|
112
122
|
{
|
|
@@ -116,7 +126,7 @@ const rawLayoutScenarios: RawLayoutScenario[] = [
|
|
|
116
126
|
editionDate,
|
|
117
127
|
scenarioId: "themed-ink-study",
|
|
118
128
|
description: "A raster fixture verifies dark-mode image variant switching in solved newspaper image furniture.",
|
|
119
|
-
layoutPlan:
|
|
129
|
+
layoutPlan: createFixtureEditionLayoutPlan(articles.map((article) => article.slug)),
|
|
120
130
|
items: createThemedInkStudyArticles().map(articleToPublicationItem),
|
|
121
131
|
},
|
|
122
132
|
{
|
|
@@ -377,7 +387,7 @@ function createHeightPolicyLayoutPlan({
|
|
|
377
387
|
defaultRows?: number;
|
|
378
388
|
blockShrinkToContent?: boolean;
|
|
379
389
|
}): EditionLayoutPlan {
|
|
380
|
-
const plan = cloneLayoutPlan(
|
|
390
|
+
const plan = cloneLayoutPlan(createFixtureEditionLayoutPlan(articles.map((article) => article.slug)));
|
|
381
391
|
const region = findPlanRegion(plan, "harbor-continuation");
|
|
382
392
|
if (region) {
|
|
383
393
|
if (regionShrinkToContent === undefined) {
|
|
@@ -472,7 +482,7 @@ function cloneArticles(source: Article[]): Article[] {
|
|
|
472
482
|
}
|
|
473
483
|
|
|
474
484
|
function createCompactChromeLayoutPlan(): EditionLayoutPlan {
|
|
475
|
-
const plan = cloneLayoutPlan(
|
|
485
|
+
const plan = cloneLayoutPlan(createFixtureEditionLayoutPlan(articles.map((article) => article.slug)));
|
|
476
486
|
const frontPage = plan.pages.find((page) => page.pageNumber === 1);
|
|
477
487
|
const frontBlocks = frontPage?.regions.flatMap((region) => region.blocks) ?? [];
|
|
478
488
|
for (const block of frontBlocks) {
|
|
@@ -498,7 +508,7 @@ function createCompactChromeLayoutPlan(): EditionLayoutPlan {
|
|
|
498
508
|
}
|
|
499
509
|
|
|
500
510
|
function createFrontBodyDepthLayoutPlan(): EditionLayoutPlan {
|
|
501
|
-
const plan = cloneLayoutPlan(
|
|
511
|
+
const plan = cloneLayoutPlan(createFixtureEditionLayoutPlan(articles.map((article) => article.slug)));
|
|
502
512
|
for (const blockId of ["front-agent-procedure-patterns", "front-schools-reading-lab", "front-market-hall"]) {
|
|
503
513
|
const block = findPlanBlock(plan, blockId);
|
|
504
514
|
if (block?.type !== "articleFrame") continue;
|
|
@@ -511,7 +521,7 @@ function createFrontBodyDepthLayoutPlan(): EditionLayoutPlan {
|
|
|
511
521
|
}
|
|
512
522
|
|
|
513
523
|
function createFurnitureSufficiencyLayoutPlan(): EditionLayoutPlan {
|
|
514
|
-
const plan = cloneLayoutPlan(
|
|
524
|
+
const plan = cloneLayoutPlan(createFixtureEditionLayoutPlan(articles.map((article) => article.slug)));
|
|
515
525
|
const frontSchoolsBlock = findPlanBlock(plan, "front-schools-reading-lab");
|
|
516
526
|
if (frontSchoolsBlock?.type === "articleFrame") {
|
|
517
527
|
frontSchoolsBlock.cutPolicy = { maxBodyLines: 4, jumpTargetPage: 3 };
|
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
import { type LayoutCursor, type TextLine } from "./pretext-layout";
|
|
2
|
+
import { type ArticleImageThemeVariants } from "./articles";
|
|
3
|
+
import {
|
|
4
|
+
type ArticleFrameBlockSpec,
|
|
5
|
+
type EditorialPriorityId,
|
|
6
|
+
type HeadlineScaleId,
|
|
7
|
+
type LayoutBlockSpec,
|
|
8
|
+
type LayoutPageSpec,
|
|
9
|
+
type LayoutRegionSpec,
|
|
10
|
+
type ResponsivePlacementSpec,
|
|
11
|
+
} from "./layout-plan";
|
|
12
|
+
import { type ArticlePublicationItem, type PublicationItem } from "./publication-items";
|
|
13
|
+
import { type VerticalRhythm } from "./blog-rhythm";
|
|
14
|
+
|
|
15
|
+
export type SolvedTextLine = TextLine;
|
|
16
|
+
|
|
17
|
+
export type SolvedFurniture = SolvedImageFurniture | SolvedPullQuoteFurniture | SolvedMediaClusterFurniture | SolvedAdFurniture;
|
|
18
|
+
|
|
19
|
+
export type SolvedImageFurniture = {
|
|
20
|
+
kind: "image";
|
|
21
|
+
id: string;
|
|
22
|
+
assetId: string;
|
|
23
|
+
src: string;
|
|
24
|
+
themeVariants?: ArticleImageThemeVariants;
|
|
25
|
+
alt: string;
|
|
26
|
+
caption: string;
|
|
27
|
+
credit: string;
|
|
28
|
+
templateId: string;
|
|
29
|
+
columnStart: number;
|
|
30
|
+
columnSpan: number;
|
|
31
|
+
x: number;
|
|
32
|
+
y: number;
|
|
33
|
+
width: number;
|
|
34
|
+
height: number;
|
|
35
|
+
imageHeight: number;
|
|
36
|
+
captionHeight: number;
|
|
37
|
+
captionFontSize: number;
|
|
38
|
+
captionLineHeight: number;
|
|
39
|
+
aspectRatio: number;
|
|
40
|
+
objectFit: "contain" | "cover";
|
|
41
|
+
objectPosition: string;
|
|
42
|
+
wrapsText: boolean;
|
|
43
|
+
preferredHeight: number;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export type SolvedPullQuoteFurniture = {
|
|
47
|
+
kind: "pullQuote";
|
|
48
|
+
id: string;
|
|
49
|
+
text: string;
|
|
50
|
+
templateId: string;
|
|
51
|
+
columnStart: number;
|
|
52
|
+
columnSpan: number;
|
|
53
|
+
x: number;
|
|
54
|
+
y: number;
|
|
55
|
+
width: number;
|
|
56
|
+
height: number;
|
|
57
|
+
fontSize: number;
|
|
58
|
+
lineHeight: number;
|
|
59
|
+
wrapsText: boolean;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export type SolvedMediaClusterFurniture = {
|
|
63
|
+
kind: "mediaCluster";
|
|
64
|
+
id: string;
|
|
65
|
+
templateId: string;
|
|
66
|
+
images: SolvedImageFurniture[];
|
|
67
|
+
caption: string;
|
|
68
|
+
x: number;
|
|
69
|
+
y: number;
|
|
70
|
+
width: number;
|
|
71
|
+
height: number;
|
|
72
|
+
wrapsText: false;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export type SolvedAdFurniture = {
|
|
76
|
+
kind: "ad";
|
|
77
|
+
id: string;
|
|
78
|
+
src?: string;
|
|
79
|
+
label: string;
|
|
80
|
+
x: number;
|
|
81
|
+
y: number;
|
|
82
|
+
width: number;
|
|
83
|
+
height: number;
|
|
84
|
+
wrapsText: false;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export type SolvedChromeBox = {
|
|
88
|
+
id: string;
|
|
89
|
+
slot: "label" | "headline" | "deck" | "byline";
|
|
90
|
+
text: string;
|
|
91
|
+
columnStart: number;
|
|
92
|
+
columnSpan: number;
|
|
93
|
+
x: number;
|
|
94
|
+
y: number;
|
|
95
|
+
width: number;
|
|
96
|
+
height: number;
|
|
97
|
+
fontSize: number;
|
|
98
|
+
lineHeight: number;
|
|
99
|
+
paintBuffer: number;
|
|
100
|
+
paddingTop?: number;
|
|
101
|
+
paddingBottom?: number;
|
|
102
|
+
ruleTopHeight?: number;
|
|
103
|
+
ruleBottomHeight?: number;
|
|
104
|
+
fontFamily: "serif" | "sans";
|
|
105
|
+
fontWeight?: number | string;
|
|
106
|
+
fontStyle?: "normal" | "italic";
|
|
107
|
+
textTransform?: "uppercase";
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export type SolvedBlock = {
|
|
111
|
+
id: string;
|
|
112
|
+
type: LayoutBlockSpec["type"];
|
|
113
|
+
presetId?: string;
|
|
114
|
+
headlineScale?: HeadlineScaleId;
|
|
115
|
+
editorialPriority?: EditorialPriorityId;
|
|
116
|
+
item?: PublicationItem;
|
|
117
|
+
items?: PublicationItem[];
|
|
118
|
+
article?: ArticlePublicationItem;
|
|
119
|
+
pageNumber: number;
|
|
120
|
+
jumpTargetPage?: number;
|
|
121
|
+
jumpLabel?: string;
|
|
122
|
+
label?: string;
|
|
123
|
+
title?: string;
|
|
124
|
+
deck?: string;
|
|
125
|
+
byline?: string;
|
|
126
|
+
dateline?: string;
|
|
127
|
+
section?: string;
|
|
128
|
+
x: number;
|
|
129
|
+
y: number;
|
|
130
|
+
width: number;
|
|
131
|
+
height: number;
|
|
132
|
+
span: number;
|
|
133
|
+
columnCount: number;
|
|
134
|
+
columns: TextLine[][];
|
|
135
|
+
chromeBoxes?: SolvedChromeBox[];
|
|
136
|
+
furniture: SolvedFurniture[];
|
|
137
|
+
textRange?: PlacedTextRange;
|
|
138
|
+
hasMore?: boolean;
|
|
139
|
+
front?: SolvedFrontStoryMetrics;
|
|
140
|
+
titleChrome?: ContinuationTitleMetrics;
|
|
141
|
+
titleHeight?: number;
|
|
142
|
+
bodyHeight?: number;
|
|
143
|
+
furnitureSufficiency?: FurnitureSufficiencyReport;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export type SolvedFrontStoryMetrics = {
|
|
147
|
+
rowHeight: number;
|
|
148
|
+
bodySlotHeight: number;
|
|
149
|
+
chromeHeight: number;
|
|
150
|
+
jumpReserveHeight: number;
|
|
151
|
+
chrome: StoryChromeMetrics;
|
|
152
|
+
gridPlacement?: SolvedFrontGridPlacement;
|
|
153
|
+
composition?: {
|
|
154
|
+
mode: FrontCompositionFlowMode;
|
|
155
|
+
bodyTop: number;
|
|
156
|
+
bodyHeight: number;
|
|
157
|
+
columnCount: number;
|
|
158
|
+
copyBandTop: number;
|
|
159
|
+
copyBandTops: number[];
|
|
160
|
+
};
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
export type SolvedFrontGridPlacement = {
|
|
164
|
+
columnStart: number;
|
|
165
|
+
columnSpan: number;
|
|
166
|
+
rowStart: number;
|
|
167
|
+
rowSpan: number;
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
export type SolvedRegion = {
|
|
171
|
+
id: string;
|
|
172
|
+
type: LayoutRegionSpec["type"];
|
|
173
|
+
role?: string;
|
|
174
|
+
x: number;
|
|
175
|
+
y: number;
|
|
176
|
+
width: number;
|
|
177
|
+
height: number;
|
|
178
|
+
rowHeights?: number[];
|
|
179
|
+
columnCount: number;
|
|
180
|
+
blocks: SolvedBlock[];
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
export type SolvedFrontFooterEntry = {
|
|
184
|
+
section: string;
|
|
185
|
+
articleSlug: string;
|
|
186
|
+
articleTitle: string;
|
|
187
|
+
blockId: string;
|
|
188
|
+
pageNumber: number;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
export type SolvedFrontFooterUtilityEntry =
|
|
192
|
+
| {
|
|
193
|
+
id: "archive";
|
|
194
|
+
label: string;
|
|
195
|
+
href: string;
|
|
196
|
+
disabled: false;
|
|
197
|
+
}
|
|
198
|
+
| {
|
|
199
|
+
id: "newsDesk";
|
|
200
|
+
label: string;
|
|
201
|
+
href: string;
|
|
202
|
+
disabled: false;
|
|
203
|
+
}
|
|
204
|
+
| {
|
|
205
|
+
id: "settings";
|
|
206
|
+
label: string;
|
|
207
|
+
href: string;
|
|
208
|
+
disabled: false;
|
|
209
|
+
}
|
|
210
|
+
| {
|
|
211
|
+
id: "login";
|
|
212
|
+
label: string;
|
|
213
|
+
disabled: true;
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
export type SolvedFrontFooter = {
|
|
217
|
+
rowHeight: number;
|
|
218
|
+
marginTop: number;
|
|
219
|
+
height: number;
|
|
220
|
+
sectionRows: number;
|
|
221
|
+
sectionColumns: number;
|
|
222
|
+
entries: SolvedFrontFooterEntry[];
|
|
223
|
+
utilityEntries: SolvedFrontFooterUtilityEntry[];
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
export type SolvedPage = {
|
|
227
|
+
id: string;
|
|
228
|
+
pageNumber: number;
|
|
229
|
+
presetId: LayoutPageSpec["presetId"];
|
|
230
|
+
kind: string;
|
|
231
|
+
height: number;
|
|
232
|
+
columnCount: number;
|
|
233
|
+
regions: SolvedRegion[];
|
|
234
|
+
frontFooter?: SolvedFrontFooter;
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
export type NewspaperLayout = {
|
|
238
|
+
columnCount: number;
|
|
239
|
+
contentWidth: number;
|
|
240
|
+
gap: number;
|
|
241
|
+
rowGap: number;
|
|
242
|
+
rhythm: VerticalRhythm;
|
|
243
|
+
pageHeight: number;
|
|
244
|
+
frontPageHeight: number;
|
|
245
|
+
pageHeights: Record<number, number>;
|
|
246
|
+
pageChrome: PageChromeMetrics;
|
|
247
|
+
pages: SolvedPage[];
|
|
248
|
+
textRanges: PlacedTextRange[];
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
export type PageChromeMetrics = {
|
|
252
|
+
pagePaddingTop: number;
|
|
253
|
+
pagePaddingX: number;
|
|
254
|
+
pagePaddingBottom: number;
|
|
255
|
+
mastheadHeight: number;
|
|
256
|
+
mastheadKickerLineHeight: number;
|
|
257
|
+
mastheadTitleFontSize: number;
|
|
258
|
+
mastheadTitleLineHeight: number;
|
|
259
|
+
mastheadTitleMarginTop: number;
|
|
260
|
+
mastheadTitleMarginBottom: number;
|
|
261
|
+
mastheadTitleOpticalShift: number;
|
|
262
|
+
mastheadMetaLineHeight: number;
|
|
263
|
+
mastheadMetaGap: number;
|
|
264
|
+
mastheadMetaPaddingTop: number;
|
|
265
|
+
frontGridMarginTop: number;
|
|
266
|
+
insideHeaderHeight: number;
|
|
267
|
+
continuedTitleHeight: number;
|
|
268
|
+
continuedTitleChromeHeight: number;
|
|
269
|
+
continuedTitleFontSize: number;
|
|
270
|
+
continuedTitleLineHeight: number;
|
|
271
|
+
continuationSectionSeparatorHeight: number;
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
export type ChromeTextBoxMetrics = {
|
|
275
|
+
fontSize: number;
|
|
276
|
+
lineHeight: number;
|
|
277
|
+
paintHeight: number;
|
|
278
|
+
paintBuffer: number;
|
|
279
|
+
contentHeight: number;
|
|
280
|
+
paddingTop: number;
|
|
281
|
+
paddingBottom: number;
|
|
282
|
+
ruleTopHeight: number;
|
|
283
|
+
ruleBottomHeight: number;
|
|
284
|
+
height: number;
|
|
285
|
+
lineCount: number;
|
|
286
|
+
marginBefore: number;
|
|
287
|
+
marginAfter: number;
|
|
288
|
+
totalHeight: number;
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
export type StoryChromeMetrics = {
|
|
292
|
+
borderTopHeight: number;
|
|
293
|
+
paddingTop: number;
|
|
294
|
+
mediaPreludeHeight: number;
|
|
295
|
+
mediaPreludeMarginBottom: number;
|
|
296
|
+
labelFontSize: number;
|
|
297
|
+
labelLineHeight: number;
|
|
298
|
+
labelPaintHeight: number;
|
|
299
|
+
labelPaintBuffer: number;
|
|
300
|
+
labelHeight: number;
|
|
301
|
+
headlineFontSize: number;
|
|
302
|
+
headlineLineHeight: number;
|
|
303
|
+
headlinePaintHeight: number;
|
|
304
|
+
headlinePaintBuffer: number;
|
|
305
|
+
headlineHeight: number;
|
|
306
|
+
headlineLineCount: number;
|
|
307
|
+
headlineMarginTop: number;
|
|
308
|
+
headlineMarginBottom: number;
|
|
309
|
+
deckFontSize: number;
|
|
310
|
+
deckLineHeight: number;
|
|
311
|
+
deckPaintHeight: number;
|
|
312
|
+
deckPaintBuffer: number;
|
|
313
|
+
deckPaddingTop: number;
|
|
314
|
+
deckPaddingBottom: number;
|
|
315
|
+
deckRuleTopHeight: number;
|
|
316
|
+
deckRuleBottomHeight: number;
|
|
317
|
+
deckHeight: number;
|
|
318
|
+
deckLineCount: number;
|
|
319
|
+
deckMarginBottom: number;
|
|
320
|
+
bylineFontSize: number;
|
|
321
|
+
bylineLineHeight: number;
|
|
322
|
+
bylinePaintHeight: number;
|
|
323
|
+
bylinePaintBuffer: number;
|
|
324
|
+
bylinePaddingTop: number;
|
|
325
|
+
bylinePaddingBottom: number;
|
|
326
|
+
bylineHeight: number;
|
|
327
|
+
bylineLineCount: number;
|
|
328
|
+
bylineMarginBottom: number;
|
|
329
|
+
measureChromeHeight: number;
|
|
330
|
+
jumpFontSize: number;
|
|
331
|
+
jumpLineHeight: number;
|
|
332
|
+
jumpPaintHeight: number;
|
|
333
|
+
jumpPaintBuffer: number;
|
|
334
|
+
jumpHeight: number;
|
|
335
|
+
jumpPaddingTop: number;
|
|
336
|
+
jumpPaddingBottom: number;
|
|
337
|
+
jumpBorderTopHeight: number;
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
export type ContinuationTitleMetrics = {
|
|
341
|
+
label: ChromeTextBoxMetrics;
|
|
342
|
+
heading: ChromeTextBoxMetrics;
|
|
343
|
+
chromeHeight: number;
|
|
344
|
+
totalHeight: number;
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
export type FrontStoryRole = "feature" | "rail" | "standard";
|
|
348
|
+
|
|
349
|
+
export type ChromeTextStyle = {
|
|
350
|
+
fontSize: number;
|
|
351
|
+
fontFamily: string;
|
|
352
|
+
fontWeight?: number | string;
|
|
353
|
+
fontStyle?: "normal" | "italic";
|
|
354
|
+
lineHeightEm: number;
|
|
355
|
+
paintHeightEm: number;
|
|
356
|
+
marginBeforeEm?: number;
|
|
357
|
+
marginAfterEm?: number;
|
|
358
|
+
minHeightEm?: number;
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
export type ChromeBoxTreatment = {
|
|
362
|
+
paddingTop: number;
|
|
363
|
+
paddingBottom: number;
|
|
364
|
+
ruleTopHeight?: number;
|
|
365
|
+
ruleBottomHeight?: number;
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
export type PlacedTextRange = {
|
|
369
|
+
articleId: string;
|
|
370
|
+
pageId: string;
|
|
371
|
+
blockId: string;
|
|
372
|
+
startCursor: LayoutCursor;
|
|
373
|
+
endCursor: LayoutCursor;
|
|
374
|
+
exhausted: boolean;
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
export type ArticleFlow = {
|
|
378
|
+
article: ArticlePublicationItem;
|
|
379
|
+
currentCursor: LayoutCursor;
|
|
380
|
+
placedRanges: PlacedTextRange[];
|
|
381
|
+
usedImageAssetIds: Set<string>;
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
export type LayoutConfig = {
|
|
385
|
+
columnCount: number;
|
|
386
|
+
contentWidth: number;
|
|
387
|
+
gap: number;
|
|
388
|
+
rowGap: number;
|
|
389
|
+
rhythm: VerticalRhythm;
|
|
390
|
+
pageChrome: PageChromeMetrics;
|
|
391
|
+
lineHeight: number;
|
|
392
|
+
linePaintHeight: number;
|
|
393
|
+
bodyFontSize: number;
|
|
394
|
+
bodyFont: string;
|
|
395
|
+
frontRows: FrontRow[];
|
|
396
|
+
continuationHeight: number;
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
export type FrontRow = {
|
|
400
|
+
startIndex: number;
|
|
401
|
+
endIndex: number;
|
|
402
|
+
height: number;
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
export type FrontArticlePlacement = {
|
|
406
|
+
block: ArticleFrameBlockSpec;
|
|
407
|
+
originalIndex: number;
|
|
408
|
+
solveIndex: number;
|
|
409
|
+
gridPlacement?: SolvedFrontGridPlacement;
|
|
410
|
+
rowHeight?: number;
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
export type FrontArticleSolveOptions = {
|
|
414
|
+
enforcePlacementHeight?: boolean;
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
export type FrontCompositionFlowMode = "offsetBody" | "integrated" | "titleStackedMedia" | "stackedMedia";
|
|
418
|
+
|
|
419
|
+
export type ArticleFrameCandidate = {
|
|
420
|
+
block: SolvedBlock;
|
|
421
|
+
range: PlacedTextRange;
|
|
422
|
+
score: number;
|
|
423
|
+
whitespace: number;
|
|
424
|
+
furnitureSufficiency?: FurnitureSufficiencyReport;
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
export type FurnitureSufficiencyReport = {
|
|
428
|
+
accepted: boolean;
|
|
429
|
+
reason?: string;
|
|
430
|
+
visibleRows: number;
|
|
431
|
+
furnitureRows: number;
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
export type MediaVariant = {
|
|
435
|
+
id: string;
|
|
436
|
+
placement: ResponsivePlacementSpec | null;
|
|
437
|
+
assetRole?: string;
|
|
438
|
+
columnStart: number;
|
|
439
|
+
columnSpan: number;
|
|
440
|
+
fallbackPenalty: number;
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
export type PlacementVariant = {
|
|
444
|
+
id: string;
|
|
445
|
+
placement: ResponsivePlacementSpec;
|
|
446
|
+
columnStart: number;
|
|
447
|
+
columnSpan: number;
|
|
448
|
+
fallbackPenalty: number;
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
export type PullQuoteVariant = {
|
|
452
|
+
id: string;
|
|
453
|
+
placement: ResponsivePlacementSpec | null;
|
|
454
|
+
columnStart: number;
|
|
455
|
+
columnSpan: number;
|
|
456
|
+
fallbackPenalty: number;
|
|
457
|
+
};
|