@deepcitation/deepcitation-js 1.1.12 → 1.1.13
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/lib/react/CitationComponent.d.ts +25 -10
- package/lib/react/CitationComponent.js +23 -15
- package/lib/react/styles.css +38 -0
- package/lib/react/types.d.ts +9 -5
- package/package.json +1 -1
|
@@ -7,33 +7,43 @@ export type { CitationVariant } from "./types.js";
|
|
|
7
7
|
/**
|
|
8
8
|
* Props for the CitationComponent.
|
|
9
9
|
*
|
|
10
|
-
* @example Brackets variant (default) - shows keySpan
|
|
10
|
+
* @example Brackets variant (default) - shows keySpan in brackets with blue styling
|
|
11
11
|
* ```tsx
|
|
12
12
|
* <CitationComponent
|
|
13
|
-
* citation={{ citationNumber: 1,
|
|
13
|
+
* citation={{ citationNumber: 1, keySpan: "Revenue grew by 25%" }}
|
|
14
14
|
* verification={verificationResult}
|
|
15
15
|
* />
|
|
16
|
-
* // Renders: [
|
|
16
|
+
* // Renders: [Revenue grew by 25%✓] with blue text
|
|
17
17
|
* ```
|
|
18
18
|
*
|
|
19
|
-
* @example Numeric
|
|
19
|
+
* @example Numeric only - use displayKeySpan=false with brackets variant
|
|
20
20
|
* ```tsx
|
|
21
21
|
* <CitationComponent
|
|
22
22
|
* citation={{ citationNumber: 1, keySpan: "25% growth" }}
|
|
23
23
|
* verification={verificationResult}
|
|
24
|
-
*
|
|
24
|
+
* displayKeySpan={false}
|
|
25
25
|
* />
|
|
26
|
-
* // Renders: 1✓
|
|
26
|
+
* // Renders: [1✓]
|
|
27
27
|
* ```
|
|
28
28
|
*
|
|
29
|
-
* @example
|
|
29
|
+
* @example Without brackets - use displayBrackets=false
|
|
30
|
+
* ```tsx
|
|
31
|
+
* <CitationComponent
|
|
32
|
+
* citation={{ citationNumber: 1, keySpan: "25% growth" }}
|
|
33
|
+
* verification={verificationResult}
|
|
34
|
+
* displayBrackets={false}
|
|
35
|
+
* />
|
|
36
|
+
* // Renders: 25% growth✓ (no brackets)
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* @example Text variant - inherits parent text styling, no truncation
|
|
30
40
|
* ```tsx
|
|
31
41
|
* <CitationComponent
|
|
32
42
|
* citation={{ citationNumber: 1, keySpan: "25% growth" }}
|
|
33
43
|
* verification={verificationResult}
|
|
34
44
|
* variant="text"
|
|
35
45
|
* />
|
|
36
|
-
* // Renders: 25% growth✓
|
|
46
|
+
* // Renders: 25% growth✓ (inherits parent styling)
|
|
37
47
|
* ```
|
|
38
48
|
*
|
|
39
49
|
* @example Minimal variant - no brackets, just text and indicator
|
|
@@ -74,12 +84,17 @@ export interface CitationComponentProps extends BaseCitationProps {
|
|
|
74
84
|
/**
|
|
75
85
|
* Display variant for the citation.
|
|
76
86
|
* - `brackets`: Shows keySpan/number in brackets, blue text styling (default)
|
|
77
|
-
* - `
|
|
78
|
-
* - `text`: Shows the keySpan, no text styling, no truncate, shows indicator
|
|
87
|
+
* - `text`: Shows the keySpan, inherits parent text styling, no truncation, shows indicator
|
|
79
88
|
* - `minimal`: No brackets, just display text with indicator
|
|
80
89
|
* - `indicator`: Only the status indicator (checkmark/warning), no text
|
|
81
90
|
*/
|
|
82
91
|
variant?: CitationVariant;
|
|
92
|
+
/**
|
|
93
|
+
* Whether to show square brackets around the citation.
|
|
94
|
+
* Only applies to the `brackets` variant.
|
|
95
|
+
* @default true
|
|
96
|
+
*/
|
|
97
|
+
displayBrackets?: boolean;
|
|
83
98
|
/**
|
|
84
99
|
* Event handlers for citation interactions.
|
|
85
100
|
*/
|
|
@@ -136,12 +136,28 @@ const ImageOverlay = ({ src, alt, onClose, }) => {
|
|
|
136
136
|
// Use portal to render at body level, avoiding any parent positioning issues
|
|
137
137
|
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);
|
|
138
138
|
};
|
|
139
|
+
/**
|
|
140
|
+
* Diff details for partial/miss verification states.
|
|
141
|
+
* Shows expected vs found text.
|
|
142
|
+
*/
|
|
143
|
+
const DiffDetails = ({ citation, verification, status, }) => {
|
|
144
|
+
const { isMiss, isPartialMatch } = status;
|
|
145
|
+
if (!isMiss && !isPartialMatch)
|
|
146
|
+
return null;
|
|
147
|
+
const expectedText = citation.fullPhrase || citation.keySpan?.toString() || "";
|
|
148
|
+
const actualText = verification?.matchSnippet || "";
|
|
149
|
+
const truncatedExpected = expectedText.length > 100 ? expectedText.slice(0, 100) + "…" : expectedText;
|
|
150
|
+
const truncatedActual = actualText.length > 100 ? actualText.slice(0, 100) + "…" : actualText;
|
|
151
|
+
return (_jsxs("span", { className: "dc-diff-details", children: [truncatedExpected && (_jsxs("span", { className: "dc-status-searched", children: [_jsx("span", { className: "dc-status-label", children: "Expected" }), _jsx("span", { className: "dc-status-text", children: truncatedExpected })] })), isPartialMatch && truncatedActual && (_jsxs("span", { className: "dc-status-searched", children: [_jsx("span", { className: "dc-status-label", children: "Found" }), _jsx("span", { className: "dc-status-text", children: truncatedActual })] })), isMiss && (_jsxs("span", { className: "dc-status-searched", children: [_jsx("span", { className: "dc-status-label", children: "Found" }), _jsx("span", { className: "dc-status-text dc-status-text--miss", children: "Not found in source" })] }))] }));
|
|
152
|
+
};
|
|
139
153
|
/**
|
|
140
154
|
* Default popover content component.
|
|
141
155
|
* Shows verification image if available, otherwise shows text info.
|
|
156
|
+
* For partial/miss states, also displays expected vs found details.
|
|
142
157
|
*/
|
|
143
|
-
const DefaultPopoverContent = ({ verification, status, onImageClick, }) => {
|
|
158
|
+
const DefaultPopoverContent = ({ citation, verification, status, onImageClick, }) => {
|
|
144
159
|
const hasImage = verification?.verificationImageBase64;
|
|
160
|
+
const { isMiss, isPartialMatch } = status;
|
|
145
161
|
const handleImageClick = useCallback((e) => {
|
|
146
162
|
e.preventDefault();
|
|
147
163
|
e.stopPropagation();
|
|
@@ -149,9 +165,9 @@ const DefaultPopoverContent = ({ verification, status, onImageClick, }) => {
|
|
|
149
165
|
onImageClick(verification.verificationImageBase64);
|
|
150
166
|
}
|
|
151
167
|
}, [hasImage, verification?.verificationImageBase64, onImageClick]);
|
|
152
|
-
// If we have a verification image, show
|
|
168
|
+
// If we have a verification image, show image + diff details for partial/miss
|
|
153
169
|
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: verification.verificationImageBase64, alt: "Citation verification", className: "dc-popover-image", loading: "lazy" }) }));
|
|
170
|
+
return (_jsxs(_Fragment, { children: [_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" }) }), (isMiss || isPartialMatch) && (_jsx(DiffDetails, { citation: citation, verification: verification, status: status }))] }));
|
|
155
171
|
}
|
|
156
172
|
// No image - show text info
|
|
157
173
|
const statusLabel = getStatusLabel(status);
|
|
@@ -161,7 +177,7 @@ const DefaultPopoverContent = ({ verification, status, onImageClick, }) => {
|
|
|
161
177
|
if (!hasSnippet && !statusLabel) {
|
|
162
178
|
return null;
|
|
163
179
|
}
|
|
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] }))] }));
|
|
180
|
+
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] })), (isMiss || isPartialMatch) && (_jsx(DiffDetails, { citation: citation, verification: verification, status: status }))] }));
|
|
165
181
|
};
|
|
166
182
|
// =============================================================================
|
|
167
183
|
// MAIN COMPONENT
|
|
@@ -182,7 +198,7 @@ const DefaultPopoverContent = ({ verification, status, onImageClick, }) => {
|
|
|
182
198
|
* This means partial matches have blue text (because they were found) but
|
|
183
199
|
* an orange indicator (because they didn't match exactly).
|
|
184
200
|
*/
|
|
185
|
-
export const CitationComponent = forwardRef(({ citation, children, className, displayKeySpan =
|
|
201
|
+
export const CitationComponent = forwardRef(({ citation, children, className, displayKeySpan = true, displayBrackets = true, fallbackDisplay, verification, variant = "brackets", eventHandlers, isMobile = false, renderIndicator, renderContent, popoverPosition = "top", renderPopoverContent, }, ref) => {
|
|
186
202
|
const containerRef = useRef(null);
|
|
187
203
|
const wrapperRef = useRef(null);
|
|
188
204
|
const [expandedImageSrc, setExpandedImageSrc] = useState(null);
|
|
@@ -268,12 +284,8 @@ export const CitationComponent = forwardRef(({ citation, children, className, di
|
|
|
268
284
|
// const { isVerified, isPending } = status;
|
|
269
285
|
const { isMiss, isPartialMatch, isVerified, isPending } = status;
|
|
270
286
|
const displayText = useMemo(() => {
|
|
271
|
-
// For numeric variant, always show the citation number
|
|
272
|
-
if (variant === "numeric") {
|
|
273
|
-
return citation.citationNumber?.toString() ?? "";
|
|
274
|
-
}
|
|
275
287
|
// For text/minimal variants, always show keySpan
|
|
276
|
-
// For brackets variant, show keySpan
|
|
288
|
+
// For brackets variant, show keySpan based on displayKeySpan prop
|
|
277
289
|
return getCitationDisplayText(citation, {
|
|
278
290
|
displayKeySpan: variant === "text" ||
|
|
279
291
|
variant === "minimal" ||
|
|
@@ -348,12 +360,8 @@ export const CitationComponent = forwardRef(({ citation, children, className, di
|
|
|
348
360
|
if (variant === "minimal") {
|
|
349
361
|
return (_jsxs("span", { className: "dc-citation-text", children: [displayText, renderStatusIndicator()] }));
|
|
350
362
|
}
|
|
351
|
-
// Numeric variant - shows citation number with indicator, no brackets
|
|
352
|
-
if (variant === "numeric") {
|
|
353
|
-
return (_jsxs("span", { className: "dc-citation-text", children: [displayText, renderStatusIndicator()] }));
|
|
354
|
-
}
|
|
355
363
|
// Brackets variant (default) - keySpan/number in brackets with styling
|
|
356
|
-
return (_jsxs("span", { className: "dc-citation-bracket", "aria-hidden": "true", role: "presentation", children: ["[", _jsxs("span", { className: "dc-citation-text", children: [displayText, renderStatusIndicator()] }), "]"] }));
|
|
364
|
+
return (_jsxs("span", { className: "dc-citation-bracket", "aria-hidden": "true", role: "presentation", children: [displayBrackets && "[", _jsxs("span", { className: "dc-citation-text", children: [displayText, renderStatusIndicator()] }), displayBrackets && "]"] }));
|
|
357
365
|
};
|
|
358
366
|
// Determine if popover should be shown
|
|
359
367
|
const isPopoverHidden = popoverPosition === "hidden";
|
package/lib/react/styles.css
CHANGED
|
@@ -57,6 +57,27 @@
|
|
|
57
57
|
text-overflow: ellipsis;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
/* Text variant - plain styling that inherits from parent */
|
|
61
|
+
.dc-citation-text--plain {
|
|
62
|
+
max-width: none;
|
|
63
|
+
overflow: visible;
|
|
64
|
+
font-family: inherit;
|
|
65
|
+
font-size: inherit;
|
|
66
|
+
line-height: inherit;
|
|
67
|
+
color: inherit;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* Text variant container - inherit all styling from parent */
|
|
71
|
+
.dc-citation--text {
|
|
72
|
+
font-family: inherit;
|
|
73
|
+
font-size: inherit;
|
|
74
|
+
line-height: inherit;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.dc-citation--text .dc-citation-text--plain {
|
|
78
|
+
color: inherit;
|
|
79
|
+
}
|
|
80
|
+
|
|
60
81
|
/* Status-specific styles */
|
|
61
82
|
.dc-citation--verified .dc-citation-text {
|
|
62
83
|
color: var(--dc-color-verified, #2563eb);
|
|
@@ -423,6 +444,23 @@
|
|
|
423
444
|
text-overflow: ellipsis;
|
|
424
445
|
}
|
|
425
446
|
|
|
447
|
+
.dc-status-text--miss {
|
|
448
|
+
color: var(--dc-color-warning, #d97706);
|
|
449
|
+
font-style: italic;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/* Diff details container in popover */
|
|
453
|
+
.dc-diff-details {
|
|
454
|
+
display: flex;
|
|
455
|
+
flex-direction: column;
|
|
456
|
+
gap: 0.25rem;
|
|
457
|
+
margin-top: 0.5rem;
|
|
458
|
+
padding-top: 0.5rem;
|
|
459
|
+
border-top: 1px solid var(--dc-border-color, #e2e8f0);
|
|
460
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
461
|
+
font-size: 0.75rem;
|
|
462
|
+
}
|
|
463
|
+
|
|
426
464
|
/* Dark mode support */
|
|
427
465
|
@media (prefers-color-scheme: dark) {
|
|
428
466
|
.dc-status-tooltip {
|
package/lib/react/types.d.ts
CHANGED
|
@@ -4,13 +4,14 @@ import type { SearchState } from "../types/search.js";
|
|
|
4
4
|
/**
|
|
5
5
|
* Available citation display variants.
|
|
6
6
|
*
|
|
7
|
-
* - `brackets`: Shows keySpan/number in brackets with blue text styling (default)
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* - `brackets`: Shows keySpan/number in brackets with blue text styling (default).
|
|
8
|
+
* Use displayKeySpan=false for numeric-only display.
|
|
9
|
+
* Use displayBrackets=false to hide the square brackets.
|
|
10
|
+
* - `text`: Shows the keySpan, inherits parent text styling, no truncation, shows indicator
|
|
10
11
|
* - `minimal`: No brackets, just display text with indicator
|
|
11
12
|
* - `indicator`: Only the status indicator (checkmark/warning), no text
|
|
12
13
|
*/
|
|
13
|
-
export type CitationVariant = "brackets" | "
|
|
14
|
+
export type CitationVariant = "brackets" | "text" | "minimal" | "indicator";
|
|
14
15
|
/**
|
|
15
16
|
* URL fetch status for URL citations.
|
|
16
17
|
*/
|
|
@@ -89,7 +90,10 @@ export interface BaseCitationProps {
|
|
|
89
90
|
className?: string;
|
|
90
91
|
/** Class name for controlling inner content width */
|
|
91
92
|
innerWidthClassName?: string;
|
|
92
|
-
/**
|
|
93
|
+
/**
|
|
94
|
+
* When true, displays keySpan text. When false, displays citation number only.
|
|
95
|
+
* @default true
|
|
96
|
+
*/
|
|
93
97
|
displayKeySpan?: boolean;
|
|
94
98
|
/** Fallback display text when citation keySpan is empty */
|
|
95
99
|
fallbackDisplay?: string | null;
|