@anthusai/papyrus 5.54.0 → 5.55.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/components/auto-accepted-badge.tsx +21 -0
- package/components/needs-me-reference-detail.tsx +5 -3
- package/components/newsroom-board-skeleton.tsx +20 -0
- package/components/newsroom-board-view.tsx +2 -6
- package/components/newsroom-organizer.css +11 -0
- package/components/newsroom-references-view.tsx +10 -2
- package/components/reference-feed.tsx +3 -1
- package/components/reference-stories.tsx +34 -0
- package/components/story-reporting-trail-client.ts +45 -8
- package/components/story-reporting-trail.tsx +24 -13
- package/lib/local-preview-seed.ts +24 -2
- package/lib/reference-feed-ranking.ts +2 -1
- package/lib/reference-policy.ts +12 -0
- package/lib/story-reporting-trail.ts +33 -2
- package/package.json +4 -1
- package/routes.manifest.json +1 -1
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { BotIcon } from "lucide-react";
|
|
2
|
+
import { AUTO_ACCEPT_RULE_ACTOR } from "../lib/reference-policy";
|
|
3
|
+
import { cn } from "../lib/utils";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Shown on a Reference accepted automatically: by the Cyclotron's confidence after the editors' usual time, or, for a monitor that delivers
|
|
7
|
+
* by a fixed rule, on delivery ("by rule"). Words and an icon, never colour alone, and different from an editor's Accept.
|
|
8
|
+
*/
|
|
9
|
+
export function AutoAcceptedBadge({ actor, className, reason }: { actor?: string | null; className?: string; reason?: string | null }) {
|
|
10
|
+
const byRule = (actor ?? "").trim() === AUTO_ACCEPT_RULE_ACTOR;
|
|
11
|
+
return (
|
|
12
|
+
<span
|
|
13
|
+
className={cn("inline-flex items-center gap-1 rounded-full bg-[var(--feed-control)] px-2 py-0.5 text-[0.7rem] font-medium text-foreground", className)}
|
|
14
|
+
data-auto-accepted={byRule ? "rule" : "cyclotron"}
|
|
15
|
+
title={reason?.trim() || (byRule ? "The monitor's rule matched this item, so it was accepted on delivery. An editor can still Reject it." : "The Cyclotron accepted this automatically because nobody voted in the time editors usually take. An editor can still Reject it.")}
|
|
16
|
+
>
|
|
17
|
+
<BotIcon aria-hidden="true" className="size-3.5" />
|
|
18
|
+
Accepted automatically{byRule ? " (by rule)" : ""}
|
|
19
|
+
</span>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -4,7 +4,8 @@ import { referenceDisplayTitle, referenceLineageId, formatReferenceListDate } fr
|
|
|
4
4
|
import { referenceMetadataField } from "../lib/reference-display";
|
|
5
5
|
import { referenceDek, referencePreviewsByLineage } from "../lib/reference-preview";
|
|
6
6
|
import { DEFAULT_TEASER_WORDS } from "../lib/reference-capture";
|
|
7
|
-
import { referenceReasonCode, normalizeReferenceCurationStatus } from "../lib/reference-policy";
|
|
7
|
+
import { isAutoAccepted, referenceReasonCode, normalizeReferenceCurationStatus } from "../lib/reference-policy";
|
|
8
|
+
import { AutoAcceptedBadge } from "./auto-accepted-badge";
|
|
8
9
|
import type { ReferenceRecord } from "../lib/category-repository";
|
|
9
10
|
import type { NeedsMeEntry } from "../lib/needs-me-items";
|
|
10
11
|
import { DefaultOutcome, Heading, type DetailProps } from "./needs-me-bodies";
|
|
@@ -36,16 +37,17 @@ export function ReferenceBody(props: DetailProps & { payload: Extract<NeedsMeEnt
|
|
|
36
37
|
const summary = capture.summary ?? referenceMetadataField(null, reference.metadata, "summary");
|
|
37
38
|
const dek = referenceDek(reference, referencePreviewsByLineage(previewAttachments).get(lineageId));
|
|
38
39
|
const incomplete = item.type === "incomplete_capture";
|
|
40
|
+
const auto = isAutoAccepted(reference);
|
|
39
41
|
return (
|
|
40
42
|
<div className="space-y-4" data-reference-curation-status={status} data-news-desk-reference-detail={lineageId}>
|
|
41
43
|
<ReviewCard
|
|
42
44
|
decided={decidedChoiceOf(item)}
|
|
43
45
|
disabledReason={disabledReason}
|
|
44
|
-
initialNote={status === "pending" ? "" : reference.curationStatusReason ?? ""}
|
|
46
|
+
initialNote={status === "pending" || auto ? "" : reference.curationStatusReason ?? ""}
|
|
45
47
|
initialReason={referenceReasonCode(reference) ?? null}
|
|
46
48
|
dek={dek}
|
|
47
49
|
itemId={item.id}
|
|
48
|
-
meta={
|
|
50
|
+
meta={<><Heading item={item} />{auto ? <AutoAcceptedBadge actor={reference.curationStatusUpdatedBy} reason={reference.curationStatusReason} /> : null}</>}
|
|
49
51
|
onDecide={async () => undefined}
|
|
50
52
|
prediction={cardPrediction(item, predictionLoading)}
|
|
51
53
|
ratings={ratings}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { FEED_SURFACE_PROPS } from "./reference-feed";
|
|
2
|
+
|
|
3
|
+
/** The board's loading state: three columns of pulsing cards in the feed's flat tones and rounded rectangles, with no outlines. */
|
|
4
|
+
export function NewsroomBoardSkeleton() {
|
|
5
|
+
return (
|
|
6
|
+
<div aria-busy="true" aria-label="Loading board records" className="grid w-full grid-cols-1 gap-3 sm:grid-cols-3" data-board-skeleton role="status" {...FEED_SURFACE_PROPS}>
|
|
7
|
+
{Array.from({ length: 3 }, (_, column) => (
|
|
8
|
+
<div className="min-h-64 space-y-3 rounded-[20px] bg-[var(--feed-footer)] p-4" key={column}>
|
|
9
|
+
<div className="h-4 w-1/2 animate-pulse rounded-full bg-[var(--feed-control)]" />
|
|
10
|
+
{Array.from({ length: 2 }, (_, row) => (
|
|
11
|
+
<div className="h-24 rounded-2xl bg-[var(--feed-card)] p-3" key={row}>
|
|
12
|
+
<div className="h-4 w-3/4 animate-pulse rounded-full bg-[var(--feed-control)]" />
|
|
13
|
+
<div className="mt-3 h-3 w-1/2 animate-pulse rounded-full bg-[var(--feed-control)]" />
|
|
14
|
+
</div>
|
|
15
|
+
))}
|
|
16
|
+
</div>
|
|
17
|
+
))}
|
|
18
|
+
</div>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
@@ -31,6 +31,7 @@ import type { ContentItemVersionRecord } from "../lib/content-item-lifecycle";
|
|
|
31
31
|
import { useOptionalNewsDeskClient } from "./news-desk-client-provider";
|
|
32
32
|
import { NewsDeskAccessGate } from "./newsroom-access-gate";
|
|
33
33
|
import { isLocalPreviewEnabled } from "../lib/local-preview";
|
|
34
|
+
import { NewsroomBoardSkeleton } from "./newsroom-board-skeleton";
|
|
34
35
|
import { NewsroomArticleEditor } from "./newsroom-article-editor";
|
|
35
36
|
import { NewsroomAssignmentDetail } from "./newsroom-assignment-detail";
|
|
36
37
|
import { NewsroomOpsShell, NewsroomOpsStatusBanner } from "./newsroom-ops-shell";
|
|
@@ -174,12 +175,7 @@ export function NewsroomBoardView({ subject, deskId }: { subject: NewsroomBoardS
|
|
|
174
175
|
{error && <NewsroomOpsStatusBanner tone="error">{error}</NewsroomOpsStatusBanner>}
|
|
175
176
|
{subject === "desk" && <IssueTypeFilter config={config} value={typeFilter} onChange={setTypeFilter} name="Record type" className="organizer-board-type-filter" />}
|
|
176
177
|
<div className="organizer-board-body" data-detail-open={Boolean(issue)} ref={boardBodyRef} onScrollCapture={handleBoardScroll}>
|
|
177
|
-
{loading && !issues.length ? <
|
|
178
|
-
{Array.from({ length: 3 }, (_, column) => <div className="min-h-64 space-y-3 rounded-xl border bg-card p-4" key={column}>
|
|
179
|
-
<div className="h-4 w-1/2 rounded bg-muted" />
|
|
180
|
-
{Array.from({ length: 2 }, (_, row) => <div className="h-24 rounded-lg border bg-background p-3" key={row}><div className="h-4 w-3/4 rounded bg-muted" /><div className="mt-3 h-3 w-1/2 rounded bg-muted" /></div>)}
|
|
181
|
-
</div>)}
|
|
182
|
-
</div> : <IssueCollection issues={issues} config={config} query={{ type: subject === "desk" ? issueTypeFromFilter(typeFilter) : boardType }} view={view}
|
|
178
|
+
{loading && !issues.length ? <NewsroomBoardSkeleton /> : <IssueCollection issues={issues} config={config} query={{ type: subject === "desk" ? issueTypeFromFilter(typeFilter) : boardType }} view={view}
|
|
183
179
|
motion={lowMotion ? STATIC_MOTION : undefined}
|
|
184
180
|
collapsedColumns={columnState.collapsedColumns} onToggleCollapse={columnState.toggleColumn} initialScroll="start"
|
|
185
181
|
selectedIssueId={selected} detailOpen={Boolean(issue)} onSelectIssue={item => setSelected(item.id)} ariaLabel={pageTitle}
|
|
@@ -194,6 +194,17 @@ body:has(.newsroom-organizer)>[data-staging-banner]{flex-shrink:0}
|
|
|
194
194
|
.story-trail-row{display:flex;flex-wrap:wrap;gap:8px;align-items:baseline}
|
|
195
195
|
.story-trail-meta,.story-trail-empty{font-size:12px;color:var(--org-muted);margin:0}
|
|
196
196
|
.story-trail-chip{font-size:11px;border:1px solid var(--org-ink);padding:0 6px;text-transform:uppercase;letter-spacing:.04em}
|
|
197
|
+
.story-trail-reference{display:grid;grid-template-columns:minmax(0,96px) minmax(0,1fr);gap:12px;align-items:start;background:color-mix(in srgb,var(--org-ink) 6%,transparent);border-radius:10px;padding:12px}
|
|
198
|
+
.story-trail-reference:not(:has(.story-trail-reference-image)){grid-template-columns:minmax(0,1fr)}
|
|
199
|
+
.story-trail-reference-image{width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:8px;background:color-mix(in srgb,var(--org-ink) 10%,transparent)}
|
|
200
|
+
.story-trail-reference-text{display:grid;gap:4px;min-width:0}
|
|
201
|
+
.story-trail-reference-links{display:flex;flex-wrap:wrap;gap:4px 16px;font-size:13px}
|
|
202
|
+
.story-trail-reference-links a,.reference-stories a{display:inline-flex;align-items:center;gap:4px}
|
|
203
|
+
.story-trail-role{display:inline-flex;align-items:center;gap:4px;font-size:11px;text-transform:uppercase;letter-spacing:.04em;background:color-mix(in srgb,var(--org-ink) 12%,transparent);border-radius:999px;padding:1px 8px}
|
|
204
|
+
.reference-stories{display:grid;gap:8px}
|
|
205
|
+
.reference-stories h3{margin:0;font-size:14px;font-weight:500}
|
|
206
|
+
.reference-stories ul{list-style:none;margin:0;padding:0;display:grid;gap:8px}
|
|
207
|
+
.reference-stories li{display:flex;flex-wrap:wrap;gap:4px 12px;align-items:center;background:color-mix(in srgb,var(--org-ink) 6%,transparent);border-radius:10px;padding:10px 12px}
|
|
197
208
|
.story-trail-message pre{white-space:pre-wrap;overflow-wrap:anywhere;margin:6px 0 0;font-size:12px;max-height:320px;overflow:auto}
|
|
198
209
|
.story-trail-message summary{cursor:pointer;font-size:13px}
|
|
199
210
|
@container (min-width:900px){.story-trail{grid-template-columns:repeat(2,minmax(0,1fr));column-gap:32px;align-items:start}}
|
|
@@ -39,9 +39,11 @@ import {
|
|
|
39
39
|
type ReferenceCurationAction,
|
|
40
40
|
type ReferenceStatusFilter,
|
|
41
41
|
} from "../lib/newsroom-references";
|
|
42
|
-
import {
|
|
42
|
+
import { AutoAcceptedBadge } from "./auto-accepted-badge";
|
|
43
|
+
import { isAutoAccepted, normalizeReferenceCurationStatus, referenceReasonCode, type ReferenceRejectionReasonCode } from "../lib/reference-policy";
|
|
43
44
|
import { newsroomListRowClassName } from "../lib/newsroom-list-selection";
|
|
44
45
|
import { ReferenceSourcePreview } from "./reference-source-preview";
|
|
46
|
+
import { ReferenceStories } from "./reference-stories";
|
|
45
47
|
import { useReferencePreviewAttachments } from "./use-reference-preview-attachments";
|
|
46
48
|
import { useMediaQuery } from "./use-media-query";
|
|
47
49
|
import { FEED_SURFACE_PROPS, FEED_SURFACE_STYLE, ReferenceFeed } from "./reference-feed";
|
|
@@ -196,6 +198,7 @@ function ReferenceDetailPanel({
|
|
|
196
198
|
countsAsTrainingLabel,
|
|
197
199
|
delegated,
|
|
198
200
|
decision,
|
|
201
|
+
demo = false,
|
|
199
202
|
disabled,
|
|
200
203
|
onReview,
|
|
201
204
|
predictionState = "ready",
|
|
@@ -234,7 +237,8 @@ function ReferenceDetailPanel({
|
|
|
234
237
|
}) {
|
|
235
238
|
const lineageId = referenceLineageId(reference);
|
|
236
239
|
const status = normalizeReferenceCurationStatus(reference.curationStatus);
|
|
237
|
-
const
|
|
240
|
+
const auto = isAutoAccepted(reference);
|
|
241
|
+
const sourceNote = status === "pending" || auto ? "" : reference.curationStatusReason ?? "";
|
|
238
242
|
const sourceReason = reviewReason ?? referenceReasonCode(reference) ?? null;
|
|
239
243
|
const [chosenSectionId, setChosenSectionId] = useState<string | null>(reviewSectionId ?? null);
|
|
240
244
|
const sectionEdited = useRef(false);
|
|
@@ -272,6 +276,7 @@ function ReferenceDetailPanel({
|
|
|
272
276
|
meta={(
|
|
273
277
|
<div className="flex flex-wrap items-center gap-2">
|
|
274
278
|
<Badge variant={referenceStatusBadgeVariant(status)}>{referenceStatusLabel(status)}</Badge>
|
|
279
|
+
{auto ? <AutoAcceptedBadge actor={reference.curationStatusUpdatedBy} reason={reference.curationStatusReason} /> : null}
|
|
275
280
|
{reference.corpusId ? <Badge variant="secondary">{reference.corpusId}</Badge> : null}
|
|
276
281
|
</div>
|
|
277
282
|
)}
|
|
@@ -307,6 +312,8 @@ function ReferenceDetailPanel({
|
|
|
307
312
|
|
|
308
313
|
<Separator />
|
|
309
314
|
|
|
315
|
+
<ReferenceStories demo={demo} referenceLineageId={lineageId} />
|
|
316
|
+
|
|
310
317
|
<ReferenceCapturePanel capture={capture} showSummary={false} />
|
|
311
318
|
|
|
312
319
|
<dl className="grid gap-3 text-sm">
|
|
@@ -845,6 +852,7 @@ export function NewsroomReferencesView({
|
|
|
845
852
|
<Badge variant={referenceStatusBadgeVariant(status)}>{referenceStatusLabel(status)}</Badge>
|
|
846
853
|
</div>
|
|
847
854
|
<p className="mt-2 text-xs text-muted-foreground">{formatReferenceListDate(reference)}</p>
|
|
855
|
+
{isAutoAccepted(reference) ? <AutoAcceptedBadge actor={reference.curationStatusUpdatedBy} className="mt-1" reason={reference.curationStatusReason} /> : null}
|
|
848
856
|
{decision ? (
|
|
849
857
|
<div className="flex flex-wrap items-center gap-2" data-reference-relevance-row={lineageId}>
|
|
850
858
|
<ReferenceSuggestionRowLine decision={decision} referenceLineageId={lineageId} />
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { InfoIcon, Loader2Icon } from "lucide-react";
|
|
4
4
|
import { useEffect, useId, useRef, useState, type CSSProperties, type ReactNode } from "react";
|
|
5
5
|
import type { ReferenceRecord } from "../lib/category-repository";
|
|
6
|
-
import { REFERENCE_REJECTION_REASON_CODES, normalizeReferenceCurationStatus, type ReferenceRejectionReasonCode } from "../lib/reference-policy";
|
|
6
|
+
import { REFERENCE_REJECTION_REASON_CODES, isAutoAccepted, normalizeReferenceCurationStatus, type ReferenceRejectionReasonCode } from "../lib/reference-policy";
|
|
7
7
|
import {
|
|
8
8
|
referenceDisplayTime,
|
|
9
9
|
groupFeedItems,
|
|
@@ -22,6 +22,7 @@ import { cn } from "../lib/utils";
|
|
|
22
22
|
import { predictionFromDecision, type ReviewChoice } from "./reference-review-card";
|
|
23
23
|
import { RatingGrid } from "./review-rating-grid";
|
|
24
24
|
import { CaptureBadge } from "./reference-capture-panel";
|
|
25
|
+
import { AutoAcceptedBadge } from "./auto-accepted-badge";
|
|
25
26
|
import { useRatingStore, type RatingController } from "./use-reference-ratings";
|
|
26
27
|
import type { CaptureStatus } from "../lib/reference-capture";
|
|
27
28
|
|
|
@@ -293,6 +294,7 @@ export function FeedCard({
|
|
|
293
294
|
{host}
|
|
294
295
|
</span>
|
|
295
296
|
{capture ? <CaptureBadge status={capture} /> : null}
|
|
297
|
+
{isAutoAccepted(reference) ? <AutoAcceptedBadge actor={reference.curationStatusUpdatedBy} reason={reference.curationStatusReason} /> : null}
|
|
296
298
|
</span>
|
|
297
299
|
</span>
|
|
298
300
|
</button>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { FileTextIcon, LinkIcon } from "lucide-react";
|
|
4
|
+
import Link from "next/link";
|
|
5
|
+
import { useEffect, useState } from "react";
|
|
6
|
+
import { getNewsroomNavHref } from "../lib/newsroom-nav";
|
|
7
|
+
import { referenceRoleLabel, type ReferenceStory } from "../lib/story-reporting-trail";
|
|
8
|
+
import { loadStoriesBasedOnReference } from "./story-reporting-trail-client";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The stories made from one Reference: each links to the story's own page. The stories' text is their own; this list is the other end
|
|
12
|
+
* of the link the story view shows under References.
|
|
13
|
+
*/
|
|
14
|
+
export function ReferenceStories({ demo = false, referenceLineageId }: { demo?: boolean; referenceLineageId: string }) {
|
|
15
|
+
const [stories, setStories] = useState<ReferenceStory[] | null>(null);
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
let active = true;
|
|
18
|
+
setStories(null);
|
|
19
|
+
loadStoriesBasedOnReference(referenceLineageId, demo).then(rows => { if (active) setStories(rows); }).catch(() => { if (active) setStories([]); });
|
|
20
|
+
return () => { active = false; };
|
|
21
|
+
}, [demo, referenceLineageId]);
|
|
22
|
+
if (!stories?.length) return null;
|
|
23
|
+
return <section aria-label="Stories based on this Reference" className="reference-stories" data-reference-stories={referenceLineageId}>
|
|
24
|
+
<h3>Stories based on this Reference</h3>
|
|
25
|
+
<ul>
|
|
26
|
+
{stories.map(story => <li data-reference-story={story.lineageId} key={story.relationId}>
|
|
27
|
+
<FileTextIcon aria-hidden="true" size={16} />
|
|
28
|
+
<Link href={getNewsroomNavHref(`/newsroom/articles/${encodeURIComponent(story.itemId)}`, demo)}>{story.title ?? story.lineageId}</Link>
|
|
29
|
+
<span className="story-trail-role">{story.role === "based_on" ? <LinkIcon aria-hidden="true" size={13} /> : null}{referenceRoleLabel(story.role)}</span>
|
|
30
|
+
{story.stage && <span className="story-trail-meta">{story.stage}</span>}
|
|
31
|
+
</li>)}
|
|
32
|
+
</ul>
|
|
33
|
+
</section>;
|
|
34
|
+
}
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
import { generateClient } from "aws-amplify/data";
|
|
4
4
|
import type { Schema } from "../amplify/data/resource";
|
|
5
5
|
import { isLocalPreviewEnabled } from "../lib/local-preview";
|
|
6
|
-
import { createLocalPreviewReportingTrail } from "../lib/local-preview-seed";
|
|
7
|
-
import { EMPTY_STORY_REPORTING_TRAIL, type StoryReportingTrail, type TrailAssignment, type TrailAttachment, type TrailReference, type TrailThread, type TrailVersion } from "../lib/story-reporting-trail";
|
|
6
|
+
import { createLocalPreviewReportingTrail, createLocalPreviewStoriesForReference } from "../lib/local-preview-seed";
|
|
7
|
+
import { EMPTY_STORY_REPORTING_TRAIL, referencesByRole, siteNameOf, type ReferenceStory, type StoryReportingTrail, type TrailAssignment, type TrailAttachment, type TrailReference, type TrailThread, type TrailVersion } from "../lib/story-reporting-trail";
|
|
8
8
|
import { configureAmplifyClient } from "./amplify-client-provider";
|
|
9
9
|
|
|
10
10
|
type GraphqlResponse = { data?: Record<string, { items?: Record<string, unknown>[] | null; nextToken?: string | null } | null> | null; errors?: { message?: string }[] | null };
|
|
@@ -48,9 +48,44 @@ async function getReference(client: GraphqlClient, referenceId: string): Promise
|
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
function relationRole(value: unknown): TrailReference["role"] {
|
|
52
|
+
return value === "based_on" ? "based_on" : value === "cited" ? "cited" : "consulted";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** The Reference's preview image address and site name, from its `preview` attachment (nothing is downloaded). */
|
|
56
|
+
async function getPreview(client: GraphqlClient, referenceLineageId: string): Promise<{ imageUrl: string | null; siteName: string | null }> {
|
|
57
|
+
try {
|
|
58
|
+
const rows = await listAll(client, "listReferenceAttachmentsByReferenceLineageAndSortKey", "id role sourceUri filename metadata", ", $referenceLineageId: ID!", ", referenceLineageId: $referenceLineageId", { referenceLineageId });
|
|
59
|
+
const preview = rows.find(row => row.role === "preview");
|
|
60
|
+
const address = text(preview?.sourceUri);
|
|
61
|
+
return { imageUrl: address && /^https?:\/\//i.test(address) ? address : null, siteName: text(parseMetadata(preview?.metadata).siteName) ?? text(preview?.filename) };
|
|
62
|
+
} catch {
|
|
63
|
+
return { imageUrl: null, siteName: null };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** The stories whose `uses_evidence` link names this Reference, each with its working title and stage. */
|
|
68
|
+
export async function loadStoriesBasedOnReference(referenceLineageId: string, demo = false): Promise<ReferenceStory[]> {
|
|
69
|
+
if (demo || isLocalPreviewEnabled()) return createLocalPreviewStoriesForReference(referenceLineageId);
|
|
70
|
+
configureAmplifyClient();
|
|
71
|
+
const client = generateClient<Schema>() as unknown as GraphqlClient;
|
|
72
|
+
const relations = await listAll(client, "listSemanticRelationsByObjectState", "id predicate subjectKind subjectId subjectLineageId metadata", ", $objectStateKey: String!", ", objectStateKey: $objectStateKey", { objectStateKey: `reference#${referenceLineageId}#current` });
|
|
73
|
+
const stories = relations.filter(relation => relation.predicate === "uses_evidence" && relation.subjectKind === "item");
|
|
74
|
+
const rows = await Promise.all(stories.map(async relation => {
|
|
75
|
+
const lineageId = String(relation.subjectLineageId ?? relation.subjectId);
|
|
76
|
+
const versions = await listAll(client, "listItemsByLineageAndVersion", "id versionNumber versionState stage headline title", ", $lineageId: ID!", ", lineageId: $lineageId", { lineageId }).catch(() => []);
|
|
77
|
+
const current = versions.find(version => version.versionState === "current") ?? versions.sort((left, right) => Number(right.versionNumber ?? 0) - Number(left.versionNumber ?? 0))[0];
|
|
78
|
+
return {
|
|
79
|
+
relationId: String(relation.id), itemId: String(current?.id ?? relation.subjectId), lineageId, title: text(current?.headline) ?? text(current?.title), stage: text(current?.stage),
|
|
80
|
+
role: relationRole(parseMetadata(relation.metadata).role),
|
|
81
|
+
} satisfies ReferenceStory;
|
|
82
|
+
}));
|
|
83
|
+
return rows;
|
|
84
|
+
}
|
|
85
|
+
|
|
51
86
|
/** Reads the whole reporting trail of one story from the backend; the local preview serves a snapshot instead. */
|
|
52
|
-
export async function loadStoryReportingTrail(lineageId: string, itemId: string): Promise<StoryReportingTrail> {
|
|
53
|
-
if (isLocalPreviewEnabled()) return createLocalPreviewReportingTrail(lineageId);
|
|
87
|
+
export async function loadStoryReportingTrail(lineageId: string, itemId: string, demo = false): Promise<StoryReportingTrail> {
|
|
88
|
+
if (demo || isLocalPreviewEnabled()) return createLocalPreviewReportingTrail(lineageId);
|
|
54
89
|
configureAmplifyClient();
|
|
55
90
|
const client = generateClient<Schema>() as unknown as GraphqlClient;
|
|
56
91
|
const [versionRows, assignmentRows, attachmentRows, relationRows] = await Promise.all([
|
|
@@ -77,15 +112,17 @@ export async function loadStoryReportingTrail(lineageId: string, itemId: string)
|
|
|
77
112
|
return { id: String(row.id), title: text(row.title), threadKind: text(row.threadKind), summary: text(row.summary), messages: messageRows.map(message => ({ id: String(message.id), sequenceNumber: typeof message.sequenceNumber === "number" ? message.sequenceNumber : null, messageKind: text(message.messageKind), authorLabel: text(message.authorLabel), summary: text(message.summary), content: text(message.content), createdAt: String(message.createdAt ?? "") })) };
|
|
78
113
|
}));
|
|
79
114
|
const referenceRelations = relationRows.filter(relation => relation.predicate === "uses_evidence" && relation.subjectLineageId === lineageId && relation.objectKind === "reference");
|
|
80
|
-
const references: TrailReference[] = await Promise.all(referenceRelations.map(async relation => {
|
|
115
|
+
const references: TrailReference[] = referencesByRole(await Promise.all(referenceRelations.map(async relation => {
|
|
81
116
|
const metadata = parseMetadata(relation.metadata);
|
|
82
117
|
const referenceRow = await getReference(client, String(relation.objectId));
|
|
118
|
+
const preview = await getPreview(client, text(relation.objectLineageId) ?? String(relation.objectId));
|
|
119
|
+
const sourceUri = text(referenceRow?.sourceUri);
|
|
83
120
|
return {
|
|
84
121
|
relationId: String(relation.id), predicate: String(relation.predicate ?? ""), referenceId: String(relation.objectId), referenceLineageId: text(relation.objectLineageId),
|
|
85
|
-
title: text(referenceRow?.title), sourceUri:
|
|
86
|
-
role: metadata.role
|
|
122
|
+
title: text(referenceRow?.title), sourceUri, siteName: siteNameOf(sourceUri, preview.siteName), previewImageUrl: preview.imageUrl,
|
|
123
|
+
role: relationRole(metadata.role), note: text(metadata.note), citationKey: text(metadata.citationKey), addedBy: text(metadata.addedBy), addedAt: text(metadata.addedAt),
|
|
87
124
|
};
|
|
88
|
-
}));
|
|
125
|
+
})));
|
|
89
126
|
const attachments: TrailAttachment[] = attachmentRows.map(row => ({ id: String(row.id), role: text(row.role), filename: text(row.filename), mediaType: text(row.mediaType), byteSize: typeof row.byteSize === "number" ? row.byteSize : null }));
|
|
90
127
|
return { ...EMPTY_STORY_REPORTING_TRAIL, versions, assignments, threads, references, attachments };
|
|
91
128
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { BookmarkIcon, ExternalLinkIcon, GlobeIcon, LinkIcon } from "lucide-react";
|
|
3
4
|
import Link from "next/link";
|
|
4
5
|
import { useEffect, useState } from "react";
|
|
5
6
|
import { getNewsroomNavHref } from "../lib/newsroom-nav";
|
|
6
|
-
import { storyMessageFeed, storyReportingTrailIsEmpty, type StoryReportingTrail, type TrailReference } from "../lib/story-reporting-trail";
|
|
7
|
+
import { referenceRoleLabel, storyMessageFeed, storyReportingTrailIsEmpty, type StoryReportingTrail, type TrailReference } from "../lib/story-reporting-trail";
|
|
7
8
|
import { loadStoryReportingTrail } from "./story-reporting-trail-client";
|
|
8
9
|
|
|
9
10
|
const timestampFormat = new Intl.DateTimeFormat(undefined, { dateStyle: "medium", timeStyle: "short" });
|
|
@@ -15,17 +16,26 @@ function formatTimestamp(value: string | null | undefined): string {
|
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
function ReferenceSummary({ citedKeys, demo, focused, reference }: { citedKeys: readonly string[]; demo: boolean; focused: boolean; reference: TrailReference }) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
const basedOn = reference.role === "based_on";
|
|
20
|
+
const RoleIcon = basedOn ? LinkIcon : BookmarkIcon;
|
|
21
|
+
return <div className="story-trail-reference" data-story-trail-reference-card={reference.referenceId}>
|
|
22
|
+
{/* eslint-disable-next-line @next/next/no-img-element -- the preview is shown from its own site, never copied or optimised here */}
|
|
23
|
+
{reference.previewImageUrl && <img alt="" className="story-trail-reference-image" data-story-trail-reference-image loading="lazy" referrerPolicy="no-referrer" src={reference.previewImageUrl} />}
|
|
24
|
+
<div className="story-trail-reference-text">
|
|
25
|
+
<div className="story-trail-row">
|
|
26
|
+
<strong data-story-trail-reference-title>{reference.title ?? reference.referenceId}</strong>
|
|
27
|
+
<span className="story-trail-role" data-story-trail-reference-role={reference.role}><RoleIcon aria-hidden="true" size={13} />{referenceRoleLabel(reference.role)}</span>
|
|
28
|
+
{reference.citationKey && citedKeys.includes(reference.citationKey) && <span className="story-trail-role">In the text</span>}
|
|
29
|
+
</div>
|
|
30
|
+
{reference.siteName && <div className="story-trail-meta" data-story-trail-reference-site><GlobeIcon aria-hidden="true" size={13} /> {reference.siteName}</div>}
|
|
31
|
+
<div className="story-trail-meta">{[reference.addedBy && `added by ${reference.addedBy}`, formatTimestamp(reference.addedAt), reference.citationKey && `citation key ${reference.citationKey}`].filter(Boolean).join(" · ")}</div>
|
|
32
|
+
{reference.note && <p data-story-trail-reference-summary={focused ? "focused" : undefined}>{reference.note}</p>}
|
|
33
|
+
<div className="story-trail-reference-links">
|
|
34
|
+
{reference.referenceLineageId && <Link data-story-trail-open-reference={reference.referenceLineageId} href={getNewsroomNavHref(`/newsroom/references/${encodeURIComponent(reference.referenceLineageId)}`, demo)}><BookmarkIcon aria-hidden="true" size={14} /> Open the Reference</Link>}
|
|
35
|
+
{reference.sourceUri && <a href={reference.sourceUri} rel="noreferrer" target="_blank"><ExternalLinkIcon aria-hidden="true" size={14} /> Open the source page</a>}
|
|
36
|
+
</div>
|
|
23
37
|
</div>
|
|
24
|
-
|
|
25
|
-
{reference.note && <p data-story-trail-reference-summary={focused ? "focused" : undefined}>{reference.note}</p>}
|
|
26
|
-
{reference.sourceUri && <div className="story-trail-meta">{reference.sourceUri}</div>}
|
|
27
|
-
{reference.referenceLineageId && <Link href={getNewsroomNavHref(`/newsroom/references/${encodeURIComponent(reference.referenceLineageId)}`, demo)}>Open the full reference page</Link>}
|
|
28
|
-
</>;
|
|
38
|
+
</div>;
|
|
29
39
|
}
|
|
30
40
|
|
|
31
41
|
function TrailSection({ count, children, title }: { count: number; children: React.ReactNode; title: string }) {
|
|
@@ -46,16 +56,17 @@ export function StoryReportingTrailView({ citedKeys = [], demo = false, focusedR
|
|
|
46
56
|
useEffect(() => {
|
|
47
57
|
let cancelled = false;
|
|
48
58
|
setTrail(null); setError(null);
|
|
49
|
-
loadStoryReportingTrail(lineageId || itemId, itemId)
|
|
59
|
+
loadStoryReportingTrail(lineageId || itemId, itemId, demo)
|
|
50
60
|
.then(loaded => { if (!cancelled) setTrail(loaded); })
|
|
51
61
|
.catch((reason: unknown) => { if (!cancelled) setError(reason instanceof Error ? reason.message : "Could not load the reporting trail."); });
|
|
52
62
|
return () => { cancelled = true; };
|
|
53
|
-
}, [itemId, lineageId]);
|
|
63
|
+
}, [demo, itemId, lineageId]);
|
|
54
64
|
if (error) return <p role="alert" className="story-trail-empty">{error}</p>;
|
|
55
65
|
if (!trail) return <p className="story-trail-empty">Loading the reporting trail…</p>;
|
|
56
66
|
const messageFeed = storyMessageFeed(trail);
|
|
57
67
|
if (view === "references") return <div className="story-trail" data-story-trail data-story-trail-view="references">
|
|
58
68
|
<TrailSection count={trail.references.length} title="References">
|
|
69
|
+
{trail.references.some(reference => reference.role === "based_on") && <p className="story-trail-meta" data-story-trail-based-on-note>The text of this story is our own. It is based on the Reference marked Based on, and is written and edited here, never copied from it.</p>}
|
|
59
70
|
<ul className="story-trail-list" data-story-references-list>
|
|
60
71
|
{trail.references.map(reference => <li data-story-reference-entry={reference.referenceId} data-story-reference-focused={reference.referenceId === focusedReferenceId ? "true" : undefined} key={reference.relationId} ref={reference.referenceId === focusedReferenceId ? node => node?.scrollIntoView({ block: "nearest" }) : undefined}>
|
|
61
72
|
<ReferenceSummary citedKeys={citedKeys} demo={demo} focused={reference.referenceId === focusedReferenceId} reference={reference} />
|
|
@@ -2,7 +2,7 @@ import type { AssignmentRecord, MessageRecord, ReferenceRecord, SemanticRelation
|
|
|
2
2
|
import { SHADOW_AGREEMENT_SCHEMA, type ShadowAgreement } from "./cyclotron-suggestions";
|
|
3
3
|
import type { EditableArticle } from "./newsroom-articles";
|
|
4
4
|
import type { MediaAssetRecord } from "./newsroom-media";
|
|
5
|
-
import { EMPTY_STORY_REPORTING_TRAIL, type StoryReportingTrail, type TrailVersion } from "./story-reporting-trail";
|
|
5
|
+
import { EMPTY_STORY_REPORTING_TRAIL, type ReferenceStory, type StoryReportingTrail, type TrailReference, type TrailVersion } from "./story-reporting-trail";
|
|
6
6
|
import { contentItemStageKeys, type ContentItemVersionRecord } from "./content-item-lifecycle";
|
|
7
7
|
import { getContentItemStageDefinitions } from "./site-brand";
|
|
8
8
|
import { LOCAL_PREVIEW_TENANTS, getLocalPreviewTenantId } from "./local-preview-tenant";
|
|
@@ -159,9 +159,31 @@ export function lineageForPreviewVersion(versionId: string): string | null {
|
|
|
159
159
|
return previewLineageByVersionId.get(versionId) ?? null;
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
+
/** The demo story that is based on a demo Reference, so the story view and the Reference view can show the link without a backend. */
|
|
163
|
+
const PREVIEW_BASED_ON = {
|
|
164
|
+
storyLineageId: "demo-article-published",
|
|
165
|
+
storyTitle: "Harbor Cranes Return",
|
|
166
|
+
referenceLineageId: "reference-knowledge-corpus-demo-source-history-002",
|
|
167
|
+
};
|
|
168
|
+
const PREVIEW_IMAGE = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="96" height="72"><rect width="96" height="72" fill="#cbd5c0"/></svg>');
|
|
169
|
+
|
|
170
|
+
function previewBasedOnReference(): TrailReference {
|
|
171
|
+
return {
|
|
172
|
+
relationId: "preview-story-reference-relation", predicate: "uses_evidence", referenceId: "reference-knowledge-corpus-demo-source-history-002", referenceLineageId: PREVIEW_BASED_ON.referenceLineageId,
|
|
173
|
+
title: "Foundation Model Scaling Retrospective", sourceUri: "https://history.example.org/scaling-retrospective", siteName: "history.example.org", previewImageUrl: PREVIEW_IMAGE,
|
|
174
|
+
role: "based_on", note: null, citationKey: null, addedBy: "papyrus-cli", addedAt: isoTimestampHoursBeforeBase(30),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
162
178
|
export function createLocalPreviewReportingTrail(lineageId: string): StoryReportingTrail {
|
|
163
179
|
const saved = previewSavedVersions.get(lineageId) ?? [];
|
|
164
|
-
|
|
180
|
+
const references = lineageId === PREVIEW_BASED_ON.storyLineageId ? [previewBasedOnReference()] : [];
|
|
181
|
+
return { ...EMPTY_STORY_REPORTING_TRAIL, versions: saved, references };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function createLocalPreviewStoriesForReference(referenceLineageId: string): ReferenceStory[] {
|
|
185
|
+
if (referenceLineageId !== PREVIEW_BASED_ON.referenceLineageId) return [];
|
|
186
|
+
return [{ relationId: "preview-story-reference-relation", itemId: PREVIEW_BASED_ON.storyLineageId, lineageId: PREVIEW_BASED_ON.storyLineageId, title: PREVIEW_BASED_ON.storyTitle, stage: "copywriting", role: "based_on" }];
|
|
165
187
|
}
|
|
166
188
|
|
|
167
189
|
export function createLocalPreviewMediaAssets(_itemId: string): MediaAssetRecord[] {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import type { ReferenceRecord } from "./category-repository";
|
|
17
17
|
import type { RelevanceDecisionView } from "./relevance-decisions";
|
|
18
|
-
import { normalizeReferenceCurationStatus } from "./reference-policy";
|
|
18
|
+
import { isAutoAccepted, normalizeReferenceCurationStatus } from "./reference-policy";
|
|
19
19
|
import { referenceLineageId } from "./newsroom-references";
|
|
20
20
|
|
|
21
21
|
/** Fewer accepted examples than this and there is no freshness weight: a constant is never guessed. */
|
|
@@ -60,6 +60,7 @@ export function learnFreshness(references: ReferenceRecord[], ignore: ReadonlySe
|
|
|
60
60
|
const ages: number[] = [];
|
|
61
61
|
for (const reference of references) {
|
|
62
62
|
if (normalizeReferenceCurationStatus(reference.curationStatus) !== "accepted" || ignore.has(referenceLineageId(reference))) continue;
|
|
63
|
+
if (isAutoAccepted(reference)) continue; // an automatic acceptance happens after the editors' usual time, so it says nothing about how fast they act
|
|
63
64
|
const arrived = referenceArrivalTime(reference);
|
|
64
65
|
const decided = timeOf(reference.curationStatusUpdatedAt);
|
|
65
66
|
if (arrived === null || decided === null) continue;
|
package/lib/reference-policy.ts
CHANGED
|
@@ -30,6 +30,18 @@ export function normalizeReferenceCurationStatus(value: unknown, defaultStatus:
|
|
|
30
30
|
return "pending";
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
/** The curation actor the router writes when the Cyclotron accepts a confident, unvoted Reference on its own (src/papyrus_content/auto_accept.py). Not a person. */
|
|
34
|
+
export const AUTO_ACCEPT_ACTOR = "auto:cyclotron";
|
|
35
|
+
|
|
36
|
+
/** The actor for a Reference a monitor with a fixed delivery rule accepted on delivery: the rule match is the acceptance. */
|
|
37
|
+
export const AUTO_ACCEPT_RULE_ACTOR = "auto:rule";
|
|
38
|
+
|
|
39
|
+
/** True while a Reference stands accepted automatically, by the Cyclotron's confidence or by a monitor's rule. An editor's later Accept or Reject replaces the actor, so this is false from then on. */
|
|
40
|
+
export function isAutoAccepted(reference: { curationStatus?: unknown; curationStatusUpdatedBy?: unknown }): boolean {
|
|
41
|
+
const actor = String(reference.curationStatusUpdatedBy ?? "").trim();
|
|
42
|
+
return normalizeReferenceCurationStatus(reference.curationStatus) === "accepted" && (actor === AUTO_ACCEPT_ACTOR || actor === AUTO_ACCEPT_RULE_ACTOR);
|
|
43
|
+
}
|
|
44
|
+
|
|
33
45
|
export function referenceCurationStatusForAction(action: "accept" | "reject" | "reopen" | "archive"): ReferenceCurationStatus {
|
|
34
46
|
if (action === "accept") return "accepted";
|
|
35
47
|
if (action === "reject") return "rejected";
|
|
@@ -63,8 +63,12 @@ export type TrailReference = {
|
|
|
63
63
|
referenceLineageId: string | null;
|
|
64
64
|
title: string | null;
|
|
65
65
|
sourceUri: string | null;
|
|
66
|
-
/** consulted: read during reporting; cited: a citation in the article. */
|
|
67
|
-
role: "consulted" | "cited";
|
|
66
|
+
/** based_on: the story is made from this Reference; consulted: read during reporting; cited: a citation in the article. */
|
|
67
|
+
role: "based_on" | "consulted" | "cited";
|
|
68
|
+
/** The site that published the Reference (its preview's site name, else the host of its address). */
|
|
69
|
+
siteName: string | null;
|
|
70
|
+
/** The Reference's preview image address. It is shown from its own site, never copied. */
|
|
71
|
+
previewImageUrl: string | null;
|
|
68
72
|
note: string | null;
|
|
69
73
|
citationKey: string | null;
|
|
70
74
|
addedBy: string | null;
|
|
@@ -104,3 +108,30 @@ export function storyMessageFeed(trail: StoryReportingTrail): StoryFeedEntry[] {
|
|
|
104
108
|
const sequenceOf = (entry: StoryFeedEntry) => (entry.entryKind === "message" ? entry.sequenceNumber ?? 0 : 0);
|
|
105
109
|
return [...messages, ...references].sort((left, right) => right.createdAt.localeCompare(left.createdAt) || sequenceOf(right) - sequenceOf(left));
|
|
106
110
|
}
|
|
111
|
+
|
|
112
|
+
const ROLE_ORDER = { based_on: 0, cited: 1, consulted: 2 } as const;
|
|
113
|
+
|
|
114
|
+
/** The References of a story, the ones it is based on first. */
|
|
115
|
+
export function referencesByRole(references: readonly TrailReference[]): TrailReference[] {
|
|
116
|
+
return [...references].sort((left, right) => ROLE_ORDER[left.role] - ROLE_ORDER[right.role]);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function referenceRoleLabel(role: TrailReference["role"]): string {
|
|
120
|
+
return role === "based_on" ? "Based on" : role === "cited" ? "Cited" : "Consulted";
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** A story that a Reference supports, as the Reference view lists it. */
|
|
124
|
+
export type ReferenceStory = {
|
|
125
|
+
relationId: string;
|
|
126
|
+
itemId: string;
|
|
127
|
+
lineageId: string;
|
|
128
|
+
title: string | null;
|
|
129
|
+
stage: string | null;
|
|
130
|
+
role: TrailReference["role"];
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export function siteNameOf(sourceUri: string | null | undefined, preferred?: string | null): string | null {
|
|
134
|
+
if (preferred) return preferred;
|
|
135
|
+
if (!sourceUri) return null;
|
|
136
|
+
try { return new URL(sourceUri).hostname.replace(/^www\./, ""); } catch { return null; }
|
|
137
|
+
}
|
package/package.json
CHANGED
|
@@ -75,6 +75,7 @@
|
|
|
75
75
|
"./components/archive-grid": "./components/archive-grid.tsx",
|
|
76
76
|
"./components/archive-shell": "./components/archive-shell.tsx",
|
|
77
77
|
"./components/article-video": "./components/article-video.tsx",
|
|
78
|
+
"./components/auto-accepted-badge": "./components/auto-accepted-badge.tsx",
|
|
78
79
|
"./components/automation-panel": "./components/automation-panel.tsx",
|
|
79
80
|
"./components/automation-run-now": "./components/automation-run-now.tsx",
|
|
80
81
|
"./components/automation-view": "./components/automation-view.tsx",
|
|
@@ -108,6 +109,7 @@
|
|
|
108
109
|
"./components/newsroom-articles-view": "./components/newsroom-articles-view.tsx",
|
|
109
110
|
"./components/newsroom-assignment-detail": "./components/newsroom-assignment-detail.tsx",
|
|
110
111
|
"./components/newsroom-assignments-view": "./components/newsroom-assignments-view.tsx",
|
|
112
|
+
"./components/newsroom-board-skeleton": "./components/newsroom-board-skeleton.tsx",
|
|
111
113
|
"./components/newsroom-board-view": "./components/newsroom-board-view.tsx",
|
|
112
114
|
"./components/newsroom-client-shell": "./components/newsroom-client-shell.tsx",
|
|
113
115
|
"./components/newsroom-curation-bootstrap": "./components/newsroom-curation-bootstrap.tsx",
|
|
@@ -155,6 +157,7 @@
|
|
|
155
157
|
"./components/reference-review-card": "./components/reference-review-card.tsx",
|
|
156
158
|
"./components/reference-section-suggestion": "./components/reference-section-suggestion.tsx",
|
|
157
159
|
"./components/reference-source-preview": "./components/reference-source-preview.tsx",
|
|
160
|
+
"./components/reference-stories": "./components/reference-stories.tsx",
|
|
158
161
|
"./components/references-page-loading": "./components/references-page-loading.tsx",
|
|
159
162
|
"./components/references-prefetch": "./components/references-prefetch.ts",
|
|
160
163
|
"./components/review-rating-grid": "./components/review-rating-grid.tsx",
|
|
@@ -501,7 +504,7 @@
|
|
|
501
504
|
"optional": true
|
|
502
505
|
}
|
|
503
506
|
},
|
|
504
|
-
"version": "5.
|
|
507
|
+
"version": "5.55.0",
|
|
505
508
|
"dependencies": {
|
|
506
509
|
"@aws-amplify/adapter-nextjs": "^1.7.3",
|
|
507
510
|
"@aws-crypto/sha256-js": "5.2.0",
|
package/routes.manifest.json
CHANGED