@anthusai/papyrus 0.0.0 → 1.0.0-next.2
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/LICENSE +21 -0
- package/amplify/auth/resource.js +61 -0
- package/amplify/auth/resource.ts +65 -0
- package/amplify/data/resource.js +2037 -0
- package/amplify/data/resource.ts +2148 -0
- package/amplify/data/schema.js +1850 -0
- package/amplify/data/schema.ts +1953 -0
- package/amplify/functions/assignment-action/handler.ts +1416 -0
- package/amplify/functions/assignment-action/resource.js +8 -0
- package/amplify/functions/assignment-action/resource.ts +9 -0
- package/amplify/functions/category-action/handler.ts +2259 -0
- package/amplify/functions/category-action/resource.js +8 -0
- package/amplify/functions/category-action/resource.ts +9 -0
- package/amplify/functions/console-chat-responder/Cargo.lock +3456 -0
- package/amplify/functions/console-chat-responder/Cargo.toml +26 -0
- package/amplify/functions/console-chat-responder/Dockerfile +19 -0
- package/amplify/functions/console-chat-responder/py/execute_tactus_runner.py +116 -0
- package/amplify/functions/console-chat-responder/resource.js +153 -0
- package/amplify/functions/console-chat-responder/resource.ts +179 -0
- package/amplify/functions/console-chat-responder/src/main.rs +3242 -0
- package/amplify/functions/email-submission-processor/handler.py +46 -0
- package/amplify/functions/email-submission-processor/requirements.txt +2 -0
- package/amplify/functions/email-submission-processor/resource.js +56 -0
- package/amplify/functions/email-submission-processor/resource.ts +69 -0
- package/amplify/functions/graphql-jwt-authorizer/handler.ts +193 -0
- package/amplify/functions/graphql-jwt-authorizer/resource.js +14 -0
- package/amplify/functions/graphql-jwt-authorizer/resource.ts +15 -0
- package/amplify/functions/knowledge-query/handler.py +1 -0
- package/amplify/functions/knowledge-query/requirements.txt +3 -0
- package/amplify/functions/knowledge-query/resource.js +50 -0
- package/amplify/functions/knowledge-query/resource.ts +61 -0
- package/amplify/functions/manage-user-role/handler.ts +799 -0
- package/amplify/functions/manage-user-role/resource.js +8 -0
- package/amplify/functions/manage-user-role/resource.ts +9 -0
- package/amplify/functions/model-attachment-upload/handler.ts +387 -0
- package/amplify/functions/model-attachment-upload/resource.js +8 -0
- package/amplify/functions/model-attachment-upload/resource.ts +9 -0
- package/amplify/functions/newsroom-summary/handler.ts +321 -0
- package/amplify/functions/newsroom-summary/resource.js +8 -0
- package/amplify/functions/newsroom-summary/resource.ts +9 -0
- package/amplify/functions/procedure-action/handler.ts +621 -0
- package/amplify/functions/procedure-action/resource.js +8 -0
- package/amplify/functions/procedure-action/resource.ts +9 -0
- package/amplify/functions/reader-settings/handler.ts +322 -0
- package/amplify/functions/reader-settings/resource.js +8 -0
- package/amplify/functions/reader-settings/resource.ts +9 -0
- package/amplify/functions/ses-inbound-receive/handler.ts +493 -0
- package/amplify/functions/ses-inbound-receive/resource.js +8 -0
- package/amplify/functions/ses-inbound-receive/resource.ts +9 -0
- package/amplify/functions/shared/amplify-secrets.ts +65 -0
- package/amplify/functions/shared/console-chat-enqueue.ts +117 -0
- package/amplify/functions/shared/email-mime-intake.ts +196 -0
- package/amplify/functions/shared/email-submission.ts +415 -0
- package/amplify/functions/shared/inbound-email-intake.ts +37 -0
- package/amplify/functions/shared/lambda-data-client.ts +21 -0
- package/amplify/functions/shared/model-payloads.ts +186 -0
- package/amplify/functions/shared/python-bundle.js +115 -0
- package/amplify/functions/shared/python-bundle.ts +141 -0
- package/amplify/functions/shared/slack-events.ts +111 -0
- package/amplify/functions/slack-delivery/handler.py +112 -0
- package/amplify/functions/slack-delivery/requirements.txt +2 -0
- package/amplify/functions/slack-delivery/resource.js +38 -0
- package/amplify/functions/slack-delivery/resource.ts +51 -0
- package/amplify/functions/slack-events/handler.ts +155 -0
- package/amplify/functions/slack-events/resource.js +11 -0
- package/amplify/functions/slack-events/resource.ts +12 -0
- package/amplify/inbound-email/stack.js +46 -0
- package/amplify/inbound-email/stack.ts +61 -0
- package/amplify/package.json +3 -0
- package/amplify/site-backend-config.js +1 -0
- package/amplify/site-backend-config.ts +30 -0
- package/amplify/site-backend.js +587 -0
- package/amplify/site-backend.ts +704 -0
- package/amplify/storage/resource.js +18 -0
- package/amplify/storage/resource.ts +19 -0
- package/app/[year]/[month]/[day]/[articleSlug]/page.tsx +62 -0
- package/app/[year]/[month]/[day]/edition-route-page.tsx +53 -0
- package/app/[year]/[month]/[day]/page/[pageNumber]/page.tsx +32 -0
- package/app/[year]/[month]/[day]/page.tsx +22 -0
- package/app/[year]/[month]/[day]/section/[sectionKey]/page.tsx +32 -0
- package/app/api/archive/editions/route.ts +18 -0
- package/app/api/media/[...path]/route.ts +35 -0
- package/app/api/revalidate/route.ts +94 -0
- package/app/archive/page.tsx +24 -0
- package/app/articles/[slug]/page.tsx +35 -0
- package/app/globals.css +10329 -0
- package/app/layout.tsx +146 -0
- package/app/newsroom/[section]/[...selection]/page.tsx +50 -0
- package/app/newsroom/[section]/page.tsx +47 -0
- package/app/newsroom/forum/[threadId]/page.tsx +35 -0
- package/app/newsroom/insights/[threadId]/page.tsx +22 -0
- package/app/newsroom/insights/page.tsx +14 -0
- package/app/newsroom/layout.tsx +12 -0
- package/app/newsroom/page.tsx +31 -0
- package/app/newsroom/sections/[sectionId]/page.tsx +17 -0
- package/app/page.tsx +272 -0
- package/app/settings/page.tsx +14 -0
- package/app/tailwind.css +65 -0
- package/bin/papyrus-app.mjs +10 -0
- package/bin/papyrus-infra.mjs +39 -0
- package/components/ai-elements/conversation.tsx +168 -0
- package/components/ai-elements/message.tsx +360 -0
- package/components/ai-elements/model-selector.tsx +30 -0
- package/components/ai-elements/prompt-input.tsx +1461 -0
- package/components/ai-elements/shimmer.tsx +76 -0
- package/components/ai-elements/suggestion.tsx +54 -0
- package/components/ai-elements/tool.tsx +173 -0
- package/components/amplify-client-provider.tsx +140 -0
- package/components/archive-grid.tsx +89 -0
- package/components/archive-shell.tsx +41 -0
- package/components/article-video.tsx +47 -0
- package/components/blog-page-background.tsx +20 -0
- package/components/dev-sandbox-editor-sign-in.tsx +60 -0
- package/components/news-desk-client-provider.tsx +211 -0
- package/components/news-desk-page.tsx +119 -0
- package/components/news-desk-taxonomy-client.ts +3684 -0
- package/components/newspaper.tsx +1929 -0
- package/components/newsroom-assignments-view.tsx +664 -0
- package/components/newsroom-ops-overview.tsx +226 -0
- package/components/newsroom-ops-shell.tsx +319 -0
- package/components/newsroom-references-view.tsx +598 -0
- package/components/newsroom-topics-view.tsx +425 -0
- package/components/papyrus-console-shell.tsx +1912 -0
- package/components/pictogram-figure.tsx +65 -0
- package/components/presentation-footer.tsx +146 -0
- package/components/reader-auth-control.tsx +162 -0
- package/components/reader-auth-state.ts +122 -0
- package/components/reader-settings.ts +332 -0
- package/components/reader-theme-control.tsx +85 -0
- package/components/reference-source-preview.tsx +163 -0
- package/components/settings-page.tsx +239 -0
- package/components/topic-steering-workspace.tsx +18063 -0
- package/components/ui/badge.tsx +30 -0
- package/components/ui/button-group.tsx +87 -0
- package/components/ui/button.tsx +58 -0
- package/components/ui/card.tsx +32 -0
- package/components/ui/command.tsx +194 -0
- package/components/ui/dialog.tsx +158 -0
- package/components/ui/dropdown-menu.tsx +266 -0
- package/components/ui/hover-card.tsx +51 -0
- package/components/ui/input-group.tsx +158 -0
- package/components/ui/input.tsx +20 -0
- package/components/ui/scroll-area.tsx +16 -0
- package/components/ui/select.tsx +199 -0
- package/components/ui/separator.tsx +23 -0
- package/components/ui/sheet.tsx +92 -0
- package/components/ui/spinner.tsx +10 -0
- package/components/ui/tabs.tsx +113 -0
- package/components/ui/textarea.tsx +18 -0
- package/components/ui/tooltip.tsx +66 -0
- package/components/use-resolved-papyrus-theme.ts +47 -0
- package/infra/amplify-app-shell.js +109 -0
- package/infra/amplify-app-shell.ts +155 -0
- package/infra/index.js +12 -0
- package/lib/amplify-outputs-guard.ts +24 -0
- package/lib/amplify-outputs-path.ts +16 -0
- package/lib/amplify-server-runtime.ts +63 -0
- package/lib/archive-data.ts +30 -0
- package/lib/archive-types.ts +15 -0
- package/lib/articles.ts +320 -0
- package/lib/cached-content-repository.ts +89 -0
- package/lib/category-dashboard.ts +37 -0
- package/lib/category-repository.ts +1907 -0
- package/lib/console-chat-client.ts +1316 -0
- package/lib/console-web-context.ts +118 -0
- package/lib/content-load-timing.ts +23 -0
- package/lib/content-repository.ts +40 -0
- package/lib/content-types.ts +104 -0
- package/lib/define-site.js +3 -0
- package/lib/define-site.ts +21 -0
- package/lib/demo-amplify-outputs.ts +15 -0
- package/lib/dev-sandbox-editor-auth.ts +18 -0
- package/lib/doctrine.ts +128 -0
- package/lib/edition-routes.ts +155 -0
- package/lib/edition-sections.ts +111 -0
- package/lib/empty-edition-layout-plan.ts +109 -0
- package/lib/excerpts.ts +8 -0
- package/lib/graphql-content-repository.ts +986 -0
- package/lib/image-url.ts +10 -0
- package/lib/layout-plan.ts +981 -0
- package/lib/layout-scenarios.ts +564 -0
- package/lib/markus-body.ts +168 -0
- package/lib/markus-ir.ts +235 -0
- package/lib/markus-projection.ts +243 -0
- package/lib/markus-to-article.ts +93 -0
- package/lib/news-desk-session.ts +160 -0
- package/lib/newspaper-layout.ts +3459 -0
- package/lib/newsroom-assignments.ts +173 -0
- package/lib/newsroom-base-path.ts +48 -0
- package/lib/newsroom-card-layout.ts +102 -0
- package/lib/newsroom-category-drilldown.ts +133 -0
- package/lib/newsroom-demo-dashboard.ts +267 -0
- package/lib/newsroom-demo-profile.ts +46 -0
- package/lib/newsroom-forum-routes.ts +153 -0
- package/lib/newsroom-index-filters.ts +202 -0
- package/lib/newsroom-knowledge-query-request.ts +51 -0
- package/lib/newsroom-list-selection.ts +42 -0
- package/lib/newsroom-nav.ts +42 -0
- package/lib/newsroom-references.ts +116 -0
- package/lib/newsroom-sections.ts +52 -0
- package/lib/newsroom-topics.ts +118 -0
- package/lib/papyrus-object-kinds.ts +24 -0
- package/lib/papyrus-web-locations.ts +480 -0
- package/lib/pilobol-sample.ts +167 -0
- package/lib/presentation-footer.ts +92 -0
- package/lib/pretext-layout.ts +179 -0
- package/lib/public-placeholder-config.ts +102 -0
- package/lib/publication-items.ts +181 -0
- package/lib/reader-route-config.ts +1 -0
- package/lib/reader-static-params.ts +56 -0
- package/lib/reader-storage-url.ts +63 -0
- package/lib/reference-display.ts +74 -0
- package/lib/reference-policy.ts +97 -0
- package/lib/reference-source-preview.ts +175 -0
- package/lib/renderer-config.ts +50 -0
- package/lib/renderer.ts +87 -0
- package/lib/reporting-story-budget.ts +638 -0
- package/lib/semantic-graph.ts +554 -0
- package/lib/site-brand-tenant.ts +41 -0
- package/lib/site-brand.ts +179 -0
- package/lib/site-renderer.ts +17 -0
- package/lib/site-stack.ts +195 -0
- package/lib/themed-image.ts +30 -0
- package/lib/utils.ts +6 -0
- package/middleware.ts +78 -0
- package/package.json +296 -1
- package/publications/anth_us/brand.ts +68 -0
- package/publications/anth_us/theme.css +66 -0
- package/publications/papyrus/theme.css +11 -0
- package/publications/pilobol_us/brand.ts +37 -0
- package/publications/pilobol_us/theme.css +140 -0
- package/publications/threat_intelligence/blog-defense/graph.ts +636 -0
- package/publications/threat_intelligence/blog-defense/layout.ts +934 -0
- package/publications/threat_intelligence/blog-defense/page-background.tsx +590 -0
- package/publications/threat_intelligence/brand.ts +40 -0
- package/publications/threat_intelligence/pictograms/art.tsx +608 -0
- package/publications/threat_intelligence/pictograms/figure.tsx +82 -0
- package/publications/threat_intelligence/pictograms/registry.ts +30 -0
- package/publications/threat_intelligence/pictograms/system.tsx +305 -0
- package/publications/threat_intelligence/seed/seed-edition-content.json +629 -0
- package/publications/threat_intelligence/theme.css +1440 -0
- package/renderers/markus/index.ts +25 -0
- package/renderers/markus/stubs.tsx +33 -0
- package/renderers/pretext/article-page.tsx +174 -0
- package/renderers/pretext/index.ts +30 -0
- package/renderers/pretext/layouts.ts +16 -0
- package/renderers/pretext/presentation-shell.tsx +477 -0
- package/routes.manifest.json +182 -0
- package/scripts/fixtures/pilobol-sample.ast.json +234 -0
- package/src/backend.js +1 -0
- package/src/backend.ts +3 -0
- package/src/sync.mjs +87 -0
- package/src/with-papyrus.d.mts +2 -0
- package/src/with-papyrus.mjs +47 -0
|
@@ -0,0 +1,564 @@
|
|
|
1
|
+
import { type Article, type ArticleVideoAsset, articles, editionDate } from "./articles";
|
|
2
|
+
import type { EditionContent, NewsDeskAppendix } from "./content-types";
|
|
3
|
+
import { createEditionSectionPlan } from "./edition-sections";
|
|
4
|
+
import { createDefaultEditionLayoutPlan, type EditionLayoutPlan } from "./layout-plan";
|
|
5
|
+
import { articleToPublicationItem, cloneArticle } from "./publication-items";
|
|
6
|
+
import { PILOBOL_SAMPLE_SCENARIO_ID, createPilobolSampleEditionContent } from "./pilobol-sample";
|
|
7
|
+
import { SITE_BRAND } from "./site-brand";
|
|
8
|
+
import threatIntelligenceSeedContent from "../publications/threat_intelligence/seed/seed-edition-content.json";
|
|
9
|
+
|
|
10
|
+
export type LayoutScenario = EditionContent & {
|
|
11
|
+
source: "scenario";
|
|
12
|
+
scenarioId: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const DEFAULT_LAYOUT_SCENARIO_ID = "current-edition";
|
|
16
|
+
|
|
17
|
+
type RawLayoutScenario = Omit<LayoutScenario, "sections"> & {
|
|
18
|
+
sections?: LayoutScenario["sections"];
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const defaultScenarioSeed = getDefaultScenarioSeed();
|
|
22
|
+
|
|
23
|
+
const rawLayoutScenarios: RawLayoutScenario[] = [
|
|
24
|
+
{
|
|
25
|
+
id: DEFAULT_LAYOUT_SCENARIO_ID,
|
|
26
|
+
source: "scenario",
|
|
27
|
+
title: defaultScenarioSeed.title,
|
|
28
|
+
editionDate: defaultScenarioSeed.editionDate,
|
|
29
|
+
scenarioId: DEFAULT_LAYOUT_SCENARIO_ID,
|
|
30
|
+
description: defaultScenarioSeed.description,
|
|
31
|
+
layoutPlan: createDefaultEditionLayoutPlan(defaultScenarioSeed.articles.map((article) => article.slug)),
|
|
32
|
+
items: cloneArticles(defaultScenarioSeed.articles).map(articleToPublicationItem),
|
|
33
|
+
suppressNewsDeskAppendix: defaultScenarioSeed.suppressNewsDeskAppendix,
|
|
34
|
+
editionVideo: defaultScenarioSeed.editionVideo,
|
|
35
|
+
newsDeskAppendix: defaultScenarioSeed.newsDeskAppendix,
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: "blank-edition-title",
|
|
39
|
+
source: "scenario",
|
|
40
|
+
title: " ",
|
|
41
|
+
editionDate,
|
|
42
|
+
scenarioId: "blank-edition-title",
|
|
43
|
+
description: "A fixture edition with a blank title used to verify masthead fallback labeling.",
|
|
44
|
+
layoutPlan: createDefaultEditionLayoutPlan(articles.map((article) => article.slug)),
|
|
45
|
+
items: cloneArticles(articles).map(articleToPublicationItem),
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: "shared-blank-column-pressure",
|
|
49
|
+
source: "scenario",
|
|
50
|
+
title: "Shared Continuation Blank Column Pressure",
|
|
51
|
+
editionDate,
|
|
52
|
+
scenarioId: "shared-blank-column-pressure",
|
|
53
|
+
description:
|
|
54
|
+
"Shorter shared continuation tails with images and pull quotes, used to prove that empty columns are repaired with solved furniture.",
|
|
55
|
+
layoutPlan: createDefaultEditionLayoutPlan(articles.map((article) => article.slug)),
|
|
56
|
+
items: createSharedBlankColumnPressureArticles().map(articleToPublicationItem),
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: "shared-continuation-no-pull-quotes",
|
|
60
|
+
source: "scenario",
|
|
61
|
+
title: "Shared Continuation Without Pull Quotes",
|
|
62
|
+
editionDate,
|
|
63
|
+
scenarioId: "shared-continuation-no-pull-quotes",
|
|
64
|
+
description:
|
|
65
|
+
"The shared continuation page with editorial pull quotes removed, used to prove pull quotes are optional display furniture.",
|
|
66
|
+
layoutPlan: createDefaultEditionLayoutPlan(articles.map((article) => article.slug)),
|
|
67
|
+
items: createNoPullQuoteArticles().map(articleToPublicationItem),
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: "front-chrome-compact",
|
|
71
|
+
source: "scenario",
|
|
72
|
+
title: "Front Chrome Compact Typography",
|
|
73
|
+
editionDate,
|
|
74
|
+
scenarioId: "front-chrome-compact",
|
|
75
|
+
description:
|
|
76
|
+
"Compact headline and deck line heights prove that em-authored chrome reserves enough paint space to prevent overlap.",
|
|
77
|
+
layoutPlan: createCompactChromeLayoutPlan(),
|
|
78
|
+
items: cloneArticles(articles).map(articleToPublicationItem),
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
id: "front-body-depth-rows",
|
|
82
|
+
source: "scenario",
|
|
83
|
+
title: "Front Body Depth Rows",
|
|
84
|
+
editionDate,
|
|
85
|
+
scenarioId: "front-body-depth-rows",
|
|
86
|
+
description:
|
|
87
|
+
"Per-column front teaser depth should keep the lead trio aligned while still continuing onto later pages.",
|
|
88
|
+
layoutPlan: createFrontBodyDepthLayoutPlan(),
|
|
89
|
+
items: cloneArticles(articles).map(articleToPublicationItem),
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: "furniture-sufficiency-pressure",
|
|
93
|
+
source: "scenario",
|
|
94
|
+
title: "Furniture Sufficiency Pressure",
|
|
95
|
+
editionDate,
|
|
96
|
+
scenarioId: "furniture-sufficiency-pressure",
|
|
97
|
+
description:
|
|
98
|
+
"Oversized image and pull-quote packages prove that the solver rejects furniture that leaves too little readable copy.",
|
|
99
|
+
layoutPlan: createFurnitureSufficiencyLayoutPlan(),
|
|
100
|
+
items: createFurnitureSufficiencyArticles().map(articleToPublicationItem),
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
id: "long-image-caption",
|
|
104
|
+
source: "scenario",
|
|
105
|
+
title: "Long Image Caption",
|
|
106
|
+
editionDate,
|
|
107
|
+
scenarioId: "long-image-caption",
|
|
108
|
+
description:
|
|
109
|
+
"A continuation image with a long caption proves image furniture reserves enough rhythm rows for complete caption text.",
|
|
110
|
+
layoutPlan: createDefaultEditionLayoutPlan(articles.map((article) => article.slug)),
|
|
111
|
+
items: createLongImageCaptionArticles().map(articleToPublicationItem),
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
id: "themed-ink-study",
|
|
115
|
+
source: "scenario",
|
|
116
|
+
title: "Themed Ink Study",
|
|
117
|
+
editionDate,
|
|
118
|
+
scenarioId: "themed-ink-study",
|
|
119
|
+
description: "A raster fixture verifies dark-mode image variant switching in solved newspaper image furniture.",
|
|
120
|
+
layoutPlan: createDefaultEditionLayoutPlan(articles.map((article) => article.slug)),
|
|
121
|
+
items: createThemedInkStudyArticles().map(articleToPublicationItem),
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
id: "height-policy-fill-default",
|
|
125
|
+
source: "scenario",
|
|
126
|
+
title: "Height Policy Fill Default",
|
|
127
|
+
editionDate,
|
|
128
|
+
scenarioId: "height-policy-fill-default",
|
|
129
|
+
description:
|
|
130
|
+
"A page-two continuation without shrinkToContent proves stacked regions still preserve their allocated fill height by default.",
|
|
131
|
+
layoutPlan: createHeightPolicyLayoutPlan({}),
|
|
132
|
+
items: cloneArticles(articles).map(articleToPublicationItem),
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
id: "height-policy-default-rows",
|
|
136
|
+
source: "scenario",
|
|
137
|
+
title: "Height Policy Default Rows",
|
|
138
|
+
editionDate,
|
|
139
|
+
scenarioId: "height-policy-default-rows",
|
|
140
|
+
description:
|
|
141
|
+
"A page-two continuation with block defaultRows proves an article frame can hold an editorial row target.",
|
|
142
|
+
layoutPlan: createHeightPolicyLayoutPlan({ regionShrinkToContent: true, defaultRows: 64 }),
|
|
143
|
+
items: cloneArticles(articles).map(articleToPublicationItem),
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
id: "height-policy-default-rows-grow",
|
|
147
|
+
source: "scenario",
|
|
148
|
+
title: "Height Policy Default Rows Grow",
|
|
149
|
+
editionDate,
|
|
150
|
+
scenarioId: "height-policy-default-rows-grow",
|
|
151
|
+
description:
|
|
152
|
+
"A short article-frame row target proves exhaustive continuation text can grow beyond defaultRows.",
|
|
153
|
+
layoutPlan: createHeightPolicyLayoutPlan({ regionShrinkToContent: true, defaultRows: 12 }),
|
|
154
|
+
items: cloneArticles(articles).map(articleToPublicationItem),
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
id: "height-policy-default-rows-shrink",
|
|
158
|
+
source: "scenario",
|
|
159
|
+
title: "Height Policy Default Rows Shrink",
|
|
160
|
+
editionDate,
|
|
161
|
+
scenarioId: "height-policy-default-rows-shrink",
|
|
162
|
+
description:
|
|
163
|
+
"A shrinkable article-frame row target proves defaultRows can be released when content solves shorter.",
|
|
164
|
+
layoutPlan: createHeightPolicyLayoutPlan({ regionShrinkToContent: true, defaultRows: 64, blockShrinkToContent: true }),
|
|
165
|
+
items: cloneArticles(articles).map(articleToPublicationItem),
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
id: PILOBOL_SAMPLE_SCENARIO_ID,
|
|
169
|
+
source: "scenario",
|
|
170
|
+
scenarioId: PILOBOL_SAMPLE_SCENARIO_ID,
|
|
171
|
+
...createPilobolSampleEditionContent(),
|
|
172
|
+
},
|
|
173
|
+
];
|
|
174
|
+
|
|
175
|
+
export const layoutScenarios: LayoutScenario[] = rawLayoutScenarios.map((scenario) => ({
|
|
176
|
+
...scenario,
|
|
177
|
+
sections: scenario.sections ?? createEditionSectionPlan(scenario.items),
|
|
178
|
+
}));
|
|
179
|
+
|
|
180
|
+
export function getLayoutScenario(id: string | null | undefined): LayoutScenario {
|
|
181
|
+
return layoutScenarios.find((scenario) => scenario.id === id) ?? layoutScenarios[0];
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function getDefaultScenarioSeed(): {
|
|
185
|
+
title: string;
|
|
186
|
+
editionDate: string;
|
|
187
|
+
description: string;
|
|
188
|
+
articles: Article[];
|
|
189
|
+
suppressNewsDeskAppendix?: boolean;
|
|
190
|
+
editionVideo?: ArticleVideoAsset;
|
|
191
|
+
newsDeskAppendix?: NewsDeskAppendix | null;
|
|
192
|
+
} {
|
|
193
|
+
if (SITE_BRAND.id !== "threat-intelligence") {
|
|
194
|
+
return {
|
|
195
|
+
title: "Current Edition",
|
|
196
|
+
editionDate,
|
|
197
|
+
description: "The default Papyrus fixture edition.",
|
|
198
|
+
articles,
|
|
199
|
+
newsDeskAppendix: createDemoNewsDeskAppendix(),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
title: threatIntelligenceSeedContent.title,
|
|
205
|
+
editionDate: threatIntelligenceSeedContent.publishDate,
|
|
206
|
+
description: threatIntelligenceSeedContent.description,
|
|
207
|
+
articles: threatIntelligenceSeedContent.articles as Article[],
|
|
208
|
+
suppressNewsDeskAppendix: threatIntelligenceSeedContent.suppressNewsDeskAppendix === true,
|
|
209
|
+
editionVideo: threatIntelligenceSeedContent.video as ArticleVideoAsset | undefined,
|
|
210
|
+
newsDeskAppendix: null,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function createSharedBlankColumnPressureArticles(): Article[] {
|
|
215
|
+
return cloneArticles(articles).map((article) => {
|
|
216
|
+
if (article.slug === "schools-reading-lab") {
|
|
217
|
+
return {
|
|
218
|
+
...article,
|
|
219
|
+
body: [
|
|
220
|
+
article.body[0],
|
|
221
|
+
article.body[1],
|
|
222
|
+
article.body[2],
|
|
223
|
+
article.body[3],
|
|
224
|
+
"The test edition keeps this continuation deliberately tight. It should still spend empty column space with a solved image or pull quote instead of leaving a bare newspaper column.",
|
|
225
|
+
],
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (article.slug === "market-hall") {
|
|
230
|
+
return {
|
|
231
|
+
...article,
|
|
232
|
+
body: [
|
|
233
|
+
article.body[0],
|
|
234
|
+
article.body[1],
|
|
235
|
+
article.body[2],
|
|
236
|
+
"Editors marked this tail as short on purpose. The layout should react by choosing furniture that covers otherwise blank column space without overlapping live copy.",
|
|
237
|
+
],
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return article;
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function createNoPullQuoteArticles(): Article[] {
|
|
246
|
+
return cloneArticles(articles).map((article) => {
|
|
247
|
+
if (article.slug === "agent-procedure-patterns" || article.slug === "schools-reading-lab" || article.slug === "market-hall") {
|
|
248
|
+
return {
|
|
249
|
+
...article,
|
|
250
|
+
pullQuotes: [],
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return article;
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function createFurnitureSufficiencyArticles(): Article[] {
|
|
259
|
+
return cloneArticles(articles).map((article) => {
|
|
260
|
+
if (article.slug === "schools-reading-lab") {
|
|
261
|
+
if (!article.image) return article;
|
|
262
|
+
return {
|
|
263
|
+
...article,
|
|
264
|
+
pullQuotes: [],
|
|
265
|
+
image: {
|
|
266
|
+
...article.image,
|
|
267
|
+
layout: {
|
|
268
|
+
...article.image.layout,
|
|
269
|
+
minHeight: 120,
|
|
270
|
+
preferredHeight: 900,
|
|
271
|
+
maxHeight: 900,
|
|
272
|
+
aspectRatio: 0.2,
|
|
273
|
+
crop: "contain",
|
|
274
|
+
wrapsText: true,
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
body: [
|
|
278
|
+
article.body[0],
|
|
279
|
+
article.body[1],
|
|
280
|
+
"This pressure edition leaves only a compact continuation tail, so an oversized photo package must yield to readable copy.",
|
|
281
|
+
],
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (article.slug === "market-hall") {
|
|
286
|
+
return {
|
|
287
|
+
...article,
|
|
288
|
+
pullQuotes: [
|
|
289
|
+
"The economics depend on sharing, but a display quote that consumes a wide block of columns should not be admitted when it leaves only a few rows of live article copy around it. If the quote keeps expanding across the continuation, the solver should treat that furniture as editorially too expensive for this compact tail.",
|
|
290
|
+
],
|
|
291
|
+
body: [
|
|
292
|
+
article.body[0],
|
|
293
|
+
article.body[1],
|
|
294
|
+
"The hall still opens before dawn, but the continuation in this scenario is deliberately compact so the solver has to prefer readable copy over oversized furniture.",
|
|
295
|
+
],
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return article;
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function createThemedInkStudyArticles(): Article[] {
|
|
304
|
+
return cloneArticles(articles).map((article) => {
|
|
305
|
+
if (article.slug !== "agent-procedure-patterns") return article;
|
|
306
|
+
return {
|
|
307
|
+
...article,
|
|
308
|
+
image: {
|
|
309
|
+
...article.image,
|
|
310
|
+
src: "/seed-art/theme-ink-study-light.png",
|
|
311
|
+
themeVariants: {
|
|
312
|
+
dark: {
|
|
313
|
+
src: "/seed-art/theme-ink-study-dark.png",
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
alt: "An abstract newspaper study with theme-specific light and dark rendering",
|
|
317
|
+
credit: "Papyrus seed art",
|
|
318
|
+
caption: "Theme-aware raster image fixture.",
|
|
319
|
+
layout: {
|
|
320
|
+
minHeight: 120,
|
|
321
|
+
preferredHeight: 220,
|
|
322
|
+
maxHeight: 420,
|
|
323
|
+
aspectRatio: 1.5009,
|
|
324
|
+
crop: "contain",
|
|
325
|
+
wrapsText: true,
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
assets: [
|
|
329
|
+
{
|
|
330
|
+
id: `${article.slug}-primary-image`,
|
|
331
|
+
type: "image",
|
|
332
|
+
src: "/seed-art/theme-ink-study-light.png",
|
|
333
|
+
themeVariants: {
|
|
334
|
+
dark: {
|
|
335
|
+
src: "/seed-art/theme-ink-study-dark.png",
|
|
336
|
+
},
|
|
337
|
+
},
|
|
338
|
+
alt: "An abstract newspaper study with theme-specific light and dark rendering",
|
|
339
|
+
caption: "Theme-aware raster image fixture.",
|
|
340
|
+
credit: "Papyrus seed art",
|
|
341
|
+
roles: ["lead", "continuation", "continuationInset"],
|
|
342
|
+
layout: {
|
|
343
|
+
minHeight: 120,
|
|
344
|
+
preferredHeight: 220,
|
|
345
|
+
maxHeight: 420,
|
|
346
|
+
aspectRatio: 1.5009,
|
|
347
|
+
crop: "contain",
|
|
348
|
+
wrapsText: true,
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
],
|
|
352
|
+
};
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function createLongImageCaptionArticles(): Article[] {
|
|
357
|
+
return cloneArticles(articles).map((article) => {
|
|
358
|
+
if (article.slug !== "market-hall") return article;
|
|
359
|
+
if (!article.image) return article;
|
|
360
|
+
return {
|
|
361
|
+
...article,
|
|
362
|
+
image: {
|
|
363
|
+
...article.image,
|
|
364
|
+
caption:
|
|
365
|
+
"The renovated market hall combines prep kitchens, storefront counters, shared cold storage, and a public retail counter where small producers can test packaging and pricing before committing to wholesale runs.",
|
|
366
|
+
},
|
|
367
|
+
};
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function createHeightPolicyLayoutPlan({
|
|
372
|
+
regionShrinkToContent,
|
|
373
|
+
defaultRows,
|
|
374
|
+
blockShrinkToContent = false,
|
|
375
|
+
}: {
|
|
376
|
+
regionShrinkToContent?: boolean;
|
|
377
|
+
defaultRows?: number;
|
|
378
|
+
blockShrinkToContent?: boolean;
|
|
379
|
+
}): EditionLayoutPlan {
|
|
380
|
+
const plan = cloneLayoutPlan(createDefaultEditionLayoutPlan(articles.map((article) => article.slug)));
|
|
381
|
+
const region = findPlanRegion(plan, "harbor-continuation");
|
|
382
|
+
if (region) {
|
|
383
|
+
if (regionShrinkToContent === undefined) {
|
|
384
|
+
delete region.size;
|
|
385
|
+
} else {
|
|
386
|
+
region.size = { ...(region.size ?? {}), shrinkToContent: regionShrinkToContent };
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const block = findPlanBlock(plan, "agent-procedure-patterns-page-2");
|
|
391
|
+
if (block?.type === "articleFrame" && defaultRows !== undefined) {
|
|
392
|
+
block.size = {
|
|
393
|
+
defaultRows,
|
|
394
|
+
shrinkToContent: blockShrinkToContent,
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
return plan;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function createDemoNewsDeskAppendix(): NewsDeskAppendix {
|
|
402
|
+
const categorySetId = "category-set-demo-canonical";
|
|
403
|
+
return {
|
|
404
|
+
categorySetId,
|
|
405
|
+
corpusId: "category-corpus-demo-canonical",
|
|
406
|
+
displayName: "Canonical Category Register",
|
|
407
|
+
description: "Accepted category tree for the fixture edition.",
|
|
408
|
+
generatedAt: `${editionDate}T12:00:00.000Z`,
|
|
409
|
+
nodes: [
|
|
410
|
+
{
|
|
411
|
+
id: "category-node-foundation-model-scaling",
|
|
412
|
+
categorySetId,
|
|
413
|
+
categoryKey: "category.foundation-model-scaling",
|
|
414
|
+
parentCategoryKey: null,
|
|
415
|
+
displayName: "Foundation Model Scaling",
|
|
416
|
+
subtitle: "Capability curves, benchmark saturation, and training-compute effects",
|
|
417
|
+
description: "Research on model size, data mixtures, compute budgets, and emergent benchmark behavior.",
|
|
418
|
+
status: "accepted",
|
|
419
|
+
seedItemIds: ["research-001", "research-002"],
|
|
420
|
+
holdoutItemIds: ["research-003"],
|
|
421
|
+
rank: 1,
|
|
422
|
+
depth: 0,
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
id: "category-node-agent-memory",
|
|
426
|
+
categorySetId,
|
|
427
|
+
categoryKey: "category.agent-memory",
|
|
428
|
+
parentCategoryKey: "category.foundation-model-scaling",
|
|
429
|
+
displayName: "Agent Memory",
|
|
430
|
+
subtitle: "Retrieval, persistence, and working context for autonomous systems",
|
|
431
|
+
description: "Subtopic covering memory stores, retrieval policies, and long-horizon agent state.",
|
|
432
|
+
status: "accepted",
|
|
433
|
+
seedItemIds: ["research-001"],
|
|
434
|
+
holdoutItemIds: [],
|
|
435
|
+
rank: 1,
|
|
436
|
+
depth: 1,
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
id: "category-node-benchmark-saturation",
|
|
440
|
+
categorySetId,
|
|
441
|
+
categoryKey: "category.benchmark-saturation",
|
|
442
|
+
parentCategoryKey: "category.foundation-model-scaling",
|
|
443
|
+
displayName: "Benchmark Saturation",
|
|
444
|
+
subtitle: "When evaluation sets stop separating model capability",
|
|
445
|
+
description: "Subtopic covering benchmark exhaustion, contamination, and evaluation drift.",
|
|
446
|
+
status: "accepted",
|
|
447
|
+
seedItemIds: ["research-002"],
|
|
448
|
+
holdoutItemIds: [],
|
|
449
|
+
rank: 2,
|
|
450
|
+
depth: 1,
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
id: "category-node-symbolic-connectionist-history",
|
|
454
|
+
categorySetId,
|
|
455
|
+
categoryKey: "category.symbolic-connectionist-history",
|
|
456
|
+
parentCategoryKey: null,
|
|
457
|
+
displayName: "Symbolic And Connectionist History",
|
|
458
|
+
subtitle: "Shifts between rules, neural nets, and hybrid AI programs",
|
|
459
|
+
description: "Historical coverage of symbolic AI, neural network winters, and later hybrid systems.",
|
|
460
|
+
status: "accepted",
|
|
461
|
+
seedItemIds: ["history-001"],
|
|
462
|
+
holdoutItemIds: ["history-002"],
|
|
463
|
+
rank: 2,
|
|
464
|
+
depth: 0,
|
|
465
|
+
},
|
|
466
|
+
],
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function cloneArticles(source: Article[]): Article[] {
|
|
471
|
+
return source.map(cloneArticle);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function createCompactChromeLayoutPlan(): EditionLayoutPlan {
|
|
475
|
+
const plan = cloneLayoutPlan(createDefaultEditionLayoutPlan(articles.map((article) => article.slug)));
|
|
476
|
+
const frontPage = plan.pages.find((page) => page.pageNumber === 1);
|
|
477
|
+
const frontBlocks = frontPage?.regions.flatMap((region) => region.blocks) ?? [];
|
|
478
|
+
for (const block of frontBlocks) {
|
|
479
|
+
if (block.type !== "articleFrame") continue;
|
|
480
|
+
block.chrome = {
|
|
481
|
+
headline: {
|
|
482
|
+
lineHeight: "0.72em",
|
|
483
|
+
paintHeight: "1.14em",
|
|
484
|
+
marginAfter: "0.32em",
|
|
485
|
+
},
|
|
486
|
+
deck: {
|
|
487
|
+
lineHeight: "0.9em",
|
|
488
|
+
paintHeight: "1.12em",
|
|
489
|
+
minHeight: "2.8em",
|
|
490
|
+
},
|
|
491
|
+
byline: {
|
|
492
|
+
lineHeight: "0.95em",
|
|
493
|
+
paintHeight: "1.1em",
|
|
494
|
+
},
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
return plan;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function createFrontBodyDepthLayoutPlan(): EditionLayoutPlan {
|
|
501
|
+
const plan = cloneLayoutPlan(createDefaultEditionLayoutPlan(articles.map((article) => article.slug)));
|
|
502
|
+
for (const blockId of ["front-agent-procedure-patterns", "front-schools-reading-lab", "front-market-hall"]) {
|
|
503
|
+
const block = findPlanBlock(plan, blockId);
|
|
504
|
+
if (block?.type !== "articleFrame") continue;
|
|
505
|
+
block.cutPolicy = {
|
|
506
|
+
bodyDepthRows: 14,
|
|
507
|
+
jumpTargetPage: block.cutPolicy?.jumpTargetPage ?? (blockId === "front-agent-procedure-patterns" ? 2 : 3),
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
return plan;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function createFurnitureSufficiencyLayoutPlan(): EditionLayoutPlan {
|
|
514
|
+
const plan = cloneLayoutPlan(createDefaultEditionLayoutPlan(articles.map((article) => article.slug)));
|
|
515
|
+
const frontSchoolsBlock = findPlanBlock(plan, "front-schools-reading-lab");
|
|
516
|
+
if (frontSchoolsBlock?.type === "articleFrame") {
|
|
517
|
+
frontSchoolsBlock.cutPolicy = { maxBodyLines: 4, jumpTargetPage: 3 };
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
const frontMarketBlock = findPlanBlock(plan, "front-market-hall");
|
|
521
|
+
if (frontMarketBlock?.type === "articleFrame") {
|
|
522
|
+
frontMarketBlock.cutPolicy = { maxBodyLines: 4, jumpTargetPage: 3 };
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
const schoolsBlock = findPlanBlock(plan, "schools-reading-lab-page-3");
|
|
526
|
+
if (schoolsBlock?.type === "articleFrame") {
|
|
527
|
+
schoolsBlock.localGrid = { columns: { min: 2, preferred: 4, max: 4 } };
|
|
528
|
+
schoolsBlock.media[0].placement.crop = "cropAllowed";
|
|
529
|
+
schoolsBlock.media[0].placement.anchor = "center";
|
|
530
|
+
schoolsBlock.media[0].placement.span = { min: 4, preferred: 4, max: 4 };
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
const marketBlock = findPlanBlock(plan, "market-hall-page-3");
|
|
534
|
+
if (marketBlock?.type === "articleFrame") {
|
|
535
|
+
marketBlock.media = [];
|
|
536
|
+
marketBlock.pullQuote = {
|
|
537
|
+
required: false,
|
|
538
|
+
placements: [
|
|
539
|
+
{
|
|
540
|
+
anchor: "center",
|
|
541
|
+
span: { min: 2, preferred: 2, max: 2 },
|
|
542
|
+
vertical: "middle",
|
|
543
|
+
collapse: "omit",
|
|
544
|
+
crop: "preserve",
|
|
545
|
+
wrapsText: true,
|
|
546
|
+
},
|
|
547
|
+
],
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
return plan;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
function findPlanBlock(plan: EditionLayoutPlan, blockId: string): EditionLayoutPlan["pages"][number]["regions"][number]["blocks"][number] | undefined {
|
|
555
|
+
return plan.pages.flatMap((page) => page.regions).flatMap((region) => region.blocks).find((block) => block.id === blockId);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
function findPlanRegion(plan: EditionLayoutPlan, regionId: string): EditionLayoutPlan["pages"][number]["regions"][number] | undefined {
|
|
559
|
+
return plan.pages.flatMap((page) => page.regions).find((region) => region.id === regionId);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
function cloneLayoutPlan(plan: EditionLayoutPlan): EditionLayoutPlan {
|
|
563
|
+
return JSON.parse(JSON.stringify(plan)) as EditionLayoutPlan;
|
|
564
|
+
}
|