@anthusai/papyrus 0.0.0 → 1.0.0-next.10
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 +2069 -0
- package/amplify/data/resource.ts +2185 -0
- package/amplify/data/schema.js +1882 -0
- package/amplify/data/schema.ts +1990 -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/content-actions/handler.py +149 -0
- package/amplify/functions/content-actions/requirements.txt +4 -0
- package/amplify/functions/content-actions/resource.js +40 -0
- package/amplify/functions/content-actions/resource.ts +54 -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 +130 -0
- package/amplify/functions/shared/python-bundle.ts +162 -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/rebuild-trigger-settings.js +22 -0
- package/amplify/rebuild-trigger-settings.ts +42 -0
- package/amplify/site-backend-config.js +1 -0
- package/amplify/site-backend-config.ts +36 -0
- package/amplify/site-backend.js +615 -0
- package/amplify/site-backend.ts +739 -0
- package/amplify/storage/resource.js +21 -0
- package/amplify/storage/resource.ts +22 -0
- package/app/%5F_preview/[[...path]]/route.ts +8 -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 +10345 -0
- package/app/layout.tsx +142 -0
- package/app/newsroom/[section]/[...selection]/page.tsx +50 -0
- package/app/newsroom/[section]/page.tsx +47 -0
- package/app/newsroom/articles/[id]/page.tsx +15 -0
- package/app/newsroom/articles/page.tsx +13 -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/papyrus-theme.css +11 -0
- package/app/robots.ts +11 -0
- package/app/settings/page.tsx +14 -0
- package/app/tailwind.css +65 -0
- package/bin/papyrus-app.mjs +20 -0
- package/bin/papyrus-infra.mjs +95 -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 +156 -0
- package/components/blog-page-background.tsx +20 -0
- package/components/dev-sandbox-editor-sign-in.tsx +60 -0
- package/components/markus-ir-preview.tsx +300 -0
- package/components/news-desk-client-provider.tsx +211 -0
- package/components/news-desk-page.tsx +116 -0
- package/components/news-desk-taxonomy-client.ts +3684 -0
- package/components/newspaper.tsx +1929 -0
- package/components/newsroom-access-gate.tsx +66 -0
- package/components/newsroom-article-editor.tsx +741 -0
- package/components/newsroom-articles-view.tsx +129 -0
- package/components/newsroom-assignments-view.tsx +664 -0
- package/components/newsroom-ops-overview.tsx +226 -0
- package/components/newsroom-ops-shell.tsx +321 -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/staging-banner.tsx +11 -0
- package/components/topic-steering-workspace.tsx +18005 -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 +234 -0
- package/infra/amplify-app-shell.ts +284 -0
- package/infra/build-specs.js +85 -0
- package/infra/build-specs.ts +131 -0
- package/infra/github-oidc-provider.js +24 -0
- package/infra/github-oidc-provider.ts +30 -0
- package/infra/index.js +15 -0
- package/infra/site-config.js +186 -0
- package/infra/site-config.ts +235 -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 +90 -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-actions-client.ts +98 -0
- package/lib/content-actions-demo.ts +190 -0
- package/lib/content-load-timing.ts +23 -0
- package/lib/content-repository.ts +43 -0
- package/lib/content-source-context.ts +26 -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/empty-theme.css +1 -0
- package/lib/excerpts.ts +8 -0
- package/lib/graphql-content-repository.ts +1140 -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-articles.ts +325 -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-articles.ts +90 -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-media-backend.ts +142 -0
- package/lib/newsroom-media.ts +182 -0
- package/lib/newsroom-nav.ts +44 -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 +22 -0
- package/lib/site-brand.ts +194 -0
- package/lib/site-env.ts +52 -0
- package/lib/site-renderer.ts +17 -0
- package/lib/site-stack.ts +195 -0
- package/lib/staging-gate.ts +37 -0
- package/lib/staging-preview-object.ts +98 -0
- package/lib/themed-image.ts +30 -0
- package/lib/utils.ts +6 -0
- package/lib/video-mode.ts +42 -0
- package/lib/video-script.ts +63 -0
- package/middleware.ts +103 -0
- package/package.json +318 -1
- package/renderers/markus/index.ts +25 -0
- package/renderers/markus/stubs.tsx +33 -0
- package/renderers/pretext/article-page.tsx +169 -0
- package/renderers/pretext/index.ts +30 -0
- package/renderers/pretext/layouts.ts +16 -0
- package/renderers/pretext/presentation-shell.tsx +470 -0
- package/routes.manifest.json +208 -0
- package/scripts/fixtures/pilobol-sample.ast.json +234 -0
- package/scripts/videoml/build-browser-bundle.mjs +22 -0
- package/scripts/videoml/build-preview-bundle.mjs +50 -0
- package/scripts/videoml/bundle-common.mjs +129 -0
- package/scripts/videoml/preview/preview-entry.tsx +381 -0
- package/scripts/videoml/preview/preview-stage-fit.ts +28 -0
- package/scripts/videoml/preview/preview-timing.ts +42 -0
- package/scripts/videoml/preview.html +77 -0
- package/scripts/videoml/standard-entry.tsx +46 -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 +60 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import type { ReferenceRecord } from "./category-repository";
|
|
2
|
+
import { normalizeReferenceCurationStatus } from "./reference-policy";
|
|
3
|
+
|
|
4
|
+
export type ReferenceCurationAction = "accept" | "reject" | "archive";
|
|
5
|
+
|
|
6
|
+
export type ReferenceStatusFilter = "all" | "pending" | "accepted" | "rejected" | "archived";
|
|
7
|
+
|
|
8
|
+
export function referenceLineageId(reference: ReferenceRecord): string {
|
|
9
|
+
return reference.lineageId ?? reference.id;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function normalizeReferenceStatus(status: string | null | undefined): string {
|
|
13
|
+
return normalizeReferenceCurationStatus(status);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function referenceSortDate(reference: ReferenceRecord): string {
|
|
17
|
+
return reference.sourcePublishedAt
|
|
18
|
+
?? reference.sourceUpdatedAt
|
|
19
|
+
?? reference.retrievedAt
|
|
20
|
+
?? reference.importedAt
|
|
21
|
+
?? reference.updatedAt
|
|
22
|
+
?? "";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function compareReferencesByRecency(left: ReferenceRecord, right: ReferenceRecord): number {
|
|
26
|
+
const dateDiff = referenceSortDate(right).localeCompare(referenceSortDate(left));
|
|
27
|
+
if (dateDiff !== 0) return dateDiff;
|
|
28
|
+
return (left.title ?? left.externalItemId).localeCompare(right.title ?? right.externalItemId);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function compareReferencesForCanonicalChoice(left: ReferenceRecord, right: ReferenceRecord): number {
|
|
32
|
+
const leftCurrent = left.versionState === "current" ? 1 : 0;
|
|
33
|
+
const rightCurrent = right.versionState === "current" ? 1 : 0;
|
|
34
|
+
if (leftCurrent !== rightCurrent) return rightCurrent - leftCurrent;
|
|
35
|
+
|
|
36
|
+
const leftVersion = Number.isFinite(left.versionNumber) ? Number(left.versionNumber) : 0;
|
|
37
|
+
const rightVersion = Number.isFinite(right.versionNumber) ? Number(right.versionNumber) : 0;
|
|
38
|
+
if (leftVersion !== rightVersion) return rightVersion - leftVersion;
|
|
39
|
+
|
|
40
|
+
return compareReferencesByRecency(left, right);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function selectCanonicalReferenceRecords(references: ReferenceRecord[]): ReferenceRecord[] {
|
|
44
|
+
const byLineage = new Map<string, ReferenceRecord>();
|
|
45
|
+
for (const reference of references) {
|
|
46
|
+
const key = referenceLineageId(reference);
|
|
47
|
+
const current = byLineage.get(key);
|
|
48
|
+
if (!current || compareReferencesForCanonicalChoice(reference, current) < 0) {
|
|
49
|
+
byLineage.set(key, reference);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return Array.from(byLineage.values()).sort(compareReferencesByRecency);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function selectedReferenceRecordByLineage(
|
|
56
|
+
references: ReferenceRecord[],
|
|
57
|
+
requestedLineageId: string | null | undefined,
|
|
58
|
+
): ReferenceRecord | null {
|
|
59
|
+
if (!requestedLineageId) return null;
|
|
60
|
+
const lineageMatches = references.filter((reference) => referenceLineageId(reference) === requestedLineageId);
|
|
61
|
+
if (!lineageMatches.length) return null;
|
|
62
|
+
return lineageMatches.reduce((best, current) => (
|
|
63
|
+
compareReferencesForCanonicalChoice(current, best) < 0 ? current : best
|
|
64
|
+
));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function countReferencesByStatus(references: ReferenceRecord[]): Record<string, number> {
|
|
68
|
+
const counts: Record<string, number> = {};
|
|
69
|
+
for (const reference of references) {
|
|
70
|
+
const status = normalizeReferenceStatus(reference.curationStatus);
|
|
71
|
+
counts[status] = (counts[status] ?? 0) + 1;
|
|
72
|
+
}
|
|
73
|
+
return counts;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function filterReferencesByStatus(
|
|
77
|
+
references: ReferenceRecord[],
|
|
78
|
+
statusFilter: ReferenceStatusFilter,
|
|
79
|
+
): ReferenceRecord[] {
|
|
80
|
+
const canonical = selectCanonicalReferenceRecords(references);
|
|
81
|
+
if (statusFilter === "all") return canonical;
|
|
82
|
+
return canonical.filter((reference) => normalizeReferenceStatus(reference.curationStatus) === statusFilter);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function formatReferenceListDate(reference: ReferenceRecord): string {
|
|
86
|
+
const value = referenceSortDate(reference);
|
|
87
|
+
if (!value) return "Undated";
|
|
88
|
+
const timestamp = Date.parse(value);
|
|
89
|
+
if (Number.isNaN(timestamp)) return value;
|
|
90
|
+
return new Intl.DateTimeFormat("en-US", {
|
|
91
|
+
month: "short",
|
|
92
|
+
day: "numeric",
|
|
93
|
+
year: "numeric",
|
|
94
|
+
timeZone: "UTC",
|
|
95
|
+
}).format(new Date(timestamp));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function referenceDisplayTitle(reference: ReferenceRecord): string {
|
|
99
|
+
return reference.title?.trim() || reference.externalItemId;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function referenceStatusLabel(status: string): string {
|
|
103
|
+
if (status === "accepted") return "Accepted";
|
|
104
|
+
if (status === "rejected") return "Rejected";
|
|
105
|
+
if (status === "archived") return "Archived";
|
|
106
|
+
return "Pending";
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function referenceStatusBadgeVariant(
|
|
110
|
+
status: string,
|
|
111
|
+
): "default" | "secondary" | "outline" | "muted" {
|
|
112
|
+
if (status === "accepted") return "default";
|
|
113
|
+
if (status === "rejected") return "outline";
|
|
114
|
+
if (status === "archived") return "secondary";
|
|
115
|
+
return "muted";
|
|
116
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { NewsroomSectionRecord } from "./category-repository";
|
|
2
|
+
import { getNewsroomNavHref } from "./newsroom-nav";
|
|
3
|
+
|
|
4
|
+
const SECTION_SHORT_TITLE_FALLBACKS: Record<string, string> = {
|
|
5
|
+
news: "News",
|
|
6
|
+
business: "Business",
|
|
7
|
+
technology: "Technology",
|
|
8
|
+
science: "Science",
|
|
9
|
+
methods: "Methods",
|
|
10
|
+
history: "History",
|
|
11
|
+
opinion: "Opinion",
|
|
12
|
+
world: "World",
|
|
13
|
+
arts: "Arts",
|
|
14
|
+
sports: "Sports",
|
|
15
|
+
education: "Education",
|
|
16
|
+
health: "Health",
|
|
17
|
+
security: "Security",
|
|
18
|
+
"law-policy": "Law & Policy",
|
|
19
|
+
labor: "Labor",
|
|
20
|
+
"field-notes": "Field Notes",
|
|
21
|
+
"strangler-control": "Host encirclement",
|
|
22
|
+
"mycelial-signals": "Network routing",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export function normalizeNewsroomSectionType(value: string | null | undefined): "canonical" | "floating" {
|
|
26
|
+
return value === "floating" || value === "rotating" ? "floating" : "canonical";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function isEnabledNewsroomSection(section: NewsroomSectionRecord): boolean {
|
|
30
|
+
return section.enabled !== false && section.enabledStatus !== "disabled";
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function sortNewsroomSections(sections: NewsroomSectionRecord[]): NewsroomSectionRecord[] {
|
|
34
|
+
return [...sections].sort((left, right) => {
|
|
35
|
+
const typeDiff = (normalizeNewsroomSectionType(left.type) === "canonical" ? 0 : 1)
|
|
36
|
+
- (normalizeNewsroomSectionType(right.type) === "canonical" ? 0 : 1);
|
|
37
|
+
if (typeDiff !== 0) return typeDiff;
|
|
38
|
+
const orderDiff = (left.sortOrder ?? 999999) - (right.sortOrder ?? 999999);
|
|
39
|
+
if (orderDiff !== 0) return orderDiff;
|
|
40
|
+
return left.title.localeCompare(right.title);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function displayNewsroomSectionShortTitle(section: NewsroomSectionRecord): string {
|
|
45
|
+
const explicit = section.shortTitle?.trim();
|
|
46
|
+
if (explicit) return explicit;
|
|
47
|
+
return SECTION_SHORT_TITLE_FALLBACKS[section.id] ?? section.id.replace(/-/g, " ");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function buildNewsroomSectionHref(sectionId: string, demo?: boolean): string {
|
|
51
|
+
return getNewsroomNavHref(`/newsroom/sections/${encodeURIComponent(sectionId)}`, demo);
|
|
52
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import type { CategorySteeringProposal } from "./category-repository";
|
|
2
|
+
|
|
3
|
+
export type TopicProposalReviewAction = "accept" | "reject" | "defer" | "edit";
|
|
4
|
+
|
|
5
|
+
export type TopicProposalStatusFilter = "all" | "proposed" | "deferred" | "reviewed";
|
|
6
|
+
|
|
7
|
+
const TOPIC_PROPOSAL_BLOCKED_APPLY_KINDS = new Set([
|
|
8
|
+
"merge-category",
|
|
9
|
+
"merge-categories",
|
|
10
|
+
"split-category",
|
|
11
|
+
"archive-category",
|
|
12
|
+
"deprecate-category",
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
export function isCategoryTopicProposal(proposal: CategorySteeringProposal): boolean {
|
|
16
|
+
return proposal.steeringDomain === "category";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function filterTopicProposals(
|
|
20
|
+
proposals: CategorySteeringProposal[],
|
|
21
|
+
statusFilter: TopicProposalStatusFilter,
|
|
22
|
+
): CategorySteeringProposal[] {
|
|
23
|
+
const categoryProposals = proposals.filter(isCategoryTopicProposal);
|
|
24
|
+
if (statusFilter === "all") return categoryProposals;
|
|
25
|
+
if (statusFilter === "reviewed") {
|
|
26
|
+
return categoryProposals.filter((proposal) => (
|
|
27
|
+
proposal.status === "accepted" || proposal.status === "rejected"
|
|
28
|
+
));
|
|
29
|
+
}
|
|
30
|
+
return categoryProposals.filter((proposal) => proposal.status === statusFilter);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function countTopicProposalsByStatus(
|
|
34
|
+
proposals: CategorySteeringProposal[],
|
|
35
|
+
): Record<TopicProposalStatusFilter, number> {
|
|
36
|
+
const categoryProposals = proposals.filter(isCategoryTopicProposal);
|
|
37
|
+
return {
|
|
38
|
+
all: categoryProposals.length,
|
|
39
|
+
proposed: categoryProposals.filter((proposal) => proposal.status === "proposed").length,
|
|
40
|
+
deferred: categoryProposals.filter((proposal) => proposal.status === "deferred").length,
|
|
41
|
+
reviewed: categoryProposals.filter((proposal) => (
|
|
42
|
+
proposal.status === "accepted" || proposal.status === "rejected"
|
|
43
|
+
)).length,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function compareTopicProposals(
|
|
48
|
+
left: CategorySteeringProposal,
|
|
49
|
+
right: CategorySteeringProposal,
|
|
50
|
+
): number {
|
|
51
|
+
const statusWeight = new Map([
|
|
52
|
+
["proposed", 0],
|
|
53
|
+
["deferred", 1],
|
|
54
|
+
["accepted", 2],
|
|
55
|
+
["rejected", 3],
|
|
56
|
+
]);
|
|
57
|
+
const statusDiff = (statusWeight.get(left.status) ?? 9) - (statusWeight.get(right.status) ?? 9);
|
|
58
|
+
if (statusDiff !== 0) return statusDiff;
|
|
59
|
+
const dateDiff = (right.proposedAt ?? right.updatedAt ?? "").localeCompare(left.proposedAt ?? left.updatedAt ?? "");
|
|
60
|
+
if (dateDiff !== 0) return dateDiff;
|
|
61
|
+
return proposalDisplayTitle(left).localeCompare(proposalDisplayTitle(right));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function sortTopicProposals(proposals: CategorySteeringProposal[]): CategorySteeringProposal[] {
|
|
65
|
+
return [...proposals].sort(compareTopicProposals);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function proposalDisplayTitle(proposal: CategorySteeringProposal): string {
|
|
69
|
+
return proposal.displayName?.trim() || proposal.title?.trim() || proposal.categoryKey || proposal.id;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function proposalStatusLabel(status: string): string {
|
|
73
|
+
if (status === "accepted") return "Accepted";
|
|
74
|
+
if (status === "rejected") return "Rejected";
|
|
75
|
+
if (status === "deferred") return "Deferred";
|
|
76
|
+
return "Proposed";
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function proposalStatusBadgeVariant(
|
|
80
|
+
status: string,
|
|
81
|
+
): "default" | "secondary" | "outline" | "muted" {
|
|
82
|
+
if (status === "accepted") return "default";
|
|
83
|
+
if (status === "rejected") return "outline";
|
|
84
|
+
if (status === "deferred") return "secondary";
|
|
85
|
+
return "muted";
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function formatProposalListDate(proposal: CategorySteeringProposal): string {
|
|
89
|
+
const value = proposal.proposedAt ?? proposal.reviewedAt ?? proposal.updatedAt ?? "";
|
|
90
|
+
if (!value) return "Undated";
|
|
91
|
+
const timestamp = Date.parse(value);
|
|
92
|
+
if (Number.isNaN(timestamp)) return value;
|
|
93
|
+
return new Intl.DateTimeFormat("en-US", {
|
|
94
|
+
month: "short",
|
|
95
|
+
day: "numeric",
|
|
96
|
+
year: "numeric",
|
|
97
|
+
timeZone: "UTC",
|
|
98
|
+
}).format(new Date(timestamp));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function selectedTopicProposalById(
|
|
102
|
+
proposals: CategorySteeringProposal[],
|
|
103
|
+
proposalId: string | null | undefined,
|
|
104
|
+
): CategorySteeringProposal | null {
|
|
105
|
+
if (!proposalId) return null;
|
|
106
|
+
return proposals.find((proposal) => proposal.id === proposalId) ?? null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function topicProposalReviewBlockedReason(
|
|
110
|
+
proposal: CategorySteeringProposal,
|
|
111
|
+
action: TopicProposalReviewAction,
|
|
112
|
+
): string | null {
|
|
113
|
+
if ((action === "accept" || action === "edit") && TOPIC_PROPOSAL_BLOCKED_APPLY_KINDS.has(proposal.proposalKind)) {
|
|
114
|
+
return `${proposal.proposalKind} apply is not implemented yet.`;
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type PapyrusObjectKind =
|
|
2
|
+
| "assignment"
|
|
3
|
+
| "category"
|
|
4
|
+
| "categorySet"
|
|
5
|
+
| "item"
|
|
6
|
+
| "message"
|
|
7
|
+
| "newsroomSection"
|
|
8
|
+
| "reference"
|
|
9
|
+
| "semanticNode"
|
|
10
|
+
| "semanticRelation"
|
|
11
|
+
| "steeringProposal";
|
|
12
|
+
|
|
13
|
+
export const PAPYRUS_OBJECT_KINDS = new Set<PapyrusObjectKind>([
|
|
14
|
+
"assignment",
|
|
15
|
+
"category",
|
|
16
|
+
"categorySet",
|
|
17
|
+
"item",
|
|
18
|
+
"message",
|
|
19
|
+
"newsroomSection",
|
|
20
|
+
"reference",
|
|
21
|
+
"semanticNode",
|
|
22
|
+
"semanticRelation",
|
|
23
|
+
"steeringProposal",
|
|
24
|
+
]);
|