@anthusai/papyrus 1.0.0-next.9 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/amplify/auth/prospectus-google-client.js +66 -0
- package/amplify/auth/prospectus-google-client.ts +71 -0
- package/amplify/auth/redirect-urls.js +54 -0
- package/amplify/auth/redirect-urls.ts +66 -0
- package/amplify/auth/resource.js +23 -5
- package/amplify/auth/resource.ts +31 -5
- package/amplify/data/resource.js +42 -80
- package/amplify/data/resource.ts +42 -80
- package/amplify/data/schema.js +45 -62
- package/amplify/data/schema.ts +47 -62
- package/amplify/functions/category-action/handler.ts +5 -3
- package/amplify/functions/console-chat-responder/py/execute_tactus_runner.py +0 -10
- package/amplify/functions/console-chat-responder/resource.js +0 -7
- package/amplify/functions/console-chat-responder/resource.ts +0 -8
- package/amplify/functions/console-chat-responder/src/main.rs +0 -9
- package/amplify/functions/content-actions/handler.py +143 -1
- package/amplify/functions/content-actions/requirements.txt +2 -1
- package/amplify/functions/content-actions/resource.js +6 -1
- package/amplify/functions/content-actions/resource.ts +9 -2
- package/amplify/functions/email-submission-processor/handler.py +3 -1
- package/amplify/functions/renditions/handler.py +120 -0
- package/amplify/functions/renditions/requirements.txt +4 -0
- package/amplify/functions/ses-inbound-receive/handler.ts +32 -4
- package/amplify/functions/shared/email-submission.ts +109 -1
- package/amplify/functions/shared/python-bundle.js +42 -2
- package/amplify/functions/shared/python-bundle.ts +47 -2
- package/amplify/functions/shared/reference-curation-metadata.ts +29 -0
- package/amplify/inbound-email/ses-intake.js +86 -0
- package/amplify/inbound-email/ses-intake.ts +110 -0
- package/amplify/plugins/registry.js +19 -0
- package/amplify/plugins/registry.ts +23 -0
- package/amplify/plugins/signup/function/handler.py +200 -0
- package/amplify/plugins/signup/function/resource.js +27 -0
- package/amplify/plugins/signup/function/resource.ts +35 -0
- package/amplify/plugins/signup/papyrus.plugin.js +59 -0
- package/amplify/plugins/signup/papyrus.plugin.ts +74 -0
- package/amplify/plugins/signup/schema.js +40 -0
- package/amplify/plugins/signup/schema.ts +41 -0
- package/amplify/plugins/signup/waf.js +48 -0
- package/amplify/plugins/signup/waf.ts +51 -0
- package/amplify/plugins/types.js +1 -0
- package/amplify/plugins/types.ts +39 -0
- package/amplify/plugins/wire.js +24 -0
- package/amplify/plugins/wire.ts +26 -0
- package/amplify/publication/default-publication.js +18 -0
- package/amplify/publication/default-publication.ts +20 -0
- package/amplify/publication/invalidation-alarm.js +26 -0
- package/amplify/publication/invalidation-alarm.ts +30 -0
- package/amplify/publication/redirect-and-index.js +37 -0
- package/amplify/publication/renditions-function.js +74 -0
- package/amplify/publication/renditions-function.ts +112 -0
- package/amplify/publication/renditions-settings.js +51 -0
- package/amplify/publication/renditions-settings.ts +58 -0
- package/amplify/publication/reproject-on-deploy.js +73 -0
- package/amplify/publication/reproject-on-deploy.ts +78 -0
- package/amplify/publication/s3-projection-settings.js +67 -0
- package/amplify/publication/s3-projection-settings.ts +101 -0
- package/amplify/publication/s3-projection.js +151 -0
- package/amplify/publication/s3-projection.ts +201 -0
- package/amplify/resolver-waves.js +26 -0
- package/amplify/resolver-waves.ts +34 -0
- package/amplify/revalidate-invalidation-settings.js +30 -0
- package/amplify/revalidate-invalidation-settings.ts +35 -0
- package/amplify/revalidate-secret-policy.js +13 -0
- package/amplify/revalidate-secret-policy.ts +19 -0
- package/amplify/site-backend-config.js +65 -1
- package/amplify/site-backend-config.ts +153 -2
- package/amplify/site-backend-names.js +103 -0
- package/amplify/site-backend-names.ts +166 -0
- package/amplify/site-backend.js +150 -176
- package/amplify/site-backend.ts +189 -201
- package/amplify/storage-backups/construct.js +31 -0
- package/amplify/storage-backups/construct.ts +47 -0
- package/app/[year]/[month]/[day]/[articleSlug]/page.tsx +8 -1
- package/app/[year]/[month]/[day]/edition-route-page.tsx +2 -0
- package/app/[year]/[month]/[day]/page/[pageNumber]/page.tsx +6 -1
- package/app/[year]/[month]/[day]/page.tsx +1 -1
- package/app/[year]/[month]/[day]/section/[sectionKey]/page.tsx +6 -1
- package/app/api/archive/editions/route.ts +1 -1
- package/app/api/auth/prospectus/callback/route.ts +9 -0
- package/app/api/auth/prospectus/selection/route.ts +9 -0
- package/app/api/auth/prospectus/session/route.ts +9 -0
- package/app/api/auth/prospectus/sign-in/route.ts +9 -0
- package/app/api/auth/prospectus/sign-out/route.ts +9 -0
- package/app/api/cyclotron/priming-proof/route.ts +11 -0
- package/app/api/media/[...path]/route.ts +5 -16
- package/app/api/revalidate/route.ts +15 -7
- package/app/archive/page.tsx +1 -1
- package/app/articles/[slug]/page.tsx +7 -5
- package/app/blog-rhythm.css +278 -0
- package/app/cyclotron/page.tsx +13 -0
- package/app/globals.css +2 -0
- package/app/layout.tsx +16 -0
- package/app/newsroom/[section]/[...selection]/page.tsx +9 -6
- package/app/newsroom/[section]/page.tsx +6 -3
- package/app/newsroom/layout.tsx +4 -9
- package/app/newsroom/settings/page.tsx +6 -0
- package/app/newsroom/work/page.tsx +10 -0
- package/app/page.tsx +22 -8
- package/app/robots.ts +2 -5
- package/app/tailwind.css +1 -6
- package/components/amplify-client-provider.tsx +5 -22
- package/components/archive-shell.tsx +2 -22
- package/components/article-video.tsx +152 -5
- package/components/blog-page-background.tsx +23 -6
- package/components/news-desk-page.tsx +4 -15
- package/components/news-desk-taxonomy-client.ts +90 -3
- package/components/newspaper.tsx +6 -2
- package/components/newsroom-access-gate.tsx +2 -1
- package/components/newsroom-board-view.tsx +99 -0
- package/components/newsroom-client-shell.tsx +12 -0
- package/components/newsroom-curation-provider.tsx +17 -0
- package/components/newsroom-fonts/playfair-display-700.ttf +0 -0
- package/components/newsroom-fonts/playfair-display-900.ttf +0 -0
- package/components/newsroom-fonts/playfair-display-OFL.txt +93 -0
- package/components/newsroom-fonts/plus-jakarta-sans-400.ttf +0 -0
- package/components/newsroom-fonts/plus-jakarta-sans-600.ttf +0 -0
- package/components/newsroom-fonts/plus-jakarta-sans-OFL.txt +93 -0
- package/components/newsroom-newsprint-board.css +48 -0
- package/components/newsroom-ops-overview.tsx +28 -11
- package/components/newsroom-ops-shell.tsx +7 -1
- package/components/newsroom-organizer-shell.tsx +130 -0
- package/components/newsroom-organizer.css +63 -0
- package/components/newsroom-references-view.tsx +92 -8
- package/components/papyrus-console-shell.tsx +4 -2
- package/components/pictogram-figure.tsx +9 -2
- package/components/presentation-header.tsx +135 -0
- package/components/presentation-rhythm-hrule.tsx +17 -0
- package/components/priming-proof-page.tsx +94 -0
- package/components/publication-cyclotron-panel.tsx +57 -0
- package/components/reader-auth-control.tsx +8 -5
- package/components/reference-curation-controls.tsx +77 -0
- package/components/reference-cyclotron-status.tsx +109 -0
- package/components/reference-priming.tsx +18 -0
- package/components/reference-relevance-review.tsx +75 -0
- package/components/settings-page.tsx +2 -1
- package/components/signup-entries-panel.tsx +94 -0
- package/components/topic-steering-workspace.tsx +78 -8
- package/components/use-blog-header-obstacles.ts +81 -0
- package/components/use-brand-fonts-ready.ts +28 -0
- package/components/use-near-viewport.ts +30 -0
- package/components/use-rhythm-overlay.ts +29 -0
- package/infra/amplify-app-shell.js +166 -27
- package/infra/amplify-app-shell.ts +180 -31
- package/infra/build-specs.js +23 -7
- package/infra/build-specs.ts +27 -10
- package/infra/site-config.js +248 -36
- package/infra/site-config.ts +313 -51
- package/lib/amplify-outputs-path.ts +5 -1
- package/lib/amplify-server-runtime.ts +36 -1
- package/lib/approved-priming-proof-reports.ts +6 -0
- package/lib/blog-feature-solver.ts +459 -0
- package/lib/blog-rhythm.ts +134 -0
- package/lib/brand-fonts-watch.ts +43 -0
- package/lib/brand-fonts.ts +112 -0
- package/lib/cached-content-repository.ts +12 -0
- package/lib/category-repository.ts +43 -93
- package/lib/content-item-lifecycle.ts +55 -0
- package/lib/content-repository.ts +3 -0
- package/lib/content-types.ts +4 -0
- package/lib/curation-client.ts +61 -0
- package/lib/edition-routes.ts +31 -29
- package/lib/graphql-content-repository.ts +56 -22
- package/lib/media-route-response.ts +45 -0
- package/lib/newspaper-layout.ts +3 -2
- package/lib/newsroom-base-path.ts +12 -3
- package/lib/newsroom-board.ts +99 -0
- package/lib/newsroom-category-drilldown.ts +3 -2
- package/lib/newsroom-forum-routes.ts +6 -4
- package/lib/newsroom-index-filters.ts +14 -10
- package/lib/newsroom-nav.ts +3 -1
- package/lib/newsroom-organizer-nav.ts +37 -0
- package/lib/newsroom-section-routing.ts +20 -0
- package/lib/oauth-redirect-priority.ts +40 -0
- package/lib/papyrus-web-locations.ts +7 -6
- package/lib/plugin-settings.ts +11 -0
- package/lib/presentation-footer.ts +3 -2
- package/lib/pretext-layout.ts +12 -3
- package/lib/priming-proof-report.ts +90 -0
- package/lib/prospectus-publication-access.ts +53 -0
- package/lib/prospectus-session.ts +196 -0
- package/lib/prospectus-sign-in-routes.ts +284 -0
- package/lib/prospectus-site-identity.ts +303 -0
- package/lib/publication-cyclotron.ts +74 -0
- package/lib/rate-limit-retry.ts +55 -0
- package/lib/reader-base-path.ts +24 -0
- package/lib/reader-cache-lifetime.ts +5 -0
- package/lib/reader-static-params.ts +85 -4
- package/lib/reader-storage-url.ts +9 -12
- package/lib/reference-policy.ts +5 -0
- package/lib/relevance-decisions.ts +103 -0
- package/lib/relevance-status.ts +63 -0
- package/lib/renderer.ts +7 -0
- package/lib/revalidate-prewarm.ts +53 -0
- package/lib/revalidate-secret.ts +59 -0
- package/lib/robots-policy.ts +14 -0
- package/lib/root-route-redirect.ts +16 -0
- package/lib/semantic-graph.ts +7 -0
- package/lib/shared-credentials-provider.ts +61 -0
- package/lib/signup-csv.ts +24 -0
- package/lib/site-brand.ts +59 -0
- package/lib/staging-gated-path.ts +23 -0
- package/lib/staging-preview-object.ts +21 -5
- package/lib/test-editor-override.ts +4 -0
- package/lib/video-loading.ts +22 -0
- package/lib/video-script.ts +14 -0
- package/middleware.ts +11 -19
- package/package.json +73 -8
- package/renderers/pretext/article-page.tsx +39 -3
- package/renderers/pretext/presentation-shared.tsx +113 -0
- package/renderers/pretext/presentation-shell.tsx +15 -102
- package/renderers/pretext/rhythm-article-page.tsx +165 -0
- package/renderers/pretext/rhythm-blog-presentation.tsx +279 -0
- package/renderers/pretext/rhythm-shell-style.ts +35 -0
- package/routes.manifest.json +123 -12
- package/src/sync.mjs +30 -6
- package/src/with-papyrus.d.mts +9 -0
- package/src/with-papyrus.mjs +48 -3
- package/amplify/functions/graphql-jwt-authorizer/handler.ts +0 -193
- package/amplify/functions/graphql-jwt-authorizer/resource.js +0 -14
- package/amplify/functions/graphql-jwt-authorizer/resource.ts +0 -15
- package/components/ai-elements/message.tsx +0 -360
|
@@ -10,6 +10,9 @@ import Link from "next/link";
|
|
|
10
10
|
import { usePathname, useRouter } from "next/navigation";
|
|
11
11
|
import type { ReactNode, RefObject } from "react";
|
|
12
12
|
import { useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useTransition } from "react";
|
|
13
|
+
import amplifyOutputs from "papyrus-amplify-outputs";
|
|
14
|
+
import { SignupEntriesPanel } from "./signup-entries-panel";
|
|
15
|
+
import { pluginSettings, type PluginOutputs } from "../lib/plugin-settings";
|
|
13
16
|
import { generateClient } from "aws-amplify/data";
|
|
14
17
|
import type { Schema } from "../amplify/data/resource";
|
|
15
18
|
import {
|
|
@@ -53,6 +56,8 @@ import {
|
|
|
53
56
|
type ForumThreadWithMessages,
|
|
54
57
|
type KnowledgeQueryResponse,
|
|
55
58
|
type NewsroomRecordPage,
|
|
59
|
+
loadCyclotronStatus,
|
|
60
|
+
requestCyclotronReviewRate,
|
|
56
61
|
} from "./news-desk-taxonomy-client";
|
|
57
62
|
import { listConsoleThreads } from "../lib/console-chat-client";
|
|
58
63
|
import {
|
|
@@ -76,6 +81,7 @@ import { buildNewsroomKnowledgeQueryInput, type NewsroomKnowledgeQueryAnchor as
|
|
|
76
81
|
import { NewsroomConsoleProgressToggle, PapyrusConsoleChatIcon, usePapyrusConsole } from "./papyrus-console-shell";
|
|
77
82
|
import { useResolvedPapyrusTheme } from "./use-resolved-papyrus-theme";
|
|
78
83
|
import { SITE_BRAND } from "../lib/site-brand";
|
|
84
|
+
import { browserPathToInternalNewsroomPath, toPublicNewsroomPath } from "../lib/newsroom-base-path";
|
|
79
85
|
import { getNewsroomNavHref } from "../lib/newsroom-nav";
|
|
80
86
|
import { cn } from "../lib/utils";
|
|
81
87
|
import { useOptionalNewsDeskClient } from "./news-desk-client-provider";
|
|
@@ -150,9 +156,13 @@ import {
|
|
|
150
156
|
type SemanticObjectSummary,
|
|
151
157
|
} from "../lib/semantic-graph";
|
|
152
158
|
import {
|
|
159
|
+
normalizeReferenceRejectionReasonCode,
|
|
153
160
|
REFERENCE_REJECTION_REASON_CODES,
|
|
154
161
|
referenceCurationStatusForAction,
|
|
155
162
|
} from "../lib/reference-policy";
|
|
163
|
+
import { currentRelevanceDecisions } from "../lib/relevance-decisions";
|
|
164
|
+
import { buildReviewRateRequest, DEMO_CYCLOTRON_STATUS } from "../lib/relevance-status";
|
|
165
|
+
import type { CyclotronStatus } from "cyclotron/cyclotron-status";
|
|
156
166
|
import {
|
|
157
167
|
referenceDisplaySummary,
|
|
158
168
|
referenceMetadataField,
|
|
@@ -228,7 +238,7 @@ type AssignmentAction = "claim" | "release" | "complete" | "cancel" | "reopen" |
|
|
|
228
238
|
type ReportingPacketReviewDecision = "select" | "merge" | "brief" | "hold" | "kill";
|
|
229
239
|
type AssignmentDeskViewMode = "queue" | "budget";
|
|
230
240
|
type UserRoleAction = "grant" | "revoke";
|
|
231
|
-
type AdministrationPanel = "users" | "policies" | "sections" | "procedures";
|
|
241
|
+
type AdministrationPanel = "users" | "policies" | "sections" | "procedures" | "signups";
|
|
232
242
|
export type NewsDeskTab = "overview" | "desks" | "topics" | "concepts" | "references" | "insights" | "messages" | "assignments" | "administration" | "search";
|
|
233
243
|
type LexicalRuleScope = "publication" | "corpus" | "classifier" | "category";
|
|
234
244
|
type AnalysisReindexMode = "online-update" | "classifier-retrain" | "scoped-topic-rebuild" | "entity-graph-rebuild" | "generated-analysis-rebuild";
|
|
@@ -253,7 +263,6 @@ type TopicProposalEditState = {
|
|
|
253
263
|
};
|
|
254
264
|
type AnalysisCommandPlanEntry = {
|
|
255
265
|
label: string;
|
|
256
|
-
cwd: string;
|
|
257
266
|
executable: string;
|
|
258
267
|
args: string[];
|
|
259
268
|
metadata?: Record<string, unknown>;
|
|
@@ -775,6 +784,24 @@ function NewsDeskDashboard({
|
|
|
775
784
|
const [referenceAttachments, setReferenceAttachments] = useState(dashboard.referenceAttachments);
|
|
776
785
|
const [messages, setMessages] = useState(dashboard.messages);
|
|
777
786
|
const [semanticRelations, setSemanticRelations] = useState(dashboard.semanticRelations);
|
|
787
|
+
const relevanceDecisions = useMemo(() => currentRelevanceDecisions(semanticRelations), [semanticRelations]);
|
|
788
|
+
const relevanceCyclotronId = useMemo(
|
|
789
|
+
() => [...relevanceDecisions.values()].find((decision) => decision.cyclotronId)?.cyclotronId ?? null,
|
|
790
|
+
[relevanceDecisions],
|
|
791
|
+
);
|
|
792
|
+
const [cyclotronStatus, setCyclotronStatus] = useState<CyclotronStatus | null>(null);
|
|
793
|
+
useEffect(() => {
|
|
794
|
+
if (!relevanceCyclotronId) return;
|
|
795
|
+
if (dashboard.isDemo) {
|
|
796
|
+
setCyclotronStatus(DEMO_CYCLOTRON_STATUS);
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
let cancelled = false;
|
|
800
|
+
loadCyclotronStatus(relevanceCyclotronId)
|
|
801
|
+
.then((status) => { if (!cancelled) setCyclotronStatus(status); })
|
|
802
|
+
.catch(() => { if (!cancelled) setCyclotronStatus(null); });
|
|
803
|
+
return () => { cancelled = true; };
|
|
804
|
+
}, [dashboard.isDemo, relevanceCyclotronId]);
|
|
778
805
|
const [semanticNodes, setSemanticNodes] = useState(dashboard.semanticNodes);
|
|
779
806
|
const [assignments, setAssignments] = useState(dashboard.assignments);
|
|
780
807
|
const [assignmentEvents, setAssignmentEvents] = useState(dashboard.assignmentEvents);
|
|
@@ -1506,7 +1533,13 @@ function NewsDeskDashboard({
|
|
|
1506
1533
|
});
|
|
1507
1534
|
}
|
|
1508
1535
|
|
|
1509
|
-
function runReferenceCurationAction(
|
|
1536
|
+
function runReferenceCurationAction(
|
|
1537
|
+
reference: ReferenceRecord,
|
|
1538
|
+
action: ReferenceCurationAction,
|
|
1539
|
+
note?: string,
|
|
1540
|
+
reasonCode?: ReferenceRejectionReasonCode | null,
|
|
1541
|
+
relevance?: { decisionRelationId: string; shareable: boolean },
|
|
1542
|
+
) {
|
|
1510
1543
|
const nextStatus = referenceCurationStatusForAction(action);
|
|
1511
1544
|
setActionState({ id: reference.id, message: `${action} pending`, tone: "pending" });
|
|
1512
1545
|
if (dashboard.isDemo) {
|
|
@@ -1534,6 +1567,7 @@ function NewsDeskDashboard({
|
|
|
1534
1567
|
action,
|
|
1535
1568
|
curationStatus: nextStatus,
|
|
1536
1569
|
reasonCode: action === "reject" ? reasonCode ?? null : null,
|
|
1570
|
+
...(relevance ? { decisionRelationId: relevance.decisionRelationId, shareable: relevance.shareable } : {}),
|
|
1537
1571
|
},
|
|
1538
1572
|
}, ...current]);
|
|
1539
1573
|
setSemanticRelations((current) => [{
|
|
@@ -1574,6 +1608,8 @@ function NewsDeskDashboard({
|
|
|
1574
1608
|
actorLabel: authState.label,
|
|
1575
1609
|
note: note?.trim() || undefined,
|
|
1576
1610
|
reasonCode: action === "reject" ? reasonCode ?? undefined : undefined,
|
|
1611
|
+
decisionRelationId: relevance?.decisionRelationId,
|
|
1612
|
+
shareable: relevance ? relevance.shareable : undefined,
|
|
1577
1613
|
},
|
|
1578
1614
|
{ authMode: USER_POOL_AUTH_MODE },
|
|
1579
1615
|
);
|
|
@@ -3152,6 +3188,20 @@ function NewsDeskDashboard({
|
|
|
3152
3188
|
demo={Boolean(dashboard.isDemo)}
|
|
3153
3189
|
disabled={controlsDisabled}
|
|
3154
3190
|
initialReferenceLineageId={initialSelection.reference ?? pathnameReferenceLineageId}
|
|
3191
|
+
cyclotronStatus={cyclotronStatus}
|
|
3192
|
+
decisions={relevanceDecisions}
|
|
3193
|
+
onRequestReviewRate={relevanceCyclotronId ? async (choice) => {
|
|
3194
|
+
const request = buildReviewRateRequest(choice, authState.label);
|
|
3195
|
+
if (dashboard.isDemo) return;
|
|
3196
|
+
await requestCyclotronReviewRate(relevanceCyclotronId, request);
|
|
3197
|
+
} : undefined}
|
|
3198
|
+
onRelevanceReview={(reference, curation) => runReferenceCurationAction(
|
|
3199
|
+
reference,
|
|
3200
|
+
curation.action,
|
|
3201
|
+
curation.note ?? undefined,
|
|
3202
|
+
normalizeReferenceRejectionReasonCode(curation.reasonCode),
|
|
3203
|
+
{ decisionRelationId: curation.decisionRelationId, shareable: curation.shareable },
|
|
3204
|
+
)}
|
|
3155
3205
|
onReview={(reference, action) => runReferenceCurationAction(reference, action)}
|
|
3156
3206
|
referenceAttachments={referenceAttachments}
|
|
3157
3207
|
references={references}
|
|
@@ -4404,8 +4454,20 @@ function AdministrationDeskView({
|
|
|
4404
4454
|
<strong>Procedures</strong>
|
|
4405
4455
|
<span>Procedure registry and versioned Tactus code</span>
|
|
4406
4456
|
</Link>
|
|
4457
|
+
{signupPluginEnabled() ? (
|
|
4458
|
+
<Link
|
|
4459
|
+
href={administrationPanelHref("signups", isDemo)}
|
|
4460
|
+
data-news-desk-admin-nav="signups"
|
|
4461
|
+
data-active={administrationPanel === "signups" || undefined}
|
|
4462
|
+
onClick={() => onPanelChange("signups")}
|
|
4463
|
+
>
|
|
4464
|
+
<strong>Signups</strong>
|
|
4465
|
+
<span>Addresses from the site's signup forms</span>
|
|
4466
|
+
</Link>
|
|
4467
|
+
) : null}
|
|
4407
4468
|
</nav>
|
|
4408
4469
|
<div className="news-desk-settings-panel">
|
|
4470
|
+
{administrationPanel === "signups" ? <SignupEntriesPanel /> : null}
|
|
4409
4471
|
{administrationPanel === "users" ? (
|
|
4410
4472
|
<AdministrationUsersPanel
|
|
4411
4473
|
canManageUsers={canManageUsers}
|
|
@@ -13544,7 +13606,6 @@ function buildAnalysisDestructivePreview({
|
|
|
13544
13606
|
function analysisCommand(label: string, args: string[], mode: AnalysisReindexMode): AnalysisCommandPlanEntry {
|
|
13545
13607
|
return {
|
|
13546
13608
|
label,
|
|
13547
|
-
cwd: "/Users/ryan/Projects/Biblicus",
|
|
13548
13609
|
executable: "uv",
|
|
13549
13610
|
args: ["run", "--extra", "topic-modeling", "biblicus", ...args],
|
|
13550
13611
|
metadata: { mode },
|
|
@@ -13598,8 +13659,13 @@ function semanticVersionKey(kind: string, id: string): string {
|
|
|
13598
13659
|
return `${kind}#${id}`;
|
|
13599
13660
|
}
|
|
13600
13661
|
|
|
13662
|
+
function signupPluginEnabled(): boolean {
|
|
13663
|
+
return pluginSettings(amplifyOutputs as PluginOutputs, "signup") !== null;
|
|
13664
|
+
}
|
|
13665
|
+
|
|
13601
13666
|
function normalizeAdministrationPanel(value: string | null | undefined): AdministrationPanel {
|
|
13602
13667
|
if (value === "policies" || value === "sections" || value === "procedures") return value;
|
|
13668
|
+
if (value === "signups" && signupPluginEnabled()) return value;
|
|
13603
13669
|
return "users";
|
|
13604
13670
|
}
|
|
13605
13671
|
|
|
@@ -14974,6 +15040,9 @@ function buildUiCopywritingAssignment({ actorLabel, assignment, decision, messag
|
|
|
14974
15040
|
message: MessageRecord;
|
|
14975
15041
|
now: string;
|
|
14976
15042
|
}): AssignmentRecord {
|
|
15043
|
+
if (!assignment.itemLineageId) {
|
|
15044
|
+
throw new Error(`Assignment ${assignment.id} has no content item lineage; copywriting must continue the same content item.`);
|
|
15045
|
+
}
|
|
14977
15046
|
const type = decision === "brief" ? "brief" : "article";
|
|
14978
15047
|
const assignmentTypeKey = type === "brief" ? "copywriting.brief-draft" : "copywriting.article-draft";
|
|
14979
15048
|
const section = assignment.sectionKey ?? assignment.sectionId ?? "unsectioned";
|
|
@@ -15026,6 +15095,7 @@ function buildUiCopywritingAssignment({ actorLabel, assignment, decision, messag
|
|
|
15026
15095
|
createdAt: now,
|
|
15027
15096
|
updatedAt: now,
|
|
15028
15097
|
newsroomFeedKey: "assignment#open",
|
|
15098
|
+
itemLineageId: assignment.itemLineageId,
|
|
15029
15099
|
metadata,
|
|
15030
15100
|
};
|
|
15031
15101
|
}
|
|
@@ -15187,14 +15257,14 @@ function normalizeKnowledgeQueryAnchorKind(value: string | null | undefined): Kn
|
|
|
15187
15257
|
|
|
15188
15258
|
function normalizeNewsroomFromHref(value: string | null | undefined): string | null {
|
|
15189
15259
|
if (!value) return null;
|
|
15190
|
-
const trimmed = value.trim();
|
|
15260
|
+
const trimmed = browserPathToInternalNewsroomPath(value.trim());
|
|
15191
15261
|
if (!trimmed.startsWith("/newsroom")) return null;
|
|
15192
|
-
return trimmed;
|
|
15262
|
+
return toPublicNewsroomPath(trimmed);
|
|
15193
15263
|
}
|
|
15194
15264
|
|
|
15195
15265
|
function currentNewsroomOriginHref(fallback: string | null): string | null {
|
|
15196
15266
|
if (typeof window === "undefined") return fallback;
|
|
15197
|
-
const current = `${window.location.pathname}${window.location.search}
|
|
15267
|
+
const current = browserPathToInternalNewsroomPath(`${window.location.pathname}${window.location.search}`);
|
|
15198
15268
|
return current.startsWith("/newsroom/search") ? fallback : normalizeNewsroomFromHref(current);
|
|
15199
15269
|
}
|
|
15200
15270
|
|
|
@@ -15228,7 +15298,7 @@ function buildNewsroomSearchHref(request: NewsroomSearchRequest, demo?: boolean)
|
|
|
15228
15298
|
if (request.from) params.set("from", request.from);
|
|
15229
15299
|
if (demo) params.set("demo", "1");
|
|
15230
15300
|
const queryString = params.toString();
|
|
15231
|
-
return queryString ? `/newsroom/search?${queryString}` : "/newsroom/search";
|
|
15301
|
+
return toPublicNewsroomPath(queryString ? `/newsroom/search?${queryString}` : "/newsroom/search");
|
|
15232
15302
|
}
|
|
15233
15303
|
|
|
15234
15304
|
function focusNewsroomSearchForm() {
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState, type RefObject } from "react";
|
|
4
|
+
import type { BlogHeaderObstacle } from "./blog-page-background";
|
|
5
|
+
|
|
6
|
+
const HEADER_OBSTACLE_SELECTORS = [
|
|
7
|
+
".presentation-header__eyebrow",
|
|
8
|
+
".presentation-header__word",
|
|
9
|
+
".presentation-header__meta",
|
|
10
|
+
".presentation-header__subtitle",
|
|
11
|
+
".presentation-header__date",
|
|
12
|
+
".presentation-header__tagline",
|
|
13
|
+
".presentation-section-nav a",
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
function measureObstacleRect(element: HTMLElement): DOMRect {
|
|
17
|
+
const range = document.createRange();
|
|
18
|
+
range.selectNodeContents(element);
|
|
19
|
+
const contentRect = range.getBoundingClientRect();
|
|
20
|
+
range.detach();
|
|
21
|
+
if (contentRect.width > 0 && contentRect.height > 0) return contentRect;
|
|
22
|
+
return element.getBoundingClientRect();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Measures the rendered header text (eyebrow, masthead words, meta, tagline,
|
|
27
|
+
* section links) as rectangles in the coordinates of `.blog-page-background`,
|
|
28
|
+
* so a brand's background artwork can keep clear of them. Block-level header
|
|
29
|
+
* elements span the full page width even when their text ends mid-line, so
|
|
30
|
+
* the text range is measured instead of the element box.
|
|
31
|
+
*/
|
|
32
|
+
export function useBlogHeaderObstacles(
|
|
33
|
+
pageRef: RefObject<HTMLElement | null>,
|
|
34
|
+
paintBuffer: number,
|
|
35
|
+
): BlogHeaderObstacle[] {
|
|
36
|
+
const [obstacles, setObstacles] = useState<BlogHeaderObstacle[]>([]);
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
const page = pageRef.current;
|
|
40
|
+
if (!page) return;
|
|
41
|
+
|
|
42
|
+
const update = () => {
|
|
43
|
+
const background = page.querySelector<HTMLElement>(".blog-page-background");
|
|
44
|
+
const backgroundRect = background?.getBoundingClientRect();
|
|
45
|
+
if (!backgroundRect) return;
|
|
46
|
+
|
|
47
|
+
const next: BlogHeaderObstacle[] = [];
|
|
48
|
+
for (const selector of HEADER_OBSTACLE_SELECTORS) {
|
|
49
|
+
for (const element of Array.from(page.querySelectorAll<HTMLElement>(selector))) {
|
|
50
|
+
const rect = measureObstacleRect(element);
|
|
51
|
+
const intersectionTop = Math.max(rect.top, backgroundRect.top);
|
|
52
|
+
const intersectionBottom = Math.min(rect.bottom, backgroundRect.bottom);
|
|
53
|
+
const intersectionLeft = Math.max(rect.left, backgroundRect.left);
|
|
54
|
+
const intersectionRight = Math.min(rect.right, backgroundRect.right);
|
|
55
|
+
const width = intersectionRight - intersectionLeft;
|
|
56
|
+
const height = intersectionBottom - intersectionTop;
|
|
57
|
+
if (width <= 0 || height <= 0) continue;
|
|
58
|
+
next.push({
|
|
59
|
+
x: intersectionLeft - backgroundRect.left,
|
|
60
|
+
y: intersectionTop - backgroundRect.top,
|
|
61
|
+
width,
|
|
62
|
+
height: height + paintBuffer * 2,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
setObstacles(next);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
update();
|
|
70
|
+
const observer = new ResizeObserver(update);
|
|
71
|
+
observer.observe(page);
|
|
72
|
+
window.addEventListener("resize", update);
|
|
73
|
+
void document.fonts?.ready.then(update);
|
|
74
|
+
return () => {
|
|
75
|
+
observer.disconnect();
|
|
76
|
+
window.removeEventListener("resize", update);
|
|
77
|
+
};
|
|
78
|
+
}, [pageRef, paintBuffer]);
|
|
79
|
+
|
|
80
|
+
return obstacles;
|
|
81
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
import { listBrandFontFamilies } from "../lib/brand-fonts";
|
|
5
|
+
import { watchBrandFontsLoaded } from "../lib/brand-fonts-watch";
|
|
6
|
+
import { clearPretextMeasurementCache } from "../lib/pretext-layout";
|
|
7
|
+
import { SITE_BRAND } from "../lib/site-brand";
|
|
8
|
+
|
|
9
|
+
/**
|
|
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.
|
|
14
|
+
*/
|
|
15
|
+
export function useBrandFontsReadyVersion(): number {
|
|
16
|
+
const [version, setVersion] = useState(0);
|
|
17
|
+
|
|
18
|
+
useEffect(() => {
|
|
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
|
+
});
|
|
25
|
+
}, []);
|
|
26
|
+
|
|
27
|
+
return version;
|
|
28
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState, type RefObject } from "react";
|
|
4
|
+
|
|
5
|
+
export function useNearViewport(targetRef: RefObject<Element | null>, rootMargin: string): boolean {
|
|
6
|
+
const [isNearViewport, setIsNearViewport] = useState(false);
|
|
7
|
+
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
if (isNearViewport) return;
|
|
10
|
+
const target = targetRef.current;
|
|
11
|
+
if (!target) return;
|
|
12
|
+
if (typeof IntersectionObserver === "undefined") {
|
|
13
|
+
setIsNearViewport(true);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const observer = new IntersectionObserver(
|
|
17
|
+
(entries) => {
|
|
18
|
+
if (entries.some((entry) => entry.isIntersecting)) {
|
|
19
|
+
setIsNearViewport(true);
|
|
20
|
+
observer.disconnect();
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
{ rootMargin },
|
|
24
|
+
);
|
|
25
|
+
observer.observe(target);
|
|
26
|
+
return () => observer.disconnect();
|
|
27
|
+
}, [isNearViewport, rootMargin, targetRef]);
|
|
28
|
+
|
|
29
|
+
return isNearViewport;
|
|
30
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
|
|
5
|
+
export function useRhythmOverlay(): boolean {
|
|
6
|
+
const [showRhythmOverlay, setShowRhythmOverlay] = useState(false);
|
|
7
|
+
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
const handleKeyDown = (event: KeyboardEvent) => {
|
|
10
|
+
if (isEditableEventTarget(event.target)) return;
|
|
11
|
+
if ((event.key === "=" || event.code === "Equal") && event.ctrlKey && !event.metaKey && !event.altKey) {
|
|
12
|
+
event.preventDefault();
|
|
13
|
+
setShowRhythmOverlay((current) => !current);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
18
|
+
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
19
|
+
}, []);
|
|
20
|
+
|
|
21
|
+
return showRhythmOverlay;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function isEditableEventTarget(target: EventTarget | null): boolean {
|
|
25
|
+
if (!(target instanceof HTMLElement)) return false;
|
|
26
|
+
const tagName = target.tagName.toLowerCase();
|
|
27
|
+
if (tagName === "input" || tagName === "textarea" || tagName === "select") return true;
|
|
28
|
+
return target.isContentEditable || target.closest("[contenteditable='true']") !== null;
|
|
29
|
+
}
|