@anthusai/papyrus 1.10.0 → 1.11.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.
Files changed (61) hide show
  1. package/amplify/data/resource.js +3 -3
  2. package/amplify/data/resource.ts +3 -3
  3. package/amplify/functions/category-action/handler.ts +18 -18
  4. package/amplify/functions/console-chat-responder/src/main.rs +1 -1
  5. package/amplify/functions/content-actions/handler.py +23 -0
  6. package/amplify/functions/content-actions/requirements.txt +1 -1
  7. package/amplify/functions/content-actions/resource.js +1 -1
  8. package/amplify/functions/content-actions/resource.ts +1 -1
  9. package/amplify/functions/renditions/requirements.txt +1 -1
  10. package/amplify/functions/shared/slack-events.ts +2 -2
  11. package/amplify/publication/renditions-function.js +1 -1
  12. package/amplify/publication/renditions-function.ts +1 -1
  13. package/app/api/local-preview/derive/route.ts +28 -0
  14. package/app/api/local-preview/media/route.ts +25 -0
  15. package/app/globals.css +28 -28
  16. package/app/layout.tsx +8 -5
  17. package/app/newsroom/[section]/[...selection]/page.tsx +0 -1
  18. package/app/newsroom/[section]/page.tsx +0 -1
  19. package/app/newsroom/insights/[threadId]/page.tsx +5 -16
  20. package/app/newsroom/insights/page.tsx +4 -12
  21. package/components/board-card-excerpts.tsx +45 -0
  22. package/components/markus-ir-preview.tsx +47 -2
  23. package/components/news-desk-page.tsx +0 -1
  24. package/components/news-desk-taxonomy-client.ts +25 -220
  25. package/components/newsroom-article-editor.tsx +135 -39
  26. package/components/newsroom-assignment-detail.tsx +35 -1
  27. package/components/newsroom-board-view.tsx +29 -7
  28. package/components/newsroom-newsprint-board.css +9 -2
  29. package/components/newsroom-ops-overview.tsx +27 -5
  30. package/components/newsroom-organizer-shell.tsx +25 -7
  31. package/components/newsroom-organizer.css +196 -4
  32. package/components/priming-proof-page.tsx +17 -8
  33. package/components/prospectus-account-chooser.tsx +103 -0
  34. package/components/story-reporting-trail-client.ts +91 -0
  35. package/components/story-reporting-trail.tsx +115 -0
  36. package/components/topic-steering-workspace.tsx +154 -671
  37. package/components/ui/sheet.tsx +1 -0
  38. package/components/wiki-concepts-view.tsx +153 -0
  39. package/components/wiki-index-client.ts +51 -0
  40. package/lib/content-actions-client.ts +3 -0
  41. package/lib/content-actions-demo.ts +55 -3
  42. package/lib/content-item-lifecycle.ts +4 -2
  43. package/lib/front-matter-fields.ts +42 -0
  44. package/lib/local-preview-seed.ts +33 -0
  45. package/lib/newsroom-articles.ts +2 -0
  46. package/lib/newsroom-board.ts +4 -0
  47. package/lib/newsroom-forum-routes.ts +2 -43
  48. package/lib/newsroom-index-filters.ts +3 -40
  49. package/lib/newsroom-media-backend.ts +5 -0
  50. package/lib/newsroom-nav.ts +1 -3
  51. package/lib/newsroom-organizer-nav.ts +5 -12
  52. package/lib/newsroom-section-routing.ts +1 -1
  53. package/lib/newsroom-sections.ts +12 -0
  54. package/lib/papyrus-web-locations.ts +3 -24
  55. package/lib/priming-proof-report.ts +1 -0
  56. package/lib/semantic-graph.ts +5 -5
  57. package/lib/site-brand.ts +9 -0
  58. package/lib/story-reporting-trail.ts +106 -0
  59. package/lib/wiki.ts +56 -0
  60. package/package.json +12 -1
  61. package/routes.manifest.json +23 -7
@@ -0,0 +1,115 @@
1
+ "use client";
2
+
3
+ import Link from "next/link";
4
+ import { useEffect, useState } from "react";
5
+ import { getNewsroomNavHref } from "../lib/newsroom-nav";
6
+ import { storyMessageFeed, storyReportingTrailIsEmpty, type StoryReportingTrail, type TrailReference } from "../lib/story-reporting-trail";
7
+ import { loadStoryReportingTrail } from "./story-reporting-trail-client";
8
+
9
+ const timestampFormat = new Intl.DateTimeFormat(undefined, { dateStyle: "medium", timeStyle: "short" });
10
+
11
+ function formatTimestamp(value: string | null | undefined): string {
12
+ if (!value) return "";
13
+ const parsed = new Date(value);
14
+ return Number.isNaN(parsed.getTime()) ? value : timestampFormat.format(parsed);
15
+ }
16
+
17
+ function ReferenceSummary({ citedKeys, demo, focused, reference }: { citedKeys: readonly string[]; demo: boolean; focused: boolean; reference: TrailReference }) {
18
+ return <>
19
+ <div className="story-trail-row">
20
+ <strong>{reference.title ?? reference.referenceId}</strong>
21
+ <span className="story-trail-chip" data-story-trail-reference-role={reference.role}>{reference.role === "cited" ? "Cited" : "Consulted"}</span>
22
+ {reference.citationKey && citedKeys.includes(reference.citationKey) && <span className="story-trail-chip">In the text</span>}
23
+ </div>
24
+ <div className="story-trail-meta">{[reference.addedBy && `added by ${reference.addedBy}`, formatTimestamp(reference.addedAt), reference.citationKey && `citation key ${reference.citationKey}`].filter(Boolean).join(" · ")}</div>
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
+ </>;
29
+ }
30
+
31
+ function TrailSection({ count, children, title }: { count: number; children: React.ReactNode; title: string }) {
32
+ return <section className="story-trail-section" data-story-trail-section={title.toLowerCase()}>
33
+ <h3>{title} <small>{count}</small></h3>
34
+ {count ? children : <p className="story-trail-empty">None recorded.</p>}
35
+ </section>;
36
+ }
37
+
38
+ /**
39
+ * The messages and references of one story: draft versions, the assignments and agent events that did the work,
40
+ * notes and packets, the references behind it, and attached files. Container queries lay the sections out
41
+ * in one column in a narrow Board panel and side by side on a wide page.
42
+ */
43
+ export function StoryReportingTrailView({ citedKeys = [], demo = false, focusedReferenceId = null, itemId, lineageId, onOpenVersion, onShowReference, view = "messages" }: { citedKeys?: readonly string[]; demo?: boolean; focusedReferenceId?: string | null; itemId: string; lineageId: string | null | undefined; onOpenVersion?: (versionId: string) => void; onShowReference?: (referenceId: string) => void; view?: "messages" | "references" }) {
44
+ const [trail, setTrail] = useState<StoryReportingTrail | null>(null);
45
+ const [error, setError] = useState<string | null>(null);
46
+ useEffect(() => {
47
+ let cancelled = false;
48
+ setTrail(null); setError(null);
49
+ loadStoryReportingTrail(lineageId || itemId, itemId)
50
+ .then(loaded => { if (!cancelled) setTrail(loaded); })
51
+ .catch((reason: unknown) => { if (!cancelled) setError(reason instanceof Error ? reason.message : "Could not load the reporting trail."); });
52
+ return () => { cancelled = true; };
53
+ }, [itemId, lineageId]);
54
+ if (error) return <p role="alert" className="story-trail-empty">{error}</p>;
55
+ if (!trail) return <p className="story-trail-empty">Loading the reporting trail…</p>;
56
+ const messageFeed = storyMessageFeed(trail);
57
+ if (view === "references") return <div className="story-trail" data-story-trail data-story-trail-view="references">
58
+ <TrailSection count={trail.references.length} title="References">
59
+ <ul className="story-trail-list" data-story-references-list>
60
+ {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
+ <ReferenceSummary citedKeys={citedKeys} demo={demo} focused={reference.referenceId === focusedReferenceId} reference={reference} />
62
+ </li>)}
63
+ </ul>
64
+ </TrailSection>
65
+ </div>;
66
+ return <div className="story-trail" data-story-trail data-story-trail-view="messages">
67
+ {storyReportingTrailIsEmpty(trail) && <p className="story-trail-empty">No messages have been recorded for this story yet. Notes and packets, the work done on it, its draft versions and the references behind it appear here as agents and editors work on it.</p>}
68
+ <TrailSection count={messageFeed.length} title="Messages">
69
+ <ol className="story-trail-list" data-story-trail-feed>
70
+ {messageFeed.map(entry => entry.entryKind === "reference"
71
+ ? <li data-story-trail-feed-reference={entry.reference.referenceId} key={entry.id}>
72
+ <div className="story-trail-row"><span className="story-trail-chip">Reference found</span><span className="story-trail-meta">{formatTimestamp(entry.createdAt)}</span></div>
73
+ <ReferenceSummary citedKeys={citedKeys} demo={demo} focused={false} reference={entry.reference} />
74
+ {onShowReference && <button className="newsroom-article-link-button" data-story-trail-show-reference={entry.reference.referenceId} onClick={() => onShowReference(entry.reference.referenceId)} type="button">Show in the References list</button>}
75
+ </li>
76
+ : <li key={entry.id}>
77
+ <div className="story-trail-row"><strong>{entry.authorLabel ?? "Unknown author"}</strong>{entry.messageKind && <span className="story-trail-chip">{entry.messageKind.replace(/_/g, " ")}</span>}<span className="story-trail-meta">{formatTimestamp(entry.createdAt)}</span></div>
78
+ {entry.summary && <p><strong>{entry.summary}</strong></p>}
79
+ {entry.content && <details className="story-trail-message" open={messageFeed.length <= 3}><summary>Show the message</summary><pre>{entry.content}</pre></details>}
80
+ </li>)}
81
+ </ol>
82
+ </TrailSection>
83
+ <TrailSection count={trail.versions.length} title="Draft versions">
84
+ <ol className="story-trail-list">
85
+ {trail.versions.map((version, versionIndex) => <li key={version.id}>
86
+ <div className="story-trail-row"><strong>Version {version.versionNumber}</strong>{versionIndex === 0 && <span className="story-trail-chip">Latest</span>}<span className="story-trail-chip">{version.versionState ?? "draft"}</span>{version.stage && <span className="story-trail-chip">{version.stage}</span>}</div>
87
+ <div className="story-trail-meta">{[formatTimestamp(version.versionCreatedAt ?? version.updatedAt), version.versionCreatedBy, version.changeReason].filter(Boolean).join(" · ")}</div>
88
+ {onOpenVersion
89
+ ? <button className="newsroom-article-link-button" data-story-trail-open-version={version.versionNumber} onClick={() => onOpenVersion(version.id)} type="button">{versionIndex === 0 ? "Open the latest version" : "Open this older version (read-only)"}</button>
90
+ : <Link href={getNewsroomNavHref(`/newsroom/articles/${encodeURIComponent(version.id)}`, demo)}>{versionIndex === 0 ? "Open the latest version" : "Open this older version (read-only)"}</Link>}
91
+ </li>)}
92
+ </ol>
93
+ </TrailSection>
94
+ <TrailSection count={trail.assignments.length} title="Work">
95
+ <ol className="story-trail-list">
96
+ {trail.assignments.map(assignment => <li key={assignment.id}>
97
+ <div className="story-trail-row"><strong>{assignment.title}</strong> <span className="story-trail-chip">{assignment.status}</span></div>
98
+ <div className="story-trail-meta">{[assignment.assignmentTypeKey, assignment.assigneeKey, formatTimestamp(assignment.createdAt)].filter(Boolean).join(" · ")}</div>
99
+ {assignment.summary && <p>{assignment.summary}</p>}
100
+ {assignment.events.length > 0 && <ol className="story-trail-events">
101
+ {assignment.events.map(event => <li key={event.id}>
102
+ <span className="story-trail-meta">{formatTimestamp(event.createdAt)}</span> {event.eventType}{event.toStatus ? ` → ${event.toStatus}` : ""}{event.actorLabel ? ` by ${event.actorLabel}` : ""}{event.note ? `: ${event.note}` : ""}
103
+ </li>)}
104
+ </ol>}
105
+ <Link href={getNewsroomNavHref(`/newsroom/assignments/${encodeURIComponent(assignment.id)}`, demo)}>Open assignment</Link>
106
+ </li>)}
107
+ </ol>
108
+ </TrailSection>
109
+ <TrailSection count={trail.attachments.length} title="Files">
110
+ <ul className="story-trail-list">
111
+ {trail.attachments.map(attachment => <li key={attachment.id}><strong>{attachment.filename ?? attachment.id}</strong> <span className="story-trail-meta">{[attachment.role, attachment.mediaType].filter(Boolean).join(" · ")}</span></li>)}
112
+ </ul>
113
+ </TrailSection>
114
+ </div>;
115
+ }