@deepcitation/deepcitation-js 1.1.26 → 1.1.28
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/LICENSE +21 -21
- package/README.md +253 -253
- package/lib/chunk-2IZXUOQR.js +66 -0
- package/lib/chunk-4FGOHQFP.cjs +66 -0
- package/lib/chunk-CFXDRAJL.cjs +1 -0
- package/lib/chunk-DEUSSEFH.js +2 -0
- package/lib/chunk-F2MMVEVC.cjs +1 -0
- package/lib/chunk-J7U6YFOI.cjs +2 -0
- package/lib/chunk-O2XFH626.js +1 -0
- package/lib/chunk-RQPZSRID.js +1 -0
- package/lib/client/index.cjs +1 -0
- package/lib/client/{DeepCitation.d.ts → index.d.cts} +159 -3
- package/lib/client/index.d.ts +342 -2
- package/lib/client/index.js +1 -1
- package/lib/index.cjs +1 -0
- package/lib/index.d.cts +127 -0
- package/lib/index.d.ts +126 -23
- package/lib/index.js +1 -22
- package/lib/prompts/index.cjs +1 -0
- package/lib/prompts/index.d.cts +196 -0
- package/lib/prompts/index.d.ts +196 -3
- package/lib/prompts/index.js +1 -3
- package/lib/react/index.cjs +4 -0
- package/lib/react/index.js +4 -18
- package/lib/types/index.cjs +1 -0
- package/lib/types/index.d.cts +96 -0
- package/lib/types/index.d.ts +96 -11
- package/lib/types/index.js +1 -7
- package/package.json +62 -10
- package/lib/client/DeepCitation.js +0 -374
- package/lib/client/types.d.ts +0 -154
- package/lib/client/types.js +0 -1
- package/lib/parsing/normalizeCitation.d.ts +0 -5
- package/lib/parsing/normalizeCitation.js +0 -198
- package/lib/parsing/parseCitation.d.ts +0 -79
- package/lib/parsing/parseCitation.js +0 -431
- package/lib/parsing/parseWorkAround.d.ts +0 -2
- package/lib/parsing/parseWorkAround.js +0 -73
- package/lib/prompts/citationPrompts.d.ts +0 -138
- package/lib/prompts/citationPrompts.js +0 -168
- package/lib/prompts/promptCompression.d.ts +0 -14
- package/lib/prompts/promptCompression.js +0 -127
- package/lib/prompts/types.d.ts +0 -4
- package/lib/prompts/types.js +0 -1
- package/lib/react/CitationComponent.d.ts +0 -93
- package/lib/react/CitationComponent.js +0 -371
- package/lib/react/CitationVariants.d.ts +0 -132
- package/lib/react/CitationVariants.js +0 -284
- package/lib/react/DiffDisplay.d.ts +0 -10
- package/lib/react/DiffDisplay.js +0 -33
- package/lib/react/Popover.d.ts +0 -15
- package/lib/react/Popover.js +0 -20
- package/lib/react/UrlCitationComponent.d.ts +0 -83
- package/lib/react/UrlCitationComponent.js +0 -224
- package/lib/react/VerificationTabs.d.ts +0 -10
- package/lib/react/VerificationTabs.js +0 -36
- package/lib/react/icons.d.ts +0 -18
- package/lib/react/icons.js +0 -16
- package/lib/react/index.d.ts +0 -16
- package/lib/react/primitives.d.ts +0 -101
- package/lib/react/primitives.js +0 -193
- package/lib/react/types.d.ts +0 -283
- package/lib/react/types.js +0 -1
- package/lib/react/useSmartDiff.d.ts +0 -16
- package/lib/react/useSmartDiff.js +0 -64
- package/lib/react/utils.d.ts +0 -43
- package/lib/react/utils.js +0 -89
- package/lib/types/boxes.d.ts +0 -11
- package/lib/types/boxes.js +0 -1
- package/lib/types/citation.d.ts +0 -39
- package/lib/types/citation.js +0 -1
- package/lib/types/search.d.ts +0 -19
- package/lib/types/search.js +0 -1
- package/lib/types/verification.d.ts +0 -27
- package/lib/types/verification.js +0 -11
- package/lib/utils/diff.d.ts +0 -60
- package/lib/utils/diff.js +0 -414
- package/lib/utils/sha.d.ts +0 -10
- package/lib/utils/sha.js +0 -108
|
@@ -1,371 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { forwardRef, memo, useCallback, useEffect, useMemo, useState, } from "react";
|
|
3
|
-
import { createPortal } from "react-dom";
|
|
4
|
-
import { CheckIcon, SpinnerIcon, WarningIcon } from "./icons.js";
|
|
5
|
-
import { Popover, PopoverContent, PopoverTrigger } from "./Popover.js";
|
|
6
|
-
import { cn, generateCitationInstanceId, generateCitationKey, getCitationDisplayText, } from "./utils.js";
|
|
7
|
-
import { useSmartDiff } from "./useSmartDiff.js";
|
|
8
|
-
function getStatusLabel(status) {
|
|
9
|
-
if (status.isVerified && !status.isPartialMatch)
|
|
10
|
-
return "Verified";
|
|
11
|
-
if (status.isPartialMatch)
|
|
12
|
-
return "Partial Match";
|
|
13
|
-
if (status.isMiss)
|
|
14
|
-
return "Not Found";
|
|
15
|
-
if (status.isPending)
|
|
16
|
-
return "Verifying...";
|
|
17
|
-
return "";
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Derive citation status from a Verification object.
|
|
21
|
-
* The status comes from verification.status.
|
|
22
|
-
*/
|
|
23
|
-
function getStatusFromVerification(verification) {
|
|
24
|
-
const status = verification?.status;
|
|
25
|
-
// No verification or no status = pending
|
|
26
|
-
if (!verification || !status) {
|
|
27
|
-
return {
|
|
28
|
-
isVerified: false,
|
|
29
|
-
isMiss: false,
|
|
30
|
-
isPartialMatch: false,
|
|
31
|
-
isPending: true,
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
const isMiss = status === "not_found";
|
|
35
|
-
const isPending = status === "pending" || status === "loading";
|
|
36
|
-
const isPartialMatch = status === "partial_text_found" ||
|
|
37
|
-
status === "found_on_other_page" ||
|
|
38
|
-
status === "found_on_other_line" ||
|
|
39
|
-
status === "first_word_found";
|
|
40
|
-
const isVerified = status === "found" ||
|
|
41
|
-
status === "found_key_span_only" ||
|
|
42
|
-
status === "found_phrase_missed_value" ||
|
|
43
|
-
isPartialMatch;
|
|
44
|
-
return { isVerified, isMiss, isPartialMatch, isPending };
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Full-screen image overlay for zoomed verification images.
|
|
48
|
-
* Click anywhere or press Escape to close.
|
|
49
|
-
*/
|
|
50
|
-
function ImageOverlay({ src, alt, onClose }) {
|
|
51
|
-
useEffect(() => {
|
|
52
|
-
const handleKeyDown = (e) => {
|
|
53
|
-
if (e.key === "Escape")
|
|
54
|
-
onClose();
|
|
55
|
-
};
|
|
56
|
-
document.addEventListener("keydown", handleKeyDown);
|
|
57
|
-
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
58
|
-
}, [onClose]);
|
|
59
|
-
return createPortal(_jsx("div", { className: "fixed inset-0 z-[9999] flex items-center justify-center bg-black/80 backdrop-blur-sm animate-in fade-in-0", onClick: onClose, role: "dialog", "aria-modal": "true", "aria-label": "Full size verification image", children: _jsx("div", { className: "relative max-w-[95vw] max-h-[95vh] cursor-zoom-out", children: _jsx("img", { src: src, alt: alt, className: "max-w-full max-h-[95vh] object-contain rounded-lg shadow-2xl", draggable: false }) }) }), document.body);
|
|
60
|
-
}
|
|
61
|
-
// =============================================================================
|
|
62
|
-
// INDICATOR COMPONENTS
|
|
63
|
-
// =============================================================================
|
|
64
|
-
//
|
|
65
|
-
// Status indicators show the verification state visually:
|
|
66
|
-
//
|
|
67
|
-
// | Status | Indicator | Color | searchState.status values |
|
|
68
|
-
// |---------------|--------------------| -------|----------------------------------------------|
|
|
69
|
-
// | Pending | Spinner | Gray | "pending", "loading", null/undefined |
|
|
70
|
-
// | Verified | Checkmark (✓) | Green | "found", "found_key_span_only", etc. |
|
|
71
|
-
// | Partial Match | Checkmark (✓) | Amber | "found_on_other_page", "partial_text_found" |
|
|
72
|
-
// | Not Found | Warning triangle | Red | "not_found" |
|
|
73
|
-
//
|
|
74
|
-
// Use `renderIndicator` prop to customize. Use `variant="indicator"` to show only the icon.
|
|
75
|
-
// =============================================================================
|
|
76
|
-
/** Verified indicator - green checkmark for exact matches */
|
|
77
|
-
const VerifiedIndicator = () => (_jsx("span", { className: "inline-flex relative ml-0.5 text-green-600 dark:text-green-500", "aria-hidden": "true", children: _jsx(CheckIcon, {}) }));
|
|
78
|
-
/** Partial match indicator - amber checkmark for partial/relocated matches */
|
|
79
|
-
const PartialIndicator = () => (_jsx("span", { className: "inline-flex relative ml-0.5 text-amber-600 dark:text-amber-500", "aria-hidden": "true", children: _jsx(CheckIcon, {}) }));
|
|
80
|
-
/** Pending indicator - spinner for loading state */
|
|
81
|
-
const PendingIndicator = () => (_jsx("span", { className: "inline-flex ml-1 text-gray-400 dark:text-gray-500", "aria-hidden": "true", children: _jsx(SpinnerIcon, {}) }));
|
|
82
|
-
/** Miss indicator - red warning triangle for not found */
|
|
83
|
-
const MissIndicator = () => (_jsx("span", { className: "inline-flex relative ml-0.5 text-red-500 dark:text-red-400", "aria-hidden": "true", children: _jsx(WarningIcon, {}) }));
|
|
84
|
-
function DefaultPopoverContent({ citation, verification, status, onImageClick, }) {
|
|
85
|
-
const hasImage = verification?.verificationImageBase64;
|
|
86
|
-
const { isMiss, isPartialMatch } = status;
|
|
87
|
-
// Image view
|
|
88
|
-
if (hasImage) {
|
|
89
|
-
return (_jsxs("div", { className: "p-1", children: [_jsx("button", { type: "button", className: "block cursor-zoom-in", onClick: (e) => {
|
|
90
|
-
e.preventDefault();
|
|
91
|
-
e.stopPropagation();
|
|
92
|
-
onImageClick?.();
|
|
93
|
-
}, "aria-label": "Click to view full size", children: _jsx("img", { src: verification.verificationImageBase64, alt: "Citation verification", className: "max-w-[700px] max-h-[500px] w-auto h-auto object-contain rounded bg-gray-50 dark:bg-gray-800", loading: "lazy" }) }), (isMiss || isPartialMatch) && (_jsx(DiffDetails, { citation: citation, verification: verification, status: status }))] }));
|
|
94
|
-
}
|
|
95
|
-
// Text-only view
|
|
96
|
-
const statusLabel = getStatusLabel(status);
|
|
97
|
-
const hasSnippet = verification?.verifiedMatchSnippet;
|
|
98
|
-
const pageNumber = verification?.verifiedPageNumber;
|
|
99
|
-
if (!hasSnippet && !statusLabel)
|
|
100
|
-
return null;
|
|
101
|
-
return (_jsxs("div", { className: "p-3 flex flex-col gap-2 min-w-[200px] max-w-[400px]", children: [statusLabel && (_jsx("span", { className: cn("text-xs font-medium", status.isVerified &&
|
|
102
|
-
!status.isPartialMatch &&
|
|
103
|
-
"text-green-600 dark:text-green-500", status.isPartialMatch && "text-amber-600 dark:text-amber-500", status.isMiss && "text-red-600 dark:text-red-500", status.isPending && "text-gray-500 dark:text-gray-400"), children: statusLabel })), hasSnippet && (_jsxs("span", { className: "text-sm text-gray-700 dark:text-gray-300 italic", children: ["\"", verification.verifiedMatchSnippet, "\""] })), pageNumber && pageNumber > 0 && (_jsxs("span", { className: "text-xs text-gray-500 dark:text-gray-400", children: ["Page ", pageNumber] })), (isMiss || isPartialMatch) && (_jsx(DiffDetails, { citation: citation, verification: verification, status: status }))] }));
|
|
104
|
-
}
|
|
105
|
-
// =============================================================================
|
|
106
|
-
// DIFF DETAILS COMPONENT
|
|
107
|
-
// =============================================================================
|
|
108
|
-
/**
|
|
109
|
-
* Renders diff highlighting between expected citation text and actual found text.
|
|
110
|
-
* Uses the `diff` library via useSmartDiff hook for word-level highlighting.
|
|
111
|
-
*/
|
|
112
|
-
function DiffDetails({ citation, verification, status, }) {
|
|
113
|
-
const { isMiss, isPartialMatch } = status;
|
|
114
|
-
const expectedText = citation.fullPhrase || citation.keySpan?.toString() || "";
|
|
115
|
-
const actualText = verification?.verifiedMatchSnippet || "";
|
|
116
|
-
// Use the diff library for smart word-level diffing
|
|
117
|
-
const { diffResult, hasDiff, isHighVariance } = useSmartDiff(expectedText, actualText);
|
|
118
|
-
if (!isMiss && !isPartialMatch)
|
|
119
|
-
return null;
|
|
120
|
-
const expectedLineIds = citation.lineIds;
|
|
121
|
-
const actualLineIds = verification?.verifiedLineIds;
|
|
122
|
-
const lineIdDiffers = expectedLineIds &&
|
|
123
|
-
actualLineIds &&
|
|
124
|
-
JSON.stringify(expectedLineIds) !== JSON.stringify(actualLineIds);
|
|
125
|
-
const expectedPage = citation.pageNumber;
|
|
126
|
-
const actualPage = verification?.verifiedPageNumber;
|
|
127
|
-
const pageDiffers = expectedPage != null && actualPage != null && expectedPage !== actualPage;
|
|
128
|
-
// For "not_found" status, show expected text and "Not found" message
|
|
129
|
-
if (isMiss) {
|
|
130
|
-
return (_jsxs("div", { className: "mt-2 pt-2 border-t border-gray-200 dark:border-gray-700 text-xs space-y-2", children: [expectedText && (_jsxs("div", { children: [_jsx("span", { className: "text-gray-500 dark:text-gray-400 font-medium uppercase text-[10px]", children: "Expected" }), _jsx("p", { className: "mt-1 p-2 bg-gray-100 dark:bg-gray-800 rounded font-mono text-[11px] break-words text-red-600 dark:text-red-400 line-through opacity-70", children: expectedText.length > 100
|
|
131
|
-
? expectedText.slice(0, 100) + "…"
|
|
132
|
-
: expectedText })] })), _jsxs("div", { children: [_jsx("span", { className: "text-gray-500 dark:text-gray-400 font-medium uppercase text-[10px]", children: "Found" }), _jsx("p", { className: "mt-1 p-2 bg-gray-100 dark:bg-gray-800 rounded font-mono text-[11px] text-amber-600 dark:text-amber-500 italic", children: "Not found in source" })] })] }));
|
|
133
|
-
}
|
|
134
|
-
// For partial matches, show word-level diff
|
|
135
|
-
return (_jsxs("div", { className: "mt-2 pt-2 border-t border-gray-200 dark:border-gray-700 text-xs space-y-2", children: [expectedText && actualText && hasDiff ? (_jsxs("div", { children: [_jsx("span", { className: "text-gray-500 dark:text-gray-400 font-medium uppercase text-[10px]", children: "Diff" }), _jsx("div", { className: "mt-1 p-2 bg-gray-100 dark:bg-gray-800 rounded font-mono text-[11px] break-words text-gray-700 dark:text-gray-300", children: isHighVariance ? (_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { children: [_jsxs("span", { className: "text-gray-500 dark:text-gray-400 text-[10px]", children: ["Expected:", " "] }), _jsx("span", { className: "text-red-600 dark:text-red-400 line-through opacity-70", children: expectedText.length > 100
|
|
136
|
-
? expectedText.slice(0, 100) + "…"
|
|
137
|
-
: expectedText })] }), _jsxs("div", { children: [_jsxs("span", { className: "text-gray-500 dark:text-gray-400 text-[10px]", children: ["Found:", " "] }), _jsx("span", { className: "text-green-600 dark:text-green-400", children: actualText.length > 100
|
|
138
|
-
? actualText.slice(0, 100) + "…"
|
|
139
|
-
: actualText })] })] })) : (
|
|
140
|
-
// Inline word-level diff
|
|
141
|
-
diffResult.map((block, blockIndex) => (_jsx("span", { children: block.parts.map((part, partIndex) => {
|
|
142
|
-
const key = `p-${blockIndex}-${partIndex}`;
|
|
143
|
-
if (part.removed) {
|
|
144
|
-
return (_jsx("span", { className: "bg-red-200 dark:bg-red-900/50 text-red-700 dark:text-red-300 line-through", title: "Expected text", children: part.value }, key));
|
|
145
|
-
}
|
|
146
|
-
if (part.added) {
|
|
147
|
-
return (_jsx("span", { className: "bg-green-200 dark:bg-green-900/50 text-green-700 dark:text-green-300", title: "Actual text found", children: part.value }, key));
|
|
148
|
-
}
|
|
149
|
-
// Unchanged text
|
|
150
|
-
return _jsx("span", { children: part.value }, key);
|
|
151
|
-
}) }, `block-${blockIndex}`)))) })] })) : expectedText && !hasDiff ? (
|
|
152
|
-
// Text matches exactly (partial match is due to location difference)
|
|
153
|
-
_jsxs("div", { children: [_jsx("span", { className: "text-gray-500 dark:text-gray-400 font-medium uppercase text-[10px]", children: "Text" }), _jsx("p", { className: "mt-1 p-2 bg-gray-100 dark:bg-gray-800 rounded font-mono text-[11px] break-words text-gray-700 dark:text-gray-300", children: expectedText.length > 100
|
|
154
|
-
? expectedText.slice(0, 100) + "…"
|
|
155
|
-
: expectedText })] })) : null, pageDiffers && (_jsxs("div", { children: [_jsx("span", { className: "text-gray-500 dark:text-gray-400 font-medium uppercase text-[10px]", children: "Page" }), _jsxs("p", { className: "mt-1 font-mono text-[11px] text-gray-700 dark:text-gray-300", children: [_jsx("span", { className: "text-red-600 dark:text-red-400 line-through opacity-70", children: expectedPage }), " → ", actualPage] })] })), lineIdDiffers && (_jsxs("div", { children: [_jsx("span", { className: "text-gray-500 dark:text-gray-400 font-medium uppercase text-[10px]", children: "Line" }), _jsxs("p", { className: "mt-1 font-mono text-[11px] text-gray-700 dark:text-gray-300", children: [_jsx("span", { className: "text-red-600 dark:text-red-400 line-through opacity-70", children: expectedLineIds?.join(", ") }), " → ", actualLineIds?.join(", ")] })] }))] }));
|
|
156
|
-
}
|
|
157
|
-
// =============================================================================
|
|
158
|
-
// MAIN COMPONENT
|
|
159
|
-
// =============================================================================
|
|
160
|
-
/**
|
|
161
|
-
* CitationComponent displays a citation with verification status.
|
|
162
|
-
*
|
|
163
|
-
* ## Interaction Pattern
|
|
164
|
-
*
|
|
165
|
-
* - **Hover**: Shows popover with verification image or details
|
|
166
|
-
* - **Click**: Opens full-size image overlay (if image available)
|
|
167
|
-
* - **Escape / Click overlay**: Closes the image overlay
|
|
168
|
-
*
|
|
169
|
-
* ## Customization
|
|
170
|
-
*
|
|
171
|
-
* Use `behaviorConfig.onClick` to completely replace the click behavior,
|
|
172
|
-
* or `eventHandlers.onClick` to add side effects (which disables defaults).
|
|
173
|
-
*/
|
|
174
|
-
export const CitationComponent = forwardRef(({ citation, children, className, hideKeySpan = false, hideBrackets = false, fallbackDisplay, verification, variant = "brackets", eventHandlers, behaviorConfig, isMobile = false, renderIndicator, renderContent, popoverPosition = "top", renderPopoverContent, }, ref) => {
|
|
175
|
-
const [isHovering, setIsHovering] = useState(false);
|
|
176
|
-
const [expandedImageSrc, setExpandedImageSrc] = useState(null);
|
|
177
|
-
const citationKey = useMemo(() => generateCitationKey(citation), [citation]);
|
|
178
|
-
const citationInstanceId = useMemo(() => generateCitationInstanceId(citationKey), [citationKey]);
|
|
179
|
-
// Derive status from verification object
|
|
180
|
-
const status = useMemo(() => getStatusFromVerification(verification), [verification]);
|
|
181
|
-
const { isMiss, isPartialMatch, isVerified, isPending } = status;
|
|
182
|
-
const displayText = useMemo(() => {
|
|
183
|
-
return getCitationDisplayText(citation, {
|
|
184
|
-
hideKeySpan: variant !== "text" && variant !== "minimal" && hideKeySpan,
|
|
185
|
-
fallbackDisplay,
|
|
186
|
-
});
|
|
187
|
-
}, [citation, variant, hideKeySpan, fallbackDisplay]);
|
|
188
|
-
// Behavior context for custom handlers
|
|
189
|
-
const getBehaviorContext = useCallback(() => ({
|
|
190
|
-
citation,
|
|
191
|
-
citationKey,
|
|
192
|
-
verification: verification ?? null,
|
|
193
|
-
isTooltipExpanded: isHovering,
|
|
194
|
-
isImageExpanded: !!expandedImageSrc,
|
|
195
|
-
hasImage: !!verification?.verificationImageBase64,
|
|
196
|
-
}), [citation, citationKey, verification, isHovering, expandedImageSrc]);
|
|
197
|
-
// Apply behavior actions from custom handler
|
|
198
|
-
const applyBehaviorActions = useCallback((actions) => {
|
|
199
|
-
if (actions.setImageExpanded !== undefined) {
|
|
200
|
-
if (typeof actions.setImageExpanded === "string") {
|
|
201
|
-
setExpandedImageSrc(actions.setImageExpanded);
|
|
202
|
-
}
|
|
203
|
-
else if (actions.setImageExpanded === true &&
|
|
204
|
-
verification?.verificationImageBase64) {
|
|
205
|
-
setExpandedImageSrc(verification.verificationImageBase64);
|
|
206
|
-
}
|
|
207
|
-
else if (actions.setImageExpanded === false) {
|
|
208
|
-
setExpandedImageSrc(null);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}, [verification?.verificationImageBase64]);
|
|
212
|
-
// Click handler
|
|
213
|
-
const handleClick = useCallback((e) => {
|
|
214
|
-
e.preventDefault();
|
|
215
|
-
e.stopPropagation();
|
|
216
|
-
const context = getBehaviorContext();
|
|
217
|
-
// Custom onClick via behaviorConfig replaces default
|
|
218
|
-
if (behaviorConfig?.onClick) {
|
|
219
|
-
const result = behaviorConfig.onClick(context, e);
|
|
220
|
-
if (result && typeof result === "object") {
|
|
221
|
-
applyBehaviorActions(result);
|
|
222
|
-
}
|
|
223
|
-
eventHandlers?.onClick?.(citation, citationKey, e);
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
// Custom eventHandlers.onClick disables default
|
|
227
|
-
if (eventHandlers?.onClick) {
|
|
228
|
-
eventHandlers.onClick(citation, citationKey, e);
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
// Default: click opens image if available
|
|
232
|
-
if (verification?.verificationImageBase64) {
|
|
233
|
-
setExpandedImageSrc(verification.verificationImageBase64);
|
|
234
|
-
}
|
|
235
|
-
}, [
|
|
236
|
-
behaviorConfig,
|
|
237
|
-
eventHandlers,
|
|
238
|
-
citation,
|
|
239
|
-
citationKey,
|
|
240
|
-
verification?.verificationImageBase64,
|
|
241
|
-
getBehaviorContext,
|
|
242
|
-
applyBehaviorActions,
|
|
243
|
-
]);
|
|
244
|
-
// Hover handlers
|
|
245
|
-
const handleMouseEnter = useCallback(() => {
|
|
246
|
-
setIsHovering(true);
|
|
247
|
-
if (behaviorConfig?.onHover?.onEnter) {
|
|
248
|
-
behaviorConfig.onHover.onEnter(getBehaviorContext());
|
|
249
|
-
}
|
|
250
|
-
eventHandlers?.onMouseEnter?.(citation, citationKey);
|
|
251
|
-
}, [
|
|
252
|
-
eventHandlers,
|
|
253
|
-
behaviorConfig,
|
|
254
|
-
citation,
|
|
255
|
-
citationKey,
|
|
256
|
-
getBehaviorContext,
|
|
257
|
-
]);
|
|
258
|
-
const handleMouseLeave = useCallback(() => {
|
|
259
|
-
setIsHovering(false);
|
|
260
|
-
if (behaviorConfig?.onHover?.onLeave) {
|
|
261
|
-
behaviorConfig.onHover.onLeave(getBehaviorContext());
|
|
262
|
-
}
|
|
263
|
-
eventHandlers?.onMouseLeave?.(citation, citationKey);
|
|
264
|
-
}, [
|
|
265
|
-
eventHandlers,
|
|
266
|
-
behaviorConfig,
|
|
267
|
-
citation,
|
|
268
|
-
citationKey,
|
|
269
|
-
getBehaviorContext,
|
|
270
|
-
]);
|
|
271
|
-
// Touch handler for mobile
|
|
272
|
-
const handleTouchEnd = useCallback((e) => {
|
|
273
|
-
if (isMobile) {
|
|
274
|
-
e.preventDefault();
|
|
275
|
-
e.stopPropagation();
|
|
276
|
-
eventHandlers?.onTouchEnd?.(citation, citationKey, e);
|
|
277
|
-
}
|
|
278
|
-
}, [eventHandlers, citation, citationKey, isMobile]);
|
|
279
|
-
// Early return for miss with fallback display
|
|
280
|
-
if (fallbackDisplay !== null &&
|
|
281
|
-
fallbackDisplay !== undefined &&
|
|
282
|
-
!hideKeySpan &&
|
|
283
|
-
isMiss) {
|
|
284
|
-
return (_jsx("span", { className: cn("text-gray-400 dark:text-gray-500", className), children: fallbackDisplay }));
|
|
285
|
-
}
|
|
286
|
-
// Status classes for text styling
|
|
287
|
-
const statusClasses = cn(
|
|
288
|
-
// Found status (text color) - verified or partial match
|
|
289
|
-
(isVerified || isPartialMatch) &&
|
|
290
|
-
variant === "brackets" &&
|
|
291
|
-
"text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 hover:underline", isMiss && "opacity-70 line-through text-gray-400 dark:text-gray-500", isPending && "text-gray-500 dark:text-gray-400");
|
|
292
|
-
// Render indicator based on status priority:
|
|
293
|
-
// 1. Custom renderIndicator (if provided)
|
|
294
|
-
// 2. Pending → Spinner
|
|
295
|
-
// 3. Miss → Warning triangle
|
|
296
|
-
// 4. Partial match → Amber checkmark
|
|
297
|
-
// 5. Verified → Green checkmark
|
|
298
|
-
const renderStatusIndicator = () => {
|
|
299
|
-
if (renderIndicator)
|
|
300
|
-
return renderIndicator(status);
|
|
301
|
-
if (isPending)
|
|
302
|
-
return _jsx(PendingIndicator, {});
|
|
303
|
-
if (isMiss)
|
|
304
|
-
return _jsx(MissIndicator, {});
|
|
305
|
-
if (isPartialMatch)
|
|
306
|
-
return _jsx(PartialIndicator, {});
|
|
307
|
-
if (isVerified)
|
|
308
|
-
return _jsx(VerifiedIndicator, {});
|
|
309
|
-
return null;
|
|
310
|
-
};
|
|
311
|
-
// Render citation content
|
|
312
|
-
const renderCitationContent = () => {
|
|
313
|
-
if (renderContent) {
|
|
314
|
-
return renderContent({
|
|
315
|
-
citation,
|
|
316
|
-
status,
|
|
317
|
-
citationKey,
|
|
318
|
-
displayText,
|
|
319
|
-
isMergedDisplay: variant === "text" || variant === "brackets" || !hideKeySpan,
|
|
320
|
-
});
|
|
321
|
-
}
|
|
322
|
-
if (variant === "indicator") {
|
|
323
|
-
return _jsx("span", { children: renderStatusIndicator() });
|
|
324
|
-
}
|
|
325
|
-
if (variant === "text") {
|
|
326
|
-
return (_jsxs("span", { className: statusClasses, children: [displayText, renderStatusIndicator()] }));
|
|
327
|
-
}
|
|
328
|
-
if (variant === "minimal") {
|
|
329
|
-
return (_jsxs("span", { className: cn("max-w-80 overflow-hidden text-ellipsis", statusClasses), children: [displayText, renderStatusIndicator()] }));
|
|
330
|
-
}
|
|
331
|
-
// brackets variant (default)
|
|
332
|
-
return (_jsxs("span", { className: cn("inline-flex items-baseline gap-0.5 whitespace-nowrap", "font-mono text-xs leading-tight", "text-gray-500 dark:text-gray-400", "transition-colors"), "aria-hidden": "true", children: [!hideBrackets && "[", _jsxs("span", { className: cn("max-w-80 overflow-hidden text-ellipsis", statusClasses), children: [displayText, renderStatusIndicator()] }), !hideBrackets && "]"] }));
|
|
333
|
-
};
|
|
334
|
-
// Popover visibility
|
|
335
|
-
const isPopoverHidden = popoverPosition === "hidden";
|
|
336
|
-
const shouldShowPopover = !isPopoverHidden &&
|
|
337
|
-
verification &&
|
|
338
|
-
(verification.verificationImageBase64 ||
|
|
339
|
-
verification.verifiedMatchSnippet);
|
|
340
|
-
const hasImage = !!verification?.verificationImageBase64;
|
|
341
|
-
// Image overlay
|
|
342
|
-
const imageOverlay = expandedImageSrc ? (_jsx(ImageOverlay, { src: expandedImageSrc, alt: "Citation verification - full size", onClose: () => setExpandedImageSrc(null) })) : null;
|
|
343
|
-
// Shared trigger element props
|
|
344
|
-
const triggerProps = {
|
|
345
|
-
"data-citation-id": citationKey,
|
|
346
|
-
"data-citation-instance": citationInstanceId,
|
|
347
|
-
className: cn("relative inline-flex items-baseline cursor-pointer", "px-0.5 -mx-0.5 rounded-sm", "transition-all duration-150", "hover:bg-blue-500/10 dark:hover:bg-blue-400/10", hasImage && "hover:cursor-zoom-in", className),
|
|
348
|
-
onMouseEnter: handleMouseEnter,
|
|
349
|
-
onMouseLeave: handleMouseLeave,
|
|
350
|
-
onClick: handleClick,
|
|
351
|
-
onTouchEndCapture: isMobile ? handleTouchEnd : undefined,
|
|
352
|
-
"aria-label": displayText ? `[${displayText}]` : undefined,
|
|
353
|
-
};
|
|
354
|
-
// Render with Radix Popover
|
|
355
|
-
if (shouldShowPopover) {
|
|
356
|
-
const popoverContentElement = renderPopoverContent ? (renderPopoverContent({
|
|
357
|
-
citation,
|
|
358
|
-
verification: verification ?? null,
|
|
359
|
-
status,
|
|
360
|
-
})) : (_jsx(DefaultPopoverContent, { citation: citation, verification: verification ?? null, status: status, onImageClick: () => {
|
|
361
|
-
if (verification?.verificationImageBase64) {
|
|
362
|
-
setExpandedImageSrc(verification.verificationImageBase64);
|
|
363
|
-
}
|
|
364
|
-
} }));
|
|
365
|
-
return (_jsxs(_Fragment, { children: [children, _jsxs(Popover, { open: isHovering, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx("span", { ref: ref, ...triggerProps, children: renderCitationContent() }) }), _jsx(PopoverContent, { side: popoverPosition === "bottom" ? "bottom" : "top", onPointerDownOutside: (e) => e.preventDefault(), onInteractOutside: (e) => e.preventDefault(), children: popoverContentElement })] }), imageOverlay] }));
|
|
366
|
-
}
|
|
367
|
-
// Render without popover
|
|
368
|
-
return (_jsxs(_Fragment, { children: [children, _jsx("span", { ref: ref, ...triggerProps, children: renderCitationContent() }), imageOverlay] }));
|
|
369
|
-
});
|
|
370
|
-
CitationComponent.displayName = "CitationComponent";
|
|
371
|
-
export const MemoizedCitationComponent = memo(CitationComponent);
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import React, { type ReactNode } from "react";
|
|
2
|
-
import type { CitationStatus } from "../types/citation.js";
|
|
3
|
-
import type { Verification } from "../types/verification.js";
|
|
4
|
-
import type { BaseCitationProps, CitationVariant as CitationVariantType, CitationEventHandlers } from "./types.js";
|
|
5
|
-
/**
|
|
6
|
-
* Shared props for all citation variant components.
|
|
7
|
-
*/
|
|
8
|
-
export interface CitationVariantProps extends BaseCitationProps {
|
|
9
|
-
/** Found citation highlight location data */
|
|
10
|
-
verification?: Verification | null;
|
|
11
|
-
/** Event handlers */
|
|
12
|
-
eventHandlers?: CitationEventHandlers;
|
|
13
|
-
/** Whether on mobile device */
|
|
14
|
-
isMobile?: boolean;
|
|
15
|
-
/** Whether tooltips should be prevented */
|
|
16
|
-
preventTooltips?: boolean;
|
|
17
|
-
/** Custom pending text content */
|
|
18
|
-
pendingContent?: ReactNode;
|
|
19
|
-
/** Custom render function for verified indicator */
|
|
20
|
-
renderVerifiedIndicator?: (status: CitationStatus) => ReactNode;
|
|
21
|
-
/** Custom render function for partial match indicator */
|
|
22
|
-
renderPartialIndicator?: (status: CitationStatus) => ReactNode;
|
|
23
|
-
}
|
|
24
|
-
export interface ChipCitationProps extends CitationVariantProps {
|
|
25
|
-
/** Chip size */
|
|
26
|
-
size?: "sm" | "md" | "lg";
|
|
27
|
-
/** Whether to show an icon before the text */
|
|
28
|
-
showIcon?: boolean;
|
|
29
|
-
/** Custom icon to display */
|
|
30
|
-
icon?: ReactNode;
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Chip/Badge style citation component.
|
|
34
|
-
* Displays citation as a rounded pill/badge.
|
|
35
|
-
*
|
|
36
|
-
* @example
|
|
37
|
-
* ```tsx
|
|
38
|
-
* <ChipCitation citation={citation} verification={found} size="md" />
|
|
39
|
-
* ```
|
|
40
|
-
*/
|
|
41
|
-
export declare const ChipCitation: React.ForwardRefExoticComponent<ChipCitationProps & React.RefAttributes<HTMLSpanElement>>;
|
|
42
|
-
export interface SuperscriptCitationProps extends CitationVariantProps {
|
|
43
|
-
/** Whether to hide brackets around the superscript */
|
|
44
|
-
hideBrackets?: boolean;
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Superscript style citation component.
|
|
48
|
-
* Displays citation as a superscript number like academic papers.
|
|
49
|
-
*
|
|
50
|
-
* @example
|
|
51
|
-
* ```tsx
|
|
52
|
-
* <SuperscriptCitation citation={citation} verification={found} />
|
|
53
|
-
* // Renders: Text content¹
|
|
54
|
-
* ```
|
|
55
|
-
*/
|
|
56
|
-
export declare const SuperscriptCitation: React.ForwardRefExoticComponent<SuperscriptCitationProps & React.RefAttributes<HTMLSpanElement>>;
|
|
57
|
-
export interface FootnoteCitationProps extends CitationVariantProps {
|
|
58
|
-
/** Footnote symbol style */
|
|
59
|
-
symbolStyle?: "number" | "asterisk" | "dagger" | "custom";
|
|
60
|
-
/** Custom symbol (when symbolStyle is "custom") */
|
|
61
|
-
customSymbol?: string;
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Footnote style citation component.
|
|
65
|
-
* Displays citation as a footnote marker.
|
|
66
|
-
*
|
|
67
|
-
* @example
|
|
68
|
-
* ```tsx
|
|
69
|
-
* <FootnoteCitation citation={citation} symbolStyle="asterisk" />
|
|
70
|
-
* // Renders: Text content*
|
|
71
|
-
* ```
|
|
72
|
-
*/
|
|
73
|
-
export declare const FootnoteCitation: React.ForwardRefExoticComponent<FootnoteCitationProps & React.RefAttributes<HTMLSpanElement>>;
|
|
74
|
-
export interface InlineCitationProps extends CitationVariantProps {
|
|
75
|
-
/** Underline style */
|
|
76
|
-
underlineStyle?: "solid" | "dotted" | "dashed" | "none";
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Inline style citation component.
|
|
80
|
-
* Displays citation inline with subtle underline decoration.
|
|
81
|
-
*
|
|
82
|
-
* @example
|
|
83
|
-
* ```tsx
|
|
84
|
-
* <InlineCitation citation={citation} underlineStyle="dotted" />
|
|
85
|
-
* // Renders: "quoted text" with subtle underline
|
|
86
|
-
* ```
|
|
87
|
-
*/
|
|
88
|
-
export declare const InlineCitation: React.ForwardRefExoticComponent<InlineCitationProps & React.RefAttributes<HTMLSpanElement>>;
|
|
89
|
-
export interface MinimalCitationProps extends CitationVariantProps {
|
|
90
|
-
/** Whether to show status indicator */
|
|
91
|
-
showStatusIndicator?: boolean;
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Minimal style citation component.
|
|
95
|
-
* Displays just the citation number with minimal decoration.
|
|
96
|
-
*
|
|
97
|
-
* @example
|
|
98
|
-
* ```tsx
|
|
99
|
-
* <MinimalCitation citation={citation} />
|
|
100
|
-
* // Renders: 1
|
|
101
|
-
* ```
|
|
102
|
-
*/
|
|
103
|
-
export declare const MinimalCitation: React.ForwardRefExoticComponent<MinimalCitationProps & React.RefAttributes<HTMLSpanElement>>;
|
|
104
|
-
export interface VariantCitationProps extends CitationVariantProps {
|
|
105
|
-
/** The variant to render */
|
|
106
|
-
variant?: CitationVariantType;
|
|
107
|
-
/** Chip-specific props */
|
|
108
|
-
chipProps?: Partial<ChipCitationProps>;
|
|
109
|
-
/** Superscript-specific props */
|
|
110
|
-
superscriptProps?: Partial<SuperscriptCitationProps>;
|
|
111
|
-
/** Footnote-specific props */
|
|
112
|
-
footnoteProps?: Partial<FootnoteCitationProps>;
|
|
113
|
-
/** Inline-specific props */
|
|
114
|
-
inlineProps?: Partial<InlineCitationProps>;
|
|
115
|
-
/** Minimal-specific props */
|
|
116
|
-
minimalProps?: Partial<MinimalCitationProps>;
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Factory component that renders the appropriate citation variant.
|
|
120
|
-
*
|
|
121
|
-
* @example
|
|
122
|
-
* ```tsx
|
|
123
|
-
* <CitationVariantFactory variant="chip" citation={citation} chipProps={{ size: "lg" }} />
|
|
124
|
-
* ```
|
|
125
|
-
*/
|
|
126
|
-
export declare const CitationVariantFactory: React.ForwardRefExoticComponent<VariantCitationProps & React.RefAttributes<HTMLSpanElement>>;
|
|
127
|
-
export declare const MemoizedChipCitation: React.NamedExoticComponent<ChipCitationProps & React.RefAttributes<HTMLSpanElement>>;
|
|
128
|
-
export declare const MemoizedSuperscriptCitation: React.NamedExoticComponent<SuperscriptCitationProps & React.RefAttributes<HTMLSpanElement>>;
|
|
129
|
-
export declare const MemoizedFootnoteCitation: React.NamedExoticComponent<FootnoteCitationProps & React.RefAttributes<HTMLSpanElement>>;
|
|
130
|
-
export declare const MemoizedInlineCitation: React.NamedExoticComponent<InlineCitationProps & React.RefAttributes<HTMLSpanElement>>;
|
|
131
|
-
export declare const MemoizedMinimalCitation: React.NamedExoticComponent<MinimalCitationProps & React.RefAttributes<HTMLSpanElement>>;
|
|
132
|
-
export declare const MemoizedCitationVariantFactory: React.NamedExoticComponent<VariantCitationProps & React.RefAttributes<HTMLSpanElement>>;
|