@deepcitation/deepcitation-js 1.1.27 → 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.
Files changed (79) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +253 -253
  3. package/lib/chunk-2IZXUOQR.js +66 -0
  4. package/lib/chunk-4FGOHQFP.cjs +66 -0
  5. package/lib/chunk-CFXDRAJL.cjs +1 -0
  6. package/lib/chunk-DEUSSEFH.js +2 -0
  7. package/lib/chunk-F2MMVEVC.cjs +1 -0
  8. package/lib/chunk-J7U6YFOI.cjs +2 -0
  9. package/lib/chunk-O2XFH626.js +1 -0
  10. package/lib/chunk-RQPZSRID.js +1 -0
  11. package/lib/client/index.cjs +1 -0
  12. package/lib/client/{DeepCitation.d.ts → index.d.cts} +159 -3
  13. package/lib/client/index.d.ts +342 -2
  14. package/lib/client/index.js +1 -1
  15. package/lib/index.cjs +1 -0
  16. package/lib/index.d.cts +127 -0
  17. package/lib/index.d.ts +126 -22
  18. package/lib/index.js +1 -20
  19. package/lib/prompts/index.cjs +1 -0
  20. package/lib/prompts/index.d.cts +196 -0
  21. package/lib/prompts/index.d.ts +196 -3
  22. package/lib/prompts/index.js +1 -3
  23. package/lib/react/index.cjs +4 -0
  24. package/lib/react/index.js +4 -20
  25. package/lib/types/index.cjs +1 -0
  26. package/lib/types/index.d.cts +96 -0
  27. package/lib/types/index.d.ts +96 -11
  28. package/lib/types/index.js +1 -7
  29. package/package.json +46 -11
  30. package/lib/client/DeepCitation.js +0 -374
  31. package/lib/client/types.d.ts +0 -154
  32. package/lib/client/types.js +0 -1
  33. package/lib/parsing/normalizeCitation.d.ts +0 -5
  34. package/lib/parsing/normalizeCitation.js +0 -198
  35. package/lib/parsing/parseCitation.d.ts +0 -79
  36. package/lib/parsing/parseCitation.js +0 -431
  37. package/lib/parsing/parseWorkAround.d.ts +0 -2
  38. package/lib/parsing/parseWorkAround.js +0 -73
  39. package/lib/prompts/citationPrompts.d.ts +0 -138
  40. package/lib/prompts/citationPrompts.js +0 -168
  41. package/lib/prompts/promptCompression.d.ts +0 -14
  42. package/lib/prompts/promptCompression.js +0 -127
  43. package/lib/prompts/types.d.ts +0 -4
  44. package/lib/prompts/types.js +0 -1
  45. package/lib/react/CitationComponent.d.ts +0 -106
  46. package/lib/react/CitationComponent.js +0 -419
  47. package/lib/react/CitationVariants.d.ts +0 -132
  48. package/lib/react/CitationVariants.js +0 -277
  49. package/lib/react/DiffDisplay.d.ts +0 -10
  50. package/lib/react/DiffDisplay.js +0 -33
  51. package/lib/react/Popover.d.ts +0 -15
  52. package/lib/react/Popover.js +0 -20
  53. package/lib/react/UrlCitationComponent.d.ts +0 -83
  54. package/lib/react/UrlCitationComponent.js +0 -224
  55. package/lib/react/VerificationTabs.d.ts +0 -10
  56. package/lib/react/VerificationTabs.js +0 -36
  57. package/lib/react/icons.d.ts +0 -22
  58. package/lib/react/icons.js +0 -16
  59. package/lib/react/index.d.ts +0 -17
  60. package/lib/react/primitives.d.ts +0 -99
  61. package/lib/react/primitives.js +0 -187
  62. package/lib/react/types.d.ts +0 -315
  63. package/lib/react/types.js +0 -1
  64. package/lib/react/useSmartDiff.d.ts +0 -16
  65. package/lib/react/useSmartDiff.js +0 -64
  66. package/lib/react/utils.d.ts +0 -44
  67. package/lib/react/utils.js +0 -88
  68. package/lib/types/boxes.d.ts +0 -11
  69. package/lib/types/boxes.js +0 -1
  70. package/lib/types/citation.d.ts +0 -39
  71. package/lib/types/citation.js +0 -1
  72. package/lib/types/search.d.ts +0 -19
  73. package/lib/types/search.js +0 -1
  74. package/lib/types/verification.d.ts +0 -27
  75. package/lib/types/verification.js +0 -11
  76. package/lib/utils/diff.d.ts +0 -60
  77. package/lib/utils/diff.js +0 -414
  78. package/lib/utils/sha.d.ts +0 -10
  79. package/lib/utils/sha.js +0 -108
@@ -1,419 +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, } from "./utils.js";
7
- import { useSmartDiff } from "./useSmartDiff.js";
8
- /**
9
- * Get the default content type based on variant.
10
- */
11
- function getDefaultContent(variant) {
12
- switch (variant) {
13
- case "chip":
14
- case "text":
15
- return "keySpan";
16
- case "brackets":
17
- case "superscript":
18
- case "minimal":
19
- default:
20
- return "number";
21
- }
22
- }
23
- /**
24
- * Get display text based on content type and citation data.
25
- * Returns "1" as fallback if no citation number is available.
26
- */
27
- function getDisplayText(citation, content, fallbackDisplay) {
28
- if (content === "indicator") {
29
- return "";
30
- }
31
- if (content === "keySpan") {
32
- return (citation.keySpan?.toString() ||
33
- citation.citationNumber?.toString() ||
34
- fallbackDisplay ||
35
- "1");
36
- }
37
- // content === "number"
38
- return citation.citationNumber?.toString() || "1";
39
- }
40
- function getStatusLabel(status) {
41
- if (status.isVerified && !status.isPartialMatch)
42
- return "Verified";
43
- if (status.isPartialMatch)
44
- return "Partial Match";
45
- if (status.isMiss)
46
- return "Not Found";
47
- if (status.isPending)
48
- return "Verifying...";
49
- return "";
50
- }
51
- /**
52
- * Derive citation status from a Verification object.
53
- * The status comes from verification.status.
54
- */
55
- function getStatusFromVerification(verification) {
56
- const status = verification?.status;
57
- // No verification or no status = pending
58
- if (!verification || !status) {
59
- return {
60
- isVerified: false,
61
- isMiss: false,
62
- isPartialMatch: false,
63
- isPending: true,
64
- };
65
- }
66
- const isMiss = status === "not_found";
67
- const isPending = status === "pending" || status === "loading";
68
- const isPartialMatch = status === "partial_text_found" ||
69
- status === "found_on_other_page" ||
70
- status === "found_on_other_line" ||
71
- status === "first_word_found";
72
- const isVerified = status === "found" ||
73
- status === "found_key_span_only" ||
74
- status === "found_phrase_missed_value" ||
75
- isPartialMatch;
76
- return { isVerified, isMiss, isPartialMatch, isPending };
77
- }
78
- /**
79
- * Full-screen image overlay for zoomed verification images.
80
- * Click anywhere or press Escape to close.
81
- */
82
- function ImageOverlay({ src, alt, onClose }) {
83
- useEffect(() => {
84
- const handleKeyDown = (e) => {
85
- if (e.key === "Escape")
86
- onClose();
87
- };
88
- document.addEventListener("keydown", handleKeyDown);
89
- return () => document.removeEventListener("keydown", handleKeyDown);
90
- }, [onClose]);
91
- 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);
92
- }
93
- // =============================================================================
94
- // INDICATOR COMPONENTS
95
- // =============================================================================
96
- //
97
- // Status indicators show the verification state visually:
98
- //
99
- // | Status | Indicator | Color | searchState.status values |
100
- // |---------------|--------------------| -------|----------------------------------------------|
101
- // | Pending | Spinner | Gray | "pending", "loading", null/undefined |
102
- // | Verified | Checkmark (✓) | Green | "found", "found_key_span_only", etc. |
103
- // | Partial Match | Checkmark (✓) | Amber | "found_on_other_page", "partial_text_found" |
104
- // | Not Found | Warning triangle | Red | "not_found" |
105
- //
106
- // Use `renderIndicator` prop to customize. Use `variant="indicator"` to show only the icon.
107
- // =============================================================================
108
- /** Verified indicator - green checkmark for exact matches */
109
- const VerifiedIndicator = () => (_jsx("span", { className: "inline-flex relative ml-0.5 text-green-600 dark:text-green-500", "aria-hidden": "true", children: _jsx(CheckIcon, {}) }));
110
- /** Partial match indicator - amber checkmark for partial/relocated matches */
111
- const PartialIndicator = () => (_jsx("span", { className: "inline-flex relative ml-0.5 text-amber-600 dark:text-amber-500", "aria-hidden": "true", children: _jsx(CheckIcon, {}) }));
112
- /** Pending indicator - spinner for loading state */
113
- const PendingIndicator = () => (_jsx("span", { className: "inline-flex ml-1 text-gray-400 dark:text-gray-500", "aria-hidden": "true", children: _jsx(SpinnerIcon, {}) }));
114
- /** Miss indicator - red warning triangle for not found */
115
- const MissIndicator = () => (_jsx("span", { className: "inline-flex relative ml-0.5 text-red-500 dark:text-red-400", "aria-hidden": "true", children: _jsx(WarningIcon, {}) }));
116
- function DefaultPopoverContent({ citation, verification, status, onImageClick, }) {
117
- const hasImage = verification?.verificationImageBase64;
118
- const { isMiss, isPartialMatch } = status;
119
- // Image view
120
- if (hasImage) {
121
- return (_jsxs("div", { className: "p-1", children: [_jsx("button", { type: "button", className: "block cursor-zoom-in", onClick: (e) => {
122
- e.preventDefault();
123
- e.stopPropagation();
124
- onImageClick?.();
125
- }, "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 }))] }));
126
- }
127
- // Text-only view
128
- const statusLabel = getStatusLabel(status);
129
- const hasSnippet = verification?.verifiedMatchSnippet;
130
- const pageNumber = verification?.verifiedPageNumber;
131
- if (!hasSnippet && !statusLabel)
132
- return null;
133
- 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 &&
134
- !status.isPartialMatch &&
135
- "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 }))] }));
136
- }
137
- // =============================================================================
138
- // DIFF DETAILS COMPONENT
139
- // =============================================================================
140
- /**
141
- * Renders diff highlighting between expected citation text and actual found text.
142
- * Uses the `diff` library via useSmartDiff hook for word-level highlighting.
143
- */
144
- function DiffDetails({ citation, verification, status, }) {
145
- const { isMiss, isPartialMatch } = status;
146
- const expectedText = citation.fullPhrase || citation.keySpan?.toString() || "";
147
- const actualText = verification?.verifiedMatchSnippet || "";
148
- // Use the diff library for smart word-level diffing
149
- const { diffResult, hasDiff, isHighVariance } = useSmartDiff(expectedText, actualText);
150
- if (!isMiss && !isPartialMatch)
151
- return null;
152
- const expectedLineIds = citation.lineIds;
153
- const actualLineIds = verification?.verifiedLineIds;
154
- const lineIdDiffers = expectedLineIds &&
155
- actualLineIds &&
156
- JSON.stringify(expectedLineIds) !== JSON.stringify(actualLineIds);
157
- const expectedPage = citation.pageNumber;
158
- const actualPage = verification?.verifiedPageNumber;
159
- const pageDiffers = expectedPage != null && actualPage != null && expectedPage !== actualPage;
160
- // For "not_found" status, show expected text and "Not found" message
161
- if (isMiss) {
162
- 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
163
- ? expectedText.slice(0, 100) + "…"
164
- : 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" })] })] }));
165
- }
166
- // For partial matches, show word-level diff
167
- 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
168
- ? expectedText.slice(0, 100) + "…"
169
- : 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
170
- ? actualText.slice(0, 100) + "…"
171
- : actualText })] })] })) : (
172
- // Inline word-level diff
173
- diffResult.map((block, blockIndex) => (_jsx("span", { children: block.parts.map((part, partIndex) => {
174
- const key = `p-${blockIndex}-${partIndex}`;
175
- if (part.removed) {
176
- 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));
177
- }
178
- if (part.added) {
179
- 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));
180
- }
181
- // Unchanged text
182
- return _jsx("span", { children: part.value }, key);
183
- }) }, `block-${blockIndex}`)))) })] })) : expectedText && !hasDiff ? (
184
- // Text matches exactly (partial match is due to location difference)
185
- _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
186
- ? expectedText.slice(0, 100) + "…"
187
- : 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(", ")] })] }))] }));
188
- }
189
- // =============================================================================
190
- // MAIN COMPONENT
191
- // =============================================================================
192
- /**
193
- * CitationComponent displays a citation with verification status.
194
- *
195
- * ## Interaction Pattern
196
- *
197
- * - **Hover**: Shows popover with verification image or details
198
- * - **Click**: Opens full-size image overlay (if image available)
199
- * - **Escape / Click overlay**: Closes the image overlay
200
- *
201
- * ## Customization
202
- *
203
- * Use `behaviorConfig.onClick` to completely replace the click behavior,
204
- * or `eventHandlers.onClick` to add side effects (which disables defaults).
205
- */
206
- export const CitationComponent = forwardRef(({ citation, children, className, fallbackDisplay, verification, variant = "brackets", content: contentProp, eventHandlers, behaviorConfig, isMobile = false, renderIndicator, renderContent, popoverPosition = "top", renderPopoverContent, }, ref) => {
207
- // Resolve content: explicit content prop or default for variant
208
- const resolvedContent = useMemo(() => {
209
- if (contentProp)
210
- return contentProp;
211
- return getDefaultContent(variant);
212
- }, [contentProp, variant]);
213
- const [isHovering, setIsHovering] = useState(false);
214
- const [expandedImageSrc, setExpandedImageSrc] = useState(null);
215
- const citationKey = useMemo(() => generateCitationKey(citation), [citation]);
216
- const citationInstanceId = useMemo(() => generateCitationInstanceId(citationKey), [citationKey]);
217
- // Derive status from verification object
218
- const status = useMemo(() => getStatusFromVerification(verification), [verification]);
219
- const { isMiss, isPartialMatch, isVerified, isPending } = status;
220
- const displayText = useMemo(() => {
221
- return getDisplayText(citation, resolvedContent, fallbackDisplay);
222
- }, [citation, resolvedContent, fallbackDisplay]);
223
- // Behavior context for custom handlers
224
- const getBehaviorContext = useCallback(() => ({
225
- citation,
226
- citationKey,
227
- verification: verification ?? null,
228
- isTooltipExpanded: isHovering,
229
- isImageExpanded: !!expandedImageSrc,
230
- hasImage: !!verification?.verificationImageBase64,
231
- }), [citation, citationKey, verification, isHovering, expandedImageSrc]);
232
- // Apply behavior actions from custom handler
233
- const applyBehaviorActions = useCallback((actions) => {
234
- if (actions.setImageExpanded !== undefined) {
235
- if (typeof actions.setImageExpanded === "string") {
236
- setExpandedImageSrc(actions.setImageExpanded);
237
- }
238
- else if (actions.setImageExpanded === true &&
239
- verification?.verificationImageBase64) {
240
- setExpandedImageSrc(verification.verificationImageBase64);
241
- }
242
- else if (actions.setImageExpanded === false) {
243
- setExpandedImageSrc(null);
244
- }
245
- }
246
- }, [verification?.verificationImageBase64]);
247
- // Click handler
248
- const handleClick = useCallback((e) => {
249
- e.preventDefault();
250
- e.stopPropagation();
251
- const context = getBehaviorContext();
252
- // Custom onClick via behaviorConfig replaces default
253
- if (behaviorConfig?.onClick) {
254
- const result = behaviorConfig.onClick(context, e);
255
- if (result && typeof result === "object") {
256
- applyBehaviorActions(result);
257
- }
258
- eventHandlers?.onClick?.(citation, citationKey, e);
259
- return;
260
- }
261
- // Custom eventHandlers.onClick disables default
262
- if (eventHandlers?.onClick) {
263
- eventHandlers.onClick(citation, citationKey, e);
264
- return;
265
- }
266
- // Default: click opens image if available
267
- if (verification?.verificationImageBase64) {
268
- setExpandedImageSrc(verification.verificationImageBase64);
269
- }
270
- }, [
271
- behaviorConfig,
272
- eventHandlers,
273
- citation,
274
- citationKey,
275
- verification?.verificationImageBase64,
276
- getBehaviorContext,
277
- applyBehaviorActions,
278
- ]);
279
- // Hover handlers
280
- const handleMouseEnter = useCallback(() => {
281
- setIsHovering(true);
282
- if (behaviorConfig?.onHover?.onEnter) {
283
- behaviorConfig.onHover.onEnter(getBehaviorContext());
284
- }
285
- eventHandlers?.onMouseEnter?.(citation, citationKey);
286
- }, [
287
- eventHandlers,
288
- behaviorConfig,
289
- citation,
290
- citationKey,
291
- getBehaviorContext,
292
- ]);
293
- const handleMouseLeave = useCallback(() => {
294
- setIsHovering(false);
295
- if (behaviorConfig?.onHover?.onLeave) {
296
- behaviorConfig.onHover.onLeave(getBehaviorContext());
297
- }
298
- eventHandlers?.onMouseLeave?.(citation, citationKey);
299
- }, [
300
- eventHandlers,
301
- behaviorConfig,
302
- citation,
303
- citationKey,
304
- getBehaviorContext,
305
- ]);
306
- // Touch handler for mobile
307
- const handleTouchEnd = useCallback((e) => {
308
- if (isMobile) {
309
- e.preventDefault();
310
- e.stopPropagation();
311
- eventHandlers?.onTouchEnd?.(citation, citationKey, e);
312
- }
313
- }, [eventHandlers, citation, citationKey, isMobile]);
314
- // Early return for miss with fallback display (only when showing keySpan)
315
- if (fallbackDisplay !== null &&
316
- fallbackDisplay !== undefined &&
317
- resolvedContent === "keySpan" &&
318
- isMiss) {
319
- return (_jsx("span", { className: cn("text-gray-400 dark:text-gray-500", className), children: fallbackDisplay }));
320
- }
321
- // Status classes for text styling
322
- const statusClasses = cn(
323
- // Found status (text color) - verified or partial match
324
- (isVerified || isPartialMatch) &&
325
- variant === "brackets" &&
326
- "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");
327
- // Render indicator based on status priority:
328
- // 1. Custom renderIndicator (if provided)
329
- // 2. Pending → Spinner
330
- // 3. Miss → Warning triangle
331
- // 4. Partial match → Amber checkmark
332
- // 5. Verified → Green checkmark
333
- const renderStatusIndicator = () => {
334
- if (renderIndicator)
335
- return renderIndicator(status);
336
- if (isPending)
337
- return _jsx(PendingIndicator, {});
338
- if (isMiss)
339
- return _jsx(MissIndicator, {});
340
- if (isPartialMatch)
341
- return _jsx(PartialIndicator, {});
342
- if (isVerified)
343
- return _jsx(VerifiedIndicator, {});
344
- return null;
345
- };
346
- // Render citation content
347
- const renderCitationContent = () => {
348
- if (renderContent) {
349
- return renderContent({
350
- citation,
351
- status,
352
- citationKey,
353
- displayText,
354
- isMergedDisplay: resolvedContent === "keySpan",
355
- });
356
- }
357
- // Content type: indicator only
358
- if (resolvedContent === "indicator") {
359
- return _jsx("span", { children: renderStatusIndicator() });
360
- }
361
- // Variant: chip (pill/badge style)
362
- if (variant === "chip") {
363
- const chipStatusClasses = cn(isVerified && !isPartialMatch && "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400", isPartialMatch && "bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400", isMiss && "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400 line-through", isPending && "bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400", !isVerified && !isMiss && !isPending && "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400");
364
- return (_jsxs("span", { className: cn("inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-sm font-medium", chipStatusClasses), children: [_jsx("span", { className: "max-w-60 overflow-hidden text-ellipsis whitespace-nowrap", children: displayText }), renderStatusIndicator()] }));
365
- }
366
- // Variant: superscript (footnote style)
367
- if (variant === "superscript") {
368
- const supStatusClasses = cn(isVerified && !isPartialMatch && "text-green-600 dark:text-green-500", isPartialMatch && "text-amber-600 dark:text-amber-500", isMiss && "text-red-500 dark:text-red-400 line-through", isPending && "text-gray-400 dark:text-gray-500", !isVerified && !isMiss && !isPending && "text-blue-600 dark:text-blue-400");
369
- return (_jsxs("sup", { className: cn("text-xs font-medium transition-colors hover:underline", supStatusClasses), children: ["[", displayText, renderStatusIndicator(), "]"] }));
370
- }
371
- // Variant: text (inherits parent styling)
372
- if (variant === "text") {
373
- return (_jsxs("span", { className: statusClasses, children: [displayText, renderStatusIndicator()] }));
374
- }
375
- // Variant: minimal (compact with truncation)
376
- if (variant === "minimal") {
377
- return (_jsxs("span", { className: cn("max-w-80 overflow-hidden text-ellipsis", statusClasses), children: [displayText, renderStatusIndicator()] }));
378
- }
379
- // Variant: brackets (default)
380
- 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: ["[", _jsxs("span", { className: cn("max-w-80 overflow-hidden text-ellipsis", statusClasses), children: [displayText, renderStatusIndicator()] }), "]"] }));
381
- };
382
- // Popover visibility
383
- const isPopoverHidden = popoverPosition === "hidden";
384
- const shouldShowPopover = !isPopoverHidden &&
385
- verification &&
386
- (verification.verificationImageBase64 ||
387
- verification.verifiedMatchSnippet);
388
- const hasImage = !!verification?.verificationImageBase64;
389
- // Image overlay
390
- const imageOverlay = expandedImageSrc ? (_jsx(ImageOverlay, { src: expandedImageSrc, alt: "Citation verification - full size", onClose: () => setExpandedImageSrc(null) })) : null;
391
- // Shared trigger element props
392
- const triggerProps = {
393
- "data-citation-id": citationKey,
394
- "data-citation-instance": citationInstanceId,
395
- 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),
396
- onMouseEnter: handleMouseEnter,
397
- onMouseLeave: handleMouseLeave,
398
- onClick: handleClick,
399
- onTouchEndCapture: isMobile ? handleTouchEnd : undefined,
400
- "aria-label": displayText ? `[${displayText}]` : undefined,
401
- };
402
- // Render with Radix Popover
403
- if (shouldShowPopover) {
404
- const popoverContentElement = renderPopoverContent ? (renderPopoverContent({
405
- citation,
406
- verification: verification ?? null,
407
- status,
408
- })) : (_jsx(DefaultPopoverContent, { citation: citation, verification: verification ?? null, status: status, onImageClick: () => {
409
- if (verification?.verificationImageBase64) {
410
- setExpandedImageSrc(verification.verificationImageBase64);
411
- }
412
- } }));
413
- 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] }));
414
- }
415
- // Render without popover
416
- return (_jsxs(_Fragment, { children: [children, _jsx("span", { ref: ref, ...triggerProps, children: renderCitationContent() }), imageOverlay] }));
417
- });
418
- CitationComponent.displayName = "CitationComponent";
419
- 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>>;