@anthusai/papyrus 1.0.0-next.20 → 1.0.0-next.22
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/app/articles/[slug]/page.tsx +3 -3
- package/components/newspaper.tsx +4 -1
- package/components/use-brand-fonts-ready.ts +12 -15
- package/infra/build-specs.js +7 -3
- package/infra/build-specs.ts +7 -3
- package/lib/brand-fonts-watch.ts +43 -0
- package/lib/cached-content-repository.ts +12 -0
- package/lib/content-repository.ts +3 -0
- package/lib/content-types.ts +1 -0
- package/lib/graphql-content-repository.ts +38 -1
- package/lib/pretext-layout.ts +5 -0
- package/lib/video-script.ts +14 -0
- package/package.json +2 -1
- package/renderers/pretext/rhythm-blog-presentation.tsx +1 -2
- package/routes.manifest.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { notFound } from "next/navigation";
|
|
2
2
|
import { getSiteRenderer } from "../../../lib/site-renderer";
|
|
3
|
-
import { getCachedArticle } from "../../../lib/cached-content-repository";
|
|
3
|
+
import { getCachedArticle, getCachedVideoScript } from "../../../lib/cached-content-repository";
|
|
4
4
|
import { generateArticleStaticParams, siteServesNextReaderRoutes } from "../../../lib/reader-static-params";
|
|
5
5
|
import { SITE_BRAND } from "../../../lib/site-brand";
|
|
6
6
|
|
|
@@ -29,9 +29,9 @@ export async function generateMetadata({ params }: ArticlePageProps) {
|
|
|
29
29
|
export default async function ArticlePage({ params }: ArticlePageProps) {
|
|
30
30
|
if (!siteServesNextReaderRoutes()) notFound();
|
|
31
31
|
const { slug } = await params;
|
|
32
|
-
const article = await getCachedArticle(slug);
|
|
32
|
+
const [article, videoScript] = await Promise.all([getCachedArticle(slug), getCachedVideoScript(slug)]);
|
|
33
33
|
if (!article) notFound();
|
|
34
34
|
|
|
35
35
|
const siteRenderer = getSiteRenderer();
|
|
36
|
-
return <siteRenderer.renderArticle article={article} backHref="/" />;
|
|
36
|
+
return <siteRenderer.renderArticle article={article} videoScript={videoScript} backHref="/" />;
|
|
37
37
|
}
|
package/components/newspaper.tsx
CHANGED
|
@@ -12,6 +12,7 @@ import { ARCHIVE_PREVIEW_HEIGHT, ARCHIVE_PREVIEW_WIDTH } from "../lib/archive-ty
|
|
|
12
12
|
import type { EditionContent, NewsDeskAppendix, NewsDeskCategoryTreeNode } from "../lib/content-types";
|
|
13
13
|
import { shouldBypassImageOptimization } from "../lib/image-url";
|
|
14
14
|
import type { PublicationItem } from "../lib/publication-items";
|
|
15
|
+
import { clearPretextMeasurementCache } from "../lib/pretext-layout";
|
|
15
16
|
import { toPublicReaderPath } from "../lib/reader-base-path";
|
|
16
17
|
import { SITE_BRAND } from "../lib/site-brand";
|
|
17
18
|
import { resolveThemedImageSrc, type ResolvedTheme } from "../lib/themed-image";
|
|
@@ -131,7 +132,9 @@ export function Newspaper({
|
|
|
131
132
|
if (!fonts) return;
|
|
132
133
|
let cancelled = false;
|
|
133
134
|
const refreshAfterFontLoad = () => {
|
|
134
|
-
if (
|
|
135
|
+
if (cancelled) return;
|
|
136
|
+
clearPretextMeasurementCache();
|
|
137
|
+
setFontRevision((revision) => revision + 1);
|
|
135
138
|
};
|
|
136
139
|
fonts.ready.then(refreshAfterFontLoad).catch(() => undefined);
|
|
137
140
|
fonts.addEventListener("loadingdone", refreshAfterFontLoad);
|
|
@@ -2,29 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
import { useEffect, useState } from "react";
|
|
4
4
|
import { listBrandFontFamilies } from "../lib/brand-fonts";
|
|
5
|
+
import { watchBrandFontsLoaded } from "../lib/brand-fonts-watch";
|
|
6
|
+
import { clearPretextMeasurementCache } from "../lib/pretext-layout";
|
|
5
7
|
import { SITE_BRAND } from "../lib/site-brand";
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
|
-
* Returns a number that increments
|
|
9
|
-
*
|
|
10
|
-
* re-run with the real glyph
|
|
10
|
+
* Returns a number that increments each time the brand's declared font
|
|
11
|
+
* families finish loading. Pretext's width cache is cleared first, so
|
|
12
|
+
* measurements taken with fallback metrics are re-run with the real glyph
|
|
13
|
+
* widths. Stays 0 for brands without a font slot, and on the server.
|
|
11
14
|
*/
|
|
12
15
|
export function useBrandFontsReadyVersion(): number {
|
|
13
16
|
const [version, setVersion] = useState(0);
|
|
14
17
|
|
|
15
18
|
useEffect(() => {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (!isCancelled) setVersion((current) => current + 1);
|
|
23
|
-
})
|
|
24
|
-
.catch(() => undefined);
|
|
25
|
-
return () => {
|
|
26
|
-
isCancelled = true;
|
|
27
|
-
};
|
|
19
|
+
return watchBrandFontsLoaded({
|
|
20
|
+
fontFaceSet: typeof document === "undefined" ? undefined : document.fonts,
|
|
21
|
+
families: listBrandFontFamilies(SITE_BRAND.fonts),
|
|
22
|
+
clearMeasurementCaches: clearPretextMeasurementCache,
|
|
23
|
+
onFontsSettled: () => setVersion((current) => current + 1),
|
|
24
|
+
});
|
|
28
25
|
}, []);
|
|
29
26
|
|
|
30
27
|
return version;
|
package/infra/build-specs.js
CHANGED
|
@@ -15,7 +15,7 @@ function commandLines(commands, indent) {
|
|
|
15
15
|
const NPM_CLEAN_INSTALL = "npm ci --cache .npm --prefer-offline";
|
|
16
16
|
const FRONTEND_ENVIRONMENT_CAPTURE = "env | grep -e '^PAPYRUS_' -e '^SITE_ENV=' -e '^NEXT_PUBLIC_' >> .env.production";
|
|
17
17
|
const CHECKOUT_AS_PAPYRUS_ROOT = "PAPYRUS_ROOT=\"$PWD\"";
|
|
18
|
-
const
|
|
18
|
+
const RUNTIME_OUTPUTS_COMMANDS = [
|
|
19
19
|
"cp amplify_outputs.json .next/amplify_outputs.json",
|
|
20
20
|
"echo PAPYRUS_AMPLIFY_OUTPUTS=.next/amplify_outputs.json >> .env.production",
|
|
21
21
|
];
|
|
@@ -55,7 +55,11 @@ export function cmsProductionBuildSpec(config) {
|
|
|
55
55
|
"if [ -z \"${PAPYRUS_CONSOLE_RESPONDER_IMAGE_URI:-}\" ]; then export PAPYRUS_CONSOLE_RESPONDER_ALLOW_LOCAL_BUILD=true; fi",
|
|
56
56
|
"npx ampx pipeline-deploy --branch $AWS_BRANCH --app-id $AWS_APP_ID",
|
|
57
57
|
], " "),
|
|
58
|
-
...frontendSection([], [
|
|
58
|
+
...frontendSection([], [
|
|
59
|
+
FRONTEND_ENVIRONMENT_CAPTURE,
|
|
60
|
+
"npx papyrus-app sync && npm run build",
|
|
61
|
+
...(config.frontend === "pretext" ? RUNTIME_OUTPUTS_COMMANDS : []),
|
|
62
|
+
], ".next", [".next/cache/**/*", ".npm/**/*"]),
|
|
59
63
|
].join("\n") + "\n";
|
|
60
64
|
}
|
|
61
65
|
export function cmsStagingBuildSpec(config) {
|
|
@@ -71,7 +75,7 @@ export function cmsStagingBuildSpec(config) {
|
|
|
71
75
|
...frontendSection(preBuild, [
|
|
72
76
|
FRONTEND_ENVIRONMENT_CAPTURE,
|
|
73
77
|
"npx papyrus-app sync && npm run build",
|
|
74
|
-
...
|
|
78
|
+
...RUNTIME_OUTPUTS_COMMANDS,
|
|
75
79
|
], ".next", cachePaths),
|
|
76
80
|
].join("\n") + "\n";
|
|
77
81
|
}
|
package/infra/build-specs.ts
CHANGED
|
@@ -24,7 +24,7 @@ const FRONTEND_ENVIRONMENT_CAPTURE =
|
|
|
24
24
|
|
|
25
25
|
const CHECKOUT_AS_PAPYRUS_ROOT = "PAPYRUS_ROOT=\"$PWD\"";
|
|
26
26
|
|
|
27
|
-
const
|
|
27
|
+
const RUNTIME_OUTPUTS_COMMANDS = [
|
|
28
28
|
"cp amplify_outputs.json .next/amplify_outputs.json",
|
|
29
29
|
"echo PAPYRUS_AMPLIFY_OUTPUTS=.next/amplify_outputs.json >> .env.production",
|
|
30
30
|
];
|
|
@@ -83,7 +83,11 @@ export function cmsProductionBuildSpec(config: AmplifyAppShellSiteConfig): strin
|
|
|
83
83
|
),
|
|
84
84
|
...frontendSection(
|
|
85
85
|
[],
|
|
86
|
-
[
|
|
86
|
+
[
|
|
87
|
+
FRONTEND_ENVIRONMENT_CAPTURE,
|
|
88
|
+
"npx papyrus-app sync && npm run build",
|
|
89
|
+
...(config.frontend === "pretext" ? RUNTIME_OUTPUTS_COMMANDS : []),
|
|
90
|
+
],
|
|
87
91
|
".next",
|
|
88
92
|
[".next/cache/**/*", ".npm/**/*"],
|
|
89
93
|
),
|
|
@@ -110,7 +114,7 @@ export function cmsStagingBuildSpec(config: AmplifyAppShellSiteConfig): string {
|
|
|
110
114
|
[
|
|
111
115
|
FRONTEND_ENVIRONMENT_CAPTURE,
|
|
112
116
|
"npx papyrus-app sync && npm run build",
|
|
113
|
-
...
|
|
117
|
+
...RUNTIME_OUTPUTS_COMMANDS,
|
|
114
118
|
],
|
|
115
119
|
".next",
|
|
116
120
|
cachePaths,
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export type FontFaceSetLike = {
|
|
2
|
+
load(font: string): Promise<unknown>;
|
|
3
|
+
ready: Promise<unknown>;
|
|
4
|
+
addEventListener(type: string, listener: () => void): void;
|
|
5
|
+
removeEventListener(type: string, listener: () => void): void;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type WatchBrandFontsOptions = {
|
|
9
|
+
fontFaceSet: FontFaceSetLike | undefined;
|
|
10
|
+
families: string[];
|
|
11
|
+
clearMeasurementCaches: () => void;
|
|
12
|
+
onFontsSettled: () => void;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Clears Pretext measurement caches and then notifies the caller every time
|
|
17
|
+
* the declared families finish loading, so layout never reuses widths that
|
|
18
|
+
* were measured with fallback metrics. Clearing always happens before the
|
|
19
|
+
* notification, which makes the re-layout order deterministic.
|
|
20
|
+
*/
|
|
21
|
+
export function watchBrandFontsLoaded({
|
|
22
|
+
fontFaceSet,
|
|
23
|
+
families,
|
|
24
|
+
clearMeasurementCaches,
|
|
25
|
+
onFontsSettled,
|
|
26
|
+
}: WatchBrandFontsOptions): () => void {
|
|
27
|
+
if (families.length === 0 || !fontFaceSet) return () => undefined;
|
|
28
|
+
let isCancelled = false;
|
|
29
|
+
const settle = () => {
|
|
30
|
+
if (isCancelled) return;
|
|
31
|
+
clearMeasurementCaches();
|
|
32
|
+
onFontsSettled();
|
|
33
|
+
};
|
|
34
|
+
fontFaceSet.addEventListener("loadingdone", settle);
|
|
35
|
+
void Promise.all(families.map((family) => fontFaceSet.load(`16px ${JSON.stringify(family)}`)))
|
|
36
|
+
.then(() => fontFaceSet.ready)
|
|
37
|
+
.then(settle)
|
|
38
|
+
.catch(() => undefined);
|
|
39
|
+
return () => {
|
|
40
|
+
isCancelled = true;
|
|
41
|
+
fontFaceSet.removeEventListener("loadingdone", settle);
|
|
42
|
+
};
|
|
43
|
+
}
|
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
} from "./content-types";
|
|
11
11
|
import { graphqlContentRepository } from "./graphql-content-repository";
|
|
12
12
|
import { READER_REVALIDATE_SECONDS } from "./reader-route-config";
|
|
13
|
+
import type { VideoScriptRef } from "./video-script";
|
|
13
14
|
import { getContentSource } from "./site-env";
|
|
14
15
|
|
|
15
16
|
const bypassReaderCacheInDevelopment = process.env.NODE_ENV === "development";
|
|
@@ -88,3 +89,14 @@ export function getCachedLatestPublishedEdition(): Promise<EditionRouteSummary |
|
|
|
88
89
|
async () => graphqlContentRepository.getLatestPublishedEdition(),
|
|
89
90
|
);
|
|
90
91
|
}
|
|
92
|
+
|
|
93
|
+
export function getCachedVideoScript(targetSlug: string): Promise<VideoScriptRef | null> {
|
|
94
|
+
if (bypassReaderCacheInDevelopment) {
|
|
95
|
+
return Promise.resolve(graphqlContentRepository.loadVideoScript(targetSlug)).then((result) => result ?? null);
|
|
96
|
+
}
|
|
97
|
+
return withReaderCache(
|
|
98
|
+
["reader-video-script", targetSlug],
|
|
99
|
+
[articleCacheTag(targetSlug), ARTICLES_CACHE_TAG],
|
|
100
|
+
async () => graphqlContentRepository.loadVideoScript(targetSlug),
|
|
101
|
+
);
|
|
102
|
+
}
|
|
@@ -31,6 +31,9 @@ export const contentRepository: ContentRepository = {
|
|
|
31
31
|
listArticleSlugs() {
|
|
32
32
|
return graphqlContentRepository.listArticleSlugs();
|
|
33
33
|
},
|
|
34
|
+
loadVideoScript(targetSlug: string) {
|
|
35
|
+
return graphqlContentRepository.loadVideoScript(targetSlug);
|
|
36
|
+
},
|
|
34
37
|
};
|
|
35
38
|
|
|
36
39
|
export function getScenarioIdParam(scenario: string | string[] | null | undefined): string | null {
|
package/lib/content-types.ts
CHANGED
|
@@ -104,4 +104,5 @@ export type ContentRepository = {
|
|
|
104
104
|
getEditionArticle(options: GetEditionArticleOptions): Article | undefined | Promise<Article | undefined>;
|
|
105
105
|
getEditionItem(options: GetEditionItemOptions): PublicationItem | undefined | Promise<PublicationItem | undefined>;
|
|
106
106
|
listArticleSlugs(): string[] | Promise<string[]>;
|
|
107
|
+
loadVideoScript(targetSlug: string): VideoScriptRef | null | Promise<VideoScriptRef | null>;
|
|
107
108
|
};
|
|
@@ -18,6 +18,8 @@ import {
|
|
|
18
18
|
} from "./publication-items";
|
|
19
19
|
import { BodyIrError, matchBodyImages, projectBodyIr, type BodyProjection, type BodyProjectionImage } from "./markus-body";
|
|
20
20
|
import { SITE_BRAND } from "./site-brand";
|
|
21
|
+
import { shouldFetchVideoScripts } from "./video-mode";
|
|
22
|
+
import { collectVideoScriptTargets, parseVideoScriptRef, videomlItemSlug, type VideoScriptRef } from "./video-script";
|
|
21
23
|
import { currentContentSource, currentRequestClient, runWithContentSource } from "./content-source-context";
|
|
22
24
|
import { getContentSource } from "./site-env";
|
|
23
25
|
|
|
@@ -236,6 +238,10 @@ export const graphqlContentRepository: ContentRepository = {
|
|
|
236
238
|
return withReaderGraphQLContext(() => loadEditionItem(editionDate, itemSlug));
|
|
237
239
|
},
|
|
238
240
|
|
|
241
|
+
loadVideoScript(targetSlug) {
|
|
242
|
+
return withReaderGraphQLContext(() => loadVideoScriptForTarget(targetSlug));
|
|
243
|
+
},
|
|
244
|
+
|
|
239
245
|
listArticleSlugs() {
|
|
240
246
|
return withReaderGraphQLContext(async () => {
|
|
241
247
|
const items = await listItemsByTypeStatus(`${ARTICLE_TYPE}#${PUBLISHED_STATUS}`);
|
|
@@ -379,6 +385,9 @@ async function loadEditionContentFromEdition(edition: GraphQLEdition): Promise<E
|
|
|
379
385
|
const sanitizedLayoutPlan = pruneLayoutPlanUnavailableItems(layoutPlan, availableItemSlugs, edition.id);
|
|
380
386
|
validateEditionLayoutPlanForItems(sanitizedLayoutPlan, items, `PublishedEdition(${edition.id}).layoutPlan`);
|
|
381
387
|
|
|
388
|
+
const editionVideo = await normalizeEditionVideoAsset(editionMetadata?.editionVideo);
|
|
389
|
+
const videoScripts = await loadEditionVideoScripts(items, editionVideo);
|
|
390
|
+
|
|
382
391
|
return {
|
|
383
392
|
id: edition.id,
|
|
384
393
|
source: "graphql",
|
|
@@ -389,10 +398,38 @@ async function loadEditionContentFromEdition(edition: GraphQLEdition): Promise<E
|
|
|
389
398
|
items,
|
|
390
399
|
sections: createEditionSectionPlan(items, edition.metadata),
|
|
391
400
|
suppressNewsDeskAppendix: editionMetadata?.suppressNewsDeskAppendix === true,
|
|
392
|
-
editionVideo
|
|
401
|
+
editionVideo,
|
|
402
|
+
videoScripts,
|
|
393
403
|
};
|
|
394
404
|
}
|
|
395
405
|
|
|
406
|
+
function videoScriptsEnabled(): boolean {
|
|
407
|
+
return Boolean(SITE_BRAND.videoPlayer) && shouldFetchVideoScripts();
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
async function loadVideoScriptForTarget(targetSlug: string): Promise<VideoScriptRef | null> {
|
|
411
|
+
if (!videoScriptsEnabled()) return null;
|
|
412
|
+
const item = await getItemBySlug(videomlItemSlug(targetSlug));
|
|
413
|
+
if (!item || item.type !== "videoml" || !isVisibleItemStatus(item.status)) return null;
|
|
414
|
+
return parseVideoScriptRef(item);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
async function loadEditionVideoScripts(
|
|
418
|
+
items: PublicationItem[],
|
|
419
|
+
editionVideo: ArticleVideoAsset | null,
|
|
420
|
+
): Promise<Record<string, VideoScriptRef> | undefined> {
|
|
421
|
+
if (!videoScriptsEnabled()) return undefined;
|
|
422
|
+
const targets = collectVideoScriptTargets(items, editionVideo !== null);
|
|
423
|
+
const entries = await Promise.all(
|
|
424
|
+
targets.map(async (targetSlug) => {
|
|
425
|
+
const script = await loadVideoScriptForTarget(targetSlug);
|
|
426
|
+
return script ? ([targetSlug, script] as const) : null;
|
|
427
|
+
}),
|
|
428
|
+
);
|
|
429
|
+
const videoScripts = Object.fromEntries(entries.filter((entry): entry is [string, VideoScriptRef] => entry !== null));
|
|
430
|
+
return Object.keys(videoScripts).length > 0 ? videoScripts : undefined;
|
|
431
|
+
}
|
|
432
|
+
|
|
396
433
|
async function normalizeEditionVideoAsset(value: unknown): Promise<ArticleVideoAsset | null> {
|
|
397
434
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
398
435
|
const record = value as Record<string, unknown>;
|
package/lib/pretext-layout.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
clearCache,
|
|
2
3
|
layoutNextLine,
|
|
3
4
|
prepareWithSegments,
|
|
4
5
|
type LayoutCursor,
|
|
@@ -181,3 +182,7 @@ function toTextLine(line: LayoutLine, x: number, y: number, fontSize: number, fo
|
|
|
181
182
|
function clamp(value: number, min: number, max: number): number {
|
|
182
183
|
return Math.max(min, Math.min(max, value));
|
|
183
184
|
}
|
|
185
|
+
|
|
186
|
+
export function clearPretextMeasurementCache(): void {
|
|
187
|
+
clearCache();
|
|
188
|
+
}
|
package/lib/video-script.ts
CHANGED
|
@@ -21,6 +21,20 @@ export type VideoScriptRef = {
|
|
|
21
21
|
targetKind: VideoScriptTargetKind;
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
+
export const EDITION_OVERVIEW_VIDEO_KEY = "edition-overview";
|
|
25
|
+
|
|
26
|
+
export function collectVideoScriptTargets(
|
|
27
|
+
items: ReadonlyArray<{ slug: string; video?: unknown }>,
|
|
28
|
+
hasEditionVideo: boolean,
|
|
29
|
+
): string[] {
|
|
30
|
+
const targets = new Set<string>();
|
|
31
|
+
if (hasEditionVideo) targets.add(EDITION_OVERVIEW_VIDEO_KEY);
|
|
32
|
+
for (const item of items) {
|
|
33
|
+
if (item.video) targets.add(item.slug);
|
|
34
|
+
}
|
|
35
|
+
return [...targets];
|
|
36
|
+
}
|
|
37
|
+
|
|
24
38
|
export function videomlItemSlug(targetSlug: string): string {
|
|
25
39
|
return `${targetSlug}--videoml`;
|
|
26
40
|
}
|
package/package.json
CHANGED
|
@@ -133,6 +133,7 @@
|
|
|
133
133
|
"./lib/articles": "./lib/articles.ts",
|
|
134
134
|
"./lib/blog-feature-solver": "./lib/blog-feature-solver.ts",
|
|
135
135
|
"./lib/blog-rhythm": "./lib/blog-rhythm.ts",
|
|
136
|
+
"./lib/brand-fonts-watch": "./lib/brand-fonts-watch.ts",
|
|
136
137
|
"./lib/brand-fonts": "./lib/brand-fonts.ts",
|
|
137
138
|
"./lib/cached-content-repository": "./lib/cached-content-repository.ts",
|
|
138
139
|
"./lib/category-dashboard": "./lib/category-dashboard.ts",
|
|
@@ -292,7 +293,7 @@
|
|
|
292
293
|
"optional": true
|
|
293
294
|
}
|
|
294
295
|
},
|
|
295
|
-
"version": "1.0.0-next.
|
|
296
|
+
"version": "1.0.0-next.22",
|
|
296
297
|
"dependencies": {
|
|
297
298
|
"@aws-amplify/adapter-nextjs": "^1.7.3",
|
|
298
299
|
"@aws-sdk/client-cognito-identity-provider": "3.1045.0",
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
type PublicationItem,
|
|
16
16
|
} from "../../lib/publication-items";
|
|
17
17
|
import { SITE_BRAND } from "../../lib/site-brand";
|
|
18
|
-
import type
|
|
18
|
+
import { EDITION_OVERVIEW_VIDEO_KEY, type VideoScriptRef } from "../../lib/video-script";
|
|
19
19
|
import { ArticleVideoFigure } from "../../components/article-video";
|
|
20
20
|
import { BlogPageBackground as GenericBlogPageBackground } from "../../components/blog-page-background";
|
|
21
21
|
import { PictogramFigure as GenericPictogramFigure } from "../../components/pictogram-figure";
|
|
@@ -40,7 +40,6 @@ import { BLOG_RHYTHM, getFeaturedLayoutStyle, getRhythmShellStyle } from "./rhyt
|
|
|
40
40
|
const BlogPageBackground = SITE_BRAND.components?.BlogPageBackground ?? GenericBlogPageBackground;
|
|
41
41
|
const PictogramFigure = SITE_BRAND.components?.PictogramFigure ?? GenericPictogramFigure;
|
|
42
42
|
const BLOG_TEXT_STYLE = createDefaultBlogTextStyle(SITE_BRAND.textFont);
|
|
43
|
-
const EDITION_OVERVIEW_VIDEO_KEY = "edition-overview";
|
|
44
43
|
const SECONDARY_PAIR_ITEM_INDEX = 2;
|
|
45
44
|
|
|
46
45
|
type PapyrusTestWindow = Window & typeof globalThis & {
|
package/routes.manifest.json
CHANGED