@manta-eu/react 0.1.0 → 0.1.1
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/README.md +101 -35
- package/dist/components/channel-icon/index.d.ts +1 -2
- package/dist/components/channel-icon/index.d.ts.map +1 -1
- package/dist/components/channel-icon/index.js +0 -4
- package/dist/components/plan-row/index.d.ts +2 -1
- package/dist/components/plan-row/index.d.ts.map +1 -1
- package/dist/components/plan-row/index.js +2 -14
- package/dist/globals.css +146 -90
- package/dist/graph/nodes/AdMockupCard.js +1 -1
- package/dist/graph/nodes/CreativeMediaCard.js +1 -1
- package/dist/graph/nodes/EntityCardShell.js +1 -1
- package/dist/hooks/use-conversation-starters.d.ts +1 -1
- package/dist/hooks/useValidateActionPlan.d.ts.map +1 -1
- package/dist/routes/memories/components/ChangeScopeDialog.d.ts +12 -0
- package/dist/routes/memories/components/ChangeScopeDialog.d.ts.map +1 -0
- package/dist/routes/memories/components/ChangeScopeDialog.js +89 -0
- package/dist/routes/memories/components/ConflictMemoryCard.d.ts +13 -0
- package/dist/routes/memories/components/ConflictMemoryCard.d.ts.map +1 -0
- package/dist/routes/memories/components/ConflictMemoryCard.js +43 -0
- package/dist/routes/memories/components/ConflictTimeoutFooter.d.ts +7 -0
- package/dist/routes/memories/components/ConflictTimeoutFooter.d.ts.map +1 -0
- package/dist/routes/memories/components/ConflictTimeoutFooter.js +6 -0
- package/dist/routes/memories/components/ConsolidationReviewDialog.d.ts +61 -0
- package/dist/routes/memories/components/ConsolidationReviewDialog.d.ts.map +1 -0
- package/dist/routes/memories/components/ConsolidationReviewDialog.js +121 -0
- package/dist/routes/memories/components/EmptyStates.d.ts +18 -0
- package/dist/routes/memories/components/EmptyStates.d.ts.map +1 -0
- package/dist/routes/memories/components/EmptyStates.js +22 -0
- package/dist/routes/memories/components/ImportanceSlider.d.ts +11 -0
- package/dist/routes/memories/components/ImportanceSlider.d.ts.map +1 -0
- package/dist/routes/memories/components/ImportanceSlider.js +31 -0
- package/dist/routes/memories/components/LiveContextTab.d.ts +6 -0
- package/dist/routes/memories/components/LiveContextTab.d.ts.map +1 -0
- package/dist/routes/memories/components/LiveContextTab.js +37 -0
- package/dist/routes/memories/components/LoadingSkeleton.d.ts +2 -0
- package/dist/routes/memories/components/LoadingSkeleton.d.ts.map +1 -0
- package/dist/routes/memories/components/LoadingSkeleton.js +7 -0
- package/dist/routes/memories/components/MemoriesFilters.d.ts +31 -0
- package/dist/routes/memories/components/MemoriesFilters.d.ts.map +1 -0
- package/dist/routes/memories/components/MemoriesFilters.js +88 -0
- package/dist/routes/memories/components/MemoriesPanel.d.ts +6 -0
- package/dist/routes/memories/components/MemoriesPanel.d.ts.map +1 -0
- package/dist/routes/memories/components/MemoriesPanel.js +99 -0
- package/dist/routes/memories/components/MemoriesTabPanel.d.ts +30 -0
- package/dist/routes/memories/components/MemoriesTabPanel.d.ts.map +1 -0
- package/dist/routes/memories/components/MemoriesTabPanel.js +11 -0
- package/dist/routes/memories/components/MemoryActions.d.ts +12 -0
- package/dist/routes/memories/components/MemoryActions.d.ts.map +1 -0
- package/dist/routes/memories/components/MemoryActions.js +24 -0
- package/dist/routes/memories/components/MemoryFields.d.ts +32 -0
- package/dist/routes/memories/components/MemoryFields.d.ts.map +1 -0
- package/dist/routes/memories/components/MemoryFields.js +30 -0
- package/dist/routes/memories/components/MemoryFormDialog.d.ts +15 -0
- package/dist/routes/memories/components/MemoryFormDialog.d.ts.map +1 -0
- package/dist/routes/memories/components/MemoryFormDialog.js +299 -0
- package/dist/routes/memories/components/MemoryOnboardingWizard.d.ts +9 -0
- package/dist/routes/memories/components/MemoryOnboardingWizard.d.ts.map +1 -0
- package/dist/routes/memories/components/MemoryOnboardingWizard.js +99 -0
- package/dist/routes/memories/components/RestoreBlockedDialog.d.ts +8 -0
- package/dist/routes/memories/components/RestoreBlockedDialog.d.ts.map +1 -0
- package/dist/routes/memories/components/RestoreBlockedDialog.js +7 -0
- package/dist/routes/memories/components/SchemasTab.d.ts +6 -0
- package/dist/routes/memories/components/SchemasTab.d.ts.map +1 -0
- package/dist/routes/memories/components/SchemasTab.js +35 -0
- package/dist/routes/memories/components/SupersessionConflictDialog.d.ts +18 -0
- package/dist/routes/memories/components/SupersessionConflictDialog.d.ts.map +1 -0
- package/dist/routes/memories/components/SupersessionConflictDialog.js +78 -0
- package/dist/routes/memories/components/TeamPicker.d.ts +8 -0
- package/dist/routes/memories/components/TeamPicker.d.ts.map +1 -0
- package/dist/routes/memories/components/TeamPicker.js +25 -0
- package/dist/routes/memories/components/badges.d.ts +50 -0
- package/dist/routes/memories/components/badges.d.ts.map +1 -0
- package/dist/routes/memories/components/badges.js +106 -0
- package/dist/routes/memories/expiry-utils.d.ts +23 -0
- package/dist/routes/memories/expiry-utils.d.ts.map +1 -0
- package/dist/routes/memories/expiry-utils.js +97 -0
- package/dist/routes/memories/hooks/useConflictCheck.d.ts +18 -0
- package/dist/routes/memories/hooks/useConflictCheck.d.ts.map +1 -0
- package/dist/routes/memories/hooks/useConflictCheck.js +35 -0
- package/dist/routes/memories/hooks/useDuplicateDetection.d.ts +11 -0
- package/dist/routes/memories/hooks/useDuplicateDetection.d.ts.map +1 -0
- package/dist/routes/memories/hooks/useDuplicateDetection.js +73 -0
- package/dist/routes/memories/hooks/useLiveContext.d.ts +23 -0
- package/dist/routes/memories/hooks/useLiveContext.d.ts.map +1 -0
- package/dist/routes/memories/hooks/useLiveContext.js +102 -0
- package/dist/routes/memories/hooks/useMemoryColumns.d.ts +33 -0
- package/dist/routes/memories/hooks/useMemoryColumns.d.ts.map +1 -0
- package/dist/routes/memories/hooks/useMemoryColumns.js +188 -0
- package/dist/routes/memories/hooks/useMemoryTable.d.ts +33 -0
- package/dist/routes/memories/hooks/useMemoryTable.d.ts.map +1 -0
- package/dist/routes/memories/hooks/useMemoryTable.js +121 -0
- package/dist/routes/memories/index.d.ts +11 -0
- package/dist/routes/memories/index.d.ts.map +1 -0
- package/dist/routes/memories/index.js +472 -0
- package/dist/routes/memories/memory-form-validation.d.ts +8 -0
- package/dist/routes/memories/memory-form-validation.d.ts.map +1 -0
- package/dist/routes/memories/memory-form-validation.js +33 -0
- package/dist/routes/memories/memory-mutation-input.d.ts +6 -0
- package/dist/routes/memories/memory-mutation-input.d.ts.map +1 -0
- package/dist/routes/memories/memory-mutation-input.js +41 -0
- package/dist/routes/plans/components/actionPlansTableConfig.d.ts +3 -3
- package/package.json +12 -7
- package/src/globals.css +13 -18
- package/src/themes/manta-dark.css +0 -4
- package/src/themes/standard.css +11 -11
- package/dist/components/channel-icon/marketer-icon.d.ts +0 -4
- package/dist/components/channel-icon/marketer-icon.d.ts.map +0 -1
- package/dist/components/channel-icon/marketer-icon.js +0 -6
- package/src/themes/marketer-dark.css +0 -70
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { GQLMemoryKind, useCreateMemoryMutation, useMemoriesQuery } from "@manta-eu/client";
|
|
3
|
+
import { parseError } from "@manta-eu/client/shared/error-utils";
|
|
4
|
+
import { userManualMemorySource } from "@manta-eu/client/shared/memory-sources";
|
|
5
|
+
import { isPresent } from "@manta-eu/lib/present";
|
|
6
|
+
import { useQueryClient } from "@tanstack/react-query";
|
|
7
|
+
import { ArrowLeft, ArrowRight, Brain, Check } from "lucide-react";
|
|
8
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
9
|
+
import { toast } from "sonner";
|
|
10
|
+
import { Button } from "#components/button/index";
|
|
11
|
+
import { Textarea } from "#components/input/index";
|
|
12
|
+
import { useMantaClient } from "#providers/index";
|
|
13
|
+
const wizardSteps = [
|
|
14
|
+
{
|
|
15
|
+
id: "objectives",
|
|
16
|
+
kind: GQLMemoryKind.Objective,
|
|
17
|
+
label: "What are your primary objectives?",
|
|
18
|
+
hint: "Manta will keep these in mind when making recommendations and generating plans.",
|
|
19
|
+
placeholder: "e.g. Grow revenue by 30% this quarter, reduce customer acquisition cost below $50",
|
|
20
|
+
memoryLabel: "Primary objective",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: "facts",
|
|
24
|
+
kind: GQLMemoryKind.Fact,
|
|
25
|
+
label: "What key facts should Manta always know?",
|
|
26
|
+
hint: "Background context that shapes every conversation - about you, your team, or your business.",
|
|
27
|
+
placeholder: "e.g. We sell premium skincare products, our main market is the US, average order value ~$80",
|
|
28
|
+
memoryLabel: "Key fact",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: "preferences",
|
|
32
|
+
kind: GQLMemoryKind.Preference,
|
|
33
|
+
label: "How do you prefer Manta to communicate?",
|
|
34
|
+
hint: "This shapes the style and format of every response.",
|
|
35
|
+
placeholder: "e.g. Concise bullet points with key numbers upfront, avoid jargon, flag risks explicitly",
|
|
36
|
+
memoryLabel: "Communication preference",
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
export function MemoryOnboardingWizard(props) {
|
|
40
|
+
const { scope, onComplete, onSkip } = props;
|
|
41
|
+
const { mantaClient } = useMantaClient();
|
|
42
|
+
const queryClient = useQueryClient();
|
|
43
|
+
const createMutation = useCreateMemoryMutation(mantaClient);
|
|
44
|
+
const [currentStep, setCurrentStep] = useState(0);
|
|
45
|
+
const [answers, setAnswers] = useState({});
|
|
46
|
+
const [isSaving, setIsSaving] = useState(false);
|
|
47
|
+
const [isDone, setIsDone] = useState(false);
|
|
48
|
+
const textareaRef = useRef(null);
|
|
49
|
+
const step = wizardSteps[currentStep];
|
|
50
|
+
const currentAnswer = step ? (answers[step.id] ?? "") : "";
|
|
51
|
+
const isLast = currentStep === wizardSteps.length - 1;
|
|
52
|
+
const progress = ((currentStep + 1) / wizardSteps.length) * 100;
|
|
53
|
+
const handleAnswer = useCallback((value) => {
|
|
54
|
+
if (!step)
|
|
55
|
+
return;
|
|
56
|
+
setAnswers((prev) => ({ ...prev, [step.id]: value }));
|
|
57
|
+
}, [step]);
|
|
58
|
+
const handleNext = useCallback(() => {
|
|
59
|
+
if (!isLast)
|
|
60
|
+
setCurrentStep((s) => s + 1);
|
|
61
|
+
}, [isLast]);
|
|
62
|
+
const handleBack = useCallback(() => {
|
|
63
|
+
setCurrentStep((s) => Math.max(0, s - 1));
|
|
64
|
+
}, []);
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
textareaRef.current?.focus();
|
|
67
|
+
}, [currentStep]);
|
|
68
|
+
const handleFinish = useCallback(async () => {
|
|
69
|
+
setIsSaving(true);
|
|
70
|
+
try {
|
|
71
|
+
const stepsWithAnswers = wizardSteps.filter((s) => isPresent(answers[s.id]));
|
|
72
|
+
await Promise.all(stepsWithAnswers.map((s) => createMutation.mutateAsync({
|
|
73
|
+
input: {
|
|
74
|
+
label: s.memoryLabel,
|
|
75
|
+
description: answers[s.id],
|
|
76
|
+
kind: s.kind,
|
|
77
|
+
memorySource: userManualMemorySource,
|
|
78
|
+
...(scope ? { scope } : {}),
|
|
79
|
+
},
|
|
80
|
+
})));
|
|
81
|
+
void queryClient.invalidateQueries({ queryKey: useMemoriesQuery.getKey() });
|
|
82
|
+
setIsDone(true);
|
|
83
|
+
toast.success(`${stepsWithAnswers.length} memories created`);
|
|
84
|
+
}
|
|
85
|
+
catch (e) {
|
|
86
|
+
const err = parseError(e, "Failed to save memories");
|
|
87
|
+
toast.error(err.message, { description: err.description });
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
setIsSaving(false);
|
|
91
|
+
}
|
|
92
|
+
}, [answers, createMutation, queryClient, scope]);
|
|
93
|
+
if (isDone) {
|
|
94
|
+
return (_jsxs("div", { className: "flex flex-col items-center gap-4 py-8 text-center", children: [_jsx("div", { className: "flex size-12 items-center justify-center rounded-full bg-success/10", children: _jsx(Check, { className: "size-6 text-success" }) }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "typography-large-body-semibold", children: "Memories set up!" }), _jsx("p", { className: "typography-small text-muted-foreground", children: "Manta will now use these in every conversation." })] }), _jsx(Button, { onClick: onComplete, children: "View my memories" })] }));
|
|
95
|
+
}
|
|
96
|
+
if (!step)
|
|
97
|
+
return null;
|
|
98
|
+
return (_jsxs("div", { className: "flex flex-col gap-6 max-w-xl w-full", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Brain, { className: "size-5 text-muted-foreground shrink-0" }), _jsxs("div", { className: "flex-1", children: [_jsx("p", { className: "typography-caption-medium", children: "Set up Manta's memory" }), _jsxs("p", { className: "typography-tiny text-muted-foreground", children: ["Step ", currentStep + 1, " of ", wizardSteps.length] })] }), _jsx("button", { onClick: onSkip, className: "typography-small text-muted-foreground hover:text-default-foreground transition-colors", children: "Skip" })] }), _jsx("div", { className: "h-1 bg-muted rounded-full overflow-hidden", children: _jsx("div", { className: "h-full bg-primary rounded-full transition-all duration-300", style: { width: `${progress}%` } }) }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("label", { className: "typography-large-body-semibold", children: step.label }), _jsx("p", { className: "typography-small text-muted-foreground", children: step.hint }), _jsx(Textarea, { ref: textareaRef, id: "memory-onboarding-answer", label: step.label, hideLabel: true, placeholder: step.placeholder, value: currentAnswer, onChange: (e) => handleAnswer(e.target.value), minRows: 3 })] }), _jsxs("div", { className: "flex gap-2 justify-between", children: [_jsx(Button, { variant: "ghost", onClick: handleBack, disabled: currentStep === 0, icon: ArrowLeft, children: "Back" }), isLast ? (_jsx(Button, { onClick: handleFinish, disabled: isSaving, icon: Check, children: isSaving ? "Saving…" : "Save memories" })) : (_jsx(Button, { onClick: handleNext, icon: ArrowRight, children: "Next" }))] })] }));
|
|
99
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type RestoreBlockedDialogProps = {
|
|
2
|
+
open: boolean;
|
|
3
|
+
onOpenChange: (open: boolean) => void;
|
|
4
|
+
blockedByLabel: string | null;
|
|
5
|
+
onConfirm: () => void;
|
|
6
|
+
};
|
|
7
|
+
export declare function RestoreBlockedDialog(props: RestoreBlockedDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
//# sourceMappingURL=RestoreBlockedDialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RestoreBlockedDialog.d.ts","sourceRoot":"","sources":["../../../../src/routes/memories/components/RestoreBlockedDialog.tsx"],"names":[],"mappings":"AAWA,MAAM,MAAM,yBAAyB,GAAG;IACtC,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,yBAAyB,2CAsBpE"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "#components/alert-dialog/index";
|
|
3
|
+
export function RestoreBlockedDialog(props) {
|
|
4
|
+
const { open, onOpenChange, blockedByLabel, onConfirm } = props;
|
|
5
|
+
const label = blockedByLabel ?? "the newer memory";
|
|
6
|
+
return (_jsx(AlertDialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Can't restore this memory" }), _jsxs(AlertDialogDescription, { children: ["It was replaced by \"", label, "\". Delete or edit that memory first, or delete it now to restore this one."] })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: "Cancel" }), _jsxs(AlertDialogAction, { variant: "destructive", onClick: onConfirm, children: ["Delete \"", label, "\" & restore"] })] })] }) }));
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SchemasTab.d.ts","sourceRoot":"","sources":["../../../../src/routes/memories/components/SchemasTab.tsx"],"names":[],"mappings":"AA4FA,KAAK,eAAe,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1C,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,2CA+ChD"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useMemorySchemasQuery } from "@manta-eu/client";
|
|
3
|
+
import { formatEntityName } from "@manta-eu/client/shared/rdf-namespaces";
|
|
4
|
+
import { humanPlural } from "@manta-eu/lib/humanize";
|
|
5
|
+
import { isPresent } from "@manta-eu/lib/present";
|
|
6
|
+
import { ChevronRight, Layers } from "lucide-react";
|
|
7
|
+
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "#components/collapsible/index";
|
|
8
|
+
import { EmptyState } from "#components/empty-state/index";
|
|
9
|
+
import { Skeleton } from "#components/skeleton/index";
|
|
10
|
+
import { useMantaClient } from "#providers/index";
|
|
11
|
+
function shortDatatype(iri) {
|
|
12
|
+
if (iri.endsWith("#string"))
|
|
13
|
+
return "text";
|
|
14
|
+
if (iri.endsWith("#decimal"))
|
|
15
|
+
return "decimal";
|
|
16
|
+
if (iri.endsWith("#integer"))
|
|
17
|
+
return "integer";
|
|
18
|
+
if (iri.endsWith("#boolean"))
|
|
19
|
+
return "boolean";
|
|
20
|
+
if (iri.endsWith("#anyURI"))
|
|
21
|
+
return "url";
|
|
22
|
+
return formatEntityName(iri);
|
|
23
|
+
}
|
|
24
|
+
function SchemaRow(props) {
|
|
25
|
+
const { schema } = props;
|
|
26
|
+
return (_jsxs(Collapsible, { children: [_jsxs(CollapsibleTrigger, { className: "group flex w-full items-center gap-2 px-4 py-3 text-left hover:bg-faint transition-colors", children: [_jsx(ChevronRight, { className: "size-3.5 shrink-0 text-muted-foreground transition-transform group-data-[state=open]:rotate-90" }), _jsx("span", { className: "typography-small-medium text-default-foreground truncate", children: schema.label }), _jsxs("span", { className: "ml-auto shrink-0 typography-tiny text-muted-foreground", children: [schema.properties.length, " ", humanPlural("property", schema.properties.length)] })] }), _jsx(CollapsibleContent, { children: _jsxs("div", { className: "border-t border-muted-border", children: [schema.description && (_jsx("p", { className: "px-4 py-3 typography-small text-muted-foreground border-b border-muted-border", children: schema.description })), isPresent(schema.properties) && (_jsxs("table", { className: "w-full table-fixed text-left", children: [_jsxs("colgroup", { children: [_jsx("col", { className: "w-48" }), _jsx("col", { className: "w-20" }), _jsx("col", { className: "w-24" }), _jsx("col", {})] }), _jsx("tbody", { children: schema.properties.map((prop) => (_jsxs("tr", { className: "border-b border-muted-border last:border-0", children: [_jsx("td", { className: "px-4 py-2.5 align-top", children: _jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: "typography-small-medium text-default-foreground", children: prop.label }), _jsx("span", { className: "typography-tiny font-mono text-muted-foreground break-all", children: prop.path })] }) }), _jsx("td", { className: "px-4 py-2.5 align-top", children: _jsx("span", { className: "typography-tiny text-muted-foreground", children: shortDatatype(prop.dataType) }) }), _jsx("td", { className: "px-4 py-2.5 align-top", children: prop.required ? (_jsx("span", { className: "typography-tiny rounded-full bg-accent px-2 py-0.5 text-muted-foreground", children: "required" })) : (_jsx("span", { className: "typography-tiny rounded-full bg-muted px-2 py-0.5 text-muted-foreground", children: "optional" })) }), _jsx("td", { className: "px-4 py-2.5 align-top", children: isPresent(prop.allowedValues) && (_jsx("div", { className: "flex flex-wrap gap-1", children: prop.allowedValues.map((val) => (_jsx("span", { className: "typography-tiny rounded-full bg-muted px-2 py-0.5", children: val }, val))) })) })] }, prop.path))) })] }))] }) })] }));
|
|
27
|
+
}
|
|
28
|
+
export function SchemasTab(props) {
|
|
29
|
+
const { query = "" } = props;
|
|
30
|
+
const { mantaClient } = useMantaClient();
|
|
31
|
+
const { data, isLoading } = useMemorySchemasQuery(mantaClient);
|
|
32
|
+
const schemas = data?.memorySchemas.schemas ?? [];
|
|
33
|
+
const filtered = query.trim() ? schemas.filter((s) => s.label.toLowerCase().includes(query.toLowerCase())) : schemas;
|
|
34
|
+
return (_jsxs("div", { className: "flex flex-col gap-3", children: [isLoading && (_jsx("div", { className: "flex flex-col w-full max-w-3xl mx-auto rounded-xl border border-muted-border overflow-hidden divide-y divide-muted-border", children: [0, 1, 2].map((i) => (_jsxs("div", { className: "flex items-center gap-2 px-4 py-3", children: [_jsx(Skeleton, { className: "size-3.5 rounded-sm" }), _jsx(Skeleton, { className: "h-4 w-40 rounded-md" }), _jsx(Skeleton, { className: "ml-auto h-4 w-16 rounded-md" })] }, i))) })), !isLoading && !isPresent(schemas) && (_jsx("div", { className: "flex flex-1 items-center justify-center", children: _jsx(EmptyState, { icon: Layers, title: "No schemas defined", description: "Memory schemas define structured properties for typed memories." }) })), !isLoading && isPresent(schemas) && !isPresent(filtered) && (_jsx("div", { className: "flex flex-1 items-center justify-center", children: _jsx(EmptyState, { icon: Layers, title: "No schemas match", description: `No schemas found for "${query}".` }) })), !isLoading && isPresent(filtered) && (_jsx("div", { className: "w-full max-w-3xl mx-auto rounded-xl border border-muted-border bg-card overflow-hidden divide-y divide-muted-border", children: filtered.map((schema) => (_jsx(SchemaRow, { schema: schema }, schema.iri))) }))] }));
|
|
35
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { GQLConflictingMemory } from "@manta-eu/client";
|
|
2
|
+
import { GQLMemoryScope } from "@manta-eu/client";
|
|
3
|
+
export type ConflictResolution = {
|
|
4
|
+
supersedesIris: string[];
|
|
5
|
+
mergedTeams?: string[];
|
|
6
|
+
};
|
|
7
|
+
export type SupersessionConflictDialogProps = {
|
|
8
|
+
open: boolean;
|
|
9
|
+
conflicts: GQLConflictingMemory[];
|
|
10
|
+
scope: GQLMemoryScope;
|
|
11
|
+
newTeams: string[];
|
|
12
|
+
currentUserId?: string;
|
|
13
|
+
onConfirm: (resolution: ConflictResolution) => void;
|
|
14
|
+
onSaveWithout: () => void;
|
|
15
|
+
onCancel: () => void;
|
|
16
|
+
};
|
|
17
|
+
export declare function SupersessionConflictDialog(props: SupersessionConflictDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
//# sourceMappingURL=SupersessionConflictDialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SupersessionConflictDialog.d.ts","sourceRoot":"","sources":["../../../../src/routes/memories/components/SupersessionConflictDialog.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAelD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,+BAA+B,GAAG;IAC5C,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,oBAAoB,EAAE,CAAC;IAClC,KAAK,EAAE,cAAc,CAAC;IACtB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,CAAC,UAAU,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACpD,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB,CAAC;AAEF,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,+BAA+B,2CA8HhF"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { GQLMemoryScope } from "@manta-eu/client";
|
|
3
|
+
import { humanCount } from "@manta-eu/lib/humanize";
|
|
4
|
+
import { isPresent } from "@manta-eu/lib/present";
|
|
5
|
+
import { useCallback, useEffect, useState } from "react";
|
|
6
|
+
import { ConflictMemoryCard } from "./ConflictMemoryCard.js";
|
|
7
|
+
import { Button } from "#components/button/index";
|
|
8
|
+
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "#components/modal/index";
|
|
9
|
+
export function SupersessionConflictDialog(props) {
|
|
10
|
+
const { open, conflicts, scope, newTeams, currentUserId, onConfirm, onSaveWithout, onCancel } = props;
|
|
11
|
+
const isTeam = scope === GQLMemoryScope.Team;
|
|
12
|
+
const [checked, setChecked] = useState(new Set());
|
|
13
|
+
const [merging, setMerging] = useState(new Set());
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (open) {
|
|
16
|
+
setChecked(new Set(conflicts.map((c) => c.iri)));
|
|
17
|
+
setMerging(new Set());
|
|
18
|
+
}
|
|
19
|
+
}, [open, conflicts]);
|
|
20
|
+
const toggleChecked = useCallback((iri) => {
|
|
21
|
+
setChecked((prev) => {
|
|
22
|
+
const next = new Set(prev);
|
|
23
|
+
if (next.has(iri)) {
|
|
24
|
+
next.delete(iri);
|
|
25
|
+
// Unchecking stops superseding it, so it must also stop contributing its teams.
|
|
26
|
+
setMerging((m) => {
|
|
27
|
+
if (!m.has(iri))
|
|
28
|
+
return m;
|
|
29
|
+
const nm = new Set(m);
|
|
30
|
+
nm.delete(iri);
|
|
31
|
+
return nm;
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
next.add(iri);
|
|
36
|
+
}
|
|
37
|
+
return next;
|
|
38
|
+
});
|
|
39
|
+
}, []);
|
|
40
|
+
const toggleMerge = useCallback((iri) => {
|
|
41
|
+
setMerging((prev) => {
|
|
42
|
+
const next = new Set(prev);
|
|
43
|
+
if (next.has(iri)) {
|
|
44
|
+
next.delete(iri);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
next.add(iri);
|
|
48
|
+
// Merge implies supersession - ensure checked
|
|
49
|
+
setChecked((c) => new Set([...c, iri]));
|
|
50
|
+
}
|
|
51
|
+
return next;
|
|
52
|
+
});
|
|
53
|
+
}, []);
|
|
54
|
+
const allChecked = isPresent(conflicts) && conflicts.every((c) => checked.has(c.iri));
|
|
55
|
+
const toggleAll = useCallback(() => {
|
|
56
|
+
if (allChecked) {
|
|
57
|
+
setChecked(new Set());
|
|
58
|
+
setMerging(new Set());
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
setChecked(new Set(conflicts.map((c) => c.iri)));
|
|
62
|
+
}
|
|
63
|
+
}, [allChecked, conflicts]);
|
|
64
|
+
const handleConfirm = useCallback(() => {
|
|
65
|
+
const supersedesIris = [...checked];
|
|
66
|
+
let mergedTeams;
|
|
67
|
+
if (isTeam && merging.size > 0) {
|
|
68
|
+
const allOldTeams = conflicts.filter((c) => merging.has(c.iri)).flatMap((c) => c.currentTeams);
|
|
69
|
+
mergedTeams = [...new Set([...newTeams, ...allOldTeams])];
|
|
70
|
+
}
|
|
71
|
+
onConfirm({ supersedesIris, mergedTeams });
|
|
72
|
+
}, [checked, merging, isTeam, conflicts, newTeams, onConfirm]);
|
|
73
|
+
const checkedCount = checked.size;
|
|
74
|
+
return (_jsx(Dialog, { open: open, onOpenChange: (o) => {
|
|
75
|
+
if (!o)
|
|
76
|
+
onCancel();
|
|
77
|
+
}, children: _jsxs(DialogContent, { className: "max-w-2xl", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { text: "Similar memories already exist" }), _jsx(DialogDescription, { children: "The memory you're saving may replace the following existing memories. Select which ones to replace and they will be hidden and superseded by the new memory." })] }), _jsxs("div", { className: "flex flex-col gap-3 max-h-72 overflow-y-auto py-1", children: [_jsx("button", { type: "button", className: "text-left typography-small text-accent-foreground hover:underline w-fit", onClick: toggleAll, children: allChecked ? "Select none" : "Select all" }), conflicts.map((conflict) => (_jsx(ConflictMemoryCard, { conflict: conflict, scope: scope, checked: checked.has(conflict.iri), onToggleChecked: () => toggleChecked(conflict.iri), showMerge: isTeam, merging: merging.has(conflict.iri), onToggleMerge: () => toggleMerge(conflict.iri), currentUserId: currentUserId }, conflict.iri)))] }), _jsxs(DialogFooter, { className: "flex-col sm:flex-row gap-2", children: [_jsx(Button, { variant: "tertiary", onClick: onCancel, className: "sm:mr-auto", children: "Cancel" }), _jsx(Button, { variant: "secondary", onClick: onSaveWithout, children: "Save & keep both" }), _jsx(Button, { onClick: handleConfirm, disabled: checkedCount === 0, children: checkedCount > 0 ? `Save & replace ${humanCount(checkedCount, "memory")}` : "Save & replace selected" })] })] }) }));
|
|
78
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type TeamPickerProps = {
|
|
2
|
+
selected: string[];
|
|
3
|
+
onChange: (teams: string[]) => void;
|
|
4
|
+
isOperator: boolean;
|
|
5
|
+
error?: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function TeamPicker(props: TeamPickerProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
//# sourceMappingURL=TeamPicker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TeamPicker.d.ts","sourceRoot":"","sources":["../../../../src/routes/memories/components/TeamPicker.tsx"],"names":[],"mappings":"AAeA,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACpC,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAMF,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,2CAoEhD"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useAvailableTeamsQuery } from "@manta-eu/client";
|
|
3
|
+
import { isPresent } from "@manta-eu/lib/present";
|
|
4
|
+
import { ChevronDown, Users, X } from "lucide-react";
|
|
5
|
+
import { Button } from "#components/button/index";
|
|
6
|
+
import { Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList, ComboboxTrigger, } from "#components/combobox/index";
|
|
7
|
+
import { useMantaClient } from "#providers/index";
|
|
8
|
+
function getTeamName(path) {
|
|
9
|
+
return path.split("/").filter(Boolean).pop() ?? path;
|
|
10
|
+
}
|
|
11
|
+
export function TeamPicker(props) {
|
|
12
|
+
const { selected, onChange, isOperator, error } = props;
|
|
13
|
+
const { mantaClient } = useMantaClient();
|
|
14
|
+
const { data } = useAvailableTeamsQuery(mantaClient, undefined, { enabled: isOperator });
|
|
15
|
+
const availableTeams = data?.availableTeams.teams.map((g) => g.name) ?? [];
|
|
16
|
+
function toggle(path) {
|
|
17
|
+
onChange(selected.includes(path) ? selected.filter((t) => t !== path) : [...selected, path]);
|
|
18
|
+
}
|
|
19
|
+
const triggerLabel = !isPresent(selected)
|
|
20
|
+
? "Select teams…"
|
|
21
|
+
: selected.length === 1
|
|
22
|
+
? getTeamName(selected[0] ?? "")
|
|
23
|
+
: `${selected.length} teams selected`;
|
|
24
|
+
return (_jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx("label", { className: "typography-small text-default-foreground", children: "Target Teams" }), _jsxs(Combobox, { children: [_jsx(ComboboxTrigger, { asChild: true, children: _jsxs(Button, { variant: "tertiary", size: "base", rounded: "lg", className: "w-full justify-start", children: [_jsx(Users, { className: "size-4 shrink-0 text-muted-foreground" }), _jsx("span", { className: "flex-1 text-left truncate", children: triggerLabel }), _jsx(ChevronDown, { className: "size-4 shrink-0 text-muted-foreground" })] }) }), _jsxs(ComboboxContent, { align: "start", className: "w-full", children: [_jsx(ComboboxInput, { placeholder: "Search teams\u2026" }), _jsxs(ComboboxList, { children: [_jsx(ComboboxEmpty, { children: "No teams found" }), availableTeams.map((path) => (_jsx(ComboboxItem, { value: path, selected: selected.includes(path), closeOnSelect: false, onSelect: () => toggle(path), children: getTeamName(path) }, path)))] })] })] }), isPresent(selected) && (_jsx("div", { className: "flex flex-wrap gap-1.5 mt-1", children: selected.map((path) => (_jsxs("span", { className: "inline-flex items-center gap-1 rounded-full px-2 py-0.5 typography-tiny bg-accent/10 text-accent-foreground", children: [getTeamName(path), _jsx("button", { type: "button", onClick: () => toggle(path), className: "size-3 flex items-center justify-center hover:opacity-70", "aria-label": `Remove ${path}`, children: _jsx(X, { className: "size-2.5" }) })] }, path))) })), error && _jsx("p", { className: "typography-small text-error-foreground", children: error })] }));
|
|
25
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { GQLMemoryScope, type GQLMemoryKind } from "@manta-eu/client";
|
|
2
|
+
import { type MemoryPreloadCandidate } from "@manta-eu/client/shared/memory-preload-gate";
|
|
3
|
+
import { type MemoryWindowState } from "../expiry-utils.js";
|
|
4
|
+
export type ScopeBadgeProps = {
|
|
5
|
+
scope: GQLMemoryScope;
|
|
6
|
+
};
|
|
7
|
+
export declare function ScopeBadge(props: ScopeBadgeProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export declare function AutoCapturedBadge(props: {
|
|
9
|
+
confidence: number;
|
|
10
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export type DuplicateMatch = {
|
|
12
|
+
label: string;
|
|
13
|
+
reason: string;
|
|
14
|
+
};
|
|
15
|
+
export declare function DuplicateBadge(props: {
|
|
16
|
+
matches: DuplicateMatch[];
|
|
17
|
+
}): import("react/jsx-runtime").JSX.Element | null;
|
|
18
|
+
export type KindBadgeProps = {
|
|
19
|
+
kind: GQLMemoryKind;
|
|
20
|
+
};
|
|
21
|
+
export declare function KindBadge(props: KindBadgeProps): import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
export type SchemaBadgeProps = {
|
|
23
|
+
label?: string | null;
|
|
24
|
+
};
|
|
25
|
+
export declare function SchemaBadge(props: SchemaBadgeProps): import("react/jsx-runtime").JSX.Element | null;
|
|
26
|
+
export type OutOfWindowBadgeProps = {
|
|
27
|
+
state: Exclude<MemoryWindowState, "active">;
|
|
28
|
+
};
|
|
29
|
+
export declare function OutOfWindowBadge(props: OutOfWindowBadgeProps): import("react/jsx-runtime").JSX.Element;
|
|
30
|
+
export type PreloadedBadgeProps = {
|
|
31
|
+
memory: MemoryPreloadCandidate;
|
|
32
|
+
};
|
|
33
|
+
export type PreloadBadgeTone = "pinned" | "heldOut" | "unreachable";
|
|
34
|
+
export type PreloadBadgeContent = {
|
|
35
|
+
label: "Pinned" | "Unconfirmed" | "Unreachable" | "Cold";
|
|
36
|
+
preloaded: boolean;
|
|
37
|
+
tone: PreloadBadgeTone;
|
|
38
|
+
why: string;
|
|
39
|
+
};
|
|
40
|
+
export declare function preloadBadgeContent(memory: MemoryPreloadCandidate, now?: Date): PreloadBadgeContent | null;
|
|
41
|
+
export declare function PreloadedBadge(props: PreloadedBadgeProps): import("react/jsx-runtime").JSX.Element | null;
|
|
42
|
+
export type MemoryEvidenceProps = {
|
|
43
|
+
evidence?: string | null;
|
|
44
|
+
};
|
|
45
|
+
export declare function MemoryEvidence(props: MemoryEvidenceProps): import("react/jsx-runtime").JSX.Element | null;
|
|
46
|
+
export type MemoryAboutProps = {
|
|
47
|
+
aboutEntityIris?: string[] | null;
|
|
48
|
+
};
|
|
49
|
+
export declare function MemoryAbout(props: MemoryAboutProps): import("react/jsx-runtime").JSX.Element | null;
|
|
50
|
+
//# sourceMappingURL=badges.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"badges.d.ts","sourceRoot":"","sources":["../../../../src/routes/memories/components/badges.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAKL,KAAK,sBAAsB,EAC5B,MAAM,6CAA6C,CAAC;AAKrD,OAAO,EAA2B,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAKlF,MAAM,MAAM,eAAe,GAAG;IAAE,KAAK,EAAE,cAAc,CAAA;CAAE,CAAC;AAExD,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,2CAahD;AAeD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,2CAa9D;AAED,MAAM,MAAM,cAAc,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/D,wBAAgB,cAAc,CAAC,KAAK,EAAE;IAAE,OAAO,EAAE,cAAc,EAAE,CAAA;CAAE,kDAwBlE;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,aAAa,CAAC;CACrB,CAAC;AAEF,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,2CAQ9C;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC;AAEF,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,kDASlD;AAaD,MAAM,MAAM,qBAAqB,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC,iBAAiB,EAAE,QAAQ,CAAC,CAAA;CAAE,CAAC;AAEpF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,2CAa5D;AAED,MAAM,MAAM,mBAAmB,GAAG;IAAE,MAAM,EAAE,sBAAsB,CAAA;CAAE,CAAC;AAErE,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,SAAS,GAAG,aAAa,CAAC;AAEpE,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,QAAQ,GAAG,aAAa,GAAG,aAAa,GAAG,MAAM,CAAC;IACzD,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,gBAAgB,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,sBAAsB,EAAE,GAAG,CAAC,EAAE,IAAI,GAAG,mBAAmB,GAAG,IAAI,CAc1G;AAQD,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,kDAcxD;AAED,MAAM,MAAM,mBAAmB,GAAG;IAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AAE/D,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,kDAkBxD;AAED,MAAM,MAAM,gBAAgB,GAAG;IAAE,eAAe,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;CAAE,CAAC;AAErE,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,kDAclD"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { GQLMemoryScope } from "@manta-eu/client";
|
|
3
|
+
import { describeMemoryPreloadExclusions, isMemoryArchived, isMemorySearchable, memoryPreloadExclusions, } from "@manta-eu/client/shared/memory-preload-gate";
|
|
4
|
+
import { formatEntityName } from "@manta-eu/client/shared/rdf-namespaces";
|
|
5
|
+
import { isPresent } from "@manta-eu/lib/present";
|
|
6
|
+
import { CopyCheck, Link2, Quote } from "lucide-react";
|
|
7
|
+
import { match, P } from "ts-pattern";
|
|
8
|
+
import { unconfirmedHorizonLabel } from "../expiry-utils.js";
|
|
9
|
+
import { Badge } from "#components/badge/index";
|
|
10
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from "#components/tooltip/index";
|
|
11
|
+
import { cn } from "#lib/utils";
|
|
12
|
+
export function ScopeBadge(props) {
|
|
13
|
+
const { scope } = props;
|
|
14
|
+
const { className, label } = match(scope)
|
|
15
|
+
.with(GQLMemoryScope.Company, () => ({ className: "bg-accent/10 text-accent-foreground", label: "Company" }))
|
|
16
|
+
.with(GQLMemoryScope.Team, () => ({ className: "bg-warning/10 text-warning-foreground", label: "Team" }))
|
|
17
|
+
.with(GQLMemoryScope.User, () => ({ className: "bg-muted-foreground/10 text-muted-foreground", label: "Personal" }))
|
|
18
|
+
.exhaustive();
|
|
19
|
+
return (_jsx(Badge, { size: "sm", className: className, children: label }));
|
|
20
|
+
}
|
|
21
|
+
function autoCapturedTooltip(confidence) {
|
|
22
|
+
const pct = Math.round(confidence * 100);
|
|
23
|
+
const prefix = `Auto-captured from chat history (${pct}% confidence).`;
|
|
24
|
+
// TODO: revisit these confidence tiers/copy - refine until good enough or drop entirely.
|
|
25
|
+
const note = match(confidence)
|
|
26
|
+
.with(P.number.gte(0.9), () => "Likely accurate, but worth a quick glance.")
|
|
27
|
+
.with(P.number.gte(0.7), () => "Decent signal - review before relying on it.")
|
|
28
|
+
.with(P.number.gte(0.5), () => "Moderate confidence - verify this.")
|
|
29
|
+
.otherwise(() => "Low confidence - treat with caution.");
|
|
30
|
+
return `${prefix} ${note}`;
|
|
31
|
+
}
|
|
32
|
+
export function AutoCapturedBadge(props) {
|
|
33
|
+
const { confidence } = props;
|
|
34
|
+
const pct = Math.round(confidence * 100);
|
|
35
|
+
return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs(Badge, { size: "sm", className: "bg-faint border border-muted-border text-muted-foreground cursor-default", children: ["Captured \u00B7 ", pct, "%"] }) }), _jsx(TooltipContent, { className: "max-w-xs typography-small", children: autoCapturedTooltip(confidence) })] }));
|
|
36
|
+
}
|
|
37
|
+
export function DuplicateBadge(props) {
|
|
38
|
+
const { matches } = props;
|
|
39
|
+
if (!isPresent(matches))
|
|
40
|
+
return null;
|
|
41
|
+
return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs(Badge, { size: "sm", className: "bg-warning/10 text-warning-foreground border border-warning/30 cursor-default", children: [_jsx(CopyCheck, { className: "size-3" }), "Possible duplicate"] }) }), _jsx(TooltipContent, { className: "max-w-xs typography-small text-left", children: _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("span", { className: "text-muted-foreground", children: "Looks like it duplicates:" }), matches.map((match) => (_jsxs("div", { children: [_jsx("span", { className: "typography-small-medium", children: match.label }), match.reason ? ` - ${match.reason}` : ""] }, match.label)))] }) })] }));
|
|
42
|
+
}
|
|
43
|
+
export function KindBadge(props) {
|
|
44
|
+
const { kind } = props;
|
|
45
|
+
return (_jsx(Badge, { size: "sm", variant: "secondary", className: "capitalize", children: kind }));
|
|
46
|
+
}
|
|
47
|
+
export function SchemaBadge(props) {
|
|
48
|
+
const { label } = props;
|
|
49
|
+
if (!label)
|
|
50
|
+
return null;
|
|
51
|
+
return (_jsx(Badge, { size: "sm", variant: "secondary", className: "max-w-[28ch] truncate", title: label, children: label }));
|
|
52
|
+
}
|
|
53
|
+
const outOfWindowCopy = {
|
|
54
|
+
expired: {
|
|
55
|
+
label: "Expired",
|
|
56
|
+
why: "Its expiry date has passed, so the agent can neither pre-load nor search it. Nothing was deleted — edit the expiry date to bring it back.",
|
|
57
|
+
},
|
|
58
|
+
scheduled: {
|
|
59
|
+
label: "Scheduled",
|
|
60
|
+
why: "Its start date is in the future, so the agent cannot use it yet. Edit the start date to bring it forward.",
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
export function OutOfWindowBadge(props) {
|
|
64
|
+
const { label, why } = outOfWindowCopy[props.state];
|
|
65
|
+
return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Badge, { size: "sm", className: "bg-warning/10 text-warning-foreground border border-warning/30 cursor-default", children: label }) }), _jsx(TooltipContent, { className: "max-w-xs typography-small", children: why })] }));
|
|
66
|
+
}
|
|
67
|
+
export function preloadBadgeContent(memory, now) {
|
|
68
|
+
if (isMemoryArchived(memory))
|
|
69
|
+
return null;
|
|
70
|
+
const exclusions = memoryPreloadExclusions(memory, now);
|
|
71
|
+
const preloaded = !isPresent(exclusions);
|
|
72
|
+
const why = describeMemoryPreloadExclusions(memory, exclusions);
|
|
73
|
+
if (preloaded)
|
|
74
|
+
return { label: "Pinned", preloaded, tone: "pinned", why };
|
|
75
|
+
if (!isMemorySearchable(memory))
|
|
76
|
+
return { label: "Unreachable", preloaded, tone: "unreachable", why };
|
|
77
|
+
if (exclusions.includes("importance"))
|
|
78
|
+
return null;
|
|
79
|
+
if (exclusions.length === 1 && exclusions[0] === "cold") {
|
|
80
|
+
return { label: "Cold", preloaded, tone: "heldOut", why };
|
|
81
|
+
}
|
|
82
|
+
return { label: unconfirmedHorizonLabel, preloaded, tone: "heldOut", why };
|
|
83
|
+
}
|
|
84
|
+
const preloadBadgeToneClass = {
|
|
85
|
+
pinned: "bg-faint border-muted-border text-default-foreground",
|
|
86
|
+
heldOut: "bg-muted-foreground/10 border-muted-border text-muted-foreground",
|
|
87
|
+
unreachable: "bg-warning/10 border-warning/30 text-warning-foreground",
|
|
88
|
+
};
|
|
89
|
+
export function PreloadedBadge(props) {
|
|
90
|
+
const content = preloadBadgeContent(props.memory);
|
|
91
|
+
if (!content)
|
|
92
|
+
return null;
|
|
93
|
+
return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Badge, { size: "sm", className: cn("border cursor-default", preloadBadgeToneClass[content.tone]), children: content.label }) }), _jsx(TooltipContent, { className: "max-w-xs typography-small", children: content.why })] }));
|
|
94
|
+
}
|
|
95
|
+
export function MemoryEvidence(props) {
|
|
96
|
+
const { evidence } = props;
|
|
97
|
+
if (!isPresent(evidence))
|
|
98
|
+
return null;
|
|
99
|
+
return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("span", { className: "mt-1 flex cursor-default items-start gap-1.5 border-l-2 border-muted-border pl-2 typography-tiny italic text-muted-foreground", children: [_jsx(Quote, { className: "mt-0.5 size-3 shrink-0" }), _jsx("span", { className: "line-clamp-2 break-words", children: evidence })] }) }), _jsx(TooltipContent, { className: "max-w-xs typography-small text-left", children: "What the user said that this memory was extracted from. Chat history is deleted after 30 days, so this quote stays as the record of where the memory came from." })] }));
|
|
100
|
+
}
|
|
101
|
+
export function MemoryAbout(props) {
|
|
102
|
+
const { aboutEntityIris } = props;
|
|
103
|
+
if (!isPresent(aboutEntityIris))
|
|
104
|
+
return null;
|
|
105
|
+
return (_jsxs("span", { className: "mt-1 flex flex-wrap items-center gap-1", children: [_jsx(Link2, { className: "size-3 shrink-0 text-muted-foreground" }), aboutEntityIris.map((iri) => (_jsx(Badge, { size: "sm", variant: "secondary", className: "max-w-[24ch] truncate", title: iri, children: formatEntityName(iri) }, iri)))] }));
|
|
106
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export declare function formatExpiryRelative(expiresAt: Date | string | null | undefined, now?: Date): string;
|
|
2
|
+
export declare function formatExpiryDate(expiresAt: Date | string | null | undefined, now?: Date): string;
|
|
3
|
+
export type MemoryHorizon = {
|
|
4
|
+
kind: "none";
|
|
5
|
+
at: null;
|
|
6
|
+
} | {
|
|
7
|
+
kind: "expiry";
|
|
8
|
+
at: Date | string;
|
|
9
|
+
} | {
|
|
10
|
+
kind: "trust";
|
|
11
|
+
at: Date | string;
|
|
12
|
+
};
|
|
13
|
+
export declare function memoryHorizon(memoryValidUntil: Date | string | null | undefined, memoryTrustedUntil: Date | string | null | undefined): MemoryHorizon;
|
|
14
|
+
export type MemoryWindowState = "active" | "expired" | "scheduled";
|
|
15
|
+
export declare function memoryWindowState(memoryValidFrom: Date | string | null | undefined, memoryValidUntil: Date | string | null | undefined, now?: Date): MemoryWindowState;
|
|
16
|
+
export declare function isTrustExpired(memoryTrustedUntil: Date | string | null | undefined, now?: Date): boolean;
|
|
17
|
+
export declare const unconfirmedHorizonLabel = "Unconfirmed";
|
|
18
|
+
export declare function formatHorizonDate(horizon: MemoryHorizon, now?: Date): string;
|
|
19
|
+
export declare function formatHorizonRelative(horizon: MemoryHorizon, now?: Date): string;
|
|
20
|
+
export declare function compareMemoryHorizons(a: MemoryHorizon, b: MemoryHorizon): number;
|
|
21
|
+
export declare function shouldShowValidFrom(updatedAt: Date | string | null | undefined, memoryValidFrom: Date | string | null | undefined): boolean;
|
|
22
|
+
export declare function formatHorizonTooltip(horizon: MemoryHorizon, now?: Date): string;
|
|
23
|
+
//# sourceMappingURL=expiry-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expiry-utils.d.ts","sourceRoot":"","sources":["../../../src/routes/memories/expiry-utils.ts"],"names":[],"mappings":"AASA,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,GAAG,GAAE,IAAiB,GAAG,MAAM,CAQhH;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,GAAG,GAAE,IAAiB,GAAG,MAAM,CAK5G;AAED,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,IAAI,CAAA;CAAE,GAC1B;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,EAAE,EAAE,IAAI,GAAG,MAAM,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,EAAE,EAAE,IAAI,GAAG,MAAM,CAAA;CAAE,CAAC;AAEzC,wBAAgB,aAAa,CAC3B,gBAAgB,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,EAClD,kBAAkB,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GACnD,aAAa,CAIf;AAED,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,CAAC;AAEnE,wBAAgB,iBAAiB,CAC/B,eAAe,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,EACjD,gBAAgB,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,EAClD,GAAG,GAAE,IAAiB,GACrB,iBAAiB,CAMnB;AAGD,wBAAgB,cAAc,CAAC,kBAAkB,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,GAAG,GAAE,IAAiB,GAAG,OAAO,CAEpH;AAED,eAAO,MAAM,uBAAuB,gBAAgB,CAAC;AAErD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,aAAa,EAAE,GAAG,GAAE,IAAiB,GAAG,MAAM,CAUxF;AAUD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,aAAa,EAAE,GAAG,GAAE,IAAiB,GAAG,MAAM,CAM5F;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,aAAa,GAAG,MAAM,CAKhF;AAED,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,EAC3C,eAAe,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAChD,OAAO,CAKT;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,aAAa,EAAE,GAAG,GAAE,IAAiB,GAAG,MAAM,CAU3F"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { isPastTrustedUntilAt, parseMemoryDate, validFromWorthShowingDays, } from "@manta-eu/client/shared/memory-preload-gate";
|
|
2
|
+
import { differenceInCalendarDays, format } from "date-fns";
|
|
3
|
+
import { match } from "ts-pattern";
|
|
4
|
+
// Compares by timestamp, not calendar day, so a memory expiring tonight is not expired yet.
|
|
5
|
+
export function formatExpiryRelative(expiresAt, now = new Date()) {
|
|
6
|
+
const at = parseMemoryDate(expiresAt);
|
|
7
|
+
if (!at)
|
|
8
|
+
return "Never expires";
|
|
9
|
+
if (at.getTime() <= now.getTime())
|
|
10
|
+
return "Expired";
|
|
11
|
+
const days = differenceInCalendarDays(at, now);
|
|
12
|
+
if (days <= 0)
|
|
13
|
+
return "Expires today";
|
|
14
|
+
if (days === 1)
|
|
15
|
+
return "Expires tomorrow";
|
|
16
|
+
return `Expires in ${days}d`;
|
|
17
|
+
}
|
|
18
|
+
export function formatExpiryDate(expiresAt, now = new Date()) {
|
|
19
|
+
const at = parseMemoryDate(expiresAt);
|
|
20
|
+
if (!at)
|
|
21
|
+
return "Never";
|
|
22
|
+
if (at.getTime() <= now.getTime())
|
|
23
|
+
return "Expired";
|
|
24
|
+
return format(at, "MMM d, yyyy");
|
|
25
|
+
}
|
|
26
|
+
export function memoryHorizon(memoryValidUntil, memoryTrustedUntil) {
|
|
27
|
+
if (memoryValidUntil)
|
|
28
|
+
return { kind: "expiry", at: memoryValidUntil };
|
|
29
|
+
if (memoryTrustedUntil)
|
|
30
|
+
return { kind: "trust", at: memoryTrustedUntil };
|
|
31
|
+
return { kind: "none", at: null };
|
|
32
|
+
}
|
|
33
|
+
export function memoryWindowState(memoryValidFrom, memoryValidUntil, now = new Date()) {
|
|
34
|
+
const until = parseMemoryDate(memoryValidUntil);
|
|
35
|
+
if (until && until.getTime() <= now.getTime())
|
|
36
|
+
return "expired";
|
|
37
|
+
const from = parseMemoryDate(memoryValidFrom);
|
|
38
|
+
if (from && from.getTime() > now.getTime())
|
|
39
|
+
return "scheduled";
|
|
40
|
+
return "active";
|
|
41
|
+
}
|
|
42
|
+
// Past horizon means the data is still valid and never deleted, just no longer relied on.
|
|
43
|
+
export function isTrustExpired(memoryTrustedUntil, now = new Date()) {
|
|
44
|
+
return isPastTrustedUntilAt(memoryTrustedUntil, now);
|
|
45
|
+
}
|
|
46
|
+
export const unconfirmedHorizonLabel = "Unconfirmed";
|
|
47
|
+
export function formatHorizonDate(horizon, now = new Date()) {
|
|
48
|
+
return match(horizon)
|
|
49
|
+
.with({ kind: "none" }, () => "Never")
|
|
50
|
+
.with({ kind: "expiry" }, ({ at }) => formatExpiryDate(at, now))
|
|
51
|
+
.with({ kind: "trust" }, ({ at }) => {
|
|
52
|
+
const parsed = parseMemoryDate(at);
|
|
53
|
+
if (!parsed)
|
|
54
|
+
return "Never";
|
|
55
|
+
return isTrustExpired(at, now) ? unconfirmedHorizonLabel : `~${format(parsed, "MMM d, yyyy")}`;
|
|
56
|
+
})
|
|
57
|
+
.exhaustive();
|
|
58
|
+
}
|
|
59
|
+
function trustedForLabel(at, now) {
|
|
60
|
+
const parsed = parseMemoryDate(at);
|
|
61
|
+
if (!parsed)
|
|
62
|
+
return "Never expires";
|
|
63
|
+
const days = differenceInCalendarDays(parsed, now);
|
|
64
|
+
if (days <= 0)
|
|
65
|
+
return "Trusted until today";
|
|
66
|
+
return days === 1 ? "Trusted until tomorrow" : `Trusted for ${days}d`;
|
|
67
|
+
}
|
|
68
|
+
export function formatHorizonRelative(horizon, now = new Date()) {
|
|
69
|
+
return match(horizon)
|
|
70
|
+
.with({ kind: "none" }, () => "Never expires")
|
|
71
|
+
.with({ kind: "expiry" }, ({ at }) => formatExpiryRelative(at, now))
|
|
72
|
+
.with({ kind: "trust" }, ({ at }) => (isTrustExpired(at, now) ? unconfirmedHorizonLabel : trustedForLabel(at, now)))
|
|
73
|
+
.exhaustive();
|
|
74
|
+
}
|
|
75
|
+
export function compareMemoryHorizons(a, b) {
|
|
76
|
+
const left = parseMemoryDate(a.at);
|
|
77
|
+
const right = parseMemoryDate(b.at);
|
|
78
|
+
if (!left || !right)
|
|
79
|
+
return left ? -1 : right ? 1 : 0;
|
|
80
|
+
return left.getTime() - right.getTime();
|
|
81
|
+
}
|
|
82
|
+
export function shouldShowValidFrom(updatedAt, memoryValidFrom) {
|
|
83
|
+
const since = parseMemoryDate(memoryValidFrom);
|
|
84
|
+
const recorded = parseMemoryDate(updatedAt);
|
|
85
|
+
if (!since || !recorded)
|
|
86
|
+
return false;
|
|
87
|
+
return differenceInCalendarDays(recorded, since) >= validFromWorthShowingDays;
|
|
88
|
+
}
|
|
89
|
+
export function formatHorizonTooltip(horizon, now = new Date()) {
|
|
90
|
+
return match(horizon)
|
|
91
|
+
.with({ kind: "none" }, () => "Never expires")
|
|
92
|
+
.with({ kind: "expiry" }, ({ at }) => `No longer true after this date. ${formatExpiryRelative(at, now)}`)
|
|
93
|
+
.with({ kind: "trust" }, (trust) => isTrustExpired(trust.at, now)
|
|
94
|
+
? "Past the date it was trusted until, so it no longer pre-loads into conversations. Still true, still searchable, and never deleted."
|
|
95
|
+
: `Stops pre-loading into conversations after this date, but stays searchable. ${formatHorizonRelative(trust, now)}`)
|
|
96
|
+
.exhaustive();
|
|
97
|
+
}
|