@semiont/react-ui 0.2.35-build.101 → 0.2.35-build.102
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-DMI4uwYk.d.mts → EventBusContext-BsIUjdWP.d.mts} +4 -4
- package/dist/{PdfAnnotationCanvas.client-HNYRKFDS.mjs → PdfAnnotationCanvas.client-COQREPXU.mjs} +7 -7
- package/dist/PdfAnnotationCanvas.client-COQREPXU.mjs.map +1 -0
- package/dist/{chunk-YI5IX5ZA.mjs → chunk-2HGWOLVN.mjs} +1 -1
- package/dist/{chunk-YI5IX5ZA.mjs.map → chunk-2HGWOLVN.mjs.map} +1 -1
- package/dist/{chunk-MWQ5CNKW.mjs → chunk-ZPV43WN2.mjs} +11 -11
- package/dist/chunk-ZPV43WN2.mjs.map +1 -0
- package/dist/index.d.mts +164 -164
- package/dist/index.mjs +194 -194
- package/dist/index.mjs.map +1 -1
- package/dist/test-utils.d.mts +2 -2
- package/dist/test-utils.mjs +1 -1
- package/package.json +1 -1
- package/src/components/AnnotateReferencesProgressWidget.tsx +5 -5
- package/src/components/CodeMirrorRenderer.tsx +5 -5
- package/src/components/Toolbar.tsx +2 -2
- package/src/components/annotation/AnnotateToolbar.tsx +9 -9
- package/src/components/annotation/__tests__/AnnotateToolbar.test.tsx +17 -17
- package/src/components/image-annotation/AnnotationOverlay.tsx +10 -10
- package/src/components/image-annotation/SvgDrawingCanvas.tsx +4 -4
- package/src/components/navigation/CollapsibleResourceNavigation.tsx +7 -7
- package/src/components/navigation/ObservableLink.tsx +3 -3
- package/src/components/navigation/SimpleNavigation.tsx +2 -2
- package/src/components/pdf-annotation/PdfAnnotationCanvas.tsx +8 -8
- package/src/components/resource/AnnotateView.tsx +12 -12
- package/src/components/resource/BrowseView.tsx +9 -9
- package/src/components/resource/ResourceViewer.tsx +23 -23
- package/src/components/resource/__tests__/BrowseView.test.tsx +27 -27
- package/src/components/resource/__tests__/ResourceViewer.mode-switch.test.tsx +1 -1
- package/src/components/resource/panels/AssessmentEntry.tsx +2 -2
- package/src/components/resource/panels/AssessmentPanel.tsx +7 -7
- package/src/components/resource/panels/AssistSection.tsx +5 -5
- package/src/components/resource/panels/CommentEntry.tsx +2 -2
- package/src/components/resource/panels/CommentsPanel.tsx +7 -7
- package/src/components/resource/panels/HighlightEntry.tsx +2 -2
- package/src/components/resource/panels/HighlightPanel.tsx +5 -5
- package/src/components/resource/panels/ReferenceEntry.tsx +8 -8
- package/src/components/resource/panels/ReferencesPanel.tsx +10 -10
- package/src/components/resource/panels/ResourceInfoPanel.tsx +6 -6
- package/src/components/resource/panels/TagEntry.tsx +2 -2
- package/src/components/resource/panels/TaggingPanel.tsx +8 -8
- package/src/components/resource/panels/UnifiedAnnotationsPanel.tsx +2 -2
- package/src/components/resource/panels/__tests__/AssessmentPanel.test.tsx +4 -4
- package/src/components/resource/panels/__tests__/AssistSection.test.tsx +4 -4
- package/src/components/resource/panels/__tests__/CommentEntry.test.tsx +8 -8
- package/src/components/resource/panels/__tests__/CommentsPanel.test.tsx +3 -3
- package/src/components/resource/panels/__tests__/HighlightPanel.annotationProgress.test.tsx +1 -1
- package/src/components/resource/panels/__tests__/ReferencesPanel.test.tsx +3 -3
- package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +9 -9
- package/src/components/resource/panels/__tests__/TaggingPanel.test.tsx +5 -5
- package/src/features/admin-devops/components/AdminDevOpsPage.tsx +1 -1
- package/src/features/admin-security/components/AdminSecurityPage.tsx +1 -1
- package/src/features/admin-users/components/AdminUsersPage.tsx +1 -1
- package/src/features/moderate-entity-tags/components/EntityTagsPage.tsx +1 -1
- package/src/features/moderate-recent/components/RecentDocumentsPage.tsx +1 -1
- package/src/features/moderate-tag-schemas/components/TagSchemasPage.tsx +1 -1
- package/src/features/resource-compose/components/ResourceComposePage.tsx +1 -1
- package/src/features/resource-discovery/components/ResourceDiscoveryPage.tsx +1 -1
- package/src/features/resource-viewer/__tests__/AnnotationCreationPending.test.tsx +26 -26
- package/src/features/resource-viewer/__tests__/AnnotationDeletionIntegration.test.tsx +21 -21
- package/src/features/resource-viewer/__tests__/AnnotationProgressDismissal.test.tsx +23 -23
- package/src/features/resource-viewer/__tests__/{ResolutionFlowIntegration.test.tsx → BindFlowIntegration.test.tsx} +49 -49
- package/src/features/resource-viewer/__tests__/DetectionFlowBug.test.tsx +18 -18
- package/src/features/resource-viewer/__tests__/DetectionFlowIntegration.test.tsx +19 -19
- package/src/features/resource-viewer/__tests__/ResourceMutations.test.tsx +19 -19
- package/src/features/resource-viewer/__tests__/ToastNotifications.test.tsx +13 -13
- package/src/features/resource-viewer/__tests__/{GenerationFlowIntegration.test.tsx → YieldFlowIntegration.test.tsx} +34 -34
- package/src/features/resource-viewer/__tests__/annotation-progress-flow.test.tsx +11 -11
- package/src/features/resource-viewer/components/ResourceViewerPage.tsx +55 -55
- package/dist/PdfAnnotationCanvas.client-HNYRKFDS.mjs.map +0 -1
- package/dist/chunk-MWQ5CNKW.mjs.map +0 -1
package/dist/test-utils.d.mts
CHANGED
|
@@ -2,8 +2,8 @@ import { ReactElement } from 'react';
|
|
|
2
2
|
import { RenderOptions, RenderResult } from '@testing-library/react';
|
|
3
3
|
export * from '@testing-library/react';
|
|
4
4
|
import { QueryClient } from '@tanstack/react-query';
|
|
5
|
-
import { T as TranslationManager, S as SessionManager, O as OpenResourcesManager } from './EventBusContext-
|
|
6
|
-
export { r as resetEventBusForTesting } from './EventBusContext-
|
|
5
|
+
import { T as TranslationManager, S as SessionManager, O as OpenResourcesManager } from './EventBusContext-BsIUjdWP.mjs';
|
|
6
|
+
export { r as resetEventBusForTesting } from './EventBusContext-BsIUjdWP.mjs';
|
|
7
7
|
import { EventBus } from '@semiont/core';
|
|
8
8
|
export { vi } from 'vitest';
|
|
9
9
|
import 'react/jsx-runtime';
|
package/dist/test-utils.mjs
CHANGED
package/package.json
CHANGED
|
@@ -2,18 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
import { useTranslations } from '../contexts/TranslationContext';
|
|
4
4
|
import { useEventBus } from '../contexts/EventBusContext';
|
|
5
|
-
import type {
|
|
5
|
+
import type { MarkProgress } from '@semiont/core';
|
|
6
6
|
import type { components } from '@semiont/core';
|
|
7
7
|
|
|
8
8
|
type Motivation = components['schemas']['Motivation'];
|
|
9
9
|
|
|
10
|
-
// Extended
|
|
11
|
-
interface
|
|
10
|
+
// Extended MarkProgress with optional request parameters
|
|
11
|
+
interface EnrichedMarkProgress extends MarkProgress {
|
|
12
12
|
requestParams?: Array<{ label: string; value: string }>;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
interface AnnotateReferencesProgressWidgetProps {
|
|
16
|
-
progress:
|
|
16
|
+
progress: MarkProgress | null;
|
|
17
17
|
annotationType?: Motivation | 'reference';
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -58,7 +58,7 @@ export function AnnotateReferencesProgressWidget({ progress, annotationType = 'r
|
|
|
58
58
|
|
|
59
59
|
{/* Request Parameters */}
|
|
60
60
|
{(() => {
|
|
61
|
-
const enrichedProgress = progress as
|
|
61
|
+
const enrichedProgress = progress as EnrichedMarkProgress;
|
|
62
62
|
return enrichedProgress.requestParams && enrichedProgress.requestParams.length > 0 && (
|
|
63
63
|
<div className="semiont-annotation-progress__params">
|
|
64
64
|
<div className="semiont-annotation-progress__params-title">Request Parameters:</div>
|
|
@@ -10,7 +10,7 @@ import { scrollAnnotationIntoView } from '../lib/scroll-utils';
|
|
|
10
10
|
import { isHighlight, isReference, isResolvedReference, isComment, isAssessment, isTag, getBodySource } from '@semiont/api-client';
|
|
11
11
|
import type { components } from '@semiont/core';
|
|
12
12
|
import type { EventBus } from "@semiont/core";
|
|
13
|
-
import { createHoverHandlers } from '../hooks/
|
|
13
|
+
import { createHoverHandlers } from '../hooks/useBeckonFlow';
|
|
14
14
|
|
|
15
15
|
type Annotation = components['schemas']['Annotation'];
|
|
16
16
|
|
|
@@ -348,7 +348,7 @@ export function CodeMirrorRenderer({
|
|
|
348
348
|
const segment = segmentsByIdRef.current.get(annotationId);
|
|
349
349
|
if (segment?.annotation) {
|
|
350
350
|
event.preventDefault();
|
|
351
|
-
eventBusRef.current.get('
|
|
351
|
+
eventBusRef.current.get('browse:click').next({
|
|
352
352
|
annotationId,
|
|
353
353
|
motivation: segment.annotation.motivation
|
|
354
354
|
});
|
|
@@ -416,7 +416,7 @@ export function CodeMirrorRenderer({
|
|
|
416
416
|
const container = view.dom;
|
|
417
417
|
|
|
418
418
|
const { handleMouseEnter, handleMouseLeave, cleanup: cleanupHover } = createHoverHandlers(
|
|
419
|
-
(annotationId) => eventBusRef.current?.get('
|
|
419
|
+
(annotationId) => eventBusRef.current?.get('beckon:hover').next({ annotationId }),
|
|
420
420
|
hoverDelayMs
|
|
421
421
|
);
|
|
422
422
|
|
|
@@ -449,10 +449,10 @@ export function CodeMirrorRenderer({
|
|
|
449
449
|
if (!annotationId || !eventBusRef.current) return;
|
|
450
450
|
|
|
451
451
|
if (isResolved && bodySource) {
|
|
452
|
-
eventBusRef.current.get('
|
|
452
|
+
eventBusRef.current.get('browse:reference-navigate').next({ documentId: bodySource });
|
|
453
453
|
} else {
|
|
454
454
|
const motivation = (widget.dataset.widgetMotivation || 'linking') as Annotation['motivation'];
|
|
455
|
-
eventBusRef.current.get('
|
|
455
|
+
eventBusRef.current.get('browse:click').next({ annotationId, motivation });
|
|
456
456
|
}
|
|
457
457
|
};
|
|
458
458
|
|
|
@@ -17,7 +17,7 @@ interface Props<T extends string = string> {
|
|
|
17
17
|
/**
|
|
18
18
|
* Toolbar component for panel navigation
|
|
19
19
|
*
|
|
20
|
-
* @emits
|
|
20
|
+
* @emits browse:panel-toggle - Toggle panel visibility. Payload: { panel: string }
|
|
21
21
|
*/
|
|
22
22
|
export function Toolbar<T extends string = string>({
|
|
23
23
|
context,
|
|
@@ -28,7 +28,7 @@ export function Toolbar<T extends string = string>({
|
|
|
28
28
|
const eventBus = useEventBus();
|
|
29
29
|
|
|
30
30
|
const handlePanelToggle = (panel: string) => {
|
|
31
|
-
eventBus.get('
|
|
31
|
+
eventBus.get('browse:panel-toggle').next({ panel });
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
return (
|
|
@@ -102,10 +102,10 @@ function DropdownGroup({
|
|
|
102
102
|
/**
|
|
103
103
|
* Toolbar for annotation controls with mode, selection, click, and shape options
|
|
104
104
|
*
|
|
105
|
-
* @emits
|
|
106
|
-
* @emits
|
|
107
|
-
* @emits
|
|
108
|
-
* @emits
|
|
105
|
+
* @emits mark:selection-changed - Selection motivation changed. Payload: { motivation: SelectionMotivation | null }
|
|
106
|
+
* @emits mark:click-changed - Click action mode changed. Payload: { action: ClickAction }
|
|
107
|
+
* @emits mark:shape-changed - Drawing shape changed. Payload: { shape: ShapeType }
|
|
108
|
+
* @emits mark:mode-toggled - View mode toggled between browse and annotate. Payload: undefined
|
|
109
109
|
*/
|
|
110
110
|
export function AnnotateToolbar({
|
|
111
111
|
selectedMotivation,
|
|
@@ -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.get('
|
|
191
|
+
eventBus.get('mark:selection-changed').next({ motivation: null });
|
|
192
192
|
} else {
|
|
193
|
-
eventBus.get('
|
|
193
|
+
eventBus.get('mark: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.get('
|
|
203
|
+
eventBus.get('mark: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.get('
|
|
210
|
+
eventBus.get('mark: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.get('
|
|
217
|
+
eventBus.get('mark:mode-toggled').next(undefined);
|
|
218
218
|
setModePinned(false);
|
|
219
219
|
setModeHovered(false);
|
|
220
220
|
};
|
|
@@ -51,10 +51,10 @@ function createEventTracker() {
|
|
|
51
51
|
};
|
|
52
52
|
|
|
53
53
|
const toolbarEvents = [
|
|
54
|
-
'
|
|
55
|
-
'
|
|
56
|
-
'
|
|
57
|
-
'
|
|
54
|
+
'mark:mode-toggled',
|
|
55
|
+
'mark:click-changed',
|
|
56
|
+
'mark:selection-changed',
|
|
57
|
+
'mark:shape-changed',
|
|
58
58
|
] as const;
|
|
59
59
|
|
|
60
60
|
toolbarEvents.forEach(eventName => {
|
|
@@ -215,7 +215,7 @@ describe('AnnotateToolbar', () => {
|
|
|
215
215
|
});
|
|
216
216
|
});
|
|
217
217
|
|
|
218
|
-
it('emits
|
|
218
|
+
it('emits mark:mode-toggled event when Browse is clicked in Annotate mode', async () => {
|
|
219
219
|
const tracker = createEventTracker();
|
|
220
220
|
renderWithIntl(
|
|
221
221
|
<AnnotateToolbar
|
|
@@ -234,11 +234,11 @@ describe('AnnotateToolbar', () => {
|
|
|
234
234
|
});
|
|
235
235
|
|
|
236
236
|
await waitFor(() => {
|
|
237
|
-
expect(tracker.events.some(e => e.event === '
|
|
237
|
+
expect(tracker.events.some(e => e.event === 'mark:mode-toggled')).toBe(true);
|
|
238
238
|
});
|
|
239
239
|
});
|
|
240
240
|
|
|
241
|
-
it('emits
|
|
241
|
+
it('emits mark:mode-toggled event when Annotate is clicked in Browse mode', async () => {
|
|
242
242
|
const tracker = createEventTracker();
|
|
243
243
|
renderWithIntl(
|
|
244
244
|
<AnnotateToolbar
|
|
@@ -257,7 +257,7 @@ describe('AnnotateToolbar', () => {
|
|
|
257
257
|
});
|
|
258
258
|
|
|
259
259
|
await waitFor(() => {
|
|
260
|
-
expect(tracker.events.some(e => e.event === '
|
|
260
|
+
expect(tracker.events.some(e => e.event === 'mark:mode-toggled')).toBe(true);
|
|
261
261
|
});
|
|
262
262
|
});
|
|
263
263
|
|
|
@@ -283,7 +283,7 @@ describe('AnnotateToolbar', () => {
|
|
|
283
283
|
|
|
284
284
|
// Verify the event was emitted
|
|
285
285
|
await waitFor(() => {
|
|
286
|
-
expect(tracker.events.some(e => e.event === '
|
|
286
|
+
expect(tracker.events.some(e => e.event === 'mark:mode-toggled')).toBe(true);
|
|
287
287
|
});
|
|
288
288
|
|
|
289
289
|
// Simulate mode change by rerendering with new mode
|
|
@@ -311,7 +311,7 @@ describe('AnnotateToolbar', () => {
|
|
|
311
311
|
});
|
|
312
312
|
|
|
313
313
|
describe('CLICK Group Interactions', () => {
|
|
314
|
-
it('emits
|
|
314
|
+
it('emits mark:click-changed event when clicking an action', async () => {
|
|
315
315
|
const tracker = createEventTracker();
|
|
316
316
|
renderWithIntl(
|
|
317
317
|
<AnnotateToolbar {...defaultProps} />,
|
|
@@ -329,7 +329,7 @@ describe('AnnotateToolbar', () => {
|
|
|
329
329
|
fireEvent.click(screen.getByText('Follow'));
|
|
330
330
|
await waitFor(() => {
|
|
331
331
|
expect(tracker.events.some(e =>
|
|
332
|
-
e.event === '
|
|
332
|
+
e.event === 'mark:click-changed' && e.payload?.action === 'follow'
|
|
333
333
|
)).toBe(true);
|
|
334
334
|
});
|
|
335
335
|
});
|
|
@@ -343,7 +343,7 @@ describe('AnnotateToolbar', () => {
|
|
|
343
343
|
});
|
|
344
344
|
|
|
345
345
|
describe('MOTIVATION Group Interactions', () => {
|
|
346
|
-
it('emits
|
|
346
|
+
it('emits mark:selection-changed event when clicking a motivation', async () => {
|
|
347
347
|
const tracker = createEventTracker();
|
|
348
348
|
renderWithIntl(
|
|
349
349
|
<AnnotateToolbar {...defaultProps} />,
|
|
@@ -361,7 +361,7 @@ describe('AnnotateToolbar', () => {
|
|
|
361
361
|
fireEvent.click(screen.getByText('Reference'));
|
|
362
362
|
await waitFor(() => {
|
|
363
363
|
expect(tracker.events.some(e =>
|
|
364
|
-
e.event === '
|
|
364
|
+
e.event === 'mark:selection-changed' && e.payload?.motivation === 'linking'
|
|
365
365
|
)).toBe(true);
|
|
366
366
|
});
|
|
367
367
|
});
|
|
@@ -389,7 +389,7 @@ describe('AnnotateToolbar', () => {
|
|
|
389
389
|
fireEvent.click(within(dropdown).getByText('Highlight'));
|
|
390
390
|
await waitFor(() => {
|
|
391
391
|
expect(tracker.events.some(e =>
|
|
392
|
-
e.event === '
|
|
392
|
+
e.event === 'mark:selection-changed' && e.payload?.motivation === 'highlighting'
|
|
393
393
|
)).toBe(true);
|
|
394
394
|
});
|
|
395
395
|
|
|
@@ -418,14 +418,14 @@ describe('AnnotateToolbar', () => {
|
|
|
418
418
|
fireEvent.click(within(dropdown2).getByText('Highlight'));
|
|
419
419
|
await waitFor(() => {
|
|
420
420
|
expect(tracker.events.some(e =>
|
|
421
|
-
e.event === '
|
|
421
|
+
e.event === 'mark:selection-changed' && e.payload?.motivation === null
|
|
422
422
|
)).toBe(true);
|
|
423
423
|
});
|
|
424
424
|
});
|
|
425
425
|
});
|
|
426
426
|
|
|
427
427
|
describe('SHAPE Group Interactions', () => {
|
|
428
|
-
it('emits
|
|
428
|
+
it('emits mark:shape-changed event when clicking a shape', async () => {
|
|
429
429
|
const tracker = createEventTracker();
|
|
430
430
|
renderWithIntl(
|
|
431
431
|
<AnnotateToolbar
|
|
@@ -447,7 +447,7 @@ describe('AnnotateToolbar', () => {
|
|
|
447
447
|
fireEvent.click(screen.getByText('Circle'));
|
|
448
448
|
await waitFor(() => {
|
|
449
449
|
expect(tracker.events.some(e =>
|
|
450
|
-
e.event === '
|
|
450
|
+
e.event === 'mark:shape-changed' && e.payload?.shape === 'circle'
|
|
451
451
|
)).toBe(true);
|
|
452
452
|
});
|
|
453
453
|
});
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useMemo } from 'react';
|
|
4
4
|
import type { components } from '@semiont/core';
|
|
5
|
-
import { createHoverHandlers } from '../../hooks/
|
|
5
|
+
import { createHoverHandlers } from '../../hooks/useBeckonFlow';
|
|
6
6
|
import { getSvgSelector, isHighlight, isReference, isAssessment, isComment, isTag, isBodyResolved, isResolvedReference } from '@semiont/api-client';
|
|
7
7
|
import { parseSvgSelector } from '@semiont/api-client';
|
|
8
8
|
import type { EventBus } from "@semiont/core"
|
|
@@ -63,8 +63,8 @@ function getAnnotationTooltip(annotation: Annotation): string {
|
|
|
63
63
|
/**
|
|
64
64
|
* Render annotation overlay - displays existing annotations as SVG shapes
|
|
65
65
|
*
|
|
66
|
-
* @emits
|
|
67
|
-
* @emits
|
|
66
|
+
* @emits beckon:hover - Annotation hovered or unhovered. Payload: { annotationId: string | null }
|
|
67
|
+
* @emits browse:click - Annotation clicked. Payload: { annotationId: string, motivation: Motivation }
|
|
68
68
|
*/
|
|
69
69
|
export function AnnotationOverlay({
|
|
70
70
|
annotations,
|
|
@@ -81,7 +81,7 @@ export function AnnotationOverlay({
|
|
|
81
81
|
const scaleY = displayHeight / imageHeight;
|
|
82
82
|
|
|
83
83
|
const { handleMouseEnter, handleMouseLeave } = useMemo(
|
|
84
|
-
() => createHoverHandlers((annotationId) => eventBus?.get('
|
|
84
|
+
() => createHoverHandlers((annotationId) => eventBus?.get('beckon:hover').next({ annotationId }), hoverDelayMs),
|
|
85
85
|
[eventBus, hoverDelayMs]
|
|
86
86
|
);
|
|
87
87
|
|
|
@@ -131,7 +131,7 @@ export function AnnotationOverlay({
|
|
|
131
131
|
className="semiont-annotation-overlay__shape"
|
|
132
132
|
data-hovered={isHovered ? 'true' : 'false'}
|
|
133
133
|
data-selected={isSelected ? 'true' : 'false'}
|
|
134
|
-
onClick={() => eventBus?.get('
|
|
134
|
+
onClick={() => eventBus?.get('browse:click').next({ annotationId: annotation.id, motivation: annotation.motivation })}
|
|
135
135
|
onMouseEnter={() => handleMouseEnter(annotation.id)}
|
|
136
136
|
onMouseLeave={handleMouseLeave}
|
|
137
137
|
/>
|
|
@@ -144,7 +144,7 @@ export function AnnotationOverlay({
|
|
|
144
144
|
style={{ userSelect: 'none' }}
|
|
145
145
|
onClick={(e) => {
|
|
146
146
|
e.stopPropagation();
|
|
147
|
-
eventBus?.get('
|
|
147
|
+
eventBus?.get('browse:click').next({ annotationId: annotation.id, motivation: annotation.motivation });
|
|
148
148
|
}}
|
|
149
149
|
onMouseEnter={() => handleMouseEnter(annotation.id)}
|
|
150
150
|
onMouseLeave={handleMouseLeave}
|
|
@@ -178,7 +178,7 @@ export function AnnotationOverlay({
|
|
|
178
178
|
className="semiont-annotation-overlay__shape"
|
|
179
179
|
data-hovered={isHovered ? 'true' : 'false'}
|
|
180
180
|
data-selected={isSelected ? 'true' : 'false'}
|
|
181
|
-
onClick={() => eventBus?.get('
|
|
181
|
+
onClick={() => eventBus?.get('browse:click').next({ annotationId: annotation.id, motivation: annotation.motivation })}
|
|
182
182
|
onMouseEnter={() => handleMouseEnter(annotation.id)}
|
|
183
183
|
onMouseLeave={handleMouseLeave}
|
|
184
184
|
/>
|
|
@@ -191,7 +191,7 @@ export function AnnotationOverlay({
|
|
|
191
191
|
style={{ userSelect: 'none' }}
|
|
192
192
|
onClick={(e) => {
|
|
193
193
|
e.stopPropagation();
|
|
194
|
-
eventBus?.get('
|
|
194
|
+
eventBus?.get('browse:click').next({ annotationId: annotation.id, motivation: annotation.motivation });
|
|
195
195
|
}}
|
|
196
196
|
onMouseEnter={() => handleMouseEnter(annotation.id)}
|
|
197
197
|
onMouseLeave={handleMouseLeave}
|
|
@@ -238,7 +238,7 @@ export function AnnotationOverlay({
|
|
|
238
238
|
className="semiont-annotation-overlay__shape"
|
|
239
239
|
data-hovered={isHovered ? 'true' : 'false'}
|
|
240
240
|
data-selected={isSelected ? 'true' : 'false'}
|
|
241
|
-
onClick={() => eventBus?.get('
|
|
241
|
+
onClick={() => eventBus?.get('browse:click').next({ annotationId: annotation.id, motivation: annotation.motivation })}
|
|
242
242
|
onMouseEnter={() => handleMouseEnter(annotation.id)}
|
|
243
243
|
onMouseLeave={handleMouseLeave}
|
|
244
244
|
/>
|
|
@@ -251,7 +251,7 @@ export function AnnotationOverlay({
|
|
|
251
251
|
style={{ userSelect: 'none' }}
|
|
252
252
|
onClick={(e) => {
|
|
253
253
|
e.stopPropagation();
|
|
254
|
-
eventBus?.get('
|
|
254
|
+
eventBus?.get('browse:click').next({ annotationId: annotation.id, motivation: annotation.motivation });
|
|
255
255
|
}}
|
|
256
256
|
onMouseEnter={() => handleMouseEnter(annotation.id)}
|
|
257
257
|
onMouseLeave={handleMouseLeave}
|
|
@@ -49,8 +49,8 @@ interface SvgDrawingCanvasProps {
|
|
|
49
49
|
/**
|
|
50
50
|
* SVG-based drawing canvas for creating image annotations with shapes
|
|
51
51
|
*
|
|
52
|
-
* @emits
|
|
53
|
-
* @emits
|
|
52
|
+
* @emits browse:click - Annotation clicked on canvas. Payload: { annotationId: string, motivation: Motivation }
|
|
53
|
+
* @emits mark:requested - New annotation drawn on canvas. Payload: { selector: SvgSelector, motivation: SelectionMotivation }
|
|
54
54
|
*/
|
|
55
55
|
export function SvgDrawingCanvas({
|
|
56
56
|
resourceUri,
|
|
@@ -216,7 +216,7 @@ export function SvgDrawingCanvas({
|
|
|
216
216
|
});
|
|
217
217
|
|
|
218
218
|
if (clickedAnnotation) {
|
|
219
|
-
eventBus?.get('
|
|
219
|
+
eventBus?.get('browse:click').next({ annotationId: clickedAnnotation.id, motivation: clickedAnnotation.motivation });
|
|
220
220
|
setIsDrawing(false);
|
|
221
221
|
setStartPoint(null);
|
|
222
222
|
setCurrentPoint(null);
|
|
@@ -279,7 +279,7 @@ export function SvgDrawingCanvas({
|
|
|
279
279
|
|
|
280
280
|
// Emit annotation:requested event with SvgSelector
|
|
281
281
|
if (eventBus && selectedMotivation) {
|
|
282
|
-
eventBus.get('
|
|
282
|
+
eventBus.get('mark:requested').next({
|
|
283
283
|
selector: {
|
|
284
284
|
type: 'SvgSelector',
|
|
285
285
|
value: nativeSvg
|
|
@@ -28,9 +28,9 @@ import './CollapsibleResourceNavigation.css';
|
|
|
28
28
|
* Supports drag and drop for resource reordering when expanded.
|
|
29
29
|
* Platform-agnostic design for use across different React environments.
|
|
30
30
|
*
|
|
31
|
-
* @emits
|
|
32
|
-
* @emits
|
|
33
|
-
* @emits
|
|
31
|
+
* @emits browse:resource-reorder - Resource tab reordered. Payload: { oldIndex: number, newIndex: number }
|
|
32
|
+
* @emits browse:resource-close - Resource tab closed. Payload: { resourceId: string }
|
|
33
|
+
* @emits browse:sidebar-toggle - Toggle sidebar collapsed/expanded state. Payload: undefined
|
|
34
34
|
*/
|
|
35
35
|
export function CollapsibleResourceNavigation({
|
|
36
36
|
fixedItems,
|
|
@@ -110,7 +110,7 @@ export function CollapsibleResourceNavigation({
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
// Emit event
|
|
113
|
-
eventBus.get('
|
|
113
|
+
eventBus.get('browse: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.get('
|
|
126
|
+
eventBus.get('browse: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.get('
|
|
154
|
+
eventBus.get('browse: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.get('
|
|
192
|
+
onClick={() => eventBus.get('browse: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}
|
|
@@ -27,7 +27,7 @@ export interface ObservableLinkProps extends React.AnchorHTMLAttributes<HTMLAnch
|
|
|
27
27
|
* - State coordination before navigation
|
|
28
28
|
* - Logging navigation flows
|
|
29
29
|
*
|
|
30
|
-
* The component emits '
|
|
30
|
+
* The component emits 'browse:link-clicked' event before allowing
|
|
31
31
|
* the browser to follow the link.
|
|
32
32
|
*
|
|
33
33
|
* @example
|
|
@@ -51,7 +51,7 @@ export interface ObservableLinkProps extends React.AnchorHTMLAttributes<HTMLAnch
|
|
|
51
51
|
* </Link>
|
|
52
52
|
* ```
|
|
53
53
|
*
|
|
54
|
-
* @emits
|
|
54
|
+
* @emits browse:link-clicked - Link clicked by user. Payload: { href: string, label?: string }
|
|
55
55
|
*/
|
|
56
56
|
export function ObservableLink({
|
|
57
57
|
href,
|
|
@@ -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.get('
|
|
73
|
+
eventBus.get('browse:link-clicked').next({
|
|
74
74
|
href,
|
|
75
75
|
label
|
|
76
76
|
});
|
|
@@ -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 browse:sidebar-toggle - Toggle sidebar collapsed/expanded state. Payload: undefined
|
|
33
33
|
*/
|
|
34
34
|
export function SimpleNavigation({
|
|
35
35
|
title,
|
|
@@ -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.get('
|
|
89
|
+
onClick={() => eventBus.get('browse: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}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React, { useRef, useState, useCallback, useEffect, useMemo } from 'react';
|
|
4
|
-
import { createHoverHandlers } from '../../hooks/
|
|
4
|
+
import { createHoverHandlers } from '../../hooks/useBeckonFlow';
|
|
5
5
|
import type { components, ResourceUri } from '@semiont/core';
|
|
6
6
|
import { getTargetSelector } from '@semiont/api-client';
|
|
7
7
|
import type { SelectionMotivation } from '../annotation/AnnotateToolbar';
|
|
@@ -61,9 +61,9 @@ interface PdfAnnotationCanvasProps {
|
|
|
61
61
|
/**
|
|
62
62
|
* PDF annotation canvas with page navigation and rectangle drawing
|
|
63
63
|
*
|
|
64
|
-
* @emits
|
|
65
|
-
* @emits
|
|
66
|
-
* @emits
|
|
64
|
+
* @emits browse:click - Annotation clicked on PDF. Payload: { annotationId: string, motivation: Motivation }
|
|
65
|
+
* @emits mark:requested - New annotation drawn on PDF. Payload: { selector: FragmentSelector, motivation: SelectionMotivation }
|
|
66
|
+
* @emits beckon:hover - Annotation hovered or unhovered. Payload: { annotationId: string | null }
|
|
67
67
|
*/
|
|
68
68
|
export function PdfAnnotationCanvas({
|
|
69
69
|
resourceUri,
|
|
@@ -287,7 +287,7 @@ export function PdfAnnotationCanvas({
|
|
|
287
287
|
});
|
|
288
288
|
|
|
289
289
|
if (clickedAnnotation) {
|
|
290
|
-
eventBus?.get('
|
|
290
|
+
eventBus?.get('browse:click').next({ annotationId: clickedAnnotation.id, motivation: clickedAnnotation.motivation });
|
|
291
291
|
setIsDrawing(false);
|
|
292
292
|
setSelection(null);
|
|
293
293
|
return;
|
|
@@ -326,7 +326,7 @@ export function PdfAnnotationCanvas({
|
|
|
326
326
|
|
|
327
327
|
// Emit annotation:requested event with FragmentSelector
|
|
328
328
|
if (selectedMotivation) {
|
|
329
|
-
eventBus.get('
|
|
329
|
+
eventBus.get('mark:requested').next({
|
|
330
330
|
selector: {
|
|
331
331
|
type: 'FragmentSelector',
|
|
332
332
|
conformsTo: 'http://tools.ietf.org/rfc/rfc3778',
|
|
@@ -364,7 +364,7 @@ export function PdfAnnotationCanvas({
|
|
|
364
364
|
|
|
365
365
|
// Hover handlers with currentHover guard and dwell delay
|
|
366
366
|
const { handleMouseEnter, handleMouseLeave } = useMemo(
|
|
367
|
-
() => createHoverHandlers((annotationId) => eventBus?.get('
|
|
367
|
+
() => createHoverHandlers((annotationId) => eventBus?.get('beckon:hover').next({ annotationId }), hoverDelayMs),
|
|
368
368
|
[eventBus, hoverDelayMs]
|
|
369
369
|
);
|
|
370
370
|
|
|
@@ -464,7 +464,7 @@ export function PdfAnnotationCanvas({
|
|
|
464
464
|
cursor: 'pointer',
|
|
465
465
|
opacity: isSelected ? 1 : isHovered ? 0.9 : 0.7
|
|
466
466
|
}}
|
|
467
|
-
onClick={() => eventBus?.get('
|
|
467
|
+
onClick={() => eventBus?.get('browse:click').next({ annotationId: ann.id, motivation: ann.motivation })}
|
|
468
468
|
onMouseEnter={() => handleMouseEnter(ann.id)}
|
|
469
469
|
onMouseLeave={handleMouseLeave}
|
|
470
470
|
/>
|
|
@@ -132,11 +132,11 @@ function segmentTextWithAnnotations(content: string, annotations: Annotation[]):
|
|
|
132
132
|
/**
|
|
133
133
|
* View component for annotating resources with text selection and drawing
|
|
134
134
|
*
|
|
135
|
-
* @emits
|
|
136
|
-
* @subscribes
|
|
137
|
-
* @subscribes
|
|
138
|
-
* @subscribes
|
|
139
|
-
* @subscribes
|
|
135
|
+
* @emits mark:requested - User requested to create annotation. Payload: { selector: Selector | Selector[], motivation: SelectionMotivation }
|
|
136
|
+
* @subscribes mark:selection-changed - Toolbar selection changed. Payload: { motivation: string | null }
|
|
137
|
+
* @subscribes mark:click-changed - Toolbar click action changed. Payload: { action: string }
|
|
138
|
+
* @subscribes mark:shape-changed - Toolbar shape changed. Payload: { shape: string }
|
|
139
|
+
* @subscribes beckon:hover - Annotation hovered. Payload: { annotationId: string | null }
|
|
140
140
|
*/
|
|
141
141
|
export function AnnotateView({
|
|
142
142
|
content,
|
|
@@ -189,10 +189,10 @@ export function AnnotateView({
|
|
|
189
189
|
|
|
190
190
|
// Subscribe to toolbar events and annotation hover
|
|
191
191
|
useEventSubscriptions({
|
|
192
|
-
'
|
|
193
|
-
'
|
|
194
|
-
'
|
|
195
|
-
'
|
|
192
|
+
'mark:selection-changed': handleToolbarSelectionChanged,
|
|
193
|
+
'mark:click-changed': handleToolbarClickChanged,
|
|
194
|
+
'mark:shape-changed': handleToolbarShapeChanged,
|
|
195
|
+
'beckon:hover': handleAnnotationHover,
|
|
196
196
|
});
|
|
197
197
|
|
|
198
198
|
// Handle text annotation with sparkle or immediate creation
|
|
@@ -214,7 +214,7 @@ export function AnnotateView({
|
|
|
214
214
|
|
|
215
215
|
const handleMouseUp = (e: MouseEvent) => {
|
|
216
216
|
// Skip if the mouseUp came from PDF or image canvas
|
|
217
|
-
// (those components handle their own
|
|
217
|
+
// (those components handle their own mark:requested events)
|
|
218
218
|
const target = e.target as Element;
|
|
219
219
|
if (target.closest('.semiont-pdf-annotation-canvas') ||
|
|
220
220
|
target.closest('.semiont-svg-drawing-canvas')) {
|
|
@@ -254,7 +254,7 @@ export function AnnotateView({
|
|
|
254
254
|
|
|
255
255
|
// Unified flow: all text annotations use BOTH TextPositionSelector and TextQuoteSelector
|
|
256
256
|
if (selectedMotivation) {
|
|
257
|
-
eventBus.get('
|
|
257
|
+
eventBus.get('mark:requested').next({
|
|
258
258
|
selector: [
|
|
259
259
|
{
|
|
260
260
|
type: 'TextPositionSelector',
|
|
@@ -288,7 +288,7 @@ export function AnnotateView({
|
|
|
288
288
|
|
|
289
289
|
// Unified flow: all text annotations use BOTH TextPositionSelector and TextQuoteSelector
|
|
290
290
|
if (selectedMotivation) {
|
|
291
|
-
eventBus.get('
|
|
291
|
+
eventBus.get('mark:requested').next({
|
|
292
292
|
selector: [
|
|
293
293
|
{
|
|
294
294
|
type: 'TextPositionSelector',
|