@semiont/react-ui 0.2.35-build.98 → 0.2.35
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 +1 -0
- package/dist/{EventBusContext-DMI4uwYk.d.mts → EventBusContext-CLnb2LmB.d.mts} +5 -5
- package/dist/{PdfAnnotationCanvas.client-HNYRKFDS.mjs → PdfAnnotationCanvas.client-COQREPXU.mjs} +7 -7
- package/dist/PdfAnnotationCanvas.client-COQREPXU.mjs.map +1 -0
- package/dist/ar-7SUXNE34.mjs +376 -0
- package/dist/ar-7SUXNE34.mjs.map +1 -0
- package/dist/bn-XOET3DOI.mjs +376 -0
- package/dist/bn-XOET3DOI.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-3FIQXKQF.mjs → chunk-Q2KV6Y2J.mjs} +28 -28
- package/dist/{chunk-MWQ5CNKW.mjs → chunk-ZPV43WN2.mjs} +11 -11
- package/dist/chunk-ZPV43WN2.mjs.map +1 -0
- package/dist/cs-X63DXX7L.mjs +376 -0
- package/dist/cs-X63DXX7L.mjs.map +1 -0
- package/dist/da-OWTCV57A.mjs +376 -0
- package/dist/da-OWTCV57A.mjs.map +1 -0
- package/dist/de-77BMFDVF.mjs +376 -0
- package/dist/de-77BMFDVF.mjs.map +1 -0
- package/dist/el-FIBNLH2V.mjs +376 -0
- package/dist/el-FIBNLH2V.mjs.map +1 -0
- package/dist/fa-3N4CIWE6.mjs +376 -0
- package/dist/fa-3N4CIWE6.mjs.map +1 -0
- package/dist/fi-JOM3M7Z4.mjs +376 -0
- package/dist/fi-JOM3M7Z4.mjs.map +1 -0
- package/dist/fr-56QSXS7E.mjs +376 -0
- package/dist/fr-56QSXS7E.mjs.map +1 -0
- package/dist/he-SNAXPJEK.mjs +376 -0
- package/dist/he-SNAXPJEK.mjs.map +1 -0
- package/dist/hi-CRBRD5TB.mjs +376 -0
- package/dist/hi-CRBRD5TB.mjs.map +1 -0
- package/dist/id-BRCVLICF.mjs +376 -0
- package/dist/id-BRCVLICF.mjs.map +1 -0
- package/dist/index.d.mts +169 -165
- package/dist/index.mjs +317 -313
- package/dist/index.mjs.map +1 -1
- package/dist/it-M2Z27BNB.mjs +376 -0
- package/dist/it-M2Z27BNB.mjs.map +1 -0
- package/dist/ja-TZUKW7HD.mjs +376 -0
- package/dist/ja-TZUKW7HD.mjs.map +1 -0
- package/dist/ko-NKBGGOL6.mjs +376 -0
- package/dist/ko-NKBGGOL6.mjs.map +1 -0
- package/dist/ms-XFXPN6RX.mjs +376 -0
- package/dist/ms-XFXPN6RX.mjs.map +1 -0
- package/dist/nl-MVYXAS5C.mjs +379 -0
- package/dist/nl-MVYXAS5C.mjs.map +1 -0
- package/dist/no-XOLO4JPV.mjs +376 -0
- package/dist/no-XOLO4JPV.mjs.map +1 -0
- package/dist/pl-TRWLMMC4.mjs +376 -0
- package/dist/pl-TRWLMMC4.mjs.map +1 -0
- package/dist/pt-M3TE24UI.mjs +376 -0
- package/dist/pt-M3TE24UI.mjs.map +1 -0
- package/dist/ro-QBFG2T64.mjs +376 -0
- package/dist/ro-QBFG2T64.mjs.map +1 -0
- package/dist/sv-IUECBXWX.mjs +376 -0
- package/dist/sv-IUECBXWX.mjs.map +1 -0
- package/dist/test-utils.d.mts +2 -2
- package/dist/test-utils.mjs +2 -2
- package/dist/th-US7KIN5Q.mjs +376 -0
- package/dist/th-US7KIN5Q.mjs.map +1 -0
- package/dist/tr-DWJ2FFUK.mjs +376 -0
- package/dist/tr-DWJ2FFUK.mjs.map +1 -0
- package/dist/uk-M4ZE4DPZ.mjs +376 -0
- package/dist/uk-M4ZE4DPZ.mjs.map +1 -0
- package/dist/vi-FERZNPSH.mjs +376 -0
- package/dist/vi-FERZNPSH.mjs.map +1 -0
- package/dist/zh-3J2I3WYK.mjs +376 -0
- package/dist/zh-3J2I3WYK.mjs.map +1 -0
- 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 +2 -1
- package/src/features/admin-security/components/AdminSecurityPage.tsx +2 -1
- package/src/features/admin-users/components/AdminUsersPage.tsx +2 -1
- package/src/features/moderate-entity-tags/components/EntityTagsPage.tsx +2 -1
- package/src/features/moderate-recent/components/RecentDocumentsPage.tsx +2 -1
- package/src/features/moderate-tag-schemas/components/TagSchemasPage.tsx +2 -1
- package/src/features/resource-compose/components/ResourceComposePage.tsx +2 -1
- package/src/features/resource-discovery/components/ResourceDiscoveryPage.tsx +2 -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/translations/ar.json +298 -298
- package/translations/bn.json +298 -298
- package/translations/cs.json +295 -295
- package/translations/da.json +282 -282
- package/translations/de.json +281 -281
- package/translations/el.json +298 -298
- package/translations/fa.json +298 -298
- package/translations/fi.json +298 -298
- package/translations/fr.json +279 -279
- package/translations/he.json +298 -298
- package/translations/hi.json +298 -298
- package/translations/id.json +295 -295
- package/translations/it.json +299 -299
- package/translations/ja.json +298 -298
- package/translations/ko.json +298 -298
- package/translations/ms.json +296 -296
- package/translations/nl.json +299 -296
- package/translations/no.json +292 -292
- package/translations/pl.json +297 -297
- package/translations/pt.json +294 -294
- package/translations/ro.json +298 -298
- package/translations/sv.json +293 -293
- package/translations/th.json +298 -298
- package/translations/tr.json +298 -298
- package/translations/uk.json +298 -298
- package/translations/vi.json +298 -298
- package/translations/zh.json +298 -298
- package/dist/PdfAnnotationCanvas.client-HNYRKFDS.mjs.map +0 -1
- package/dist/ar-MDB7HC5S.mjs +0 -376
- package/dist/ar-MDB7HC5S.mjs.map +0 -1
- package/dist/bn-3SAV2ZEM.mjs +0 -376
- package/dist/bn-3SAV2ZEM.mjs.map +0 -1
- package/dist/chunk-MWQ5CNKW.mjs.map +0 -1
- package/dist/cs-AWCETEUV.mjs +0 -376
- package/dist/cs-AWCETEUV.mjs.map +0 -1
- package/dist/da-UZZHXYLC.mjs +0 -376
- package/dist/da-UZZHXYLC.mjs.map +0 -1
- package/dist/de-LQFWN6S5.mjs +0 -376
- package/dist/de-LQFWN6S5.mjs.map +0 -1
- package/dist/el-IWOETBJ7.mjs +0 -376
- package/dist/el-IWOETBJ7.mjs.map +0 -1
- package/dist/fa-BVEJZT5S.mjs +0 -376
- package/dist/fa-BVEJZT5S.mjs.map +0 -1
- package/dist/fi-JBNCGGA6.mjs +0 -376
- package/dist/fi-JBNCGGA6.mjs.map +0 -1
- package/dist/fr-OLH7PNGI.mjs +0 -376
- package/dist/fr-OLH7PNGI.mjs.map +0 -1
- package/dist/he-KOJQ4HMA.mjs +0 -376
- package/dist/he-KOJQ4HMA.mjs.map +0 -1
- package/dist/hi-BKJFZXAY.mjs +0 -376
- package/dist/hi-BKJFZXAY.mjs.map +0 -1
- package/dist/id-DPLHJVNP.mjs +0 -376
- package/dist/id-DPLHJVNP.mjs.map +0 -1
- package/dist/it-JXHAM7NL.mjs +0 -376
- package/dist/it-JXHAM7NL.mjs.map +0 -1
- package/dist/ja-DQRAO3PU.mjs +0 -376
- package/dist/ja-DQRAO3PU.mjs.map +0 -1
- package/dist/ko-6IFCOP6F.mjs +0 -376
- package/dist/ko-6IFCOP6F.mjs.map +0 -1
- package/dist/ms-KF5S2TLL.mjs +0 -376
- package/dist/ms-KF5S2TLL.mjs.map +0 -1
- package/dist/nl-2GUUZLQM.mjs +0 -376
- package/dist/nl-2GUUZLQM.mjs.map +0 -1
- package/dist/no-2IBCZGEF.mjs +0 -376
- package/dist/no-2IBCZGEF.mjs.map +0 -1
- package/dist/pl-ZEUBJ7YU.mjs +0 -376
- package/dist/pl-ZEUBJ7YU.mjs.map +0 -1
- package/dist/pt-WLAFIZWQ.mjs +0 -376
- package/dist/pt-WLAFIZWQ.mjs.map +0 -1
- package/dist/ro-K56IXFGU.mjs +0 -376
- package/dist/ro-K56IXFGU.mjs.map +0 -1
- package/dist/sv-VFJLMJRY.mjs +0 -376
- package/dist/sv-VFJLMJRY.mjs.map +0 -1
- package/dist/th-RICLQ2GW.mjs +0 -376
- package/dist/th-RICLQ2GW.mjs.map +0 -1
- package/dist/tr-SALXWE2M.mjs +0 -376
- package/dist/tr-SALXWE2M.mjs.map +0 -1
- package/dist/uk-3U3T3O2E.mjs +0 -376
- package/dist/uk-3U3T3O2E.mjs.map +0 -1
- package/dist/vi-LIVNZXOB.mjs +0 -376
- package/dist/vi-LIVNZXOB.mjs.map +0 -1
- package/dist/zh-KDUAZPX3.mjs +0 -376
- package/dist/zh-KDUAZPX3.mjs.map +0 -1
- /package/dist/{chunk-3FIQXKQF.mjs.map → chunk-Q2KV6Y2J.mjs.map} +0 -0
|
@@ -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',
|
|
@@ -6,7 +6,7 @@ import remarkGfm from 'remark-gfm';
|
|
|
6
6
|
import { resourceUri as toResourceUri } from '@semiont/core';
|
|
7
7
|
import { getMimeCategory, isPdfMimeType } from '@semiont/api-client';
|
|
8
8
|
import { ANNOTATORS } from '../../lib/annotation-registry';
|
|
9
|
-
import { createHoverHandlers } from '../../hooks/
|
|
9
|
+
import { createHoverHandlers } from '../../hooks/useBeckonFlow';
|
|
10
10
|
import { scrollAnnotationIntoView } from '../../lib/scroll-utils';
|
|
11
11
|
import { ImageViewer } from '../viewers';
|
|
12
12
|
import { AnnotateToolbar, type ClickAction } from '../annotation/AnnotateToolbar';
|
|
@@ -63,11 +63,11 @@ const MemoizedMarkdown = memo(function MemoizedMarkdown({
|
|
|
63
63
|
* - Layer 1: Markdown renders once (MemoizedMarkdown, cached by content)
|
|
64
64
|
* - Layer 2: Annotation overlay applied via DOM Range API after paint
|
|
65
65
|
*
|
|
66
|
-
* @emits
|
|
67
|
-
* @emits
|
|
66
|
+
* @emits browse:click - User clicked on annotation. Payload: { annotationId: string, motivation: Motivation }
|
|
67
|
+
* @emits beckon:hover - User hovered over annotation. Payload: { annotationId: string | null }
|
|
68
68
|
*
|
|
69
|
-
* @subscribes
|
|
70
|
-
* @subscribes
|
|
69
|
+
* @subscribes beckon:hover - Highlight annotation on hover. Payload: { annotationId: string | null }
|
|
70
|
+
* @subscribes beckon:focus - Scroll to and highlight annotation. Payload: { annotationId: string }
|
|
71
71
|
*/
|
|
72
72
|
export const BrowseView = memo(function BrowseView({
|
|
73
73
|
content,
|
|
@@ -135,13 +135,13 @@ export const BrowseView = memo(function BrowseView({
|
|
|
135
135
|
if (annotationId && annotationType === 'reference') {
|
|
136
136
|
const annotation = allAnnotations.find(a => a.id === annotationId);
|
|
137
137
|
if (annotation) {
|
|
138
|
-
eventBus.get('
|
|
138
|
+
eventBus.get('browse:click').next({ annotationId, motivation: annotation.motivation });
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
};
|
|
142
142
|
|
|
143
143
|
const { handleMouseEnter, handleMouseLeave, cleanup: cleanupHover } = createHoverHandlers(
|
|
144
|
-
(annotationId) => eventBus.get('
|
|
144
|
+
(annotationId) => eventBus.get('beckon:hover').next({ annotationId }),
|
|
145
145
|
hoverDelayMs
|
|
146
146
|
);
|
|
147
147
|
|
|
@@ -201,8 +201,8 @@ export const BrowseView = memo(function BrowseView({
|
|
|
201
201
|
}, [scrollToAnnotation]);
|
|
202
202
|
|
|
203
203
|
useEventSubscriptions({
|
|
204
|
-
'
|
|
205
|
-
'
|
|
204
|
+
'beckon:hover': handleAnnotationHover,
|
|
205
|
+
'beckon:focus': handleAnnotationFocus,
|
|
206
206
|
});
|
|
207
207
|
|
|
208
208
|
// Route to appropriate viewer based on MIME type category
|
|
@@ -12,7 +12,7 @@ import { getExactText, getTargetSelector, isHighlight, isAssessment, isReference
|
|
|
12
12
|
import { useEventBus } from '../../contexts/EventBusContext';
|
|
13
13
|
import { useEventSubscriptions } from '../../contexts/useEventSubscription';
|
|
14
14
|
import { useCacheManager } from '../../contexts/CacheContext';
|
|
15
|
-
import { useObservableExternalNavigation } from '../../hooks/
|
|
15
|
+
import { useObservableExternalNavigation } from '../../hooks/useObservableBrowse';
|
|
16
16
|
import { ANNOTATORS } from '../../lib/annotation-registry';
|
|
17
17
|
import type { AnnotationsCollection } from '../../types/annotation-props';
|
|
18
18
|
import { getSelectorType, getSelectedShapeForSelectorType, saveSelectedShapeForSelectorType } from '../../lib/media-shapes';
|
|
@@ -24,7 +24,7 @@ type SemiontResource = components['schemas']['ResourceDescriptor'];
|
|
|
24
24
|
* ResourceViewer - Display and interact with resource content and annotations
|
|
25
25
|
*
|
|
26
26
|
* This component uses event-driven architecture for real-time updates:
|
|
27
|
-
* - Subscribes to make-meaning events (
|
|
27
|
+
* - Subscribes to make-meaning events (mark:added, mark:removed, mark:body-updated)
|
|
28
28
|
* - Automatically invalidates cache when annotations change
|
|
29
29
|
* - No manual refetch needed - events handle cache invalidation
|
|
30
30
|
*
|
|
@@ -48,17 +48,17 @@ interface Props {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
|
-
* @emits
|
|
52
|
-
* @emits
|
|
51
|
+
* @emits mark:delete - User requested to delete annotation. Payload: { annotationId: string }
|
|
52
|
+
* @emits browse:panel-open - Request to open panel with annotation. Payload: { panel: string, scrollToAnnotationId?: string, motivation?: Motivation }
|
|
53
53
|
*
|
|
54
|
-
* @subscribes
|
|
55
|
-
* @subscribes
|
|
56
|
-
* @subscribes
|
|
57
|
-
* @subscribes
|
|
58
|
-
* @subscribes
|
|
59
|
-
* @subscribes
|
|
60
|
-
* @subscribes
|
|
61
|
-
* @subscribes
|
|
54
|
+
* @subscribes mark:mode-toggled - Toggles between browse and annotate mode. Payload: { mode: 'browse' | 'annotate' }
|
|
55
|
+
* @subscribes mark:added - New annotation was added. Payload: { annotation: Annotation }
|
|
56
|
+
* @subscribes mark:removed - Annotation was removed. Payload: { annotationId: string }
|
|
57
|
+
* @subscribes mark:body-updated - Annotation was updated. Payload: { annotation: Annotation }
|
|
58
|
+
* @subscribes mark:selection-changed - Text selection tool changed. Payload: { selection: boolean }
|
|
59
|
+
* @subscribes mark:click-changed - Click annotation tool changed. Payload: { click: 'detail' | 'scroll' | null }
|
|
60
|
+
* @subscribes mark:shape-changed - Drawing shape changed. Payload: { shape: string }
|
|
61
|
+
* @subscribes browse:click - User clicked on annotation. Payload: { annotationId: string }
|
|
62
62
|
*/
|
|
63
63
|
export function ResourceViewer({
|
|
64
64
|
resource,
|
|
@@ -251,7 +251,7 @@ export function ResourceViewer({
|
|
|
251
251
|
|
|
252
252
|
// Handle deleting annotations - emit event instead of direct call
|
|
253
253
|
const handleDeleteAnnotation = useCallback((id: string) => {
|
|
254
|
-
eventBus.get('
|
|
254
|
+
eventBus.get('mark:delete').next({ annotationId: id });
|
|
255
255
|
}, []); // eventBus is stable
|
|
256
256
|
|
|
257
257
|
// Handle annotation clicks - memoized
|
|
@@ -279,7 +279,7 @@ export function ResourceViewer({
|
|
|
279
279
|
if (selectedClick === 'follow' && isReference(annotation)) {
|
|
280
280
|
const bodySource = getBodySource(annotation.body);
|
|
281
281
|
if (bodySource) {
|
|
282
|
-
// Navigate to the linked resource - emits '
|
|
282
|
+
// Navigate to the linked resource - emits 'browse:external-navigate' event
|
|
283
283
|
const resourceId = bodySource.split('/resources/')[1];
|
|
284
284
|
if (resourceId) {
|
|
285
285
|
navigate(`/know/resource/${resourceId}`, { resourceId });
|
|
@@ -340,27 +340,27 @@ export function ResourceViewer({
|
|
|
340
340
|
|
|
341
341
|
// All annotations open the unified annotations panel
|
|
342
342
|
// The panel internally switches tabs based on the motivation → tab mapping in UnifiedAnnotationsPanel
|
|
343
|
-
eventBus.get('
|
|
343
|
+
eventBus.get('browse:panel-open').next({ panel: 'annotations', scrollToAnnotationId: annotationId, motivation });
|
|
344
344
|
}, [highlights, references, assessments, comments, tags, handleAnnotationClick, selectedClick]);
|
|
345
345
|
|
|
346
346
|
// Event subscriptions - Combined into single useEventSubscriptions call to prevent hook ordering issues
|
|
347
347
|
// IMPORTANT: All event subscriptions MUST be in a single call to maintain consistent hook order between renders
|
|
348
348
|
useEventSubscriptions({
|
|
349
349
|
// View mode
|
|
350
|
-
'
|
|
350
|
+
'mark:mode-toggled': handleViewModeToggle,
|
|
351
351
|
|
|
352
352
|
// Annotation cache invalidation
|
|
353
|
-
'
|
|
354
|
-
'
|
|
355
|
-
'
|
|
353
|
+
'mark:added': handleAnnotateAdded,
|
|
354
|
+
'mark:removed': handleAnnotateRemoved,
|
|
355
|
+
'mark:body-updated': handleAnnotateBodyUpdated,
|
|
356
356
|
|
|
357
357
|
// Toolbar state
|
|
358
|
-
'
|
|
359
|
-
'
|
|
360
|
-
'
|
|
358
|
+
'mark:selection-changed': handleToolbarSelectionChanged,
|
|
359
|
+
'mark:click-changed': handleToolbarClickChanged,
|
|
360
|
+
'mark:shape-changed': handleToolbarShapeChanged,
|
|
361
361
|
|
|
362
362
|
// Annotation clicks
|
|
363
|
-
'
|
|
363
|
+
'browse:click': handleAnnotationClickEvent,
|
|
364
364
|
});
|
|
365
365
|
|
|
366
366
|
// Prepare props for child components (memoized to prevent unnecessary re-renders of BrowseView/AnnotateView)
|