@deepcitation/deepcitation-js 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,6 +18,12 @@
18
18
 
19
19
  LLMs hallucinate. Even when given source documents, they make up quotes, invent statistics, and cite pages that don't exist. DeepCitation solves this by **deterministically verifying every citation** against your source documents—and generating visual proof.
20
20
 
21
+ <div align="center">
22
+ <img src="./examples/assets/deepcitation-medical-demo.gif" alt="DeepCitation medical documentation demo showing verified inline citations" width="700" />
23
+ <br />
24
+ <em>Medical documentation with verified inline citations — certainty at a glance</em>
25
+ </div>
26
+
21
27
  ```
22
28
  Before: "Revenue grew 45% [1]" → ❓ Did the LLM make this up?
23
29
  After: "Revenue grew 45% [1]" → ✅ Verified on page 3, line 12 (with screenshot)
@@ -59,7 +65,7 @@ import { DeepCitation, wrapCitationPrompt } from "@deepcitation/deepcitation-js"
59
65
 
60
66
  const dc = new DeepCitation({ apiKey: process.env.DEEPCITATION_API_KEY });
61
67
 
62
- // Upload source files
68
+ // Upload source files, this can be done before the user types their prompt
63
69
  const { fileDataParts, deepTextPromptPortion } = await dc.prepareFiles([
64
70
  { file: pdfBuffer, filename: "report.pdf" },
65
71
  ]);
@@ -87,31 +93,49 @@ Verify citations against the source documents.
87
93
  ```typescript
88
94
  const result = await dc.verifyCitations({
89
95
  llmOutput: response.content,
90
- fileDataParts,
96
+ fileDataParts, //optional
91
97
  });
92
98
 
93
- // result.citations contains verification status + visual proof
99
+ // result.verifications contains verification status + visual proof
100
+ const { citations, verifications } = result;
101
+
94
102
  ```
95
103
 
96
104
  ### Step 3: Display
97
105
 
98
- Render verified citations with React components.
106
+ Parse the LLM output and render verified citations inline with React components.
99
107
 
100
108
  ```tsx
101
109
  import { CitationComponent } from "@deepcitation/deepcitation-js/react";
110
+ import {
111
+ parseCitation,
112
+ generateCitationKey,
113
+ } from "@deepcitation/deepcitation-js";
102
114
  import "@deepcitation/deepcitation-js/react/styles.css";
103
115
 
104
- function Response({ citations, verifications }) {
105
- return (
106
- <p>
107
- Revenue grew by
108
- <CitationComponent
109
- citation={citations["1"]}
110
- foundCitation={verifications["1"]}
111
- />
112
- this quarter.
113
- </p>
114
- );
116
+ function Response({ llmOutput, verifications }) {
117
+ // Split LLM output by citation tags and render inline
118
+ const renderWithCitations = (text: string) => {
119
+ const parts = text.split(/(<cite\s+[^>]*\/>)/g);
120
+
121
+ return parts.map((part, index) => {
122
+ if (part.startsWith("<cite")) {
123
+ const { citation } = parseCitation(part);
124
+ const citationKey = generateCitationKey(citation);
125
+
126
+ return (
127
+ <CitationComponent
128
+ key={index}
129
+ citation={citation}
130
+ verification={verifications[citationKey]}
131
+ />
132
+ );
133
+ }
134
+ return <span key={index}>{part}</span>;
135
+ });
136
+ };
137
+
138
+ return <div>{renderWithCitations(llmOutput)}</div>;
115
139
  }
116
140
  ```
117
141
 
@@ -123,8 +147,7 @@ function Response({ citations, verifications }) {
123
147
 
124
148
  ```typescript
125
149
  const dc = new DeepCitation({
126
- apiKey: string, // Your API key (sk-dc-*)
127
- apiUrl?: string, // Optional: Custom API URL
150
+ apiKey: string // Your API key (sk-dc-*)
128
151
  });
129
152
 
130
153
  // Upload and prepare source files
@@ -100,8 +100,9 @@ export const parseCitation = (fragment, mdAttachmentId, citationCounterRef, isVe
100
100
  const citationMatches = [...middleCite.matchAll(citationRegex)];
101
101
  const match = citationMatches?.[0];
102
102
  const pageNumber = match?.[2] ? parseInt(match?.[2]) : undefined;
103
+ const pageIndex = match?.[3] ? parseInt(match?.[3]) : undefined;
103
104
  let fileId = match?.[1];
104
- let attachmentId = fileId?.length === 20 ? fileId : mdAttachmentId || match?.[1];
105
+ let attachmentId = fileId?.length === 20 ? fileId : mdAttachmentId || fileId;
105
106
  // Use helper to handle escaped quotes inside the phrase
106
107
  let fullPhrase = cleanAndUnescape(match?.[4]);
107
108
  let keySpan = cleanAndUnescape(match?.[5]);
@@ -138,8 +139,7 @@ export const parseCitation = (fragment, mdAttachmentId, citationCounterRef, isVe
138
139
  let timestamps;
139
140
  if (avMatch) {
140
141
  fileId = avMatch?.[1];
141
- attachmentId =
142
- fileId?.length === 20 ? fileId : mdAttachmentId || avMatch?.[1];
142
+ attachmentId = fileId?.length === 20 ? fileId : mdAttachmentId || fileId;
143
143
  fullPhrase = cleanAndUnescape(avMatch?.[2]);
144
144
  const timestampsString = avMatch?.[3]?.replace(/timestamps=['"]|['"]/g, "");
145
145
  const [startTime, endTime] = timestampsString?.split("-") || [];
@@ -156,6 +156,7 @@ export const parseCitation = (fragment, mdAttachmentId, citationCounterRef, isVe
156
156
  const citation = {
157
157
  fileId: attachmentId,
158
158
  pageNumber,
159
+ startPageKey: `page_number_${pageNumber || 1}_index_${pageIndex || 0}`,
159
160
  fullPhrase,
160
161
  keySpan,
161
162
  citationNumber,
@@ -194,13 +195,21 @@ const parseJsonCitation = (jsonCitation, citationNumber) => {
194
195
  if (!fullPhrase) {
195
196
  return null;
196
197
  }
197
- // Parse startPageKey format: "page_number_PAGE_index_INDEX"
198
+ // Parse startPageKey format: "page_number_PAGE_index_INDEX" or simple "PAGE_INDEX"
198
199
  let pageNumber;
199
200
  if (startPageKey) {
201
+ // Try full format first: page_number_5_index_2 or pageKey_5_index_2
200
202
  const pageMatch = startPageKey.match(/page[_a-zA-Z]*(\d+)_index_(\d+)/i);
201
203
  if (pageMatch) {
202
204
  pageNumber = parseInt(pageMatch[1], 10);
203
205
  }
206
+ else {
207
+ // Try simple n_m format: 5_4 (page 5, index 4)
208
+ const simpleMatch = startPageKey.match(/^(\d+)_(\d+)$/);
209
+ if (simpleMatch) {
210
+ pageNumber = parseInt(simpleMatch[1], 10);
211
+ }
212
+ }
204
213
  }
205
214
  // Sort lineIds if present
206
215
  const lineIds = rawLineIds?.length
@@ -11,7 +11,7 @@ export type { CitationVariant } from "./types.js";
11
11
  * ```tsx
12
12
  * <CitationComponent
13
13
  * citation={{ citationNumber: 1, fullPhrase: "Revenue grew by 25%" }}
14
- * foundCitation={verificationResult}
14
+ * verification={verificationResult}
15
15
  * />
16
16
  * // Renders: [1✓] with blue text
17
17
  * ```
@@ -20,7 +20,7 @@ export type { CitationVariant } from "./types.js";
20
20
  * ```tsx
21
21
  * <CitationComponent
22
22
  * citation={{ citationNumber: 1, value: "25% growth" }}
23
- * foundCitation={verificationResult}
23
+ * verification={verificationResult}
24
24
  * variant="numeric"
25
25
  * />
26
26
  * // Renders: 1✓
@@ -30,7 +30,7 @@ export type { CitationVariant } from "./types.js";
30
30
  * ```tsx
31
31
  * <CitationComponent
32
32
  * citation={{ citationNumber: 1, value: "25% growth" }}
33
- * foundCitation={verificationResult}
33
+ * verification={verificationResult}
34
34
  * variant="text"
35
35
  * />
36
36
  * // Renders: 25% growth✓
@@ -40,7 +40,7 @@ export type { CitationVariant } from "./types.js";
40
40
  * ```tsx
41
41
  * <CitationComponent
42
42
  * citation={citation}
43
- * foundCitation={verificationResult}
43
+ * verification={verificationResult}
44
44
  * variant="minimal"
45
45
  * />
46
46
  * // Renders: Revenue grew...✓
@@ -50,7 +50,7 @@ export type { CitationVariant } from "./types.js";
50
50
  * ```tsx
51
51
  * <CitationComponent
52
52
  * citation={citation}
53
- * foundCitation={verificationResult}
53
+ * verification={verificationResult}
54
54
  * variant="indicator"
55
55
  * />
56
56
  * // Renders: ✓
@@ -60,7 +60,7 @@ export type { CitationVariant } from "./types.js";
60
60
  * ```tsx
61
61
  * <CitationComponent
62
62
  * citation={citation}
63
- * foundCitation={verificationResult}
63
+ * verification={verificationResult}
64
64
  * popoverPosition="hidden"
65
65
  * />
66
66
  * ```
@@ -70,7 +70,7 @@ export interface CitationComponentProps extends BaseCitationProps {
70
70
  * Verification result from the DeepCitation API.
71
71
  * Contains match snippet, page number, and verification image.
72
72
  */
73
- foundCitation?: Verification | null;
73
+ verification?: Verification | null;
74
74
  /**
75
75
  * Display variant for the citation.
76
76
  * - `brackets`: Shows value/number in brackets, blue text styling (default)
@@ -110,7 +110,7 @@ export interface CitationComponentProps extends BaseCitationProps {
110
110
  */
111
111
  renderPopoverContent?: (props: {
112
112
  citation: BaseCitationProps["citation"];
113
- foundCitation: Verification | null;
113
+ verification: Verification | null;
114
114
  status: CitationStatus;
115
115
  }) => ReactNode;
116
116
  }
@@ -74,12 +74,12 @@ function getFoundStatusClass(status) {
74
74
  * Status tooltip content for miss/partial states.
75
75
  * Shows explanation when hovering over citations with issues.
76
76
  */
77
- const StatusTooltipContent = ({ citation, status, foundCitation, isExpanded, onToggleExpand, }) => {
77
+ const StatusTooltipContent = ({ citation, status, verification, isExpanded, onToggleExpand, }) => {
78
78
  const { isMiss, isPartialMatch } = status;
79
79
  if (!isMiss && !isPartialMatch)
80
80
  return null;
81
- // Get search attempts from foundCitation
82
- const searchAttempts = foundCitation?.searchState?.searchAttempts;
81
+ // Get search attempts from verification
82
+ const searchAttempts = verification?.searchState?.searchAttempts;
83
83
  const failedAttempts = searchAttempts?.filter((a) => !a.success) || [];
84
84
  // Collect all unique phrases tried
85
85
  const allPhrases = [];
@@ -105,7 +105,7 @@ const StatusTooltipContent = ({ citation, status, foundCitation, isExpanded, onT
105
105
  }
106
106
  if (isPartialMatch) {
107
107
  const expectedText = citation.fullPhrase || citation.value || "";
108
- const actualText = foundCitation?.matchSnippet || "";
108
+ const actualText = verification?.matchSnippet || "";
109
109
  const truncatedExpected = expectedText.length > 100
110
110
  ? expectedText.slice(0, 100) + "…"
111
111
  : expectedText;
@@ -140,28 +140,28 @@ const ImageOverlay = ({ src, alt, onClose, }) => {
140
140
  * Default popover content component.
141
141
  * Shows verification image if available, otherwise shows text info.
142
142
  */
143
- const DefaultPopoverContent = ({ foundCitation, status, onImageClick, }) => {
144
- const hasImage = foundCitation?.verificationImageBase64;
143
+ const DefaultPopoverContent = ({ verification, status, onImageClick, }) => {
144
+ const hasImage = verification?.verificationImageBase64;
145
145
  const handleImageClick = useCallback((e) => {
146
146
  e.preventDefault();
147
147
  e.stopPropagation();
148
148
  if (hasImage && onImageClick) {
149
- onImageClick(foundCitation.verificationImageBase64);
149
+ onImageClick(verification.verificationImageBase64);
150
150
  }
151
- }, [hasImage, foundCitation?.verificationImageBase64, onImageClick]);
151
+ }, [hasImage, verification?.verificationImageBase64, onImageClick]);
152
152
  // If we have a verification image, show only the image
153
153
  if (hasImage) {
154
- 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" }) }));
154
+ return (_jsx("button", { type: "button", className: "dc-popover-image-button", onClick: handleImageClick, "aria-label": "Click to view full size", children: _jsx("img", { src: verification.verificationImageBase64, alt: "Citation verification", className: "dc-popover-image", loading: "lazy" }) }));
155
155
  }
156
156
  // No image - show text info
157
157
  const statusLabel = getStatusLabel(status);
158
158
  const statusClass = getPopoverStatusClass(status);
159
- const hasSnippet = foundCitation?.matchSnippet;
160
- const pageNumber = foundCitation?.pageNumber;
159
+ const hasSnippet = verification?.matchSnippet;
160
+ const pageNumber = verification?.pageNumber;
161
161
  if (!hasSnippet && !statusLabel) {
162
162
  return null;
163
163
  }
164
- 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] }))] }));
164
+ return (_jsxs(_Fragment, { children: [statusLabel && (_jsx("span", { className: classNames("dc-popover-status", statusClass), children: statusLabel })), hasSnippet && (_jsxs("span", { className: "dc-popover-snippet", children: ["\"", verification.matchSnippet, "\""] })), pageNumber && pageNumber > 0 && (_jsxs("span", { className: "dc-popover-page", children: ["Page ", pageNumber] }))] }));
165
165
  };
166
166
  // =============================================================================
167
167
  // MAIN COMPONENT
@@ -182,7 +182,7 @@ const DefaultPopoverContent = ({ foundCitation, status, onImageClick, }) => {
182
182
  * This means partial matches have blue text (because they were found) but
183
183
  * an orange indicator (because they didn't match exactly).
184
184
  */
185
- export const CitationComponent = forwardRef(({ citation, children, className, displayCitationValue = false, fallbackDisplay, foundCitation, variant = "brackets", eventHandlers, isMobile = false, renderIndicator, renderContent, popoverPosition = "top", renderPopoverContent, }, ref) => {
185
+ export const CitationComponent = forwardRef(({ citation, children, className, displayCitationValue = false, fallbackDisplay, verification, variant = "brackets", eventHandlers, isMobile = false, renderIndicator, renderContent, popoverPosition = "top", renderPopoverContent, }, ref) => {
186
186
  const containerRef = useRef(null);
187
187
  const wrapperRef = useRef(null);
188
188
  const [expandedImageSrc, setExpandedImageSrc] = useState(null);
@@ -235,7 +235,7 @@ export const CitationComponent = forwardRef(({ citation, children, className, di
235
235
  e.preventDefault();
236
236
  e.stopPropagation();
237
237
  // If we have a verification image
238
- if (foundCitation?.verificationImageBase64) {
238
+ if (verification?.verificationImageBase64) {
239
239
  if (expandedImageSrc) {
240
240
  // Image is open - close it and unpin
241
241
  setExpandedImageSrc(null);
@@ -243,7 +243,7 @@ export const CitationComponent = forwardRef(({ citation, children, className, di
243
243
  }
244
244
  else if (isTooltipExpanded) {
245
245
  // Already pinned - second click expands image
246
- setExpandedImageSrc(foundCitation.verificationImageBase64);
246
+ setExpandedImageSrc(verification.verificationImageBase64);
247
247
  }
248
248
  else {
249
249
  // First click - just pin the popover open
@@ -260,11 +260,11 @@ export const CitationComponent = forwardRef(({ citation, children, className, di
260
260
  eventHandlers,
261
261
  citation,
262
262
  citationKey,
263
- foundCitation?.verificationImageBase64,
263
+ verification?.verificationImageBase64,
264
264
  expandedImageSrc,
265
265
  isTooltipExpanded,
266
266
  ]);
267
- const status = getCitationStatus(foundCitation ?? null);
267
+ const status = getCitationStatus(verification ?? null);
268
268
  // const { isVerified, isPending } = status;
269
269
  const { isMiss, isPartialMatch, isVerified, isPending } = status;
270
270
  const displayText = useMemo(() => {
@@ -359,19 +359,19 @@ export const CitationComponent = forwardRef(({ citation, children, className, di
359
359
  // Determine if popover should be shown
360
360
  const isPopoverHidden = popoverPosition === "hidden";
361
361
  const shouldShowPopover = !isPopoverHidden &&
362
- foundCitation &&
363
- (foundCitation.verificationImageBase64 || foundCitation.matchSnippet);
362
+ verification &&
363
+ (verification.verificationImageBase64 || verification.matchSnippet);
364
364
  // Determine if status tooltip should be shown (miss/partial without full verification)
365
365
  const shouldShowStatusTooltip = !isPopoverHidden && (isMiss || isPartialMatch) && !shouldShowPopover;
366
366
  // Popover content - determine position class (only "top" or "bottom" add classes)
367
367
  const popoverPositionClass = popoverPosition === "bottom" ? "dc-popover--bottom" : "";
368
368
  const popoverContent = shouldShowPopover ? (_jsx("span", { className: classNames("dc-popover", popoverPositionClass), children: renderPopoverContent ? (renderPopoverContent({
369
369
  citation,
370
- foundCitation: foundCitation ?? null,
370
+ verification: verification ?? null,
371
371
  status,
372
- })) : (_jsx(DefaultPopoverContent, { citation: citation, foundCitation: foundCitation ?? null, status: status, onImageClick: handleImageClick })) })) : null;
372
+ })) : (_jsx(DefaultPopoverContent, { citation: citation, verification: verification ?? null, status: status, onImageClick: handleImageClick })) })) : null;
373
373
  // Status tooltip for miss/partial explanations
374
- const statusTooltipContent = shouldShowStatusTooltip ? (_jsx(StatusTooltipContent, { citation: citation, status: status, foundCitation: foundCitation ?? null, isExpanded: isPhrasesExpanded, onToggleExpand: handleTogglePhrases })) : null;
374
+ const statusTooltipContent = shouldShowStatusTooltip ? (_jsx(StatusTooltipContent, { citation: citation, status: status, verification: verification ?? null, isExpanded: isPhrasesExpanded, onToggleExpand: handleTogglePhrases })) : null;
375
375
  const citationTrigger = (_jsx("span", { ref: (node) => {
376
376
  containerRef.current =
377
377
  node;
@@ -381,7 +381,7 @@ export const CitationComponent = forwardRef(({ citation, children, className, di
381
381
  else if (ref) {
382
382
  ref.current = node;
383
383
  }
384
- }, "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() }));
384
+ }, "data-citation-id": citationKey, "data-citation-instance": citationInstanceId, "data-tooltip-expanded": isTooltipExpanded, "data-has-image": !!verification?.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() }));
385
385
  // Image overlay for full-size view
386
386
  const imageOverlay = expandedImageSrc ? (_jsx(ImageOverlay, { src: expandedImageSrc, alt: "Citation verification - full size", onClose: handleCloseOverlay })) : null;
387
387
  // Wrap with popover or status tooltip if needed
@@ -8,7 +8,7 @@ import type { BaseCitationProps, CitationVariant as CitationVariantType, Citatio
8
8
  */
9
9
  export interface CitationVariantProps extends BaseCitationProps {
10
10
  /** Found citation highlight location data */
11
- foundCitation?: Verification | null;
11
+ verification?: Verification | null;
12
12
  /** Current search state for the citation */
13
13
  searchState?: SearchState | null;
14
14
  /** Event handlers */
@@ -38,7 +38,7 @@ export interface ChipCitationProps extends CitationVariantProps {
38
38
  *
39
39
  * @example
40
40
  * ```tsx
41
- * <ChipCitation citation={citation} foundCitation={found} size="md" />
41
+ * <ChipCitation citation={citation} verification={found} size="md" />
42
42
  * ```
43
43
  */
44
44
  export declare const ChipCitation: React.ForwardRefExoticComponent<ChipCitationProps & React.RefAttributes<HTMLSpanElement>>;
@@ -52,7 +52,7 @@ export interface SuperscriptCitationProps extends CitationVariantProps {
52
52
  *
53
53
  * @example
54
54
  * ```tsx
55
- * <SuperscriptCitation citation={citation} foundCitation={found} />
55
+ * <SuperscriptCitation citation={citation} verification={found} />
56
56
  * // Renders: Text content¹
57
57
  * ```
58
58
  */
@@ -5,13 +5,13 @@ import { generateCitationKey, generateCitationInstanceId, getCitationDisplayText
5
5
  const TWO_DOTS_THINKING_CONTENT = "..";
6
6
  /**
7
7
  * Hook to get common citation data.
8
- * NOTE: Status is not memoized because foundCitation may be mutated in place.
8
+ * NOTE: Status is not memoized because verification may be mutated in place.
9
9
  */
10
- function useCitationData(citation, foundCitation) {
10
+ function useCitationData(citation, verification) {
11
11
  const citationKey = useMemo(() => generateCitationKey(citation), [citation]);
12
12
  const citationInstanceId = useMemo(() => generateCitationInstanceId(citationKey), [citationKey]);
13
13
  // Don't memoize - object reference as dependency causes stale values on mutation
14
- const status = getCitationStatus(foundCitation ?? null);
14
+ const status = getCitationStatus(verification ?? null);
15
15
  return { citationKey, citationInstanceId, status };
16
16
  }
17
17
  /**
@@ -28,11 +28,11 @@ const DefaultPartialIndicator = () => (_jsx("span", { className: "citation-parti
28
28
  *
29
29
  * @example
30
30
  * ```tsx
31
- * <ChipCitation citation={citation} foundCitation={found} size="md" />
31
+ * <ChipCitation citation={citation} verification={found} size="md" />
32
32
  * ```
33
33
  */
34
- export const ChipCitation = forwardRef(({ citation, children, className, displayCitationValue = false, fallbackDisplay, foundCitation, eventHandlers, isMobile = false, preventTooltips = false, pendingContent = TWO_DOTS_THINKING_CONTENT, renderVerifiedIndicator = () => _jsx(DefaultVerifiedIndicator, {}), renderPartialIndicator = () => _jsx(DefaultPartialIndicator, {}), size = "md", showIcon = false, icon, }, ref) => {
35
- const { citationKey, citationInstanceId, status } = useCitationData(citation, foundCitation);
34
+ export const ChipCitation = forwardRef(({ citation, children, className, displayCitationValue = false, fallbackDisplay, verification, eventHandlers, isMobile = false, preventTooltips = false, pendingContent = TWO_DOTS_THINKING_CONTENT, renderVerifiedIndicator = () => _jsx(DefaultVerifiedIndicator, {}), renderPartialIndicator = () => _jsx(DefaultPartialIndicator, {}), size = "md", showIcon = false, icon, }, ref) => {
35
+ const { citationKey, citationInstanceId, status } = useCitationData(citation, verification);
36
36
  const { isVerified, isMiss, isPartialMatch, isPending } = status;
37
37
  const displayText = useMemo(() => getCitationDisplayText(citation, {
38
38
  displayCitationValue,
@@ -77,12 +77,12 @@ ChipCitation.displayName = "ChipCitation";
77
77
  *
78
78
  * @example
79
79
  * ```tsx
80
- * <SuperscriptCitation citation={citation} foundCitation={found} />
80
+ * <SuperscriptCitation citation={citation} verification={found} />
81
81
  * // Renders: Text content¹
82
82
  * ```
83
83
  */
84
- export const SuperscriptCitation = forwardRef(({ citation, children, className, displayCitationValue = false, fallbackDisplay, foundCitation, eventHandlers, isMobile = false, preventTooltips = false, pendingContent = TWO_DOTS_THINKING_CONTENT, renderVerifiedIndicator = () => _jsx(DefaultVerifiedIndicator, {}), renderPartialIndicator = () => _jsx(DefaultPartialIndicator, {}), showBrackets = false, }, ref) => {
85
- const { citationKey, citationInstanceId, status } = useCitationData(citation, foundCitation);
84
+ export const SuperscriptCitation = forwardRef(({ citation, children, className, displayCitationValue = false, fallbackDisplay, verification, eventHandlers, isMobile = false, preventTooltips = false, pendingContent = TWO_DOTS_THINKING_CONTENT, renderVerifiedIndicator = () => _jsx(DefaultVerifiedIndicator, {}), renderPartialIndicator = () => _jsx(DefaultPartialIndicator, {}), showBrackets = false, }, ref) => {
85
+ const { citationKey, citationInstanceId, status } = useCitationData(citation, verification);
86
86
  const { isVerified, isMiss, isPartialMatch, isPending } = status;
87
87
  const displayText = useMemo(() => getCitationDisplayText(citation, {
88
88
  displayCitationValue,
@@ -123,8 +123,8 @@ const FOOTNOTE_SYMBOLS = ["*", "†", "‡", "§", "‖", "¶"];
123
123
  * // Renders: Text content*
124
124
  * ```
125
125
  */
126
- export const FootnoteCitation = forwardRef(({ citation, children, className, fallbackDisplay, foundCitation, eventHandlers, preventTooltips = false, pendingContent = TWO_DOTS_THINKING_CONTENT, renderVerifiedIndicator = () => _jsx(DefaultVerifiedIndicator, {}), renderPartialIndicator = () => _jsx(DefaultPartialIndicator, {}), symbolStyle = "number", customSymbol, }, ref) => {
127
- const { citationKey, citationInstanceId, status } = useCitationData(citation, foundCitation);
126
+ export const FootnoteCitation = forwardRef(({ citation, children, className, fallbackDisplay, verification, eventHandlers, preventTooltips = false, pendingContent = TWO_DOTS_THINKING_CONTENT, renderVerifiedIndicator = () => _jsx(DefaultVerifiedIndicator, {}), renderPartialIndicator = () => _jsx(DefaultPartialIndicator, {}), symbolStyle = "number", customSymbol, }, ref) => {
127
+ const { citationKey, citationInstanceId, status } = useCitationData(citation, verification);
128
128
  const { isVerified, isMiss, isPartialMatch, isPending } = status;
129
129
  const displaySymbol = useMemo(() => {
130
130
  if (symbolStyle === "custom" && customSymbol)
@@ -174,8 +174,8 @@ FootnoteCitation.displayName = "FootnoteCitation";
174
174
  * ```
175
175
  */
176
176
  export const InlineCitation = forwardRef(({ citation, children, className, displayCitationValue = true, // Default to merged for inline
177
- fallbackDisplay, foundCitation, eventHandlers, preventTooltips = false, pendingContent = TWO_DOTS_THINKING_CONTENT, renderVerifiedIndicator = () => _jsx(DefaultVerifiedIndicator, {}), renderPartialIndicator = () => _jsx(DefaultPartialIndicator, {}), underlineStyle = "dotted", }, ref) => {
178
- const { citationKey, citationInstanceId, status } = useCitationData(citation, foundCitation);
177
+ fallbackDisplay, verification, eventHandlers, preventTooltips = false, pendingContent = TWO_DOTS_THINKING_CONTENT, renderVerifiedIndicator = () => _jsx(DefaultVerifiedIndicator, {}), renderPartialIndicator = () => _jsx(DefaultPartialIndicator, {}), underlineStyle = "dotted", }, ref) => {
178
+ const { citationKey, citationInstanceId, status } = useCitationData(citation, verification);
179
179
  const { isVerified, isMiss, isPartialMatch, isPending } = status;
180
180
  const displayText = useMemo(() => getCitationDisplayText(citation, {
181
181
  displayCitationValue,
@@ -216,8 +216,8 @@ InlineCitation.displayName = "InlineCitation";
216
216
  * // Renders: 1
217
217
  * ```
218
218
  */
219
- export const MinimalCitation = forwardRef(({ citation, children, className, displayCitationValue = false, fallbackDisplay, foundCitation, eventHandlers, preventTooltips = false, pendingContent = TWO_DOTS_THINKING_CONTENT, renderVerifiedIndicator = () => _jsx(DefaultVerifiedIndicator, {}), renderPartialIndicator = () => _jsx(DefaultPartialIndicator, {}), showStatusIndicator = true, }, ref) => {
220
- const { citationKey, citationInstanceId, status } = useCitationData(citation, foundCitation);
219
+ export const MinimalCitation = forwardRef(({ citation, children, className, displayCitationValue = false, fallbackDisplay, verification, eventHandlers, preventTooltips = false, pendingContent = TWO_DOTS_THINKING_CONTENT, renderVerifiedIndicator = () => _jsx(DefaultVerifiedIndicator, {}), renderPartialIndicator = () => _jsx(DefaultPartialIndicator, {}), showStatusIndicator = true, }, ref) => {
220
+ const { citationKey, citationInstanceId, status } = useCitationData(citation, verification);
221
221
  const { isVerified, isMiss, isPartialMatch, isPending } = status;
222
222
  const displayText = useMemo(() => getCitationDisplayText(citation, {
223
223
  displayCitationValue,
@@ -11,7 +11,7 @@ interface CitationContextValue {
11
11
  citationKey: string;
12
12
  citationInstanceId: string;
13
13
  status: CitationStatus;
14
- foundCitation: Verification | null;
14
+ verification: Verification | null;
15
15
  searchState: SearchState | null;
16
16
  config: {
17
17
  displayCitationValue: boolean;
@@ -25,7 +25,7 @@ export declare function useCitationContext(): CitationContextValue;
25
25
  export declare function useCitationContextSafe(): CitationContextValue | null;
26
26
  export interface CitationRootProps {
27
27
  citation: CitationType;
28
- foundCitation?: Verification | null;
28
+ verification?: Verification | null;
29
29
  searchState?: SearchState | null;
30
30
  children: ReactNode;
31
31
  displayCitationValue?: boolean;
@@ -20,16 +20,16 @@ export function useCitationContextSafe() {
20
20
  return useContext(CitationContext);
21
21
  }
22
22
  /** Root component that provides citation context to all child primitives. */
23
- export const CitationRoot = forwardRef(({ citation, foundCitation = null, searchState = null, children, displayCitationValue = false, fallbackDisplay = null, pendingContent = "..", className, ...props }, ref) => {
23
+ export const CitationRoot = forwardRef(({ citation, verification = null, searchState = null, children, displayCitationValue = false, fallbackDisplay = null, pendingContent = "..", className, ...props }, ref) => {
24
24
  const citationKey = useMemo(() => generateCitationKey(citation), [citation]);
25
25
  const citationInstanceId = useMemo(() => generateCitationInstanceId(citationKey), [citationKey]);
26
- const status = getCitationStatus(foundCitation);
26
+ const status = getCitationStatus(verification);
27
27
  const contextValue = useMemo(() => ({
28
28
  citation,
29
29
  citationKey,
30
30
  citationInstanceId,
31
31
  status,
32
- foundCitation,
32
+ verification,
33
33
  searchState,
34
34
  config: {
35
35
  displayCitationValue,
@@ -41,7 +41,7 @@ export const CitationRoot = forwardRef(({ citation, foundCitation = null, search
41
41
  citationKey,
42
42
  citationInstanceId,
43
43
  status,
44
- foundCitation,
44
+ verification,
45
45
  searchState,
46
46
  displayCitationValue,
47
47
  fallbackDisplay,
@@ -130,7 +130,7 @@ export interface CitationContentProps extends BaseCitationProps {
130
130
  /** Unique instance ID for this citation render */
131
131
  citationInstanceId: string;
132
132
  /** Found citation highlight data */
133
- foundCitation: Verification | null | undefined;
133
+ verification: Verification | null | undefined;
134
134
  /** Current search state */
135
135
  searchState: SearchState | undefined | null;
136
136
  /** Actual page number where citation was found */
@@ -8,8 +8,8 @@ export function generateCitationKey(citation) {
8
8
  citation.fileId || "",
9
9
  citation.pageNumber?.toString() || "",
10
10
  citation.fullPhrase || "",
11
+ citation.keySpan?.toString() || "",
11
12
  citation.value || "",
12
- citation.citationNumber?.toString() || "",
13
13
  citation.lineIds?.join(",") || "",
14
14
  citation.timestamps?.startTime || "",
15
15
  citation.timestamps?.endTime || "",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepcitation/deepcitation-js",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "DeepCitation JavaScript SDK for deterministic AI citation verification",
5
5
  "type": "module",
6
6
  "private": false,