@semiont/react-ui 0.4.20 → 0.4.22
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 +8 -5
- package/dist/{PdfAnnotationCanvas.client-CHDCGQBR.mjs → PdfAnnotationCanvas.client-5QESNO5H.mjs} +13 -16
- package/dist/PdfAnnotationCanvas.client-5QESNO5H.mjs.map +1 -0
- package/dist/TranslationManager-9Xj3MIWQ.d.mts +16 -0
- package/dist/chunk-4NOUO3W6.mjs +7788 -0
- package/dist/chunk-4NOUO3W6.mjs.map +1 -0
- package/dist/index.d.mts +212 -1206
- package/dist/index.mjs +3332 -13712
- package/dist/index.mjs.map +1 -1
- package/dist/test-utils.d.mts +48 -21
- package/dist/test-utils.mjs +2505 -87
- package/dist/test-utils.mjs.map +1 -1
- package/package.json +2 -2
- package/src/components/AnnotateReferencesProgressWidget.tsx +21 -28
- package/src/components/CodeMirrorRenderer.tsx +12 -12
- package/src/components/LiveRegion.tsx +1 -2
- package/src/components/StatusDisplay.tsx +42 -16
- package/src/components/Toolbar.tsx +4 -4
- package/src/components/__tests__/AnnotateReferencesProgressWidget.test.tsx +34 -20
- package/src/components/__tests__/StatusDisplay.test.tsx +50 -65
- package/src/components/__tests__/Toolbar.test.tsx +4 -4
- package/src/components/annotation/AnnotateToolbar.tsx +8 -9
- package/src/components/annotation/__tests__/AnnotateToolbar.test.tsx +31 -77
- package/src/components/annotation-popups/JsonLdView.tsx +1 -2
- package/src/components/annotation-popups/__tests__/JsonLdView.test.tsx +1 -2
- package/src/components/image-annotation/AnnotationOverlay.tsx +15 -18
- package/src/components/image-annotation/SvgDrawingCanvas.tsx +12 -17
- package/src/components/modals/ConfigureGenerationStep.tsx +1 -2
- package/src/components/modals/PermissionDeniedModal.tsx +11 -11
- package/src/components/modals/ReferenceWizardModal.tsx +14 -18
- package/src/components/modals/ResourceSearchModal.tsx +12 -8
- package/src/components/modals/SearchModal.tsx +11 -6
- package/src/components/modals/SearchResultsStep.tsx +1 -3
- package/src/components/modals/SessionExpiredModal.tsx +11 -11
- package/src/components/modals/__tests__/PermissionDeniedModal.test.tsx +7 -7
- package/src/components/modals/__tests__/ResourceSearchModal.test.tsx +10 -8
- package/src/components/modals/__tests__/SearchModal.accessibility.test.tsx +6 -2
- package/src/components/modals/__tests__/SearchModal.basic.test.tsx +6 -2
- package/src/components/modals/__tests__/SearchModal.keyboard.test.tsx +6 -2
- package/src/components/modals/__tests__/SearchModal.search-wiring.test.tsx +10 -7
- package/src/components/modals/__tests__/SearchModal.visual.test.tsx +6 -2
- package/src/components/modals/__tests__/SessionExpiredModal.test.tsx +5 -5
- package/src/components/navigation/CollapsibleResourceNavigation.tsx +10 -10
- package/src/components/navigation/ObservableLink.tsx +6 -6
- package/src/components/navigation/SimpleNavigation.tsx +4 -4
- package/src/components/navigation/__tests__/ObservableLink.test.tsx +4 -4
- package/src/components/navigation/__tests__/SimpleNavigation.test.tsx +4 -4
- package/src/components/pdf-annotation/PdfAnnotationCanvas.tsx +15 -18
- package/src/components/pdf-annotation/__tests__/PdfAnnotationCanvas.test.tsx +1 -2
- package/src/components/resource/AnnotateView.tsx +8 -10
- package/src/components/resource/AnnotationHistory.tsx +9 -12
- package/src/components/resource/BrowseView.tsx +11 -8
- package/src/components/resource/ResourceViewer.tsx +22 -34
- package/src/components/resource/__tests__/AnnotationHistory.test.tsx +54 -192
- package/src/components/resource/__tests__/BrowseView.test.tsx +38 -87
- package/src/components/resource/__tests__/ResourceViewer.mode-switch.test.tsx +41 -31
- package/src/components/resource/__tests__/event-formatting.test.ts +6 -2
- package/src/components/resource/event-formatting.ts +2 -3
- package/src/components/resource/panels/AssessmentEntry.tsx +7 -8
- package/src/components/resource/panels/AssessmentPanel.tsx +21 -17
- package/src/components/resource/panels/AssistSection.tsx +15 -21
- package/src/components/resource/panels/CollaborationPanel.tsx +29 -7
- package/src/components/resource/panels/CommentEntry.tsx +7 -8
- package/src/components/resource/panels/CommentsPanel.tsx +11 -13
- package/src/components/resource/panels/HighlightEntry.tsx +7 -8
- package/src/components/resource/panels/HighlightPanel.tsx +12 -13
- package/src/components/resource/panels/ReferenceEntry.tsx +13 -15
- package/src/components/resource/panels/ReferencesPanel.tsx +17 -19
- package/src/components/resource/panels/ResourceInfoPanel.tsx +8 -7
- package/src/components/resource/panels/StatisticsPanel.tsx +2 -3
- package/src/components/resource/panels/TagEntry.tsx +7 -8
- package/src/components/resource/panels/TaggingPanel.tsx +14 -23
- package/src/components/resource/panels/UnifiedAnnotationsPanel.tsx +4 -3
- package/src/components/resource/panels/__tests__/AssessmentEntry.test.tsx +4 -4
- package/src/components/resource/panels/__tests__/AssessmentPanel.test.tsx +22 -57
- package/src/components/resource/panels/__tests__/CollaborationPanel.test.tsx +51 -20
- package/src/components/resource/panels/__tests__/CommentEntry.test.tsx +4 -4
- package/src/components/resource/panels/__tests__/CommentsPanel.test.tsx +22 -61
- package/src/components/resource/panels/__tests__/HighlightEntry.test.tsx +4 -4
- package/src/components/resource/panels/__tests__/HighlightPanel.annotationProgress.test.tsx +1 -2
- package/src/components/resource/panels/__tests__/ReferenceEntry.test.tsx +7 -8
- package/src/components/resource/panels/__tests__/ReferencesPanel.observable-flow.test.tsx +153 -0
- package/src/components/resource/panels/__tests__/ReferencesPanel.test.tsx +51 -106
- package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +28 -53
- package/src/components/resource/panels/__tests__/StatisticsPanel.test.tsx +3 -3
- package/src/components/resource/panels/__tests__/TagEntry.test.tsx +4 -4
- package/src/components/resource/panels/__tests__/TaggingPanel.test.tsx +19 -52
- package/src/components/settings/SettingsPanel.tsx +9 -9
- package/src/components/settings/__tests__/SettingsPanel.test.tsx +15 -15
- package/src/features/admin-devops/components/AdminDevOpsPage.tsx +1 -2
- package/src/features/admin-exchange/components/AdminExchangePage.tsx +1 -1
- package/src/features/admin-exchange/components/ImportCard.tsx +2 -7
- package/src/features/admin-security/components/AdminSecurityPage.tsx +1 -2
- package/src/features/admin-users/components/AdminUsersPage.tsx +1 -1
- package/src/features/moderate-entity-tags/components/EntityTagsPage.tsx +1 -2
- package/src/features/moderate-recent/components/RecentDocumentsPage.tsx +1 -2
- package/src/features/moderate-tag-schemas/components/TagSchemasPage.tsx +1 -1
- package/src/features/moderation-linked-data/components/LinkedDataPage.tsx +1 -1
- package/src/features/resource-compose/__tests__/ResourceComposePage.test.tsx +5 -3
- package/src/features/resource-compose/components/ResourceComposePage.tsx +6 -22
- package/src/features/resource-discovery/__tests__/ResourceDiscoveryPage.test.tsx +4 -3
- package/src/features/resource-discovery/components/ResourceCard.tsx +1 -2
- package/src/features/resource-discovery/components/ResourceDiscoveryPage.tsx +3 -4
- package/src/features/resource-viewer/__tests__/ResourceViewerPage.test.tsx +37 -45
- package/src/features/resource-viewer/components/ResourceViewerPage.tsx +129 -197
- package/dist/KnowledgeBaseSessionContext-BNNunwzO.d.mts +0 -175
- package/dist/PdfAnnotationCanvas.client-CHDCGQBR.mjs.map +0 -1
- package/dist/chunk-OZICDVH7.mjs +0 -62
- package/dist/chunk-OZICDVH7.mjs.map +0 -1
- package/dist/chunk-R4CCMFJH.mjs +0 -877
- package/dist/chunk-R4CCMFJH.mjs.map +0 -1
- package/dist/chunk-VN5NY4SN.mjs +0 -200
- package/dist/chunk-VN5NY4SN.mjs.map +0 -1
- package/src/components/modals/ProposeEntitiesModal.tsx +0 -179
- package/src/components/modals/__tests__/ProposeEntitiesModal.test.tsx +0 -129
- package/src/features/resource-viewer/__tests__/AnnotationCreationPending.test.tsx +0 -323
- package/src/features/resource-viewer/__tests__/AnnotationDeletionIntegration.test.tsx +0 -245
- package/src/features/resource-viewer/__tests__/AnnotationProgressDismissal.test.tsx +0 -303
- package/src/features/resource-viewer/__tests__/BindFlowIntegration.test.tsx +0 -150
- package/src/features/resource-viewer/__tests__/DetectionFlowBug.test.tsx +0 -243
- package/src/features/resource-viewer/__tests__/DetectionFlowIntegration.test.tsx +0 -383
- package/src/features/resource-viewer/__tests__/ResourceMutations.test.tsx +0 -299
- package/src/features/resource-viewer/__tests__/ToastNotifications.test.tsx +0 -186
- package/src/features/resource-viewer/__tests__/YieldFlowIntegration.test.tsx +0 -429
- package/src/features/resource-viewer/__tests__/annotation-progress-flow.test.tsx +0 -348
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React, { useState, useRef, useEffect } from 'react';
|
|
4
|
-
import {
|
|
4
|
+
import { useSemiont } from '../../session/SemiontProvider';
|
|
5
5
|
|
|
6
6
|
export interface SimpleNavigationItem {
|
|
7
7
|
name: string;
|
|
@@ -29,7 +29,7 @@ export interface SimpleNavigationProps {
|
|
|
29
29
|
* Simple navigation component for Admin and Moderation modes.
|
|
30
30
|
* Renders a section header with optional dropdown and static navigation tabs.
|
|
31
31
|
*
|
|
32
|
-
* @emits
|
|
32
|
+
* @emits shell:sidebar-toggle - Toggle sidebar collapsed/expanded state. Payload: undefined
|
|
33
33
|
*/
|
|
34
34
|
export function SimpleNavigation({
|
|
35
35
|
title,
|
|
@@ -47,7 +47,7 @@ export function SimpleNavigation({
|
|
|
47
47
|
|
|
48
48
|
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
|
49
49
|
const dropdownRef = useRef<HTMLDivElement>(null);
|
|
50
|
-
const
|
|
50
|
+
const semiont = useSemiont();
|
|
51
51
|
|
|
52
52
|
const toggleDropdown = () => setIsDropdownOpen(!isDropdownOpen);
|
|
53
53
|
const closeDropdown = () => setIsDropdownOpen(false);
|
|
@@ -86,7 +86,7 @@ export function SimpleNavigation({
|
|
|
86
86
|
!isCollapsed && <span className="semiont-nav-section__header-text">{title}</span>
|
|
87
87
|
)}
|
|
88
88
|
<button
|
|
89
|
-
onClick={() =>
|
|
89
|
+
onClick={() => semiont.emit('shell:sidebar-toggle', undefined)}
|
|
90
90
|
className="semiont-nav-section__header-icon"
|
|
91
91
|
title={isCollapsed ? expandSidebarLabel : collapseSidebarLabel}
|
|
92
92
|
aria-label={isCollapsed ? expandSidebarLabel : collapseSidebarLabel}
|
|
@@ -26,17 +26,17 @@ describe('ObservableLink', () => {
|
|
|
26
26
|
expect(screen.getByText('Click me')).toBeInTheDocument();
|
|
27
27
|
});
|
|
28
28
|
|
|
29
|
-
it('emits
|
|
29
|
+
it('emits nav:link-clicked with href and label on click', () => {
|
|
30
30
|
const handler = vi.fn();
|
|
31
31
|
|
|
32
|
-
const {
|
|
32
|
+
const { shellBus } = renderWithProviders(
|
|
33
33
|
<ObservableLink href="/discover" label="Discover">
|
|
34
34
|
Discover Resources
|
|
35
35
|
</ObservableLink>,
|
|
36
|
-
{
|
|
36
|
+
{ returnShellBus: true }
|
|
37
37
|
);
|
|
38
38
|
|
|
39
|
-
const subscription =
|
|
39
|
+
const subscription = shellBus!.get('nav:link-clicked').subscribe(handler);
|
|
40
40
|
|
|
41
41
|
const link = screen.getByRole('link');
|
|
42
42
|
fireEvent.click(link);
|
|
@@ -93,15 +93,15 @@ describe('SimpleNavigation', () => {
|
|
|
93
93
|
});
|
|
94
94
|
|
|
95
95
|
describe('sidebar toggle', () => {
|
|
96
|
-
it('emits
|
|
96
|
+
it('emits shell:sidebar-toggle on collapse button click', () => {
|
|
97
97
|
const handler = vi.fn();
|
|
98
98
|
|
|
99
|
-
const {
|
|
99
|
+
const { shellBus } = renderWithProviders(
|
|
100
100
|
<SimpleNavigation {...defaultProps} />,
|
|
101
|
-
{
|
|
101
|
+
{ returnShellBus: true }
|
|
102
102
|
);
|
|
103
103
|
|
|
104
|
-
const subscription =
|
|
104
|
+
const subscription = shellBus!.get('shell:sidebar-toggle').subscribe(handler);
|
|
105
105
|
|
|
106
106
|
const collapseButton = screen.getByLabelText('Collapse sidebar');
|
|
107
107
|
fireEvent.click(collapseButton);
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React, { useRef, useState, useCallback, useEffect, useMemo } from 'react';
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
4
|
+
import type { Annotation } from '@semiont/core';
|
|
5
|
+
import { getTargetSelector } from '@semiont/core';
|
|
6
|
+
import { createHoverHandlers, type SemiontSession } from '@semiont/sdk';
|
|
7
7
|
import type { SelectionMotivation } from '../annotation/AnnotateToolbar';
|
|
8
|
-
import type { EventBus } from "@semiont/core"
|
|
9
8
|
import {
|
|
10
9
|
canvasToPdfCoordinates,
|
|
11
10
|
pdfToCanvasCoordinates,
|
|
@@ -21,8 +20,6 @@ import {
|
|
|
21
20
|
} from '../../lib/browser-pdfjs';
|
|
22
21
|
import './PdfAnnotationCanvas.css';
|
|
23
22
|
|
|
24
|
-
type Annotation = components['schemas']['Annotation'];
|
|
25
|
-
|
|
26
23
|
export type DrawingMode = 'rectangle' | 'circle' | 'polygon' | null;
|
|
27
24
|
|
|
28
25
|
/**
|
|
@@ -52,7 +49,7 @@ interface PdfAnnotationCanvasProps {
|
|
|
52
49
|
existingAnnotations?: Annotation[];
|
|
53
50
|
drawingMode: DrawingMode;
|
|
54
51
|
selectedMotivation?: SelectionMotivation | null;
|
|
55
|
-
|
|
52
|
+
session?: SemiontSession | null | undefined;
|
|
56
53
|
hoveredAnnotationId?: string | null;
|
|
57
54
|
selectedAnnotationId?: string | null;
|
|
58
55
|
hoverDelayMs?: number;
|
|
@@ -70,7 +67,7 @@ export function PdfAnnotationCanvas({
|
|
|
70
67
|
existingAnnotations = [],
|
|
71
68
|
drawingMode,
|
|
72
69
|
selectedMotivation,
|
|
73
|
-
|
|
70
|
+
session,
|
|
74
71
|
hoveredAnnotationId,
|
|
75
72
|
selectedAnnotationId,
|
|
76
73
|
hoverDelayMs = 150
|
|
@@ -235,7 +232,7 @@ export function PdfAnnotationCanvas({
|
|
|
235
232
|
}, [isDrawing, selection]);
|
|
236
233
|
|
|
237
234
|
const handleMouseUp = useCallback(() => {
|
|
238
|
-
if (!isDrawing || !selection || !pageDimensions || !displayDimensions || !
|
|
235
|
+
if (!isDrawing || !selection || !pageDimensions || !displayDimensions || !session) {
|
|
239
236
|
setIsDrawing(false);
|
|
240
237
|
setSelection(null);
|
|
241
238
|
return;
|
|
@@ -280,7 +277,7 @@ export function PdfAnnotationCanvas({
|
|
|
280
277
|
});
|
|
281
278
|
|
|
282
279
|
if (clickedAnnotation) {
|
|
283
|
-
|
|
280
|
+
session?.client.browse.click(clickedAnnotation.id, clickedAnnotation.motivation);
|
|
284
281
|
setIsDrawing(false);
|
|
285
282
|
setSelection(null);
|
|
286
283
|
return;
|
|
@@ -319,14 +316,14 @@ export function PdfAnnotationCanvas({
|
|
|
319
316
|
|
|
320
317
|
// Emit annotation:requested event with FragmentSelector
|
|
321
318
|
if (selectedMotivation) {
|
|
322
|
-
|
|
323
|
-
|
|
319
|
+
session.client.mark.request(
|
|
320
|
+
{
|
|
324
321
|
type: 'FragmentSelector',
|
|
325
322
|
conformsTo: 'http://tools.ietf.org/rfc/rfc3778',
|
|
326
|
-
value: fragmentSelector
|
|
323
|
+
value: fragmentSelector,
|
|
327
324
|
},
|
|
328
|
-
|
|
329
|
-
|
|
325
|
+
selectedMotivation,
|
|
326
|
+
);
|
|
330
327
|
}
|
|
331
328
|
|
|
332
329
|
// Keep drawing state active to show preview until annotation is persisted
|
|
@@ -357,8 +354,8 @@ export function PdfAnnotationCanvas({
|
|
|
357
354
|
|
|
358
355
|
// Hover handlers with currentHover guard and dwell delay
|
|
359
356
|
const { handleMouseEnter, handleMouseLeave } = useMemo(
|
|
360
|
-
() => createHoverHandlers((
|
|
361
|
-
[
|
|
357
|
+
() => createHoverHandlers((id) => session?.client.beckon.hover(id), hoverDelayMs),
|
|
358
|
+
[session, hoverDelayMs]
|
|
362
359
|
);
|
|
363
360
|
|
|
364
361
|
// Calculate motivation color
|
|
@@ -457,7 +454,7 @@ export function PdfAnnotationCanvas({
|
|
|
457
454
|
cursor: 'pointer',
|
|
458
455
|
opacity: isSelected ? 1 : isHovered ? 0.9 : 0.7
|
|
459
456
|
}}
|
|
460
|
-
onClick={() =>
|
|
457
|
+
onClick={() => session?.client.browse.click(ann.id, ann.motivation)}
|
|
461
458
|
onMouseEnter={() => handleMouseEnter(ann.id)}
|
|
462
459
|
onMouseLeave={handleMouseLeave}
|
|
463
460
|
/>
|
|
@@ -14,7 +14,7 @@ import { PdfAnnotationCanvas } from '../PdfAnnotationCanvas';
|
|
|
14
14
|
import { resourceId } from '@semiont/core';
|
|
15
15
|
import type { components } from '@semiont/core';
|
|
16
16
|
|
|
17
|
-
type Annotation
|
|
17
|
+
import type { Annotation } from '@semiont/core';
|
|
18
18
|
|
|
19
19
|
// Mock browser-pdfjs module
|
|
20
20
|
vi.mock('../../../lib/browser-pdfjs', () => ({
|
|
@@ -130,7 +130,6 @@ describe('PdfAnnotationCanvas', () => {
|
|
|
130
130
|
'@context': 'http://www.w3.org/ns/anno.jsonld',
|
|
131
131
|
type: 'Annotation',
|
|
132
132
|
id: 'ann-1',
|
|
133
|
-
body: [],
|
|
134
133
|
target: {
|
|
135
134
|
source: mockResourceId,
|
|
136
135
|
selector: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useRef, useEffect, useCallback, lazy, Suspense } from 'react';
|
|
4
|
-
import { getMimeCategory, isPdfMimeType } from '@semiont/
|
|
4
|
+
import { getMimeCategory, isPdfMimeType } from '@semiont/core';
|
|
5
5
|
import { ANNOTATORS } from '../../lib/annotation-registry';
|
|
6
6
|
import { segmentTextWithAnnotations } from '../../lib/text-segmentation';
|
|
7
7
|
import { buildTextSelectors, fallbackTextPosition } from '../../lib/text-selection-handler';
|
|
@@ -14,7 +14,8 @@ const PdfAnnotationCanvas = lazy(() => import('../pdf-annotation/PdfAnnotationCa
|
|
|
14
14
|
|
|
15
15
|
import { CodeMirrorRenderer } from '../CodeMirrorRenderer';
|
|
16
16
|
import type { EditorView } from '@codemirror/view';
|
|
17
|
-
import {
|
|
17
|
+
import { useSemiont } from '../../session/SemiontProvider';
|
|
18
|
+
import { useObservable } from '../../hooks/useObservable';
|
|
18
19
|
import { useEventSubscriptions } from '../../contexts/useEventSubscription';
|
|
19
20
|
|
|
20
21
|
// Type augmentation for custom DOM properties
|
|
@@ -68,7 +69,7 @@ export function AnnotateView({
|
|
|
68
69
|
}: Props) {
|
|
69
70
|
const { newAnnotationIds } = useResourceAnnotations();
|
|
70
71
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
71
|
-
const
|
|
72
|
+
const session = useObservable(useSemiont().activeSession$);
|
|
72
73
|
|
|
73
74
|
const category = getMimeCategory(mimeType);
|
|
74
75
|
|
|
@@ -175,10 +176,7 @@ export function AnnotateView({
|
|
|
175
176
|
const selectors = buildTextSelectors(content, text, start, end);
|
|
176
177
|
if (!selectors) return;
|
|
177
178
|
|
|
178
|
-
|
|
179
|
-
selector: selectors,
|
|
180
|
-
motivation: selectedMotivation
|
|
181
|
-
});
|
|
179
|
+
session?.client.mark.request(selectors, selectedMotivation);
|
|
182
180
|
|
|
183
181
|
// Clear selection after creating annotation
|
|
184
182
|
selection.removeAllRanges();
|
|
@@ -218,7 +216,7 @@ export function AnnotateView({
|
|
|
218
216
|
showLineNumbers={showLineNumbers}
|
|
219
217
|
hoverDelayMs={hoverDelayMs}
|
|
220
218
|
enableWidgets={enableWidgets}
|
|
221
|
-
|
|
219
|
+
session={session}
|
|
222
220
|
{...(getTargetResourceName && { getTargetResourceName })}
|
|
223
221
|
{...(generatingReferenceId !== undefined && { generatingReferenceId })}
|
|
224
222
|
/>
|
|
@@ -250,7 +248,7 @@ export function AnnotateView({
|
|
|
250
248
|
existingAnnotations={allAnnotations}
|
|
251
249
|
drawingMode={selectedMotivation ? selectedShape : null}
|
|
252
250
|
selectedMotivation={selectedMotivation}
|
|
253
|
-
|
|
251
|
+
session={session}
|
|
254
252
|
hoveredAnnotationId={hoveredAnnotationId || null}
|
|
255
253
|
hoverDelayMs={hoverDelayMs}
|
|
256
254
|
/>
|
|
@@ -280,7 +278,7 @@ export function AnnotateView({
|
|
|
280
278
|
existingAnnotations={allAnnotations}
|
|
281
279
|
drawingMode={selectedMotivation ? selectedShape : null}
|
|
282
280
|
selectedMotivation={selectedMotivation}
|
|
283
|
-
|
|
281
|
+
session={session}
|
|
284
282
|
hoveredAnnotationId={hoveredAnnotationId || null}
|
|
285
283
|
hoverDelayMs={hoverDelayMs}
|
|
286
284
|
/>
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
import React, { useEffect, useRef } from 'react';
|
|
4
4
|
import { useTranslations } from '../../contexts/TranslationContext';
|
|
5
5
|
import type { RouteBuilder, LinkComponentProps } from '../../contexts/RoutingContext';
|
|
6
|
-
import {
|
|
6
|
+
import { useSemiont } from '../../session/SemiontProvider';
|
|
7
|
+
import { useObservable } from '../../hooks/useObservable';
|
|
7
8
|
import type { ResourceId } from '@semiont/core';
|
|
8
9
|
import { getAnnotationUriFromEvent, type StoredEventLike } from '@semiont/core';
|
|
9
10
|
import { HistoryEvent } from './HistoryEvent';
|
|
@@ -19,17 +20,13 @@ interface Props {
|
|
|
19
20
|
|
|
20
21
|
export function AnnotationHistory({ rUri, hoveredAnnotationId, onEventHover, onEventClick, Link, routes }: Props) {
|
|
21
22
|
const t = useTranslations('AnnotationHistory');
|
|
23
|
+
const semiont = useObservable(useSemiont().activeSession$)?.client;
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const { data: eventsData, isLoading: loading, isError: error } = resources.events.useQuery(rUri);
|
|
29
|
-
|
|
30
|
-
// Load annotations to look up text for removed/resolved events (single request)
|
|
31
|
-
const { data: annotationsData } = resources.annotations.useQuery(rUri);
|
|
32
|
-
const annotations = annotationsData?.annotations || [];
|
|
25
|
+
const eventsData = useObservable(semiont?.browse.events(rUri));
|
|
26
|
+
const annotationsData = useObservable(semiont?.browse.annotations(rUri));
|
|
27
|
+
const loading = eventsData === undefined;
|
|
28
|
+
const error = false;
|
|
29
|
+
const annotations = annotationsData ?? [];
|
|
33
30
|
|
|
34
31
|
// Refs to track event elements for scrolling
|
|
35
32
|
const eventRefs = useRef<Map<string, HTMLElement>>(new Map());
|
|
@@ -37,7 +34,7 @@ export function AnnotationHistory({ rUri, hoveredAnnotationId, onEventHover, onE
|
|
|
37
34
|
|
|
38
35
|
// Sort events by oldest first (most recent at bottom)
|
|
39
36
|
// Filter out job events - they're represented by mark:body-updated events instead
|
|
40
|
-
const events: StoredEventLike[] = !eventsData
|
|
37
|
+
const events: StoredEventLike[] = !eventsData ? [] : (eventsData as StoredEventLike[])
|
|
41
38
|
.filter((e) => {
|
|
42
39
|
return e.type !== 'job:started' && e.type !== 'job:progress' && e.type !== 'job:completed';
|
|
43
40
|
})
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
import { useEffect, useRef, useCallback, useMemo, memo, lazy, Suspense } from 'react';
|
|
4
4
|
import ReactMarkdown from 'react-markdown';
|
|
5
5
|
import remarkGfm from 'remark-gfm';
|
|
6
|
-
import {
|
|
6
|
+
import { annotationId as toAnnotationId } from '@semiont/core';
|
|
7
|
+
import { getMimeCategory, isPdfMimeType } from '@semiont/core';
|
|
8
|
+
import { createHoverHandlers } from '@semiont/sdk';
|
|
7
9
|
import { ANNOTATORS } from '../../lib/annotation-registry';
|
|
8
|
-
import { createHoverHandlers } from '../../hooks/useBeckonFlow';
|
|
9
10
|
import { scrollAnnotationIntoView } from '../../lib/scroll-utils';
|
|
10
11
|
import { ImageViewer } from '../viewers';
|
|
11
12
|
import { AnnotateToolbar, type ClickAction } from '../annotation/AnnotateToolbar';
|
|
@@ -23,7 +24,8 @@ import {
|
|
|
23
24
|
const PdfAnnotationCanvas = lazy(() => import('../pdf-annotation/PdfAnnotationCanvas.client').then(mod => ({ default: mod.PdfAnnotationCanvas })));
|
|
24
25
|
|
|
25
26
|
import { useResourceAnnotations } from '../../contexts/ResourceAnnotationsContext';
|
|
26
|
-
import {
|
|
27
|
+
import { useSemiont } from '../../session/SemiontProvider';
|
|
28
|
+
import { useObservable } from '../../hooks/useObservable';
|
|
27
29
|
import { useEventSubscriptions } from '../../contexts/useEventSubscription';
|
|
28
30
|
|
|
29
31
|
interface Props {
|
|
@@ -78,7 +80,7 @@ export const BrowseView = memo(function BrowseView({
|
|
|
78
80
|
hoverDelayMs = 150
|
|
79
81
|
}: Props) {
|
|
80
82
|
const { newAnnotationIds } = useResourceAnnotations();
|
|
81
|
-
const
|
|
83
|
+
const session = useObservable(useSemiont().activeSession$);
|
|
82
84
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
83
85
|
|
|
84
86
|
const category = getMimeCategory(mimeType);
|
|
@@ -119,6 +121,7 @@ export const BrowseView = memo(function BrowseView({
|
|
|
119
121
|
// Attach click handler, hover handler, and animations after render
|
|
120
122
|
useEffect(() => {
|
|
121
123
|
if (!containerRef.current) return;
|
|
124
|
+
if (!session) return;
|
|
122
125
|
|
|
123
126
|
const container = containerRef.current;
|
|
124
127
|
|
|
@@ -134,13 +137,13 @@ export const BrowseView = memo(function BrowseView({
|
|
|
134
137
|
if (annotationId && annotationType === 'reference') {
|
|
135
138
|
const annotation = allAnnotations.find(a => a.id === annotationId);
|
|
136
139
|
if (annotation) {
|
|
137
|
-
|
|
140
|
+
session.client.browse.click(annotation.id, annotation.motivation);
|
|
138
141
|
}
|
|
139
142
|
}
|
|
140
143
|
};
|
|
141
144
|
|
|
142
145
|
const { handleMouseEnter, handleMouseLeave, cleanup: cleanupHover } = createHoverHandlers(
|
|
143
|
-
(
|
|
146
|
+
(id) => session.client.beckon.hover(id),
|
|
144
147
|
hoverDelayMs
|
|
145
148
|
);
|
|
146
149
|
|
|
@@ -149,7 +152,7 @@ export const BrowseView = memo(function BrowseView({
|
|
|
149
152
|
const target = e.target as HTMLElement;
|
|
150
153
|
const annotationElement = target.closest('[data-annotation-id]');
|
|
151
154
|
const annotationId = annotationElement?.getAttribute('data-annotation-id');
|
|
152
|
-
if (annotationId) handleMouseEnter(annotationId);
|
|
155
|
+
if (annotationId) handleMouseEnter(toAnnotationId(annotationId));
|
|
153
156
|
};
|
|
154
157
|
|
|
155
158
|
// Single mouseout handler for the container - fires once on exit
|
|
@@ -180,7 +183,7 @@ export const BrowseView = memo(function BrowseView({
|
|
|
180
183
|
container.removeEventListener('mouseout', handleMouseOut);
|
|
181
184
|
cleanupHover();
|
|
182
185
|
};
|
|
183
|
-
}, [content, allAnnotations, newAnnotationIds, hoverDelayMs]);
|
|
186
|
+
}, [content, allAnnotations, newAnnotationIds, hoverDelayMs, session]);
|
|
184
187
|
|
|
185
188
|
// Helper to scroll annotation into view with pulse effect
|
|
186
189
|
const scrollToAnnotation = useCallback((annotationId: string | null, removePulse = false) => {
|
|
@@ -6,20 +6,16 @@ import { AnnotateView, type SelectionMotivation, type ClickAction, type ShapeTyp
|
|
|
6
6
|
import { BrowseView } from './BrowseView';
|
|
7
7
|
import { PopupContainer } from '../annotation-popups/SharedPopupElements';
|
|
8
8
|
import { JsonLdView } from '../annotation-popups/JsonLdView';
|
|
9
|
-
import type { components } from '@semiont/core';
|
|
10
|
-
import {
|
|
11
|
-
import { getExactText, getTargetSelector, isHighlight, isAssessment, isReference, isComment, isTag, getBodySource } from '@semiont/api-client';
|
|
12
|
-
import { useEventBus } from '../../contexts/EventBusContext';
|
|
9
|
+
import type { Annotation, AnnotationId, ResourceDescriptor as SemiontResource, components } from '@semiont/core';
|
|
10
|
+
import { getExactText, getTargetSelector, isHighlight, isAssessment, isReference, isComment, isTag, getBodySource } from '@semiont/core';
|
|
13
11
|
import { useEventSubscriptions } from '../../contexts/useEventSubscription';
|
|
14
|
-
import {
|
|
12
|
+
import { useSemiont } from '../../session/SemiontProvider';
|
|
13
|
+
import { useObservable } from '../../hooks/useObservable';
|
|
15
14
|
import { useObservableExternalNavigation } from '../../hooks/useObservableBrowse';
|
|
16
15
|
import { ANNOTATORS } from '../../lib/annotation-registry';
|
|
17
16
|
import type { AnnotationsCollection } from '../../types/annotation-props';
|
|
18
17
|
import { getSelectorType, getSelectedShapeForSelectorType, saveSelectedShapeForSelectorType } from '../../lib/media-shapes';
|
|
19
18
|
|
|
20
|
-
type Annotation = components['schemas']['Annotation'];
|
|
21
|
-
type SemiontResource = components['schemas']['ResourceDescriptor'];
|
|
22
|
-
|
|
23
19
|
/**
|
|
24
20
|
* ResourceViewer - Display and interact with resource content and annotations
|
|
25
21
|
*
|
|
@@ -29,7 +25,7 @@ type SemiontResource = components['schemas']['ResourceDescriptor'];
|
|
|
29
25
|
* - No manual refetch needed - events handle cache invalidation
|
|
30
26
|
*
|
|
31
27
|
* Requirements:
|
|
32
|
-
* - Must be wrapped in
|
|
28
|
+
* - Must be wrapped in SemiontProvider (which owns the session's event bus)
|
|
33
29
|
* - Must be wrapped in CacheContext (provides cache manager)
|
|
34
30
|
*
|
|
35
31
|
* Event flow:
|
|
@@ -49,7 +45,7 @@ interface Props {
|
|
|
49
45
|
|
|
50
46
|
/**
|
|
51
47
|
* @emits mark:delete - User requested to delete annotation. Payload: { annotationId: string }
|
|
52
|
-
* @emits
|
|
48
|
+
* @emits panel:open - Request to open panel with annotation. Payload: { panel: string, scrollToAnnotationId?: string, motivation?: Motivation }
|
|
53
49
|
*
|
|
54
50
|
* @subscribes mark:mode-toggled - Toggles between browse and annotate mode. Payload: { mode: 'browse' | 'annotate' }
|
|
55
51
|
* @subscribes mark:added - New annotation was added. Payload: { annotation: Annotation }
|
|
@@ -71,8 +67,8 @@ export function ResourceViewer({
|
|
|
71
67
|
const t = useTranslations('ResourceViewer');
|
|
72
68
|
const documentViewerRef = useRef<HTMLDivElement>(null);
|
|
73
69
|
|
|
74
|
-
|
|
75
|
-
const
|
|
70
|
+
const browser = useSemiont();
|
|
71
|
+
const session = useObservable(browser.activeSession$);
|
|
76
72
|
|
|
77
73
|
// Get observable navigation for event-driven routing
|
|
78
74
|
const navigate = useObservableExternalNavigation();
|
|
@@ -84,7 +80,7 @@ export function ResourceViewer({
|
|
|
84
80
|
if (!resource['@id']) {
|
|
85
81
|
throw new Error('Resource has no @id');
|
|
86
82
|
}
|
|
87
|
-
const rUri =
|
|
83
|
+
const rUri = resource['@id'];
|
|
88
84
|
|
|
89
85
|
// Helper to get MIME type from resource
|
|
90
86
|
const getMimeType = (): string => {
|
|
@@ -120,27 +116,19 @@ export function ResourceViewer({
|
|
|
120
116
|
// Determine active view based on annotate mode
|
|
121
117
|
const activeView = annotateMode ? 'annotate' : 'browse';
|
|
122
118
|
|
|
123
|
-
|
|
124
|
-
// This replaces manual onRefetchAnnotations calls with automatic updates
|
|
125
|
-
const cacheManager = useCacheManager();
|
|
119
|
+
const semiont = session?.client;
|
|
126
120
|
|
|
127
121
|
const handleAnnotateAdded = useCallback(() => {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
}, [cacheManager, rUri]);
|
|
122
|
+
semiont?.browse.invalidateAnnotationList(rUri);
|
|
123
|
+
}, [semiont, rUri]);
|
|
132
124
|
|
|
133
125
|
const handleAnnotateRemoved = useCallback(() => {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
}, [cacheManager, rUri]);
|
|
126
|
+
semiont?.browse.invalidateAnnotationList(rUri);
|
|
127
|
+
}, [semiont, rUri]);
|
|
138
128
|
|
|
139
129
|
const handleAnnotateBodyUpdated = useCallback(() => {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
}, [cacheManager, rUri]);
|
|
130
|
+
semiont?.browse.invalidateAnnotationList(rUri);
|
|
131
|
+
}, [semiont, rUri]);
|
|
144
132
|
|
|
145
133
|
// Annotation toolbar state - persisted in localStorage
|
|
146
134
|
const [selectedMotivation, setSelectedMotivation] = useState<SelectionMotivation | null>(() => {
|
|
@@ -249,10 +237,10 @@ export function ResourceViewer({
|
|
|
249
237
|
};
|
|
250
238
|
};
|
|
251
239
|
|
|
252
|
-
// Handle deleting annotations
|
|
253
|
-
const handleDeleteAnnotation = useCallback((id:
|
|
254
|
-
|
|
255
|
-
}, []);
|
|
240
|
+
// Handle deleting annotations
|
|
241
|
+
const handleDeleteAnnotation = useCallback((id: AnnotationId) => {
|
|
242
|
+
session?.client.mark.delete(rUri, id);
|
|
243
|
+
}, [session, rUri]);
|
|
256
244
|
|
|
257
245
|
// Handle annotation clicks - memoized
|
|
258
246
|
const handleAnnotationClick = useCallback((annotation: Annotation, event?: React.MouseEvent) => {
|
|
@@ -337,8 +325,8 @@ export function ResourceViewer({
|
|
|
337
325
|
|
|
338
326
|
// All annotations open the unified annotations panel
|
|
339
327
|
// The panel internally switches tabs based on the motivation → tab mapping in UnifiedAnnotationsPanel
|
|
340
|
-
|
|
341
|
-
}, [highlights, references, assessments, comments, tags, handleAnnotationClick, selectedClick]);
|
|
328
|
+
browser.emit('panel:open', { panel: 'annotations', scrollToAnnotationId: annotationId, motivation });
|
|
329
|
+
}, [highlights, references, assessments, comments, tags, handleAnnotationClick, selectedClick, session]);
|
|
342
330
|
|
|
343
331
|
// Event subscriptions - Combined into single useEventSubscriptions call to prevent hook ordering issues
|
|
344
332
|
// IMPORTANT: All event subscriptions MUST be in a single call to maintain consistent hook order between renders
|