@deepcitation/deepcitation-js 1.0.2 → 1.0.3

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 (58) hide show
  1. package/README.md +71 -1197
  2. package/lib/client/DeepCitation.d.ts +204 -0
  3. package/lib/client/DeepCitation.js +473 -0
  4. package/lib/client/index.d.ts +2 -0
  5. package/lib/client/index.js +1 -0
  6. package/lib/client/types.d.ts +157 -0
  7. package/lib/client/types.js +1 -0
  8. package/lib/index.d.ts +25 -0
  9. package/lib/index.js +22 -0
  10. package/lib/parsing/normalizeCitation.d.ts +5 -0
  11. package/lib/parsing/normalizeCitation.js +182 -0
  12. package/lib/parsing/parseCitation.d.ts +79 -0
  13. package/lib/parsing/parseCitation.js +371 -0
  14. package/lib/parsing/parseWorkAround.d.ts +2 -0
  15. package/lib/parsing/parseWorkAround.js +73 -0
  16. package/lib/prompts/citationPrompts.d.ts +133 -0
  17. package/lib/prompts/citationPrompts.js +152 -0
  18. package/lib/prompts/index.d.ts +3 -0
  19. package/lib/prompts/index.js +3 -0
  20. package/lib/prompts/promptCompression.d.ts +14 -0
  21. package/lib/prompts/promptCompression.js +109 -0
  22. package/lib/prompts/types.d.ts +4 -0
  23. package/lib/prompts/types.js +1 -0
  24. package/lib/react/CitationComponent.d.ts +134 -0
  25. package/lib/react/CitationComponent.js +376 -0
  26. package/lib/react/CitationVariants.d.ts +135 -0
  27. package/lib/react/CitationVariants.js +283 -0
  28. package/lib/react/DiffDisplay.d.ts +10 -0
  29. package/lib/react/DiffDisplay.js +33 -0
  30. package/lib/react/UrlCitationComponent.d.ts +83 -0
  31. package/lib/react/UrlCitationComponent.js +224 -0
  32. package/lib/react/VerificationTabs.d.ts +10 -0
  33. package/lib/react/VerificationTabs.js +36 -0
  34. package/lib/react/icons.d.ts +8 -0
  35. package/lib/react/icons.js +9 -0
  36. package/lib/react/index.d.ts +16 -0
  37. package/lib/react/index.js +18 -0
  38. package/lib/react/primitives.d.ts +104 -0
  39. package/lib/react/primitives.js +190 -0
  40. package/lib/react/types.d.ts +192 -0
  41. package/lib/react/types.js +1 -0
  42. package/lib/react/useSmartDiff.d.ts +16 -0
  43. package/lib/react/useSmartDiff.js +64 -0
  44. package/lib/react/utils.d.ts +34 -0
  45. package/lib/react/utils.js +59 -0
  46. package/lib/types/boxes.d.ts +11 -0
  47. package/lib/types/boxes.js +1 -0
  48. package/lib/types/citation.d.ts +44 -0
  49. package/lib/types/citation.js +2 -0
  50. package/lib/types/foundHighlight.d.ts +23 -0
  51. package/lib/types/foundHighlight.js +22 -0
  52. package/lib/types/index.d.ts +11 -0
  53. package/lib/types/index.js +7 -0
  54. package/lib/types/search.d.ts +30 -0
  55. package/lib/types/search.js +1 -0
  56. package/lib/utils/sha.d.ts +10 -0
  57. package/lib/utils/sha.js +108 -0
  58. package/package.json +5 -2
@@ -0,0 +1,376 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { forwardRef, memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
3
+ import { createPortal } from "react-dom";
4
+ import { CheckIcon, WarningIcon } from "./icons.js";
5
+ import { classNames, generateCitationInstanceId, generateCitationKey, getCitationDisplayText } from "./utils.js";
6
+ import { getCitationStatus } from "../parsing/parseCitation.js";
7
+ import "./styles.css";
8
+ const TWO_DOTS_THINKING_CONTENT = "..";
9
+ // =============================================================================
10
+ // INDICATORS
11
+ // =============================================================================
12
+ /**
13
+ * Default indicator for verified citations (exact match).
14
+ * Shows a green checkmark.
15
+ */
16
+ const DefaultVerifiedIndicator = () => (_jsx("span", { className: "dc-indicator dc-indicator--verified", "aria-hidden": "true", children: _jsx(CheckIcon, {}) }));
17
+ /**
18
+ * Default indicator for partial match citations.
19
+ * Shows an orange/warning checkmark.
20
+ */
21
+ const DefaultPartialIndicator = () => (_jsx("span", { className: "dc-indicator dc-indicator--partial", "aria-hidden": "true", children: _jsx(CheckIcon, {}) }));
22
+ // =============================================================================
23
+ // HELPER FUNCTIONS
24
+ // =============================================================================
25
+ /**
26
+ * Get status label for display in popover.
27
+ */
28
+ function getStatusLabel(status) {
29
+ if (status.isVerified && !status.isPartialMatch)
30
+ return "Verified";
31
+ if (status.isPartialMatch)
32
+ return "Partial Match";
33
+ if (status.isMiss)
34
+ return "Not Found";
35
+ if (status.isPending)
36
+ return "Verifying...";
37
+ return "";
38
+ }
39
+ /**
40
+ * Get popover status CSS class.
41
+ */
42
+ function getPopoverStatusClass(status) {
43
+ if (status.isVerified && !status.isPartialMatch)
44
+ return "dc-popover-status--verified";
45
+ if (status.isPartialMatch)
46
+ return "dc-popover-status--partial";
47
+ if (status.isMiss)
48
+ return "dc-popover-status--miss";
49
+ if (status.isPending)
50
+ return "dc-popover-status--pending";
51
+ return "";
52
+ }
53
+ /**
54
+ * Get the "found status" class for text styling.
55
+ * This determines if the text appears as found (blue) or not found (gray).
56
+ *
57
+ * Key insight: Partial matches ARE found - they just don't match exactly.
58
+ * So partial matches get "verified" text styling (blue) but with a different indicator.
59
+ */
60
+ function getFoundStatusClass(status) {
61
+ // Both verified AND partial are "found" - they get the same text styling
62
+ if (status.isVerified || status.isPartialMatch)
63
+ return "dc-citation--verified";
64
+ if (status.isMiss)
65
+ return "dc-citation--miss";
66
+ if (status.isPending)
67
+ return "dc-citation--pending";
68
+ return "";
69
+ }
70
+ // =============================================================================
71
+ // SUB-COMPONENTS
72
+ // =============================================================================
73
+ /**
74
+ * Status tooltip content for miss/partial states.
75
+ * Shows explanation when hovering over citations with issues.
76
+ */
77
+ const StatusTooltipContent = ({ citation, status, foundCitation, isExpanded, onToggleExpand, }) => {
78
+ const { isMiss, isPartialMatch } = status;
79
+ if (!isMiss && !isPartialMatch)
80
+ return null;
81
+ // Get search attempts from foundCitation
82
+ const searchAttempts = foundCitation?.searchState?.searchAttempts;
83
+ const failedAttempts = searchAttempts?.filter(a => !a.success) || [];
84
+ // Collect all unique phrases tried
85
+ const allPhrases = [];
86
+ const seenPhrases = new Set();
87
+ for (const attempt of failedAttempts) {
88
+ for (const phrase of attempt.searchPhrases || []) {
89
+ if (!seenPhrases.has(phrase)) {
90
+ seenPhrases.add(phrase);
91
+ allPhrases.push(phrase);
92
+ }
93
+ }
94
+ }
95
+ // Fallback to citation text if no phrases recorded
96
+ if (allPhrases.length === 0) {
97
+ const searchedText = citation.fullPhrase || citation.value || "";
98
+ if (searchedText) {
99
+ allPhrases.push(searchedText);
100
+ }
101
+ }
102
+ if (isMiss) {
103
+ const hiddenCount = allPhrases.length - 1;
104
+ return (_jsxs("span", { className: "dc-status-tooltip", role: "tooltip", children: [_jsxs("span", { className: "dc-status-header dc-status-header--miss", children: [_jsx(WarningIcon, {}), _jsx("span", { children: "Not found in source" })] }), allPhrases.length > 0 && (_jsxs("span", { className: "dc-search-phrases", children: [_jsxs("span", { className: "dc-search-phrases-header", children: [hiddenCount > 0 && (_jsx("button", { type: "button", className: "dc-search-phrases-toggle", onClick: onToggleExpand, children: isExpanded ? "collapse" : `+${hiddenCount} more` })), _jsxs("span", { className: "dc-status-label", children: ["Searched ", allPhrases.length, " phrase", allPhrases.length > 1 ? "s" : ""] })] }), _jsx("span", { className: "dc-search-phrases-list", children: (isExpanded ? allPhrases : allPhrases.slice(0, 1)).map((phrase, idx) => (_jsxs("span", { className: "dc-search-phrase-item", children: ["\"", phrase.length > 80 ? phrase.slice(0, 80) + "…" : phrase, "\""] }, idx))) })] }))] }));
105
+ }
106
+ if (isPartialMatch) {
107
+ const expectedText = citation.fullPhrase || citation.value || "";
108
+ const actualText = foundCitation?.matchSnippet || "";
109
+ const truncatedExpected = expectedText.length > 100 ? expectedText.slice(0, 100) + "…" : expectedText;
110
+ const truncatedActual = actualText.length > 100 ? actualText.slice(0, 100) + "…" : actualText;
111
+ return (_jsxs("span", { className: "dc-status-tooltip", role: "tooltip", children: [_jsxs("span", { className: "dc-status-header dc-status-header--partial", children: [_jsx(WarningIcon, {}), _jsx("span", { children: "Partial match" })] }), _jsx("span", { className: "dc-status-description", children: "Text differs from citation." }), truncatedExpected && (_jsxs("span", { className: "dc-status-searched", children: [_jsx("span", { className: "dc-status-label", children: "Expected" }), _jsx("span", { className: "dc-status-text", children: truncatedExpected })] })), truncatedActual && (_jsxs("span", { className: "dc-status-searched", children: [_jsx("span", { className: "dc-status-label", children: "Found" }), _jsx("span", { className: "dc-status-text", children: truncatedActual })] }))] }));
112
+ }
113
+ return null;
114
+ };
115
+ /**
116
+ * Full-size image overlay component.
117
+ * Uses portal to render at document body level.
118
+ */
119
+ const ImageOverlay = ({ src, alt, onClose }) => {
120
+ const handleBackdropClick = useCallback((e) => {
121
+ if (e.target === e.currentTarget) {
122
+ onClose();
123
+ }
124
+ }, [onClose]);
125
+ useEffect(() => {
126
+ const handleKeyDown = (e) => {
127
+ if (e.key === "Escape") {
128
+ onClose();
129
+ }
130
+ };
131
+ document.addEventListener("keydown", handleKeyDown);
132
+ return () => document.removeEventListener("keydown", handleKeyDown);
133
+ }, [onClose]);
134
+ // Use portal to render at body level, avoiding any parent positioning issues
135
+ return createPortal(_jsx("div", { className: "dc-overlay", onClick: handleBackdropClick, role: "dialog", "aria-modal": "true", "aria-label": "Full size verification image", children: _jsx("div", { className: "dc-overlay-content", onClick: onClose, children: _jsx("img", { src: src, alt: alt, className: "dc-overlay-image", draggable: false }) }) }), document.body);
136
+ };
137
+ /**
138
+ * Default popover content component.
139
+ * Shows verification image if available, otherwise shows text info.
140
+ */
141
+ const DefaultPopoverContent = ({ foundCitation, status, onImageClick, }) => {
142
+ const hasImage = foundCitation?.verificationImageBase64;
143
+ const handleImageClick = useCallback((e) => {
144
+ e.preventDefault();
145
+ e.stopPropagation();
146
+ if (hasImage && onImageClick) {
147
+ onImageClick(foundCitation.verificationImageBase64);
148
+ }
149
+ }, [hasImage, foundCitation?.verificationImageBase64, onImageClick]);
150
+ // If we have a verification image, show only the image
151
+ if (hasImage) {
152
+ return (_jsx("button", { type: "button", className: "dc-popover-image-button", onClick: handleImageClick, "aria-label": "Click to view full size", children: _jsx("img", { src: foundCitation.verificationImageBase64, alt: "Citation verification", className: "dc-popover-image", loading: "lazy" }) }));
153
+ }
154
+ // No image - show text info
155
+ const statusLabel = getStatusLabel(status);
156
+ const statusClass = getPopoverStatusClass(status);
157
+ const hasSnippet = foundCitation?.matchSnippet;
158
+ const pageNumber = foundCitation?.pageNumber;
159
+ if (!hasSnippet && !statusLabel) {
160
+ return null;
161
+ }
162
+ return (_jsxs(_Fragment, { children: [statusLabel && _jsx("span", { className: classNames("dc-popover-status", statusClass), children: statusLabel }), hasSnippet && _jsxs("span", { className: "dc-popover-snippet", children: ["\"", foundCitation.matchSnippet, "\""] }), pageNumber && pageNumber > 0 && _jsxs("span", { className: "dc-popover-page", children: ["Page ", pageNumber] })] }));
163
+ };
164
+ // =============================================================================
165
+ // MAIN COMPONENT
166
+ // =============================================================================
167
+ /**
168
+ * CitationComponent displays a citation with verification status.
169
+ *
170
+ * The component separates two concepts:
171
+ * 1. **Found status** (text styling) - whether the citation was found in the document
172
+ * - Verified & Partial both use "found" styling (blue text)
173
+ * - Miss uses "not found" styling (gray/strikethrough)
174
+ *
175
+ * 2. **Match quality** (indicator styling) - how well the citation matched
176
+ * - Exact match: green checkmark
177
+ * - Partial match: orange checkmark
178
+ * - Miss: no indicator
179
+ *
180
+ * This means partial matches have blue text (because they were found) but
181
+ * an orange indicator (because they didn't match exactly).
182
+ */
183
+ export const CitationComponent = forwardRef(({ citation, children, className, displayCitationValue = false, fallbackDisplay, foundCitation, variant = "brackets", eventHandlers, isMobile = false, renderIndicator, renderContent, popoverPosition = "top", renderPopoverContent, }, ref) => {
184
+ const containerRef = useRef(null);
185
+ const wrapperRef = useRef(null);
186
+ const [expandedImageSrc, setExpandedImageSrc] = useState(null);
187
+ const [isTooltipExpanded, setIsTooltipExpanded] = useState(false);
188
+ const [isPhrasesExpanded, setIsPhrasesExpanded] = useState(false);
189
+ const handleImageClick = useCallback((imageSrc) => {
190
+ setExpandedImageSrc(imageSrc);
191
+ }, []);
192
+ const handleCloseOverlay = useCallback(() => {
193
+ setExpandedImageSrc(null);
194
+ }, []);
195
+ const handleTogglePhrases = useCallback((e) => {
196
+ e?.preventDefault();
197
+ e?.stopPropagation();
198
+ setIsPhrasesExpanded(prev => !prev);
199
+ }, []);
200
+ // Handle click outside to close expanded tooltip
201
+ useEffect(() => {
202
+ if (!isTooltipExpanded)
203
+ return;
204
+ const handleClickOutside = (event) => {
205
+ if (wrapperRef.current && !wrapperRef.current.contains(event.target)) {
206
+ setIsTooltipExpanded(false);
207
+ }
208
+ };
209
+ // Use capture phase to handle clicks before they bubble
210
+ document.addEventListener("mousedown", handleClickOutside, true);
211
+ return () => {
212
+ document.removeEventListener("mousedown", handleClickOutside, true);
213
+ };
214
+ }, [isTooltipExpanded]);
215
+ // Handle escape key to close expanded tooltip
216
+ useEffect(() => {
217
+ if (!isTooltipExpanded)
218
+ return;
219
+ const handleEscape = (event) => {
220
+ if (event.key === "Escape") {
221
+ setIsTooltipExpanded(false);
222
+ }
223
+ };
224
+ document.addEventListener("keydown", handleEscape);
225
+ return () => {
226
+ document.removeEventListener("keydown", handleEscape);
227
+ };
228
+ }, [isTooltipExpanded]);
229
+ const citationKey = useMemo(() => generateCitationKey(citation), [citation]);
230
+ const citationInstanceId = useMemo(() => generateCitationInstanceId(citationKey), [citationKey]);
231
+ const handleToggleTooltip = useCallback((e) => {
232
+ e.preventDefault();
233
+ e.stopPropagation();
234
+ // If we have a verification image
235
+ if (foundCitation?.verificationImageBase64) {
236
+ if (expandedImageSrc) {
237
+ // Image is open - close it and unpin
238
+ setExpandedImageSrc(null);
239
+ setIsTooltipExpanded(false);
240
+ }
241
+ else if (isTooltipExpanded) {
242
+ // Already pinned - second click expands image
243
+ setExpandedImageSrc(foundCitation.verificationImageBase64);
244
+ }
245
+ else {
246
+ // First click - just pin the popover open
247
+ setIsTooltipExpanded(true);
248
+ }
249
+ }
250
+ else {
251
+ // No image - toggle phrases expansion for miss/partial tooltips
252
+ setIsTooltipExpanded(prev => !prev);
253
+ setIsPhrasesExpanded(prev => !prev);
254
+ }
255
+ eventHandlers?.onClick?.(citation, citationKey, e);
256
+ }, [
257
+ eventHandlers,
258
+ citation,
259
+ citationKey,
260
+ foundCitation?.verificationImageBase64,
261
+ expandedImageSrc,
262
+ isTooltipExpanded,
263
+ ]);
264
+ const status = getCitationStatus(foundCitation ?? null);
265
+ // const { isVerified, isPending } = status;
266
+ const { isMiss, isPartialMatch, isVerified, isPending } = status;
267
+ const displayText = useMemo(() => {
268
+ // For numeric variant, always show the citation number
269
+ if (variant === "numeric") {
270
+ return citation.citationNumber?.toString() ?? "";
271
+ }
272
+ // For text/minimal/brackets, show the value or fullPhrase
273
+ return getCitationDisplayText(citation, {
274
+ displayCitationValue: variant === "text" || variant === "minimal" || variant === "brackets" || displayCitationValue,
275
+ fallbackDisplay,
276
+ });
277
+ }, [citation, variant, displayCitationValue, fallbackDisplay]);
278
+ // Found status class for text styling (blue for found, gray for miss)
279
+ const foundStatusClass = useMemo(() => getFoundStatusClass(status), [status]);
280
+ // Event handlers
281
+ const handleMouseEnter = useCallback(() => {
282
+ eventHandlers?.onMouseEnter?.(citation, citationKey);
283
+ }, [eventHandlers, citation, citationKey]);
284
+ const handleMouseLeave = useCallback(() => {
285
+ eventHandlers?.onMouseLeave?.(citation, citationKey);
286
+ }, [eventHandlers, citation, citationKey]);
287
+ const handleTouchEnd = useCallback((e) => {
288
+ if (isMobile) {
289
+ e.preventDefault();
290
+ e.stopPropagation();
291
+ eventHandlers?.onTouchEnd?.(citation, citationKey, e);
292
+ }
293
+ }, [eventHandlers, citation, citationKey, isMobile]);
294
+ // Early return for miss with fallback display
295
+ if (fallbackDisplay !== null && fallbackDisplay !== undefined && displayCitationValue && isMiss) {
296
+ return _jsx("span", { className: classNames("dc-citation-fallback", className), children: fallbackDisplay });
297
+ }
298
+ // Render the appropriate indicator based on match quality
299
+ const renderStatusIndicator = () => {
300
+ if (renderIndicator) {
301
+ return renderIndicator(status);
302
+ }
303
+ if (isVerified) {
304
+ return _jsx(DefaultVerifiedIndicator, {});
305
+ }
306
+ else if (isPartialMatch) {
307
+ return _jsx(DefaultPartialIndicator, {});
308
+ }
309
+ else if (isPending) {
310
+ return (_jsx("span", { className: "dc-indicator dc-indicator--pending", "aria-hidden": "true", children: TWO_DOTS_THINKING_CONTENT }));
311
+ }
312
+ else if (isMiss) {
313
+ return null;
314
+ }
315
+ return null;
316
+ };
317
+ // Render the citation content based on variant
318
+ const renderCitationContent = () => {
319
+ // Custom render function takes full control
320
+ if (renderContent) {
321
+ return renderContent({
322
+ citation,
323
+ status,
324
+ citationKey,
325
+ displayText,
326
+ isMergedDisplay: variant === "text" || variant === "brackets" || displayCitationValue,
327
+ });
328
+ }
329
+ // Indicator-only variant - just the checkmark/warning
330
+ if (variant === "indicator") {
331
+ return _jsx("span", { className: "dc-citation-text", children: renderStatusIndicator() });
332
+ }
333
+ // Text variant - no special styling, shows value with indicator
334
+ if (variant === "text") {
335
+ return (_jsxs("span", { className: "dc-citation-text dc-citation-text--plain", children: [displayText, renderStatusIndicator()] }));
336
+ }
337
+ // Minimal variant - no brackets, just text with indicator
338
+ if (variant === "minimal") {
339
+ return (_jsxs("span", { className: "dc-citation-text", children: [displayText, renderStatusIndicator()] }));
340
+ }
341
+ // Numeric variant - shows citation number with indicator, no brackets
342
+ if (variant === "numeric") {
343
+ return (_jsxs("span", { className: "dc-citation-text", children: [displayText, renderStatusIndicator()] }));
344
+ }
345
+ // Brackets variant (default) - value/number in brackets with styling
346
+ return (_jsxs("span", { className: "dc-citation-bracket", "aria-hidden": "true", role: "presentation", children: ["[", _jsxs("span", { className: "dc-citation-text", children: [displayText, renderStatusIndicator()] }), "]"] }));
347
+ };
348
+ // Determine if popover should be shown
349
+ const isPopoverHidden = popoverPosition === "hidden";
350
+ const shouldShowPopover = !isPopoverHidden && foundCitation && (foundCitation.verificationImageBase64 || foundCitation.matchSnippet);
351
+ // Determine if status tooltip should be shown (miss/partial without full verification)
352
+ const shouldShowStatusTooltip = !isPopoverHidden && (isMiss || isPartialMatch) && !shouldShowPopover;
353
+ // Popover content - determine position class (only "top" or "bottom" add classes)
354
+ const popoverPositionClass = popoverPosition === "bottom" ? "dc-popover--bottom" : "";
355
+ const popoverContent = shouldShowPopover ? (_jsx("span", { className: classNames("dc-popover", popoverPositionClass), children: renderPopoverContent ? (renderPopoverContent({ citation, foundCitation: foundCitation ?? null, status })) : (_jsx(DefaultPopoverContent, { citation: citation, foundCitation: foundCitation ?? null, status: status, onImageClick: handleImageClick })) })) : null;
356
+ // Status tooltip for miss/partial explanations
357
+ const statusTooltipContent = shouldShowStatusTooltip ? (_jsx(StatusTooltipContent, { citation: citation, status: status, foundCitation: foundCitation ?? null, isExpanded: isPhrasesExpanded, onToggleExpand: handleTogglePhrases })) : null;
358
+ const citationTrigger = (_jsx("span", { ref: node => {
359
+ containerRef.current = node;
360
+ if (typeof ref === "function") {
361
+ ref(node);
362
+ }
363
+ else if (ref) {
364
+ ref.current = node;
365
+ }
366
+ }, "data-citation-id": citationKey, "data-citation-instance": citationInstanceId, "data-tooltip-expanded": isTooltipExpanded, "data-has-image": !!foundCitation?.verificationImageBase64, className: classNames("dc-citation", `dc-citation--${variant}`, foundStatusClass, className), onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, onClick: handleToggleTooltip, onTouchEndCapture: isMobile ? handleTouchEnd : undefined, "aria-label": displayText ? `[${displayText}]` : undefined, "aria-expanded": isTooltipExpanded, children: renderCitationContent() }));
367
+ // Image overlay for full-size view
368
+ const imageOverlay = expandedImageSrc ? (_jsx(ImageOverlay, { src: expandedImageSrc, alt: "Citation verification - full size", onClose: handleCloseOverlay })) : null;
369
+ // Wrap with popover or status tooltip if needed
370
+ if (shouldShowPopover || shouldShowStatusTooltip) {
371
+ return (_jsxs(_Fragment, { children: [children, _jsxs("span", { className: "dc-popover-wrapper", ref: wrapperRef, "data-expanded": isTooltipExpanded, children: [citationTrigger, popoverContent, statusTooltipContent] }), imageOverlay] }));
372
+ }
373
+ return (_jsxs(_Fragment, { children: [children, citationTrigger, imageOverlay] }));
374
+ });
375
+ CitationComponent.displayName = "CitationComponent";
376
+ export const MemoizedCitationComponent = memo(CitationComponent);
@@ -0,0 +1,135 @@
1
+ import React, { type ReactNode } from "react";
2
+ import type { CitationStatus } from "../types/citation.js";
3
+ import type { FoundHighlightLocation } from "../types/foundHighlight.js";
4
+ import type { SearchState } from "../types/search.js";
5
+ import type { BaseCitationProps, CitationVariant as CitationVariantType, CitationEventHandlers } from "./types.js";
6
+ /**
7
+ * Shared props for all citation variant components.
8
+ */
9
+ export interface CitationVariantProps extends BaseCitationProps {
10
+ /** Found citation highlight location data */
11
+ foundCitation?: FoundHighlightLocation | null;
12
+ /** Current search state for the citation */
13
+ searchState?: SearchState | null;
14
+ /** Event handlers */
15
+ eventHandlers?: CitationEventHandlers;
16
+ /** Whether on mobile device */
17
+ isMobile?: boolean;
18
+ /** Whether tooltips should be prevented */
19
+ preventTooltips?: boolean;
20
+ /** Custom pending text content */
21
+ pendingContent?: ReactNode;
22
+ /** Custom render function for verified indicator */
23
+ renderVerifiedIndicator?: (status: CitationStatus) => ReactNode;
24
+ /** Custom render function for partial match indicator */
25
+ renderPartialIndicator?: (status: CitationStatus) => ReactNode;
26
+ }
27
+ export interface ChipCitationProps extends CitationVariantProps {
28
+ /** Chip size */
29
+ size?: "sm" | "md" | "lg";
30
+ /** Whether to show an icon before the text */
31
+ showIcon?: boolean;
32
+ /** Custom icon to display */
33
+ icon?: ReactNode;
34
+ }
35
+ /**
36
+ * Chip/Badge style citation component.
37
+ * Displays citation as a rounded pill/badge.
38
+ *
39
+ * @example
40
+ * ```tsx
41
+ * <ChipCitation citation={citation} foundCitation={found} size="md" />
42
+ * ```
43
+ */
44
+ export declare const ChipCitation: React.ForwardRefExoticComponent<ChipCitationProps & React.RefAttributes<HTMLSpanElement>>;
45
+ export interface SuperscriptCitationProps extends CitationVariantProps {
46
+ /** Whether to show brackets around the superscript */
47
+ showBrackets?: boolean;
48
+ }
49
+ /**
50
+ * Superscript style citation component.
51
+ * Displays citation as a superscript number like academic papers.
52
+ *
53
+ * @example
54
+ * ```tsx
55
+ * <SuperscriptCitation citation={citation} foundCitation={found} />
56
+ * // Renders: Text content¹
57
+ * ```
58
+ */
59
+ export declare const SuperscriptCitation: React.ForwardRefExoticComponent<SuperscriptCitationProps & React.RefAttributes<HTMLSpanElement>>;
60
+ export interface FootnoteCitationProps extends CitationVariantProps {
61
+ /** Footnote symbol style */
62
+ symbolStyle?: "number" | "asterisk" | "dagger" | "custom";
63
+ /** Custom symbol (when symbolStyle is "custom") */
64
+ customSymbol?: string;
65
+ }
66
+ /**
67
+ * Footnote style citation component.
68
+ * Displays citation as a footnote marker.
69
+ *
70
+ * @example
71
+ * ```tsx
72
+ * <FootnoteCitation citation={citation} symbolStyle="asterisk" />
73
+ * // Renders: Text content*
74
+ * ```
75
+ */
76
+ export declare const FootnoteCitation: React.ForwardRefExoticComponent<FootnoteCitationProps & React.RefAttributes<HTMLSpanElement>>;
77
+ export interface InlineCitationProps extends CitationVariantProps {
78
+ /** Underline style */
79
+ underlineStyle?: "solid" | "dotted" | "dashed" | "none";
80
+ }
81
+ /**
82
+ * Inline style citation component.
83
+ * Displays citation inline with subtle underline decoration.
84
+ *
85
+ * @example
86
+ * ```tsx
87
+ * <InlineCitation citation={citation} underlineStyle="dotted" />
88
+ * // Renders: "quoted text" with subtle underline
89
+ * ```
90
+ */
91
+ export declare const InlineCitation: React.ForwardRefExoticComponent<InlineCitationProps & React.RefAttributes<HTMLSpanElement>>;
92
+ export interface MinimalCitationProps extends CitationVariantProps {
93
+ /** Whether to show status indicator */
94
+ showStatusIndicator?: boolean;
95
+ }
96
+ /**
97
+ * Minimal style citation component.
98
+ * Displays just the citation number with minimal decoration.
99
+ *
100
+ * @example
101
+ * ```tsx
102
+ * <MinimalCitation citation={citation} />
103
+ * // Renders: 1
104
+ * ```
105
+ */
106
+ export declare const MinimalCitation: React.ForwardRefExoticComponent<MinimalCitationProps & React.RefAttributes<HTMLSpanElement>>;
107
+ export interface VariantCitationProps extends CitationVariantProps {
108
+ /** The variant to render */
109
+ variant?: CitationVariantType;
110
+ /** Chip-specific props */
111
+ chipProps?: Partial<ChipCitationProps>;
112
+ /** Superscript-specific props */
113
+ superscriptProps?: Partial<SuperscriptCitationProps>;
114
+ /** Footnote-specific props */
115
+ footnoteProps?: Partial<FootnoteCitationProps>;
116
+ /** Inline-specific props */
117
+ inlineProps?: Partial<InlineCitationProps>;
118
+ /** Minimal-specific props */
119
+ minimalProps?: Partial<MinimalCitationProps>;
120
+ }
121
+ /**
122
+ * Factory component that renders the appropriate citation variant.
123
+ *
124
+ * @example
125
+ * ```tsx
126
+ * <CitationVariantFactory variant="chip" citation={citation} chipProps={{ size: "lg" }} />
127
+ * ```
128
+ */
129
+ export declare const CitationVariantFactory: React.ForwardRefExoticComponent<VariantCitationProps & React.RefAttributes<HTMLSpanElement>>;
130
+ export declare const MemoizedChipCitation: React.NamedExoticComponent<ChipCitationProps & React.RefAttributes<HTMLSpanElement>>;
131
+ export declare const MemoizedSuperscriptCitation: React.NamedExoticComponent<SuperscriptCitationProps & React.RefAttributes<HTMLSpanElement>>;
132
+ export declare const MemoizedFootnoteCitation: React.NamedExoticComponent<FootnoteCitationProps & React.RefAttributes<HTMLSpanElement>>;
133
+ export declare const MemoizedInlineCitation: React.NamedExoticComponent<InlineCitationProps & React.RefAttributes<HTMLSpanElement>>;
134
+ export declare const MemoizedMinimalCitation: React.NamedExoticComponent<MinimalCitationProps & React.RefAttributes<HTMLSpanElement>>;
135
+ export declare const MemoizedCitationVariantFactory: React.NamedExoticComponent<VariantCitationProps & React.RefAttributes<HTMLSpanElement>>;