@semiont/react-ui 0.2.33 → 0.2.34-build.89
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/dist/EventBusContext-BmzEcGHZ.d.mts +177 -0
- package/dist/{PdfAnnotationCanvas.client-FGV33CWN.mjs → PdfAnnotationCanvas.client-VLNA5O5M.mjs} +7 -7
- package/dist/PdfAnnotationCanvas.client-VLNA5O5M.mjs.map +1 -0
- package/dist/{chunk-YPYLOBA2.mjs → chunk-C63BARI7.mjs} +3 -2
- package/dist/chunk-C63BARI7.mjs.map +1 -0
- package/dist/{chunk-FC6SGLLT.mjs → chunk-M7SZRRIE.mjs} +24 -16
- package/dist/chunk-M7SZRRIE.mjs.map +1 -0
- package/dist/chunk-ULIET3MW.mjs +31 -0
- package/dist/chunk-ULIET3MW.mjs.map +1 -0
- package/dist/index.d.mts +33 -60
- package/dist/index.mjs +171 -363
- package/dist/index.mjs.map +1 -1
- package/dist/test-utils.d.mts +3 -5
- package/dist/test-utils.mjs +2 -2
- package/dist/test-utils.mjs.map +1 -1
- package/package.json +2 -3
- package/src/components/CodeMirrorRenderer.tsx +4 -4
- package/src/components/DetectionProgressWidget.tsx +3 -3
- package/src/components/LiveRegion.tsx +1 -1
- package/src/components/Toolbar.tsx +1 -1
- package/src/components/annotation/AnnotateToolbar.tsx +5 -5
- package/src/components/annotation/__tests__/AnnotateToolbar.test.tsx +4 -4
- package/src/components/annotation-popups/JsonLdView.tsx +1 -1
- package/src/components/image-annotation/AnnotationOverlay.tsx +9 -9
- package/src/components/image-annotation/SvgDrawingCanvas.tsx +5 -5
- package/src/components/navigation/CollapsibleResourceNavigation.tsx +4 -4
- package/src/components/navigation/ObservableLink.tsx +1 -1
- package/src/components/navigation/SimpleNavigation.tsx +1 -1
- package/src/components/pdf-annotation/PdfAnnotationCanvas.tsx +6 -6
- package/src/components/pdf-annotation/__tests__/PdfAnnotationCanvas.test.tsx +2 -2
- package/src/components/resource/AnnotateView.tsx +5 -4
- package/src/components/resource/AnnotationHistory.tsx +1 -1
- package/src/components/resource/BrowseView.tsx +5 -4
- package/src/components/resource/ResourceViewer.tsx +5 -4
- package/src/components/resource/__tests__/BrowseView.test.tsx +11 -22
- package/src/components/resource/__tests__/ResourceViewer.mode-switch.test.tsx +1 -1
- package/src/components/resource/event-formatting.ts +1 -1
- package/src/components/resource/panels/AssessmentEntry.tsx +2 -2
- package/src/components/resource/panels/AssessmentPanel.tsx +4 -4
- package/src/components/resource/panels/CommentEntry.tsx +2 -2
- package/src/components/resource/panels/CommentsPanel.tsx +4 -4
- package/src/components/resource/panels/DetectSection.tsx +3 -3
- package/src/components/resource/panels/HighlightEntry.tsx +2 -2
- package/src/components/resource/panels/HighlightPanel.tsx +2 -2
- package/src/components/resource/panels/JsonLdPanel.tsx +1 -1
- package/src/components/resource/panels/ReferenceEntry.tsx +6 -6
- package/src/components/resource/panels/ReferencesPanel.tsx +5 -5
- package/src/components/resource/panels/ResourceInfoPanel.tsx +3 -3
- package/src/components/resource/panels/StatisticsPanel.tsx +1 -1
- package/src/components/resource/panels/TagEntry.tsx +2 -2
- package/src/components/resource/panels/TaggingPanel.tsx +5 -5
- package/src/components/resource/panels/UnifiedAnnotationsPanel.tsx +1 -1
- package/src/components/resource/panels/__tests__/AssessmentPanel.test.tsx +5 -5
- package/src/components/resource/panels/__tests__/CommentEntry.test.tsx +10 -10
- package/src/components/resource/panels/__tests__/CommentsPanel.test.tsx +5 -5
- package/src/components/resource/panels/__tests__/DetectSection.test.tsx +9 -9
- package/src/components/resource/panels/__tests__/HighlightPanel.detectionProgress.test.tsx +1 -1
- package/src/components/resource/panels/__tests__/JsonLdPanel.test.tsx +1 -1
- package/src/components/resource/panels/__tests__/ReferencesPanel.test.tsx +4 -4
- package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +4 -4
- package/src/components/resource/panels/__tests__/TaggingPanel.test.tsx +5 -5
- package/src/components/settings/SettingsPanel.tsx +5 -5
- package/src/components/viewers/ImageViewer.tsx +1 -1
- package/src/features/resource-compose/components/ResourceComposePage.tsx +1 -1
- package/src/features/resource-discovery/components/ResourceCard.tsx +1 -1
- package/src/features/resource-discovery/components/ResourceDiscoveryPage.tsx +1 -1
- package/src/features/resource-viewer/__tests__/AnnotationCreationPending.test.tsx +7 -5
- package/src/features/resource-viewer/__tests__/AnnotationDeletionIntegration.test.tsx +5 -4
- package/src/features/resource-viewer/__tests__/DetectionFlowBug.test.tsx +5 -5
- package/src/features/resource-viewer/__tests__/DetectionFlowIntegration.test.tsx +29 -43
- package/src/features/resource-viewer/__tests__/DetectionProgressDismissal.test.tsx +20 -39
- package/src/features/resource-viewer/__tests__/GenerationFlowIntegration.test.tsx +38 -46
- package/src/features/resource-viewer/__tests__/ResolutionFlowIntegration.test.tsx +36 -43
- package/src/features/resource-viewer/__tests__/ResourceMutations.test.tsx +8 -8
- package/src/features/resource-viewer/__tests__/detection-progress-flow.test.tsx +14 -21
- package/src/features/resource-viewer/components/ResourceViewerPage.tsx +8 -7
- package/dist/EventBusContext-CJjL_cCf.d.mts +0 -462
- package/dist/PdfAnnotationCanvas.client-FGV33CWN.mjs.map +0 -1
- package/dist/chunk-FC6SGLLT.mjs.map +0 -1
- package/dist/chunk-XS27QKGP.mjs +0 -55
- package/dist/chunk-XS27QKGP.mjs.map +0 -1
- package/dist/chunk-YPYLOBA2.mjs.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@semiont/react-ui",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.34-build.89",
|
|
4
4
|
"description": "React components and hooks for Semiont",
|
|
5
5
|
"main": "./dist/index.mjs",
|
|
6
6
|
"types": "./dist/index.d.mts",
|
|
@@ -100,7 +100,6 @@
|
|
|
100
100
|
},
|
|
101
101
|
"dependencies": {
|
|
102
102
|
"@semiont/api-client": "*",
|
|
103
|
-
"@semiont/core": "*"
|
|
104
|
-
"mitt": "^3.0.1"
|
|
103
|
+
"@semiont/core": "*"
|
|
105
104
|
}
|
|
106
105
|
}
|
|
@@ -7,8 +7,8 @@ import { markdown } from '@codemirror/lang-markdown';
|
|
|
7
7
|
import { ANNOTATORS } from '../lib/annotation-registry';
|
|
8
8
|
import { ReferenceResolutionWidget } from '../lib/codemirror-widgets';
|
|
9
9
|
import { isHighlight, isReference, isResolvedReference, isComment, isAssessment, isTag, getBodySource } from '@semiont/api-client';
|
|
10
|
-
import type { components } from '@semiont/
|
|
11
|
-
import type { EventBus } from
|
|
10
|
+
import type { components } from '@semiont/core';
|
|
11
|
+
import type { EventBus } from "@semiont/core";
|
|
12
12
|
import { createHoverHandlers } from '../hooks/useAttentionFlow';
|
|
13
13
|
|
|
14
14
|
type Annotation = components['schemas']['Annotation'];
|
|
@@ -343,7 +343,7 @@ export function CodeMirrorRenderer({
|
|
|
343
343
|
const segment = segmentsRef.current.find(s => s.annotation?.id === annotationId);
|
|
344
344
|
if (segment?.annotation) {
|
|
345
345
|
event.preventDefault();
|
|
346
|
-
eventBusRef.current.
|
|
346
|
+
eventBusRef.current.get('annotation:click').next({
|
|
347
347
|
annotationId,
|
|
348
348
|
motivation: segment.annotation.motivation
|
|
349
349
|
});
|
|
@@ -411,7 +411,7 @@ export function CodeMirrorRenderer({
|
|
|
411
411
|
const container = view.dom;
|
|
412
412
|
|
|
413
413
|
const { handleMouseEnter, handleMouseLeave, cleanup: cleanupHover } = createHoverHandlers(
|
|
414
|
-
(annotationId) => eventBusRef.current?.
|
|
414
|
+
(annotationId) => eventBusRef.current?.get('annotation:hover').next({ annotationId })
|
|
415
415
|
);
|
|
416
416
|
|
|
417
417
|
const handleMouseOver = (e: MouseEvent) => {
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import { useTranslations } from '../contexts/TranslationContext';
|
|
4
4
|
import { useEventBus } from '../contexts/EventBusContext';
|
|
5
|
-
import type { DetectionProgress } from '
|
|
6
|
-
import type { components } from '@semiont/
|
|
5
|
+
import type { DetectionProgress } from '@semiont/core';
|
|
6
|
+
import type { components } from '@semiont/core';
|
|
7
7
|
|
|
8
8
|
type Motivation = components['schemas']['Motivation'];
|
|
9
9
|
|
|
@@ -28,7 +28,7 @@ export function DetectionProgressWidget({ progress, annotationType = 'reference'
|
|
|
28
28
|
|
|
29
29
|
const handleCancel = () => {
|
|
30
30
|
// Emit event for job cancellation
|
|
31
|
-
eventBus.
|
|
31
|
+
eventBus.get('job:cancel-requested').next({ jobType: 'detection' });
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
if (!progress) return null;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React, { useState, createContext, useContext, useCallback } from 'react';
|
|
4
|
-
import type { components } from '@semiont/
|
|
4
|
+
import type { components } from '@semiont/core';
|
|
5
5
|
import type { Annotator } from '../lib/annotation-registry';
|
|
6
6
|
|
|
7
7
|
type Annotation = components['schemas']['Annotation'];
|
|
@@ -188,9 +188,9 @@ export function AnnotateToolbar({
|
|
|
188
188
|
const handleSelectionClick = (motivation: SelectionMotivation | null) => {
|
|
189
189
|
// If null is clicked, always deselect. Otherwise toggle.
|
|
190
190
|
if (motivation === null) {
|
|
191
|
-
eventBus.
|
|
191
|
+
eventBus.get('toolbar:selection-changed').next({ motivation: null });
|
|
192
192
|
} else {
|
|
193
|
-
eventBus.
|
|
193
|
+
eventBus.get('toolbar:selection-changed').next({
|
|
194
194
|
motivation: selectedMotivation === motivation ? null : motivation
|
|
195
195
|
});
|
|
196
196
|
}
|
|
@@ -200,21 +200,21 @@ export function AnnotateToolbar({
|
|
|
200
200
|
};
|
|
201
201
|
|
|
202
202
|
const handleClickClick = (action: ClickAction) => {
|
|
203
|
-
eventBus.
|
|
203
|
+
eventBus.get('toolbar:click-changed').next({ action });
|
|
204
204
|
// Close dropdown after selection
|
|
205
205
|
setClickPinned(false);
|
|
206
206
|
setClickHovered(false);
|
|
207
207
|
};
|
|
208
208
|
|
|
209
209
|
const handleShapeClick = (shape: ShapeType) => {
|
|
210
|
-
eventBus.
|
|
210
|
+
eventBus.get('toolbar:shape-changed').next({ shape });
|
|
211
211
|
// Close dropdown after selection
|
|
212
212
|
setShapePinned(false);
|
|
213
213
|
setShapeHovered(false);
|
|
214
214
|
};
|
|
215
215
|
|
|
216
216
|
const handleModeToggle = () => {
|
|
217
|
-
eventBus.
|
|
217
|
+
eventBus.get('view:mode-toggled').next(undefined);
|
|
218
218
|
setModePinned(false);
|
|
219
219
|
setModeHovered(false);
|
|
220
220
|
};
|
|
@@ -55,16 +55,16 @@ function createEventTracker() {
|
|
|
55
55
|
'toolbar:click-changed',
|
|
56
56
|
'toolbar:selection-changed',
|
|
57
57
|
'toolbar:shape-changed',
|
|
58
|
-
];
|
|
58
|
+
] as const;
|
|
59
59
|
|
|
60
60
|
toolbarEvents.forEach(eventName => {
|
|
61
61
|
const handler = trackEvent(eventName);
|
|
62
|
-
eventBus.
|
|
63
|
-
handlers.push(
|
|
62
|
+
const subscription = eventBus.get(eventName).subscribe(handler);
|
|
63
|
+
handlers.push(subscription);
|
|
64
64
|
});
|
|
65
65
|
|
|
66
66
|
return () => {
|
|
67
|
-
handlers.forEach(
|
|
67
|
+
handlers.forEach(sub => sub.unsubscribe());
|
|
68
68
|
};
|
|
69
69
|
}, [eventBus]);
|
|
70
70
|
|
|
@@ -8,7 +8,7 @@ import { oneDark } from '@codemirror/theme-one-dark';
|
|
|
8
8
|
import { syntaxHighlighting } from '@codemirror/language';
|
|
9
9
|
import { jsonLightTheme, jsonLightHighlightStyle } from '../../lib/codemirror-json-theme';
|
|
10
10
|
import { useLineNumbers } from '../../hooks/useLineNumbers';
|
|
11
|
-
import type { components } from '@semiont/
|
|
11
|
+
import type { components } from '@semiont/core';
|
|
12
12
|
|
|
13
13
|
type Annotation = components['schemas']['Annotation'];
|
|
14
14
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useMemo } from 'react';
|
|
4
|
-
import type { components } from '@semiont/
|
|
4
|
+
import type { components } from '@semiont/core';
|
|
5
5
|
import { createHoverHandlers } from '../../hooks/useAttentionFlow';
|
|
6
6
|
import { getSvgSelector, isHighlight, isReference, isAssessment, isComment, isTag, isBodyResolved, isResolvedReference } from '@semiont/api-client';
|
|
7
7
|
import { parseSvgSelector } from '@semiont/api-client';
|
|
8
|
-
import type { EventBus } from
|
|
8
|
+
import type { EventBus } from "@semiont/core"
|
|
9
9
|
|
|
10
10
|
type Annotation = components['schemas']['Annotation'];
|
|
11
11
|
|
|
@@ -79,7 +79,7 @@ export function AnnotationOverlay({
|
|
|
79
79
|
const scaleY = displayHeight / imageHeight;
|
|
80
80
|
|
|
81
81
|
const { handleMouseEnter, handleMouseLeave } = useMemo(
|
|
82
|
-
() => createHoverHandlers((annotationId) => eventBus?.
|
|
82
|
+
() => createHoverHandlers((annotationId) => eventBus?.get('annotation:hover').next({ annotationId })),
|
|
83
83
|
[eventBus]
|
|
84
84
|
);
|
|
85
85
|
|
|
@@ -129,7 +129,7 @@ export function AnnotationOverlay({
|
|
|
129
129
|
className="semiont-annotation-overlay__shape"
|
|
130
130
|
data-hovered={isHovered ? 'true' : 'false'}
|
|
131
131
|
data-selected={isSelected ? 'true' : 'false'}
|
|
132
|
-
onClick={() => eventBus?.
|
|
132
|
+
onClick={() => eventBus?.get('annotation:click').next({ annotationId: annotation.id, motivation: annotation.motivation })}
|
|
133
133
|
onMouseEnter={() => handleMouseEnter(annotation.id)}
|
|
134
134
|
onMouseLeave={handleMouseLeave}
|
|
135
135
|
/>
|
|
@@ -142,7 +142,7 @@ export function AnnotationOverlay({
|
|
|
142
142
|
style={{ userSelect: 'none' }}
|
|
143
143
|
onClick={(e) => {
|
|
144
144
|
e.stopPropagation();
|
|
145
|
-
eventBus?.
|
|
145
|
+
eventBus?.get('annotation:click').next({ annotationId: annotation.id, motivation: annotation.motivation });
|
|
146
146
|
}}
|
|
147
147
|
onMouseEnter={() => handleMouseEnter(annotation.id)}
|
|
148
148
|
onMouseLeave={handleMouseLeave}
|
|
@@ -176,7 +176,7 @@ export function AnnotationOverlay({
|
|
|
176
176
|
className="semiont-annotation-overlay__shape"
|
|
177
177
|
data-hovered={isHovered ? 'true' : 'false'}
|
|
178
178
|
data-selected={isSelected ? 'true' : 'false'}
|
|
179
|
-
onClick={() => eventBus?.
|
|
179
|
+
onClick={() => eventBus?.get('annotation:click').next({ annotationId: annotation.id, motivation: annotation.motivation })}
|
|
180
180
|
onMouseEnter={() => handleMouseEnter(annotation.id)}
|
|
181
181
|
onMouseLeave={handleMouseLeave}
|
|
182
182
|
/>
|
|
@@ -189,7 +189,7 @@ export function AnnotationOverlay({
|
|
|
189
189
|
style={{ userSelect: 'none' }}
|
|
190
190
|
onClick={(e) => {
|
|
191
191
|
e.stopPropagation();
|
|
192
|
-
eventBus?.
|
|
192
|
+
eventBus?.get('annotation:click').next({ annotationId: annotation.id, motivation: annotation.motivation });
|
|
193
193
|
}}
|
|
194
194
|
onMouseEnter={() => handleMouseEnter(annotation.id)}
|
|
195
195
|
onMouseLeave={handleMouseLeave}
|
|
@@ -236,7 +236,7 @@ export function AnnotationOverlay({
|
|
|
236
236
|
className="semiont-annotation-overlay__shape"
|
|
237
237
|
data-hovered={isHovered ? 'true' : 'false'}
|
|
238
238
|
data-selected={isSelected ? 'true' : 'false'}
|
|
239
|
-
onClick={() => eventBus?.
|
|
239
|
+
onClick={() => eventBus?.get('annotation:click').next({ annotationId: annotation.id, motivation: annotation.motivation })}
|
|
240
240
|
onMouseEnter={() => handleMouseEnter(annotation.id)}
|
|
241
241
|
onMouseLeave={handleMouseLeave}
|
|
242
242
|
/>
|
|
@@ -249,7 +249,7 @@ export function AnnotationOverlay({
|
|
|
249
249
|
style={{ userSelect: 'none' }}
|
|
250
250
|
onClick={(e) => {
|
|
251
251
|
e.stopPropagation();
|
|
252
|
-
eventBus?.
|
|
252
|
+
eventBus?.get('annotation:click').next({ annotationId: annotation.id, motivation: annotation.motivation });
|
|
253
253
|
}}
|
|
254
254
|
onMouseEnter={() => handleMouseEnter(annotation.id)}
|
|
255
255
|
onMouseLeave={handleMouseLeave}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React, { useRef, useState, useEffect, useCallback, useMemo } from 'react';
|
|
4
|
-
import type { components, ResourceUri } from '@semiont/
|
|
5
|
-
import { createRectangleSvg, createCircleSvg, createPolygonSvg, scaleSvgToNative, parseSvgSelector,
|
|
4
|
+
import type { components, ResourceUri } from '@semiont/core';
|
|
5
|
+
import { createRectangleSvg, createCircleSvg, createPolygonSvg, scaleSvgToNative, parseSvgSelector, Point } from '@semiont/api-client';
|
|
6
6
|
import { AnnotationOverlay } from './AnnotationOverlay';
|
|
7
7
|
import type { SelectionMotivation } from '../annotation/AnnotateToolbar';
|
|
8
|
-
import type { EventBus } from
|
|
8
|
+
import type { EventBus } from "@semiont/core"
|
|
9
9
|
|
|
10
10
|
type Annotation = components['schemas']['Annotation'];
|
|
11
11
|
|
|
@@ -213,7 +213,7 @@ export function SvgDrawingCanvas({
|
|
|
213
213
|
});
|
|
214
214
|
|
|
215
215
|
if (clickedAnnotation) {
|
|
216
|
-
eventBus?.
|
|
216
|
+
eventBus?.get('annotation:click').next({ annotationId: clickedAnnotation.id, motivation: clickedAnnotation.motivation });
|
|
217
217
|
setIsDrawing(false);
|
|
218
218
|
setStartPoint(null);
|
|
219
219
|
setCurrentPoint(null);
|
|
@@ -276,7 +276,7 @@ export function SvgDrawingCanvas({
|
|
|
276
276
|
|
|
277
277
|
// Emit annotation:requested event with SvgSelector
|
|
278
278
|
if (eventBus && selectedMotivation) {
|
|
279
|
-
eventBus.
|
|
279
|
+
eventBus.get('annotation:requested').next({
|
|
280
280
|
selector: {
|
|
281
281
|
type: 'SvgSelector',
|
|
282
282
|
value: nativeSvg
|
|
@@ -110,7 +110,7 @@ export function CollapsibleResourceNavigation({
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
// Emit event
|
|
113
|
-
eventBus.
|
|
113
|
+
eventBus.get('navigation:resource-reorder').next({ oldIndex: currentIndex, newIndex });
|
|
114
114
|
|
|
115
115
|
// Announce the change
|
|
116
116
|
const resource = resources[currentIndex];
|
|
@@ -123,7 +123,7 @@ export function CollapsibleResourceNavigation({
|
|
|
123
123
|
e.stopPropagation();
|
|
124
124
|
|
|
125
125
|
// Emit event
|
|
126
|
-
eventBus.
|
|
126
|
+
eventBus.get('navigation:resource-close').next({ resourceId });
|
|
127
127
|
|
|
128
128
|
// If we're closing the currently viewed resource, navigate to first fixed item or trigger callback
|
|
129
129
|
const resourceHref = getResourceHref(resourceId);
|
|
@@ -151,7 +151,7 @@ export function CollapsibleResourceNavigation({
|
|
|
151
151
|
const newIndex = resources.findIndex((resource) => resource.id === over.id);
|
|
152
152
|
if (oldIndex !== -1 && newIndex !== -1) {
|
|
153
153
|
// Emit event
|
|
154
|
-
eventBus.
|
|
154
|
+
eventBus.get('navigation:resource-reorder').next({ oldIndex, newIndex });
|
|
155
155
|
const resource = resources[oldIndex];
|
|
156
156
|
announceDrop(resource.name, newIndex + 1, resources.length);
|
|
157
157
|
}
|
|
@@ -189,7 +189,7 @@ export function CollapsibleResourceNavigation({
|
|
|
189
189
|
<span className="semiont-nav-section__header-text">{mergedTranslations.title}</span>
|
|
190
190
|
)}
|
|
191
191
|
<button
|
|
192
|
-
onClick={() => eventBus.
|
|
192
|
+
onClick={() => eventBus.get('navigation:sidebar-toggle').next(undefined)}
|
|
193
193
|
className="semiont-nav-section__header-icon"
|
|
194
194
|
title={isCollapsed ? mergedTranslations.expandSidebar : mergedTranslations.collapseSidebar}
|
|
195
195
|
aria-label={isCollapsed ? mergedTranslations.expandSidebar : mergedTranslations.collapseSidebar}
|
|
@@ -70,7 +70,7 @@ export function ObservableLink({
|
|
|
70
70
|
|
|
71
71
|
const handleClick = useCallback((e: React.MouseEvent<HTMLAnchorElement>) => {
|
|
72
72
|
// Emit event for observability
|
|
73
|
-
eventBus.
|
|
73
|
+
eventBus.get('navigation:link-clicked').next({
|
|
74
74
|
href,
|
|
75
75
|
label
|
|
76
76
|
});
|
|
@@ -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={() => eventBus.
|
|
89
|
+
onClick={() => eventBus.get('navigation:sidebar-toggle').next(undefined)}
|
|
90
90
|
className="semiont-nav-section__header-icon"
|
|
91
91
|
title={isCollapsed ? expandSidebarLabel : collapseSidebarLabel}
|
|
92
92
|
aria-label={isCollapsed ? expandSidebarLabel : collapseSidebarLabel}
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
import React, { useRef, useState, useCallback, useEffect, useMemo } from 'react';
|
|
4
4
|
import { createHoverHandlers } from '../../hooks/useAttentionFlow';
|
|
5
|
-
import type { components, ResourceUri } from '@semiont/
|
|
5
|
+
import type { components, ResourceUri } from '@semiont/core';
|
|
6
6
|
import { getTargetSelector } from '@semiont/api-client';
|
|
7
7
|
import type { SelectionMotivation } from '../annotation/AnnotateToolbar';
|
|
8
|
-
import type { EventBus } from
|
|
8
|
+
import type { EventBus } from "@semiont/core"
|
|
9
9
|
import {
|
|
10
10
|
canvasToPdfCoordinates,
|
|
11
11
|
pdfToCanvasCoordinates,
|
|
@@ -285,7 +285,7 @@ export function PdfAnnotationCanvas({
|
|
|
285
285
|
});
|
|
286
286
|
|
|
287
287
|
if (clickedAnnotation) {
|
|
288
|
-
eventBus?.
|
|
288
|
+
eventBus?.get('annotation:click').next({ annotationId: clickedAnnotation.id, motivation: clickedAnnotation.motivation });
|
|
289
289
|
setIsDrawing(false);
|
|
290
290
|
setSelection(null);
|
|
291
291
|
return;
|
|
@@ -324,7 +324,7 @@ export function PdfAnnotationCanvas({
|
|
|
324
324
|
|
|
325
325
|
// Emit annotation:requested event with FragmentSelector
|
|
326
326
|
if (selectedMotivation) {
|
|
327
|
-
eventBus.
|
|
327
|
+
eventBus.get('annotation:requested').next({
|
|
328
328
|
selector: {
|
|
329
329
|
type: 'FragmentSelector',
|
|
330
330
|
conformsTo: 'http://tools.ietf.org/rfc/rfc3778',
|
|
@@ -362,7 +362,7 @@ export function PdfAnnotationCanvas({
|
|
|
362
362
|
|
|
363
363
|
// Hover handlers with currentHover guard and dwell delay
|
|
364
364
|
const { handleMouseEnter, handleMouseLeave } = useMemo(
|
|
365
|
-
() => createHoverHandlers((annotationId) => eventBus?.
|
|
365
|
+
() => createHoverHandlers((annotationId) => eventBus?.get('annotation:hover').next({ annotationId })),
|
|
366
366
|
[eventBus]
|
|
367
367
|
);
|
|
368
368
|
|
|
@@ -462,7 +462,7 @@ export function PdfAnnotationCanvas({
|
|
|
462
462
|
cursor: 'pointer',
|
|
463
463
|
opacity: isSelected ? 1 : isHovered ? 0.9 : 0.7
|
|
464
464
|
}}
|
|
465
|
-
onClick={() => eventBus?.
|
|
465
|
+
onClick={() => eventBus?.get('annotation:click').next({ annotationId: ann.id, motivation: ann.motivation })}
|
|
466
466
|
onMouseEnter={() => handleMouseEnter(ann.id)}
|
|
467
467
|
onMouseLeave={handleMouseLeave}
|
|
468
468
|
/>
|
|
@@ -11,8 +11,8 @@ import { describe, test, expect, vi, beforeEach } from 'vitest';
|
|
|
11
11
|
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
|
|
12
12
|
import userEvent from '@testing-library/user-event';
|
|
13
13
|
import { PdfAnnotationCanvas } from '../PdfAnnotationCanvas';
|
|
14
|
-
import { resourceUri } from '@semiont/
|
|
15
|
-
import type { components } from '@semiont/
|
|
14
|
+
import { resourceUri } from '@semiont/core';
|
|
15
|
+
import type { components } from '@semiont/core';
|
|
16
16
|
|
|
17
17
|
type Annotation = components['schemas']['Annotation'];
|
|
18
18
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useRef, useEffect, useCallback, lazy, Suspense } from 'react';
|
|
4
|
-
import type { components } from '@semiont/
|
|
5
|
-
import {
|
|
4
|
+
import type { components } from '@semiont/core';
|
|
5
|
+
import { resourceUri as toResourceUri } from '@semiont/core';
|
|
6
|
+
import { getTextPositionSelector, getTextQuoteSelector, getTargetSelector, getMimeCategory, isPdfMimeType, extractContext, findTextWithContext } from '@semiont/api-client';
|
|
6
7
|
import { ANNOTATORS } from '../../lib/annotation-registry';
|
|
7
8
|
import { SvgDrawingCanvas } from '../image-annotation/SvgDrawingCanvas';
|
|
8
9
|
import { useResourceAnnotations } from '../../contexts/ResourceAnnotationsContext';
|
|
@@ -247,7 +248,7 @@ export function AnnotateView({
|
|
|
247
248
|
|
|
248
249
|
// Unified flow: all text annotations use BOTH TextPositionSelector and TextQuoteSelector
|
|
249
250
|
if (selectedMotivation) {
|
|
250
|
-
eventBus.
|
|
251
|
+
eventBus.get('annotation:requested').next({
|
|
251
252
|
selector: [
|
|
252
253
|
{
|
|
253
254
|
type: 'TextPositionSelector',
|
|
@@ -281,7 +282,7 @@ export function AnnotateView({
|
|
|
281
282
|
|
|
282
283
|
// Unified flow: all text annotations use BOTH TextPositionSelector and TextQuoteSelector
|
|
283
284
|
if (selectedMotivation) {
|
|
284
|
-
eventBus.
|
|
285
|
+
eventBus.get('annotation:requested').next({
|
|
285
286
|
selector: [
|
|
286
287
|
{
|
|
287
288
|
type: 'TextPositionSelector',
|
|
@@ -4,7 +4,7 @@ import React, { useEffect, useRef } from 'react';
|
|
|
4
4
|
import { useTranslations } from '../../contexts/TranslationContext';
|
|
5
5
|
import type { RouteBuilder, LinkComponentProps } from '../../contexts/RoutingContext';
|
|
6
6
|
import { useResources } from '../../lib/api-hooks';
|
|
7
|
-
import type { ResourceUri } from '@semiont/
|
|
7
|
+
import type { ResourceUri } from '@semiont/core';
|
|
8
8
|
import type { StoredEvent } from '@semiont/core';
|
|
9
9
|
import { getAnnotationUriFromEvent } from '@semiont/core';
|
|
10
10
|
import { HistoryEvent } from './HistoryEvent';
|
|
@@ -5,8 +5,9 @@ import ReactMarkdown from 'react-markdown';
|
|
|
5
5
|
import remarkGfm from 'remark-gfm';
|
|
6
6
|
import { remarkAnnotations, type PreparedAnnotation } from '../../lib/remark-annotations';
|
|
7
7
|
import { rehypeRenderAnnotations } from '../../lib/rehype-render-annotations';
|
|
8
|
-
import type { components } from '@semiont/
|
|
9
|
-
import {
|
|
8
|
+
import type { components } from '@semiont/core';
|
|
9
|
+
import { resourceUri as toResourceUri } from '@semiont/core';
|
|
10
|
+
import { getExactText, getTextPositionSelector, getTargetSelector, getBodySource, getMimeCategory, isPdfMimeType } from '@semiont/api-client';
|
|
10
11
|
import { ANNOTATORS } from '../../lib/annotation-registry';
|
|
11
12
|
import { createHoverHandlers } from '../../hooks/useAttentionFlow';
|
|
12
13
|
import { ImageViewer } from '../viewers';
|
|
@@ -111,13 +112,13 @@ export function BrowseView({
|
|
|
111
112
|
if (annotationId && annotationType === 'reference') {
|
|
112
113
|
const annotation = allAnnotations.find(a => a.id === annotationId);
|
|
113
114
|
if (annotation) {
|
|
114
|
-
eventBus.
|
|
115
|
+
eventBus.get('annotation:click').next({ annotationId, motivation: annotation.motivation });
|
|
115
116
|
}
|
|
116
117
|
}
|
|
117
118
|
};
|
|
118
119
|
|
|
119
120
|
const { handleMouseEnter, handleMouseLeave, cleanup: cleanupHover } = createHoverHandlers(
|
|
120
|
-
(annotationId) => eventBus.
|
|
121
|
+
(annotationId) => eventBus.get('annotation:hover').next({ annotationId })
|
|
121
122
|
);
|
|
122
123
|
|
|
123
124
|
// Single mouseover handler for the container - fires once on enter
|
|
@@ -6,8 +6,9 @@ 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/
|
|
10
|
-
import {
|
|
9
|
+
import type { components } from '@semiont/core';
|
|
10
|
+
import { resourceUri } from '@semiont/core';
|
|
11
|
+
import { getExactText, getTargetSelector, isHighlight, isAssessment, isReference, isComment, isTag, getBodySource } from '@semiont/api-client';
|
|
11
12
|
import { useEventBus } from '../../contexts/EventBusContext';
|
|
12
13
|
import { useEventSubscriptions } from '../../contexts/useEventSubscription';
|
|
13
14
|
import { useCacheManager } from '../../contexts/CacheContext';
|
|
@@ -249,7 +250,7 @@ export function ResourceViewer({
|
|
|
249
250
|
|
|
250
251
|
// Handle deleting annotations - emit event instead of direct call
|
|
251
252
|
const handleDeleteAnnotation = useCallback((id: string) => {
|
|
252
|
-
eventBus.
|
|
253
|
+
eventBus.get('annotation:delete').next({ annotationId: id });
|
|
253
254
|
}, []); // eventBus is stable
|
|
254
255
|
|
|
255
256
|
// Handle annotation clicks - memoized
|
|
@@ -338,7 +339,7 @@ export function ResourceViewer({
|
|
|
338
339
|
|
|
339
340
|
// All annotations open the unified annotations panel
|
|
340
341
|
// The panel internally switches tabs based on the motivation → tab mapping in UnifiedAnnotationsPanel
|
|
341
|
-
eventBus.
|
|
342
|
+
eventBus.get('panel:open').next({ panel: 'annotations', scrollToAnnotationId: annotationId, motivation });
|
|
342
343
|
}, [highlights, references, assessments, comments, tags, handleAnnotationClick, selectedClick]);
|
|
343
344
|
|
|
344
345
|
// Event subscriptions - Combined into single useEventSubscriptions call to prevent hook ordering issues
|
|
@@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
|
4
4
|
import { BrowseView } from '../BrowseView';
|
|
5
|
-
import type { components } from '@semiont/
|
|
5
|
+
import type { components } from '@semiont/core';
|
|
6
6
|
import { EventBusProvider, resetEventBusForTesting, useEventBus } from '../../../contexts/EventBusContext';
|
|
7
7
|
|
|
8
8
|
type Annotation = components['schemas']['Annotation'];
|
|
@@ -101,20 +101,8 @@ function createEventTracker() {
|
|
|
101
101
|
function EventTrackingWrapper({ children }: { children: React.ReactNode }) {
|
|
102
102
|
const eventBus = useEventBus();
|
|
103
103
|
|
|
104
|
-
// Track subscriptions by wrapping the on method synchronously before render
|
|
105
|
-
const originalOn = React.useRef(eventBus.on.bind(eventBus));
|
|
106
|
-
|
|
107
|
-
if (!('__tracked' in eventBus.on)) {
|
|
108
|
-
const trackedOn = ((eventName: string, handler: Function) => {
|
|
109
|
-
subscriptions.add(eventName);
|
|
110
|
-
return originalOn.current(eventName, handler);
|
|
111
|
-
}) as typeof eventBus.on & { __tracked: true };
|
|
112
|
-
trackedOn.__tracked = true;
|
|
113
|
-
eventBus.on = trackedOn;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
104
|
React.useEffect(() => {
|
|
117
|
-
const handlers: Array<() => void> = [];
|
|
105
|
+
const handlers: Array<{ unsubscribe: () => void }> = [];
|
|
118
106
|
|
|
119
107
|
// Track all annotation-related events
|
|
120
108
|
const trackEvent = (eventName: string) => (payload: any) => {
|
|
@@ -125,16 +113,17 @@ function createEventTracker() {
|
|
|
125
113
|
'annotation:hover',
|
|
126
114
|
'annotation:click',
|
|
127
115
|
'annotation:focus',
|
|
128
|
-
];
|
|
116
|
+
] as const;
|
|
129
117
|
|
|
130
118
|
annotationEvents.forEach(eventName => {
|
|
119
|
+
subscriptions.add(eventName);
|
|
131
120
|
const handler = trackEvent(eventName);
|
|
132
|
-
eventBus.
|
|
133
|
-
handlers.push(
|
|
121
|
+
const subscription = eventBus.get(eventName).subscribe(handler);
|
|
122
|
+
handlers.push(subscription);
|
|
134
123
|
});
|
|
135
124
|
|
|
136
125
|
return () => {
|
|
137
|
-
handlers.forEach(
|
|
126
|
+
handlers.forEach(sub => sub.unsubscribe());
|
|
138
127
|
};
|
|
139
128
|
}, [eventBus]);
|
|
140
129
|
|
|
@@ -544,12 +533,12 @@ describe('BrowseView Component', () => {
|
|
|
544
533
|
eventTracker.push({ event: 'annotation:click', annotationId: payload?.annotationId ?? null });
|
|
545
534
|
};
|
|
546
535
|
|
|
547
|
-
eventBus.
|
|
548
|
-
eventBus.
|
|
536
|
+
const subscription1 = eventBus.get('annotation:hover').subscribe(handleHover);
|
|
537
|
+
const subscription2 = eventBus.get('annotation:click').subscribe(handleClick);
|
|
549
538
|
|
|
550
539
|
return () => {
|
|
551
|
-
|
|
552
|
-
|
|
540
|
+
subscription1.unsubscribe();
|
|
541
|
+
subscription2.unsubscribe();
|
|
553
542
|
};
|
|
554
543
|
}, [eventBus]);
|
|
555
544
|
|
|
@@ -20,7 +20,7 @@ import { TranslationProvider } from '../../../contexts/TranslationContext';
|
|
|
20
20
|
import { ResourceAnnotationsProvider } from '../../../contexts/ResourceAnnotationsContext';
|
|
21
21
|
import { ApiClientProvider } from '../../../contexts/ApiClientContext';
|
|
22
22
|
import { AuthTokenProvider } from '../../../contexts/AuthTokenContext';
|
|
23
|
-
import type { components } from '@semiont/
|
|
23
|
+
import type { components } from '@semiont/core';
|
|
24
24
|
|
|
25
25
|
type SemiontResource = components['schemas']['ResourceDescriptor'];
|
|
26
26
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { StoredEvent, ResourceEventType } from '@semiont/core';
|
|
9
|
-
import type { components } from '@semiont/
|
|
9
|
+
import type { components } from '@semiont/core';
|
|
10
10
|
import { getExactText, getTargetSelector } from '@semiont/api-client';
|
|
11
11
|
import { ANNOTATORS } from '../../lib/annotation-registry';
|
|
12
12
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { forwardRef } from 'react';
|
|
4
|
-
import type { components } from '@semiont/
|
|
4
|
+
import type { components } from '@semiont/core';
|
|
5
5
|
import { getAnnotationExactText } from '@semiont/api-client';
|
|
6
6
|
import { useEventBus } from '../../../contexts/EventBusContext';
|
|
7
7
|
import { useHoverEmitter } from '../../../hooks/useAttentionFlow';
|
|
@@ -89,7 +89,7 @@ export const AssessmentEntry = forwardRef<HTMLDivElement, AssessmentEntryProps>(
|
|
|
89
89
|
data-type="assessment"
|
|
90
90
|
data-focused={isFocused ? 'true' : 'false'}
|
|
91
91
|
onClick={() => {
|
|
92
|
-
eventBus.
|
|
92
|
+
eventBus.get('annotation:click').next({ annotationId: assessment.id, motivation: assessment.motivation });
|
|
93
93
|
}}
|
|
94
94
|
{...hoverProps}
|
|
95
95
|
>
|
|
@@ -4,7 +4,7 @@ import { useState, useEffect, useRef, useCallback, useMemo } from 'react';
|
|
|
4
4
|
import { useTranslations } from '../../../contexts/TranslationContext';
|
|
5
5
|
import { useEventBus } from '../../../contexts/EventBusContext';
|
|
6
6
|
import { useEventSubscriptions } from '../../../contexts/useEventSubscription';
|
|
7
|
-
import type { components, Selector } from '@semiont/
|
|
7
|
+
import type { components, Selector } from '@semiont/core';
|
|
8
8
|
import { getTextPositionSelector, getTargetSelector } from '@semiont/api-client';
|
|
9
9
|
import { AssessmentEntry } from './AssessmentEntry';
|
|
10
10
|
import { DetectSection } from './DetectSection';
|
|
@@ -141,7 +141,7 @@ export function AssessmentPanel({
|
|
|
141
141
|
? [{ type: 'TextualBody' as const, value: newAssessmentText, purpose: 'assessing' as const }]
|
|
142
142
|
: [];
|
|
143
143
|
|
|
144
|
-
eventBus.
|
|
144
|
+
eventBus.get('annotation:create').next({
|
|
145
145
|
motivation: 'assessing',
|
|
146
146
|
selector: pendingAnnotation.selector,
|
|
147
147
|
body,
|
|
@@ -156,7 +156,7 @@ export function AssessmentPanel({
|
|
|
156
156
|
|
|
157
157
|
const handleEscape = (e: KeyboardEvent) => {
|
|
158
158
|
if (e.key === 'Escape') {
|
|
159
|
-
eventBus.
|
|
159
|
+
eventBus.get('annotation:cancel-pending').next(undefined);
|
|
160
160
|
setNewAssessmentText('');
|
|
161
161
|
}
|
|
162
162
|
};
|
|
@@ -209,7 +209,7 @@ export function AssessmentPanel({
|
|
|
209
209
|
<div className="semiont-annotation-prompt__actions">
|
|
210
210
|
<button
|
|
211
211
|
onClick={() => {
|
|
212
|
-
eventBus.
|
|
212
|
+
eventBus.get('annotation:cancel-pending').next(undefined);
|
|
213
213
|
setNewAssessmentText('');
|
|
214
214
|
}}
|
|
215
215
|
className="semiont-button semiont-button--secondary"
|