@anthusai/papyrus 5.44.0 → 5.45.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/functions/model-attachment-upload/handler.ts +1 -0
- package/amplify/functions/monitoring-view/schema.js +8 -0
- package/amplify/functions/monitoring-view/schema.ts +8 -0
- package/components/monitoring-overview-view.tsx +52 -6
- package/components/monitoring-panel.tsx +61 -3
- package/components/news-desk-client-provider.tsx +16 -1
- package/components/news-desk-page.tsx +2 -1
- package/components/newsroom-access-gate.tsx +7 -5
- package/components/newsroom-client-shell.tsx +2 -1
- package/components/newsroom-ops-shell.tsx +1 -1
- package/components/newsroom-organizer.css +48 -0
- package/components/newsroom-references-view.tsx +181 -20
- package/components/reference-feed.tsx +469 -0
- package/components/reference-review-card.tsx +12 -15
- package/components/references-page-loading.tsx +61 -0
- package/components/references-prefetch.ts +47 -0
- package/components/review-rating-grid.tsx +164 -0
- package/components/topic-steering-workspace.tsx +32 -8
- package/lib/category-repository.ts +4 -1
- package/lib/local-preview-feed-fixture.ts +43 -0
- package/lib/local-preview-synthetic-rejects.ts +41 -0
- package/lib/monitoring-client.ts +16 -2
- package/lib/monitoring-overview.ts +20 -1
- package/lib/monitoring-paging.ts +58 -0
- package/lib/reference-feed-cache.ts +133 -0
- package/lib/reference-feed-ranking.ts +191 -0
- package/lib/reference-preview.ts +112 -0
- package/package.json +11 -1
- package/routes.manifest.json +1 -1
|
@@ -4,7 +4,7 @@ import type { PrimingPanelProps } from "cyclotron/components/priming-panel";
|
|
|
4
4
|
import { ReferenceAddEntry } from "./reference-add-entry";
|
|
5
5
|
import { ReferenceCurationControls, useReferenceImport } from "./reference-curation-controls";
|
|
6
6
|
|
|
7
|
-
import { ExternalLinkIcon } from "lucide-react";
|
|
7
|
+
import { ExternalLinkIcon, LayoutGridIcon, ListIcon, PlusIcon } from "lucide-react";
|
|
8
8
|
import { usePathname } from "next/navigation";
|
|
9
9
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
10
10
|
import { Badge } from "./ui/badge";
|
|
@@ -42,6 +42,14 @@ import {
|
|
|
42
42
|
import { normalizeReferenceCurationStatus, referenceReasonCode, type ReferenceRejectionReasonCode } from "../lib/reference-policy";
|
|
43
43
|
import { newsroomListRowClassName } from "../lib/newsroom-list-selection";
|
|
44
44
|
import { ReferenceSourcePreview } from "./reference-source-preview";
|
|
45
|
+
import { FEED_SURFACE_PROPS, FEED_SURFACE_STYLE, ReferenceFeed } from "./reference-feed";
|
|
46
|
+
import { partitionReferenceFeed, type ReferenceFeedTabKey } from "../lib/reference-feed-ranking";
|
|
47
|
+
import { referencePreviewsByLineage, type ReferencePreview } from "../lib/reference-preview";
|
|
48
|
+
import { isLocalPreviewEnabled } from "../lib/local-preview";
|
|
49
|
+
import { chooseSyntheticRejects, withSyntheticRejects } from "../lib/local-preview-synthetic-rejects";
|
|
50
|
+
import { cn } from "../lib/utils";
|
|
51
|
+
import { getKanbusTheme, SITE_BRAND } from "../lib/site-brand";
|
|
52
|
+
import { useResolvedPapyrusTheme } from "./use-resolved-papyrus-theme";
|
|
45
53
|
import {
|
|
46
54
|
ReviewCard,
|
|
47
55
|
predictionFromDecision,
|
|
@@ -120,8 +128,14 @@ type NewsroomReferencesViewProps = {
|
|
|
120
128
|
onReview: (reference: ReferenceRecord, action: ReferenceCurationAction, options?: ReferenceReviewOptions) => Promise<void> | void;
|
|
121
129
|
referenceAttachments?: ReferenceAttachmentRecord[];
|
|
122
130
|
references: ReferenceRecord[];
|
|
131
|
+
/** True while the References are still being read: the feed shows card skeletons instead of an empty page. */
|
|
132
|
+
loading?: boolean;
|
|
133
|
+
/** The last view this browser saved. Shown, read only, until the live read arrives. */
|
|
134
|
+
savedCopy?: { references: ReferenceRecord[]; previews: Array<[string, ReferencePreview]>; decisions: Array<[string, RelevanceDecisionView]> } | null;
|
|
123
135
|
};
|
|
124
136
|
|
|
137
|
+
type ReferenceView = "feed" | "list";
|
|
138
|
+
|
|
125
139
|
type ReferenceListFilter = ReferenceStatusFilter | "needs_review";
|
|
126
140
|
|
|
127
141
|
const NEEDS_REVIEW_FILTER: { key: ReferenceListFilter; label: string } = { key: "needs_review", label: "Needs review" };
|
|
@@ -442,7 +456,7 @@ export function NewsroomReferencesView({
|
|
|
442
456
|
priming,
|
|
443
457
|
cyclotronStatus,
|
|
444
458
|
cyclotronPriming,
|
|
445
|
-
decisions,
|
|
459
|
+
decisions: liveDecisions,
|
|
446
460
|
humanCuratedReferenceLineageIds,
|
|
447
461
|
delegatedCurations,
|
|
448
462
|
shadowAgreement = null,
|
|
@@ -461,9 +475,14 @@ export function NewsroomReferencesView({
|
|
|
461
475
|
initialReferenceLineageId = null,
|
|
462
476
|
onReview,
|
|
463
477
|
referenceAttachments = [],
|
|
464
|
-
references,
|
|
478
|
+
references: liveReferences,
|
|
479
|
+
loading = false,
|
|
480
|
+
savedCopy = null,
|
|
465
481
|
}: NewsroomReferencesViewProps) {
|
|
466
482
|
const pathname = usePathname();
|
|
483
|
+
// The portaled mobile sheet sits outside the newsroom root, so it carries the theme attributes itself (same as useNewsroomThemeAttributes).
|
|
484
|
+
const themeMode = useResolvedPapyrusTheme();
|
|
485
|
+
const themeAttributes = { "data-kanbus-theme": getKanbusTheme(SITE_BRAND), "data-color-mode": themeMode };
|
|
467
486
|
const sessionImport = useReferenceImport(disabled);
|
|
468
487
|
const [importedCount, setImportedCount] = useState(0);
|
|
469
488
|
const isMobileDetail = useMediaQuery("(max-width: 767px)");
|
|
@@ -473,7 +492,66 @@ export function NewsroomReferencesView({
|
|
|
473
492
|
);
|
|
474
493
|
const routeLineageId = pathname ? pathnameLineageId ?? "" : initialReferenceLineageId ?? "";
|
|
475
494
|
|
|
495
|
+
// Until the live read arrives, a saved copy of the last view fills the page. It is display only.
|
|
496
|
+
const showingSavedCopy = loading && !liveReferences.length && Boolean(savedCopy?.references.length);
|
|
497
|
+
const references = showingSavedCopy ? savedCopy!.references : liveReferences;
|
|
498
|
+
const baseDecisions = useMemo(
|
|
499
|
+
() => (showingSavedCopy && !liveDecisions?.size ? new Map(savedCopy!.decisions) : liveDecisions),
|
|
500
|
+
[liveDecisions, savedCopy, showingSavedCopy],
|
|
501
|
+
);
|
|
502
|
+
// Preview only: ?feedSynthRejects=N makes the N weakest Accept predictions into made-up Rejects (the page says so).
|
|
503
|
+
const synthRejectCount = useMemo(() => {
|
|
504
|
+
if (typeof window === "undefined" || !isLocalPreviewEnabled()) return 0;
|
|
505
|
+
const requested = Number(new URLSearchParams(window.location.search).get("feedSynthRejects"));
|
|
506
|
+
return Number.isInteger(requested) && requested > 0 ? requested : 0;
|
|
507
|
+
}, []);
|
|
508
|
+
const syntheticChoice = useRef<string[] | null>(null);
|
|
509
|
+
if (synthRejectCount && syntheticChoice.current === null && baseDecisions?.size && references.length) {
|
|
510
|
+
syntheticChoice.current = chooseSyntheticRejects(
|
|
511
|
+
baseDecisions,
|
|
512
|
+
new Set(references.filter((reference) => normalizeReferenceCurationStatus(reference.curationStatus) === "pending").map(referenceLineageId)),
|
|
513
|
+
synthRejectCount,
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
const synthetic = useMemo(
|
|
517
|
+
() => (synthRejectCount && syntheticChoice.current ? withSyntheticRejects(baseDecisions, syntheticChoice.current) : null),
|
|
518
|
+
[baseDecisions, synthRejectCount],
|
|
519
|
+
);
|
|
520
|
+
const decisions = synthetic?.decisions ?? baseDecisions;
|
|
521
|
+
const previews = useMemo(() => {
|
|
522
|
+
const live = referencePreviewsByLineage(referenceAttachments);
|
|
523
|
+
return showingSavedCopy && !live.size ? new Map(savedCopy!.previews) : live;
|
|
524
|
+
}, [referenceAttachments, savedCopy, showingSavedCopy]);
|
|
525
|
+
const [view, setView] = useState<ReferenceView>("feed");
|
|
526
|
+
const [addOpen, setAddOpen] = useState(false);
|
|
527
|
+
const [feedTab, setFeedTab] = useState<ReferenceFeedTabKey>("feed");
|
|
528
|
+
const [visitRated, setVisitRated] = useState<ReadonlySet<string>>(() => new Set());
|
|
529
|
+
const [mountedAt] = useState(() => Date.now());
|
|
530
|
+
const markVisitRated = useCallback((lineageId: string) => {
|
|
531
|
+
setVisitRated((current) => (current.has(lineageId) ? current : new Set(current).add(lineageId)));
|
|
532
|
+
}, []);
|
|
533
|
+
// Preview only: ?feedTau=<days> stands in for the learned freshness period, to review the Earlier section on a young corpus.
|
|
534
|
+
const tauDaysOverride = useMemo(() => {
|
|
535
|
+
if (typeof window === "undefined" || !isLocalPreviewEnabled()) return null;
|
|
536
|
+
const requested = Number(new URLSearchParams(window.location.search).get("feedTau"));
|
|
537
|
+
return Number.isFinite(requested) && requested > 0 ? requested : null;
|
|
538
|
+
}, []);
|
|
539
|
+
|
|
540
|
+
// Preview only: ?feedState=loading holds the page in its loading state, to review the card skeletons.
|
|
541
|
+
const previewSkeleton = useMemo(
|
|
542
|
+
() => typeof window !== "undefined" && isLocalPreviewEnabled() && new URLSearchParams(window.location.search).get("feedState") === "loading",
|
|
543
|
+
[],
|
|
544
|
+
);
|
|
545
|
+
|
|
476
546
|
const canonicalReferences = useMemo(() => selectCanonicalReferenceRecords(references), [references]);
|
|
547
|
+
const partition = useMemo(
|
|
548
|
+
() => partitionReferenceFeed(canonicalReferences, decisions, { now: mountedAt, visitRated, tauDaysOverride }),
|
|
549
|
+
[canonicalReferences, decisions, mountedAt, tauDaysOverride, visitRated],
|
|
550
|
+
);
|
|
551
|
+
const feedOrderIds = useMemo(
|
|
552
|
+
() => (feedTab === "feed" ? [...partition.feed, ...partition.earlier] : feedTab === "rated" ? partition.rated : partition.earlier).map((item) => item.lineageId),
|
|
553
|
+
[feedTab, partition],
|
|
554
|
+
);
|
|
477
555
|
const statusCounts = useMemo(() => countReferencesByStatus(canonicalReferences), [canonicalReferences]);
|
|
478
556
|
|
|
479
557
|
const [statusFilter, setStatusFilter] = useState<ReferenceListFilter>(routeLineageId ? "all" : "pending");
|
|
@@ -504,14 +582,14 @@ export function NewsroomReferencesView({
|
|
|
504
582
|
// list, but keeps its place in the queue until they move on, so "3 of 29" does not jump under them.
|
|
505
583
|
const queueRef = useRef<string[]>([]);
|
|
506
584
|
const queue = useMemo(() => {
|
|
507
|
-
const ids = filteredReferences.map(referenceLineageId);
|
|
585
|
+
const ids = view === "feed" ? [...feedOrderIds] : filteredReferences.map(referenceLineageId);
|
|
508
586
|
const open = selectedReference ? referenceLineageId(selectedReference) : "";
|
|
509
587
|
if (open && !ids.includes(open)) {
|
|
510
588
|
const before = queueRef.current.indexOf(open);
|
|
511
589
|
if (before >= 0) ids.splice(Math.min(before, ids.length), 0, open);
|
|
512
590
|
}
|
|
513
591
|
return ids;
|
|
514
|
-
}, [filteredReferences, selectedReference]);
|
|
592
|
+
}, [feedOrderIds, filteredReferences, selectedReference, view]);
|
|
515
593
|
useEffect(() => { queueRef.current = queue; }, [queue]);
|
|
516
594
|
const [caughtUp, setCaughtUp] = useState(false);
|
|
517
595
|
const selectedIndex = selectedReference ? queue.indexOf(referenceLineageId(selectedReference)) : -1;
|
|
@@ -582,9 +660,21 @@ export function NewsroomReferencesView({
|
|
|
582
660
|
|
|
583
661
|
const runReview = (action: ReferenceCurationAction, options?: ReferenceReviewOptions) => {
|
|
584
662
|
if (!selectedReference) return;
|
|
663
|
+
if (action === "accept" || action === "reject") markVisitRated(referenceLineageId(selectedReference));
|
|
585
664
|
return onReview(selectedReference, action, options);
|
|
586
665
|
};
|
|
587
666
|
|
|
667
|
+
const rateFromFeed = async (reference: ReferenceRecord, choice: ReviewChoice, reasonCode: ReferenceRejectionReasonCode | null) => {
|
|
668
|
+
const decision = decisions?.get(referenceLineageId(reference));
|
|
669
|
+
await onReview(reference, choice, {
|
|
670
|
+
reasonCode: choice === "reject" ? reasonCode : null,
|
|
671
|
+
...(decision ? { decisionRelationId: decision.decisionRelationId, shareable: false } : {}),
|
|
672
|
+
});
|
|
673
|
+
};
|
|
674
|
+
const feedDisabledReason = disabled
|
|
675
|
+
? "Editor access is required to change this reference."
|
|
676
|
+
: showingSavedCopy ? "Confirming your sign-in. You can answer in a moment." : null;
|
|
677
|
+
|
|
588
678
|
const caughtUpPanel = (
|
|
589
679
|
<div className="space-y-2 p-6 text-center" data-review-caught-up>
|
|
590
680
|
<h3 className="text-lg font-semibold">All caught up</h3>
|
|
@@ -606,23 +696,64 @@ export function NewsroomReferencesView({
|
|
|
606
696
|
data-detail-open={selectedReference || routeLineageId ? "true" : "false"}
|
|
607
697
|
>
|
|
608
698
|
<div className="flex min-h-0 min-w-0 flex-1 flex-col gap-4 md:overflow-hidden">
|
|
609
|
-
<div className="shrink-0
|
|
610
|
-
<
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
699
|
+
<div className="flex shrink-0 items-center justify-between gap-3" {...FEED_SURFACE_PROPS}>
|
|
700
|
+
<div className="min-w-0 space-y-0.5">
|
|
701
|
+
<h2 className="text-2xl font-bold tracking-tight">References</h2>
|
|
702
|
+
<p className="hidden text-sm text-muted-foreground sm:block">
|
|
703
|
+
{view === "feed" ? "A feed ranked by what you are most likely to accept." : "Review source intake before it becomes accepted evidence."}
|
|
704
|
+
</p>
|
|
705
|
+
</div>
|
|
706
|
+
<div className="flex items-center gap-2">
|
|
707
|
+
<button
|
|
708
|
+
aria-controls="reference-add-panel"
|
|
709
|
+
aria-expanded={addOpen}
|
|
710
|
+
aria-label="Add a reference and prediction activity"
|
|
711
|
+
className={cn("inline-flex min-h-10 items-center justify-center gap-1.5 rounded-full px-3 text-sm font-medium outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring", addOpen ? "bg-[var(--feed-control)] text-foreground" : "bg-[var(--feed-card)] text-muted-foreground hover:text-foreground")}
|
|
712
|
+
data-reference-add-toggle
|
|
713
|
+
onClick={() => setAddOpen((open) => !open)}
|
|
714
|
+
title="Add a reference and prediction activity"
|
|
715
|
+
type="button"
|
|
716
|
+
>
|
|
717
|
+
<PlusIcon aria-hidden="true" className="size-4" />
|
|
718
|
+
<span className="hidden sm:inline">Add</span>
|
|
719
|
+
</button>
|
|
720
|
+
<div aria-label="Reference view" className="inline-flex rounded-full bg-[var(--feed-card)] p-1" role="group">
|
|
721
|
+
{([["feed", "Feed", LayoutGridIcon], ["list", "List", ListIcon]] as const).map(([key, label, Icon]) => (
|
|
722
|
+
<button
|
|
723
|
+
aria-label={label}
|
|
724
|
+
aria-pressed={view === key}
|
|
725
|
+
className={cn(
|
|
726
|
+
"inline-flex min-h-8 items-center justify-center gap-1.5 rounded-full px-3 text-sm font-medium outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring",
|
|
727
|
+
view === key ? "bg-[var(--feed-control)] text-foreground" : "text-muted-foreground hover:text-foreground",
|
|
728
|
+
)}
|
|
729
|
+
data-reference-view={key}
|
|
730
|
+
key={key}
|
|
731
|
+
onClick={() => { setView(key); setCaughtUp(false); }}
|
|
732
|
+
title={label}
|
|
733
|
+
type="button"
|
|
734
|
+
>
|
|
735
|
+
<Icon aria-hidden="true" className="size-4" />
|
|
736
|
+
<span className="hidden sm:inline">{label}</span>
|
|
737
|
+
</button>
|
|
738
|
+
))}
|
|
739
|
+
</div>
|
|
740
|
+
</div>
|
|
614
741
|
</div>
|
|
615
|
-
{
|
|
742
|
+
{synthetic?.changed ? (
|
|
743
|
+
<p className="rounded-2xl bg-[var(--feed-card)] p-3 text-sm" data-preview-synthetic role="status" {...FEED_SURFACE_PROPS}>
|
|
744
|
+
Preview only: {synthetic.changed} of these predictions are made up to show the Reject end of the feed. Real data has none yet.
|
|
745
|
+
</p>
|
|
746
|
+
) : null}
|
|
747
|
+
{predictionState === "loading" && view === "list" ? <p role="status">Loading predictions…</p> : null}
|
|
616
748
|
{predictionState === "error" ? (
|
|
617
|
-
<div role="alert" className="space-y-2 rounded-
|
|
749
|
+
<div role="alert" className="space-y-2 rounded-2xl bg-[var(--feed-card)] p-4" {...FEED_SURFACE_PROPS}>
|
|
618
750
|
<p>Predictions unavailable. {predictionError}</p>
|
|
619
751
|
<p className="text-sm">You can still review references. Existing suggestions may be out of date.</p>
|
|
620
752
|
{onRetryPredictions ? <Button onClick={onRetryPredictions} size="sm" type="button" variant="outline">Retry predictions</Button> : null}
|
|
621
753
|
</div>
|
|
622
754
|
) : null}
|
|
623
|
-
<
|
|
624
|
-
<
|
|
625
|
-
<div className="mt-3 space-y-4">
|
|
755
|
+
<div className="shrink-0 overflow-y-auto rounded-[20px] bg-[var(--feed-card)] p-4 text-sm" data-feed-flat="" data-feed-surface="" hidden={!addOpen} id="reference-add-panel" style={{ ...FEED_SURFACE_STYLE, maxHeight: "45dvh", overflowY: "auto" }}>
|
|
756
|
+
<div className="space-y-4">
|
|
626
757
|
<ReferenceLiveAgreement agreement={shadowAgreement} />
|
|
627
758
|
<ReferenceAddEntry
|
|
628
759
|
importOptions={disabled ? null : priming ?? sessionImport?.panel}
|
|
@@ -639,8 +770,31 @@ export function NewsroomReferencesView({
|
|
|
639
770
|
</div>
|
|
640
771
|
) : null}
|
|
641
772
|
</div>
|
|
642
|
-
</
|
|
773
|
+
</div>
|
|
643
774
|
|
|
775
|
+
{view === "feed" ? (
|
|
776
|
+
<ScrollArea className="min-h-0 flex-1" data-reference-feed-scroll>
|
|
777
|
+
<div className="pb-6 pr-1">
|
|
778
|
+
<ReferenceFeed
|
|
779
|
+
disabledReason={feedDisabledReason}
|
|
780
|
+
loading={previewSkeleton || (loading && !references.length)}
|
|
781
|
+
now={mountedAt}
|
|
782
|
+
onOpen={selectReference}
|
|
783
|
+
onRate={rateFromFeed}
|
|
784
|
+
onTabChange={(next) => { setFeedTab(next); setCaughtUp(false); }}
|
|
785
|
+
onVisitRated={markVisitRated}
|
|
786
|
+
partition={partition}
|
|
787
|
+
predictionLoading={predictionState === "loading"}
|
|
788
|
+
previews={previews}
|
|
789
|
+
selectedLineageId={selectedLineageId}
|
|
790
|
+
stale={showingSavedCopy || (loading && references.length > 0)}
|
|
791
|
+
tab={feedTab}
|
|
792
|
+
visitRated={visitRated}
|
|
793
|
+
/>
|
|
794
|
+
</div>
|
|
795
|
+
</ScrollArea>
|
|
796
|
+
) : (
|
|
797
|
+
<>
|
|
644
798
|
<Tabs
|
|
645
799
|
className="shrink-0"
|
|
646
800
|
defaultValue="pending"
|
|
@@ -716,15 +870,22 @@ export function NewsroomReferencesView({
|
|
|
716
870
|
)}
|
|
717
871
|
</div>
|
|
718
872
|
</ScrollArea>
|
|
873
|
+
</>
|
|
874
|
+
)}
|
|
719
875
|
</div>
|
|
720
876
|
|
|
721
877
|
<div
|
|
722
|
-
className=
|
|
878
|
+
className={cn(
|
|
879
|
+
"hidden min-h-0 w-full shrink-0 flex-col overflow-hidden rounded-[20px] bg-[var(--feed-card)] md:flex md:w-[min(100%,24rem)] lg:w-[min(100%,28rem)]",
|
|
880
|
+
view === "feed" && !selectedReference && "md:hidden",
|
|
881
|
+
)}
|
|
723
882
|
data-newsroom-list-detail-shell
|
|
724
883
|
data-news-desk-section="references"
|
|
725
884
|
data-detail-open={selectedReference ? "true" : "false"}
|
|
885
|
+
data-feed-flat=""
|
|
886
|
+
{...FEED_SURFACE_PROPS}
|
|
726
887
|
>
|
|
727
|
-
<div className="flex shrink-0 items-center justify-between gap-2
|
|
888
|
+
<div className="flex shrink-0 items-center justify-between gap-2 px-4 pt-3 pb-1">
|
|
728
889
|
<span className="text-sm font-medium text-foreground">Detail</span>
|
|
729
890
|
</div>
|
|
730
891
|
<div className="flex min-h-0 flex-1 flex-col" data-review-gestures onPointerDown={gestures.onPointerDown} onPointerUp={gestures.onPointerUp} style={gestures.style}>
|
|
@@ -766,12 +927,12 @@ export function NewsroomReferencesView({
|
|
|
766
927
|
}}
|
|
767
928
|
open={(mobileDetailOpen && Boolean(selectedReference)) || caughtUp}
|
|
768
929
|
>
|
|
769
|
-
<SheetContent className="gap-0 p-0" side="bottom">
|
|
930
|
+
<SheetContent className="gap-0 rounded-t-[24px] bg-[var(--feed-card)] p-0" data-feed-flat="" data-feed-surface="" side="bottom" {...themeAttributes}>
|
|
770
931
|
<SheetHeader className="border-b border-border px-4 py-3">
|
|
771
932
|
<SheetTitle className="text-left text-base">Reference</SheetTitle>
|
|
772
933
|
</SheetHeader>
|
|
773
934
|
{selectedReference ? (
|
|
774
|
-
<div data-review-gestures onPointerDown={gestures.onPointerDown} onPointerUp={gestures.onPointerUp} style={gestures.style}><ScrollArea className="max-h-[70dvh]">
|
|
935
|
+
<div className="min-w-0" data-review-gestures onPointerDown={gestures.onPointerDown} onPointerUp={gestures.onPointerUp} style={gestures.style}><ScrollArea className="max-h-[70dvh]">
|
|
775
936
|
<ReferenceDetailPanel
|
|
776
937
|
key={referenceLineageId(selectedReference)}
|
|
777
938
|
predictionState={predictionState}
|