@semiont/react-ui 0.2.33-build.78 → 0.2.33-build.80

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.
Files changed (219) hide show
  1. package/dist/EventBusContext-7GvDyO0d.d.mts +414 -0
  2. package/dist/{PdfAnnotationCanvas.client-ADC4FFSE.mjs → PdfAnnotationCanvas.client-RAJRPQLU.mjs} +42 -27
  3. package/dist/PdfAnnotationCanvas.client-RAJRPQLU.mjs.map +1 -0
  4. package/dist/{ar-RNNSPLQB.mjs → ar-4ZEORRW2.mjs} +8 -4
  5. package/dist/ar-4ZEORRW2.mjs.map +1 -0
  6. package/dist/{bn-S2CDL7EC.mjs → bn-SEDE5BQJ.mjs} +8 -4
  7. package/dist/bn-SEDE5BQJ.mjs.map +1 -0
  8. package/dist/{chunk-UDX2Q35T.mjs → chunk-D7NBW4RV.mjs} +8 -4
  9. package/dist/chunk-D7NBW4RV.mjs.map +1 -0
  10. package/dist/{chunk-35LLVRFK.mjs → chunk-ZR4ZV2LY.mjs} +206 -146
  11. package/dist/chunk-ZR4ZV2LY.mjs.map +1 -0
  12. package/dist/{cs-RSV675WU.mjs → cs-7W4WF5WD.mjs} +8 -4
  13. package/dist/cs-7W4WF5WD.mjs.map +1 -0
  14. package/dist/{da-CHXNPWJC.mjs → da-75XGBCBK.mjs} +8 -4
  15. package/dist/da-75XGBCBK.mjs.map +1 -0
  16. package/dist/{de-KPEZ53D4.mjs → de-ODJVFLHM.mjs} +8 -4
  17. package/dist/de-ODJVFLHM.mjs.map +1 -0
  18. package/dist/{el-MW2BME5T.mjs → el-C4PM4WB3.mjs} +8 -4
  19. package/dist/el-C4PM4WB3.mjs.map +1 -0
  20. package/dist/{en-EVMIX24Y.mjs → en-KJCJQ4OO.mjs} +2 -2
  21. package/dist/{es-HQ24NYS3.mjs → es-WD33R7QL.mjs} +8 -4
  22. package/dist/es-WD33R7QL.mjs.map +1 -0
  23. package/dist/{fa-W34LRLHG.mjs → fa-2BP6V56P.mjs} +8 -4
  24. package/dist/fa-2BP6V56P.mjs.map +1 -0
  25. package/dist/{fi-3U44IGOA.mjs → fi-USRRW24J.mjs} +8 -4
  26. package/dist/fi-USRRW24J.mjs.map +1 -0
  27. package/dist/{fr-N7DKX6NN.mjs → fr-EC5S6WVF.mjs} +8 -4
  28. package/dist/fr-EC5S6WVF.mjs.map +1 -0
  29. package/dist/{he-CS4WRXN3.mjs → he-7TBVIKAA.mjs} +8 -4
  30. package/dist/he-7TBVIKAA.mjs.map +1 -0
  31. package/dist/{hi-GJDY46KA.mjs → hi-FO4VIZLA.mjs} +8 -4
  32. package/dist/hi-FO4VIZLA.mjs.map +1 -0
  33. package/dist/{id-WAEZJK2Y.mjs → id-7U7GGVWY.mjs} +8 -4
  34. package/dist/id-7U7GGVWY.mjs.map +1 -0
  35. package/dist/index.css +123 -85
  36. package/dist/index.css.map +1 -1
  37. package/dist/index.d.mts +699 -529
  38. package/dist/index.mjs +4291 -3491
  39. package/dist/index.mjs.map +1 -1
  40. package/dist/{it-VDNDMZPU.mjs → it-Y4OPL6I2.mjs} +8 -4
  41. package/dist/it-Y4OPL6I2.mjs.map +1 -0
  42. package/dist/{ja-5PEH56J5.mjs → ja-PK7SQL55.mjs} +8 -4
  43. package/dist/ja-PK7SQL55.mjs.map +1 -0
  44. package/dist/{ko-JYPL3WVA.mjs → ko-L25PXMYD.mjs} +8 -4
  45. package/dist/ko-L25PXMYD.mjs.map +1 -0
  46. package/dist/{ms-5PZVW76T.mjs → ms-STH777QM.mjs} +8 -4
  47. package/dist/ms-STH777QM.mjs.map +1 -0
  48. package/dist/{nl-YXES36KM.mjs → nl-Y7LECDDR.mjs} +8 -4
  49. package/dist/nl-Y7LECDDR.mjs.map +1 -0
  50. package/dist/{no-XRA2UCQD.mjs → no-KEKCEWU6.mjs} +8 -4
  51. package/dist/no-KEKCEWU6.mjs.map +1 -0
  52. package/dist/{pl-WH6LJA5G.mjs → pl-7A7OC75O.mjs} +8 -4
  53. package/dist/pl-7A7OC75O.mjs.map +1 -0
  54. package/dist/{pt-7GAG57BM.mjs → pt-35HTM7RA.mjs} +8 -4
  55. package/dist/pt-35HTM7RA.mjs.map +1 -0
  56. package/dist/{ro-BTDDRB7N.mjs → ro-VAWL5KQA.mjs} +8 -4
  57. package/dist/ro-VAWL5KQA.mjs.map +1 -0
  58. package/dist/{sv-7V5C2IT4.mjs → sv-7ZK5EQEB.mjs} +8 -4
  59. package/dist/sv-7ZK5EQEB.mjs.map +1 -0
  60. package/dist/test-utils.d.mts +18 -8
  61. package/dist/test-utils.mjs +36 -14
  62. package/dist/test-utils.mjs.map +1 -1
  63. package/dist/{th-LPKYLBX5.mjs → th-UDWZ4X34.mjs} +8 -4
  64. package/dist/th-UDWZ4X34.mjs.map +1 -0
  65. package/dist/{tr-DU4RQL4M.mjs → tr-4WMPK3UX.mjs} +8 -4
  66. package/dist/tr-4WMPK3UX.mjs.map +1 -0
  67. package/dist/{uk-36UHTDDI.mjs → uk-SSLASQYJ.mjs} +8 -4
  68. package/dist/uk-SSLASQYJ.mjs.map +1 -0
  69. package/dist/{vi-GDHOUZDH.mjs → vi-IF42Z5PU.mjs} +8 -4
  70. package/dist/vi-IF42Z5PU.mjs.map +1 -0
  71. package/dist/{zh-TYUID4XZ.mjs → zh-HRQTNTAI.mjs} +8 -4
  72. package/dist/zh-HRQTNTAI.mjs.map +1 -0
  73. package/package.json +8 -2
  74. package/src/components/CodeMirrorRenderer.tsx +66 -93
  75. package/src/components/DetectionProgressWidget.tsx +16 -5
  76. package/src/components/LiveRegion.tsx +18 -18
  77. package/src/components/ResizeHandle.tsx +10 -4
  78. package/src/components/SessionTimer.tsx +2 -2
  79. package/src/components/Toolbar.tsx +18 -9
  80. package/src/components/__tests__/SessionTimer.test.tsx +9 -9
  81. package/src/components/annotation/AnnotateToolbar.tsx +17 -15
  82. package/src/components/annotation/__tests__/AnnotateToolbar.test.tsx +165 -63
  83. package/src/components/annotation/annotation-entries.css +10 -0
  84. package/src/components/annotation-popups/JsonLdView.tsx +8 -2
  85. package/src/components/image-annotation/AnnotationOverlay.tsx +42 -22
  86. package/src/components/image-annotation/SvgDrawingCanvas.tsx +27 -30
  87. package/src/components/layout/__tests__/LeftSidebar.test.tsx +12 -33
  88. package/src/components/layout/__tests__/PageLayout.test.tsx +37 -32
  89. package/src/components/layout/__tests__/UnifiedHeader.test.tsx +21 -40
  90. package/src/components/modals/ResourceSearchModal.tsx +2 -2
  91. package/src/components/modals/SearchModal.tsx +1 -1
  92. package/src/components/navigation/CollapsibleResourceNavigation.tsx +14 -9
  93. package/src/components/navigation/NavigationTabs.css +36 -24
  94. package/src/components/navigation/ObservableLink.tsx +91 -0
  95. package/src/components/navigation/SimpleNavigation.tsx +20 -16
  96. package/src/components/navigation/SortableResourceTab.tsx +11 -5
  97. package/src/components/pdf-annotation/PdfAnnotationCanvas.tsx +51 -26
  98. package/src/components/pdf-annotation/__tests__/PdfAnnotationCanvas.test.tsx +28 -22
  99. package/src/components/resource/AnnotateView.tsx +64 -138
  100. package/src/components/resource/AnnotationHistory.tsx +12 -13
  101. package/src/components/resource/BrowseView.tsx +89 -177
  102. package/src/components/resource/HistoryEvent.tsx +16 -11
  103. package/src/components/resource/ResourceViewer.tsx +201 -370
  104. package/src/components/resource/__tests__/BrowseView.test.tsx +631 -0
  105. package/src/components/resource/__tests__/ResourceViewer.mode-switch.test.tsx +231 -0
  106. package/src/components/resource/event-formatting.ts +316 -0
  107. package/src/components/resource/panels/AssessmentEntry.tsx +25 -33
  108. package/src/components/resource/panels/AssessmentPanel.tsx +137 -31
  109. package/src/components/resource/panels/CollaborationPanel.tsx +20 -13
  110. package/src/components/resource/panels/CommentEntry.tsx +38 -32
  111. package/src/components/resource/panels/CommentsPanel.tsx +153 -31
  112. package/src/components/resource/panels/DetectSection.css +36 -1
  113. package/src/components/resource/panels/DetectSection.tsx +38 -10
  114. package/src/components/resource/panels/HighlightEntry.tsx +25 -33
  115. package/src/components/resource/panels/HighlightPanel.tsx +100 -25
  116. package/src/components/resource/panels/ReferenceEntry.tsx +61 -75
  117. package/src/components/resource/panels/ReferencesPanel.tsx +166 -49
  118. package/src/components/resource/panels/ResourceInfoPanel.tsx +47 -48
  119. package/src/components/resource/panels/StatisticsPanel.tsx +9 -19
  120. package/src/components/resource/panels/TagEntry.tsx +25 -33
  121. package/src/components/resource/panels/TaggingPanel.tsx +141 -25
  122. package/src/components/resource/panels/UnifiedAnnotationsPanel.tsx +46 -101
  123. package/src/components/resource/panels/__tests__/AssessmentPanel.test.tsx +566 -0
  124. package/src/components/resource/panels/__tests__/CommentEntry.test.tsx +86 -78
  125. package/src/components/resource/panels/__tests__/CommentsPanel.test.tsx +146 -141
  126. package/src/components/resource/panels/__tests__/DetectSection.test.tsx +480 -0
  127. package/src/components/resource/panels/__tests__/HighlightPanel.detectionProgress.test.tsx +362 -0
  128. package/src/components/resource/panels/__tests__/ReferencesPanel.test.tsx +228 -103
  129. package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +117 -61
  130. package/src/components/resource/panels/__tests__/TaggingPanel.test.tsx +586 -0
  131. package/src/components/settings/SettingsPanel.tsx +15 -12
  132. package/src/features/admin-devops/__tests__/AdminDevOpsPage.test.tsx +1 -46
  133. package/src/features/admin-devops/components/AdminDevOpsPage.tsx +0 -9
  134. package/src/features/admin-security/__tests__/AdminSecurityPage.test.tsx +0 -3
  135. package/src/features/admin-security/components/AdminSecurityPage.tsx +0 -9
  136. package/src/features/admin-users/__tests__/AdminUsersPage.test.tsx +0 -3
  137. package/src/features/admin-users/components/AdminUsersPage.tsx +0 -9
  138. package/src/features/moderate-entity-tags/__tests__/EntityTagsPage.test.tsx +0 -3
  139. package/src/features/moderate-entity-tags/components/EntityTagsPage.tsx +1 -9
  140. package/src/features/moderate-recent/__tests__/RecentDocumentsPage.test.tsx +0 -32
  141. package/src/features/moderate-recent/components/RecentDocumentsPage.tsx +1 -9
  142. package/src/features/moderate-tag-schemas/__tests__/TagSchemasPage.test.tsx +0 -32
  143. package/src/features/moderate-tag-schemas/components/TagSchemasPage.tsx +1 -9
  144. package/src/features/resource-compose/__tests__/ResourceComposePage.test.tsx +51 -54
  145. package/src/features/resource-compose/components/ResourceComposePage.tsx +3 -13
  146. package/src/features/resource-discovery/__tests__/ResourceDiscoveryPage.test.tsx +39 -45
  147. package/src/features/resource-discovery/components/ResourceDiscoveryPage.tsx +16 -27
  148. package/src/features/resource-viewer/__tests__/AnnotationDeletionIntegration.test.tsx +231 -0
  149. package/src/features/resource-viewer/__tests__/DetectionFlowBug.test.tsx +234 -0
  150. package/src/features/resource-viewer/__tests__/DetectionFlowIntegration.test.tsx +388 -0
  151. package/src/features/resource-viewer/__tests__/DetectionProgressDismissal.test.tsx +318 -0
  152. package/src/features/resource-viewer/__tests__/GenerationFlowIntegration.test.tsx +504 -0
  153. package/src/features/resource-viewer/__tests__/ResourceViewerPage.test.tsx +145 -91
  154. package/src/features/resource-viewer/__tests__/detection-progress-flow.test.tsx +322 -0
  155. package/src/features/resource-viewer/components/ResourceViewerPage.tsx +325 -476
  156. package/src/styles/motivations/motivation-assessment.css +28 -0
  157. package/src/styles/patterns/panel-helpers.css +26 -0
  158. package/translations/ar.json +7 -3
  159. package/translations/bn.json +7 -3
  160. package/translations/cs.json +7 -3
  161. package/translations/da.json +7 -3
  162. package/translations/de.json +7 -3
  163. package/translations/el.json +7 -3
  164. package/translations/en.json +7 -3
  165. package/translations/es.json +7 -3
  166. package/translations/fa.json +7 -3
  167. package/translations/fi.json +7 -3
  168. package/translations/fr.json +7 -3
  169. package/translations/he.json +7 -3
  170. package/translations/hi.json +7 -3
  171. package/translations/id.json +7 -3
  172. package/translations/it.json +7 -3
  173. package/translations/ja.json +7 -3
  174. package/translations/ko.json +7 -3
  175. package/translations/ms.json +7 -3
  176. package/translations/nl.json +7 -3
  177. package/translations/no.json +7 -3
  178. package/translations/pl.json +7 -3
  179. package/translations/pt.json +7 -3
  180. package/translations/ro.json +7 -3
  181. package/translations/sv.json +7 -3
  182. package/translations/th.json +7 -3
  183. package/translations/tr.json +7 -3
  184. package/translations/uk.json +7 -3
  185. package/translations/vi.json +7 -3
  186. package/translations/zh.json +7 -3
  187. package/dist/PdfAnnotationCanvas.client-ADC4FFSE.mjs.map +0 -1
  188. package/dist/TranslationManager-Co_5fSxl.d.mts +0 -118
  189. package/dist/ar-RNNSPLQB.mjs.map +0 -1
  190. package/dist/bn-S2CDL7EC.mjs.map +0 -1
  191. package/dist/chunk-35LLVRFK.mjs.map +0 -1
  192. package/dist/chunk-UDX2Q35T.mjs.map +0 -1
  193. package/dist/cs-RSV675WU.mjs.map +0 -1
  194. package/dist/da-CHXNPWJC.mjs.map +0 -1
  195. package/dist/de-KPEZ53D4.mjs.map +0 -1
  196. package/dist/el-MW2BME5T.mjs.map +0 -1
  197. package/dist/es-HQ24NYS3.mjs.map +0 -1
  198. package/dist/fa-W34LRLHG.mjs.map +0 -1
  199. package/dist/fi-3U44IGOA.mjs.map +0 -1
  200. package/dist/fr-N7DKX6NN.mjs.map +0 -1
  201. package/dist/he-CS4WRXN3.mjs.map +0 -1
  202. package/dist/hi-GJDY46KA.mjs.map +0 -1
  203. package/dist/id-WAEZJK2Y.mjs.map +0 -1
  204. package/dist/it-VDNDMZPU.mjs.map +0 -1
  205. package/dist/ja-5PEH56J5.mjs.map +0 -1
  206. package/dist/ko-JYPL3WVA.mjs.map +0 -1
  207. package/dist/ms-5PZVW76T.mjs.map +0 -1
  208. package/dist/nl-YXES36KM.mjs.map +0 -1
  209. package/dist/no-XRA2UCQD.mjs.map +0 -1
  210. package/dist/pl-WH6LJA5G.mjs.map +0 -1
  211. package/dist/pt-7GAG57BM.mjs.map +0 -1
  212. package/dist/ro-BTDDRB7N.mjs.map +0 -1
  213. package/dist/sv-7V5C2IT4.mjs.map +0 -1
  214. package/dist/th-LPKYLBX5.mjs.map +0 -1
  215. package/dist/tr-DU4RQL4M.mjs.map +0 -1
  216. package/dist/uk-36UHTDDI.mjs.map +0 -1
  217. package/dist/vi-GDHOUZDH.mjs.map +0 -1
  218. package/dist/zh-TYUID4XZ.mjs.map +0 -1
  219. /package/dist/{en-EVMIX24Y.mjs.map → en-KJCJQ4OO.mjs.map} +0 -0
@@ -1,115 +1,78 @@
1
1
  'use client';
2
2
 
3
- import React, { useState, useEffect, useCallback, useRef, useMemo } from 'react';
4
- // useRouter removed - using window.location for navigation
3
+ import React, { useState, useEffect, useCallback, useRef } from 'react';
5
4
  import { useTranslations } from '../../contexts/TranslationContext';
6
5
  import { AnnotateView, type SelectionMotivation, type ClickAction, type ShapeType } from './AnnotateView';
7
6
  import { BrowseView } from './BrowseView';
8
7
  import { PopupContainer } from '../annotation-popups/SharedPopupElements';
9
8
  import { JsonLdView } from '../annotation-popups/JsonLdView';
10
- import type { components, Selector } from '@semiont/api-client';
9
+ import type { components } from '@semiont/api-client';
11
10
  import { getExactText, getTargetSelector, resourceUri, isHighlight, isAssessment, isReference, isComment, isTag, getBodySource } from '@semiont/api-client';
12
- import { useResourceAnnotations } from '../../contexts/ResourceAnnotationsContext';
13
- import type { Annotator } from '../../lib/annotation-registry';
11
+ import { useEventBus } from '../../contexts/EventBusContext';
12
+ import { useEventSubscriptions } from '../../contexts/useEventSubscription';
13
+ import { useCacheManager } from '../../contexts/CacheContext';
14
+ import { useObservableExternalNavigation } from '../../hooks/useObservableNavigation';
15
+ import { ANNOTATORS } from '../../lib/annotation-registry';
14
16
  import type { AnnotationsCollection } from '../../types/annotation-props';
15
17
  import { getSelectorType, getSelectedShapeForSelectorType, saveSelectedShapeForSelectorType } from '../../lib/media-shapes';
16
18
 
17
19
  type Annotation = components['schemas']['Annotation'];
18
20
  type SemiontResource = components['schemas']['ResourceDescriptor'];
19
- type Motivation = components['schemas']['Motivation'];
20
-
21
- // Unified pending annotation type - all human-created annotations flow through this
22
- interface PendingAnnotation {
23
- selector: Selector | Selector[];
24
- motivation: Motivation;
25
- }
26
21
 
22
+ /**
23
+ * ResourceViewer - Display and interact with resource content and annotations
24
+ *
25
+ * This component uses event-driven architecture for real-time updates:
26
+ * - Subscribes to make-meaning events (annotation:added, annotation:removed, annotation:updated)
27
+ * - Automatically invalidates cache when annotations change
28
+ * - No manual refetch needed - events handle cache invalidation
29
+ *
30
+ * Requirements:
31
+ * - Must be wrapped in MakeMeaningEventBusProvider (provides event bus)
32
+ * - Must be wrapped in CacheContext (provides cache manager)
33
+ *
34
+ * Event flow:
35
+ * make-meaning → EventLog → SSE → EventBus → ResourceViewer → Cache invalidation
36
+ *
37
+ * Phase 2 complete: Event-based cache invalidation replaces manual refetch
38
+ * Phase 3 complete: Fully event-driven - all user interactions use unified event bus
39
+ */
27
40
  interface Props {
28
41
  resource: SemiontResource & { content: string };
29
42
  annotations: AnnotationsCollection;
30
- onRefetchAnnotations?: () => void;
31
- annotateMode: boolean;
32
- onAnnotateModeToggle: () => void;
33
43
  generatingReferenceId?: string | null;
34
- onAnnotationHover?: (annotationId: string | null) => void;
35
- onCommentHover?: (commentId: string | null) => void;
36
- hoveredAnnotationId?: string | null;
37
- hoveredCommentId?: string | null;
38
- scrollToAnnotationId?: string | null;
39
44
  showLineNumbers?: boolean;
40
- onAnnotationRequested?: (pending: PendingAnnotation) => void;
41
- onCommentCreationRequested?: (selection: { exact: string; start: number; end: number; svgSelector?: string; fragmentSelector?: string; conformsTo?: string }) => void;
42
- onTagCreationRequested?: (selection: { exact: string; start: number; end: number; svgSelector?: string; fragmentSelector?: string; conformsTo?: string }) => void;
43
- onAssessmentCreationRequested?: (selection: { exact: string; start: number; end: number; svgSelector?: string; fragmentSelector?: string; conformsTo?: string }) => void;
44
- onReferenceCreationRequested?: (selection: {
45
- exact: string;
46
- start: number;
47
- end: number;
48
- prefix?: string;
49
- suffix?: string;
50
- svgSelector?: string;
51
- fragmentSelector?: string;
52
- conformsTo?: string;
53
- }) => void;
54
- onCommentClick?: (commentId: string) => void;
55
- onReferenceClick?: (referenceId: string) => void;
56
- onHighlightClick?: (highlightId: string) => void;
57
- onAssessmentClick?: (assessmentId: string) => void;
58
- onTagClick?: (tagId: string) => void;
59
- annotators: Record<string, Annotator>;
45
+ hoveredAnnotationId?: string | null;
60
46
  }
61
47
 
48
+ /**
49
+ * @emits annotation:delete - User requested to delete annotation. Payload: { annotationId: string }
50
+ * @emits panel:open - Request to open panel with annotation. Payload: { panel: string, scrollToAnnotationId?: string, motivation?: Motivation }
51
+ *
52
+ * @subscribes view:mode-toggled - Toggles between browse and annotate mode. Payload: { mode: 'browse' | 'annotate' }
53
+ * @subscribes annotation:added - New annotation was added. Payload: { annotation: Annotation }
54
+ * @subscribes annotation:removed - Annotation was removed. Payload: { annotationId: string }
55
+ * @subscribes annotation:updated - Annotation was updated. Payload: { annotation: Annotation }
56
+ * @subscribes toolbar:selection-changed - Text selection tool changed. Payload: { selection: boolean }
57
+ * @subscribes toolbar:click-changed - Click annotation tool changed. Payload: { click: 'detail' | 'scroll' | null }
58
+ * @subscribes toolbar:shape-changed - Drawing shape changed. Payload: { shape: string }
59
+ * @subscribes annotation:click - User clicked on annotation. Payload: { annotationId: string }
60
+ */
62
61
  export function ResourceViewer({
63
62
  resource,
64
63
  annotations,
65
- onRefetchAnnotations,
66
- annotateMode,
67
- onAnnotateModeToggle,
68
64
  generatingReferenceId,
69
- onAnnotationHover,
70
- onCommentHover,
71
- hoveredAnnotationId,
72
- hoveredCommentId,
73
- scrollToAnnotationId,
74
65
  showLineNumbers = false,
75
- onAnnotationRequested,
76
- onCommentCreationRequested,
77
- onTagCreationRequested,
78
- onAssessmentCreationRequested,
79
- onReferenceCreationRequested,
80
- onCommentClick,
81
- onReferenceClick,
82
- onHighlightClick,
83
- onAssessmentClick,
84
- onTagClick,
85
- annotators
66
+ hoveredAnnotationId: hoveredAnnotationIdProp
86
67
  }: Props) {
87
68
  const t = useTranslations('ResourceViewer');
88
69
  const documentViewerRef = useRef<HTMLDivElement>(null);
89
70
 
90
- // Use refs for function props to prevent infinite rerenders
91
- const onRefetchAnnotationsRef = useRef(onRefetchAnnotations);
92
- const onCommentCreationRequestedRef = useRef(onCommentCreationRequested);
93
- const onTagCreationRequestedRef = useRef(onTagCreationRequested);
94
- const onReferenceCreationRequestedRef = useRef(onReferenceCreationRequested);
95
- const onCommentClickRef = useRef(onCommentClick);
96
- const onReferenceClickRef = useRef(onReferenceClick);
97
- const onHighlightClickRef = useRef(onHighlightClick);
98
- const onAssessmentClickRef = useRef(onAssessmentClick);
99
- const onTagClickRef = useRef(onTagClick);
100
-
101
- // Keep refs up to date
102
- useEffect(() => {
103
- onRefetchAnnotationsRef.current = onRefetchAnnotations;
104
- onCommentCreationRequestedRef.current = onCommentCreationRequested;
105
- onTagCreationRequestedRef.current = onTagCreationRequested;
106
- onReferenceCreationRequestedRef.current = onReferenceCreationRequested;
107
- onCommentClickRef.current = onCommentClick;
108
- onReferenceClickRef.current = onReferenceClick;
109
- onHighlightClickRef.current = onHighlightClick;
110
- onAssessmentClickRef.current = onAssessmentClick;
111
- onTagClickRef.current = onTagClick;
112
- });
71
+ // Get unified event bus for emitting UI events
72
+ const eventBus = useEventBus();
73
+
74
+ // Get observable navigation for event-driven routing
75
+ const navigate = useObservableExternalNavigation();
113
76
 
114
77
  const { highlights, references, assessments, comments, tags } = annotations;
115
78
 
@@ -131,12 +94,50 @@ export function ResourceViewer({
131
94
 
132
95
  const mimeType = getMimeType();
133
96
 
134
- // Use prop directly instead of internal state
97
+ // Annotate mode state - persisted in localStorage
98
+ const [annotateMode, setAnnotateMode] = useState<boolean>(() => {
99
+ if (typeof window !== 'undefined') {
100
+ return localStorage.getItem('annotateMode') === 'true';
101
+ }
102
+ return false;
103
+ });
104
+
105
+ // Persist annotateMode to localStorage
106
+ useEffect(() => {
107
+ if (typeof window !== 'undefined') {
108
+ localStorage.setItem('annotateMode', annotateMode.toString());
109
+ }
110
+ }, [annotateMode]);
111
+
112
+ // Event handlers (extracted to avoid inline arrow functions)
113
+ const handleViewModeToggle = useCallback(() => {
114
+ setAnnotateMode(prev => !prev);
115
+ }, []);
116
+
117
+ // Determine active view based on annotate mode
135
118
  const activeView = annotateMode ? 'annotate' : 'browse';
136
- const {
137
- deleteAnnotation,
138
- createAnnotation
139
- } = useResourceAnnotations();
119
+
120
+ // Event-based cache invalidation - subscribe to make-meaning events
121
+ // This replaces manual onRefetchAnnotations calls with automatic updates
122
+ const cacheManager = useCacheManager();
123
+
124
+ const handleAnnotationAdded = useCallback(() => {
125
+ if (cacheManager) {
126
+ cacheManager.invalidateAnnotations(rUri);
127
+ }
128
+ }, [cacheManager, rUri]);
129
+
130
+ const handleAnnotationRemoved = useCallback(() => {
131
+ if (cacheManager) {
132
+ cacheManager.invalidateAnnotations(rUri);
133
+ }
134
+ }, [cacheManager, rUri]);
135
+
136
+ const handleAnnotationUpdated = useCallback(() => {
137
+ if (cacheManager) {
138
+ cacheManager.invalidateAnnotations(rUri);
139
+ }
140
+ }, [cacheManager, rUri]);
140
141
 
141
142
  // Annotation toolbar state - persisted in localStorage
142
143
  const [selectedMotivation, setSelectedMotivation] = useState<SelectionMotivation | null>(() => {
@@ -168,6 +169,19 @@ export function ResourceViewer({
168
169
  return getSelectedShapeForSelectorType(selectorType);
169
170
  });
170
171
 
172
+ // Toolbar event handlers (extracted to avoid inline arrow functions)
173
+ const handleToolbarSelectionChanged = useCallback(({ motivation }: { motivation: string | null }) => {
174
+ setSelectedMotivation(motivation as SelectionMotivation | null);
175
+ }, []);
176
+
177
+ const handleToolbarClickChanged = useCallback(({ action }: { action: string }) => {
178
+ setSelectedClick(action as ClickAction);
179
+ }, []);
180
+
181
+ const handleToolbarShapeChanged = useCallback(({ shape }: { shape: string }) => {
182
+ setSelectedShape(shape as ShapeType);
183
+ }, []);
184
+
171
185
  // Persist toolbar state to localStorage
172
186
  useEffect(() => {
173
187
  if (selectedMotivation === null) {
@@ -204,8 +218,24 @@ export function ResourceViewer({
204
218
  position: { x: number; y: number };
205
219
  } | null>(null);
206
220
 
221
+ // Internal UI state for hover, focus, and scroll
222
+ // Use prop value when provided (controlled by parent), otherwise null
223
+ const hoveredAnnotationId = hoveredAnnotationIdProp ?? null;
224
+ const [hoveredCommentId, _setHoveredCommentId] = useState<string | null>(null);
225
+ const [scrollToAnnotationId, setScrollToAnnotationId] = useState<string | null>(null);
226
+ const [_focusedAnnotationId, setFocusedAnnotationId] = useState<string | null>(null);
227
+
228
+ // Focus annotation helper
229
+ const focusAnnotation = useCallback((annotationId: string) => {
230
+ setFocusedAnnotationId(annotationId);
231
+ setScrollToAnnotationId(annotationId);
232
+
233
+ // Clear focus after 3 seconds
234
+ setTimeout(() => setFocusedAnnotationId(null), 3000);
235
+ }, []);
236
+
207
237
  // Calculate centered position for JSON-LD modal
208
- const jsonLdModalPosition = useMemo(() => {
238
+ const getJsonLdModalPosition = () => {
209
239
  if (typeof window === 'undefined') return { x: 0, y: 0 };
210
240
 
211
241
  const popupWidth = 800;
@@ -215,47 +245,24 @@ export function ResourceViewer({
215
245
  x: Math.max(0, (window.innerWidth - popupWidth) / 2),
216
246
  y: Math.max(0, (window.innerHeight - popupHeight) / 2),
217
247
  };
218
- }, []);
248
+ };
219
249
 
220
- // Handle deleting annotations - memoized
221
- const handleDeleteAnnotation = useCallback(async (id: string) => {
222
- try {
223
- await deleteAnnotation(id, rUri);
224
- onRefetchAnnotationsRef.current?.();
225
- } catch (err) {
226
- console.error('Failed to delete annotation:', err);
227
- }
228
- }, [deleteAnnotation, rUri]);
250
+ // Handle deleting annotations - emit event instead of direct call
251
+ const handleDeleteAnnotation = useCallback((id: string) => {
252
+ eventBus.emit('annotation:delete', { annotationId: id });
253
+ }, []); // eventBus is stable
229
254
 
230
255
  // Handle annotation clicks - memoized
231
256
  const handleAnnotationClick = useCallback((annotation: Annotation, event?: React.MouseEvent) => {
232
- const metadata = Object.values(annotators).find(a => a.matchesAnnotation(annotation));
257
+ const metadata = Object.values(ANNOTATORS).find(a => a.matchesAnnotation(annotation));
233
258
 
234
259
  // If annotation has a side panel, only open it when Detail mode is active
235
260
  // For delete/jsonld/follow modes, let those handlers below process it
236
261
  if (metadata?.hasSidePanel) {
237
262
  if (selectedClick === 'detail') {
238
- // Route to appropriate panel based on annotation type
239
- if (isComment(annotation) && onCommentClickRef.current) {
240
- onCommentClickRef.current(annotation.id);
241
- return;
242
- }
243
- if (isReference(annotation) && onReferenceClickRef.current) {
244
- onReferenceClickRef.current(annotation.id);
245
- return;
246
- }
247
- if (isHighlight(annotation) && onHighlightClickRef.current) {
248
- onHighlightClickRef.current(annotation.id);
249
- return;
250
- }
251
- if (isAssessment(annotation) && onAssessmentClickRef.current) {
252
- onAssessmentClickRef.current(annotation.id);
253
- return;
254
- }
255
- if (isTag(annotation) && onTagClickRef.current) {
256
- onTagClickRef.current(annotation.id);
257
- return;
258
- }
263
+ // Focus annotation (sets internal focus and scroll state, plus calls parent callback for backward compat)
264
+ focusAnnotation(annotation.id);
265
+ return;
259
266
  }
260
267
  // Don't return early for delete/jsonld/follow modes - let them be handled below
261
268
  if (selectedClick !== 'deleting' && selectedClick !== 'jsonld' && selectedClick !== 'follow') {
@@ -270,10 +277,10 @@ export function ResourceViewer({
270
277
  if (selectedClick === 'follow' && isReference(annotation)) {
271
278
  const bodySource = getBodySource(annotation.body);
272
279
  if (bodySource) {
273
- // Navigate to the linked resource
280
+ // Navigate to the linked resource - emits 'navigation:external-navigate' event
274
281
  const resourceId = bodySource.split('/resources/')[1];
275
282
  if (resourceId) {
276
- window.location.href = `/know/resource/${resourceId}`;
283
+ navigate(`/know/resource/${resourceId}`, { resourceId });
277
284
  }
278
285
  }
279
286
  return;
@@ -299,238 +306,79 @@ export function ResourceViewer({
299
306
  setDeleteConfirmation({ annotation, position });
300
307
  return;
301
308
  }
302
- }, [annotateMode, selectedClick, handleDeleteAnnotation, annotators]);
303
-
304
- // Unified annotation creation handler - works for both text and images
305
- const handleAnnotationCreate = useCallback(async (params: import('../../types/annotation-props').UICreateAnnotationParams) => {
306
- const { motivation, selector } = params;
307
-
308
- try {
309
- switch (motivation) {
310
- case 'highlighting':
311
- case 'assessing':
312
- // Create highlight/assessment immediately using generic createAnnotation
313
- if (selector.type === 'TextQuoteSelector' && selector.exact) {
314
- // Build selectors array for text annotation
315
- const selectors: any[] = [
316
- {
317
- type: 'TextQuoteSelector',
318
- exact: selector.exact,
319
- ...(selector.prefix && { prefix: selector.prefix }),
320
- ...(selector.suffix && { suffix: selector.suffix })
321
- },
322
- {
323
- type: 'TextPositionSelector',
324
- start: selector.start || 0,
325
- end: selector.end || 0
326
- }
327
- ];
328
-
329
- const annotation = await createAnnotation(
330
- rUri,
331
- motivation,
332
- selectors,
333
- []
334
- );
335
-
336
- // Focus the new annotation to trigger panel tab switch
337
- if (annotation) {
338
- if (motivation === 'highlighting' && onHighlightClickRef.current) {
339
- onHighlightClickRef.current(annotation.id);
340
- } else if (motivation === 'assessing' && onAssessmentClickRef.current) {
341
- onAssessmentClickRef.current(annotation.id);
342
- }
343
- }
344
- onRefetchAnnotationsRef.current?.();
345
- } else if (selector.type === 'SvgSelector' && selector.value) {
346
- // Image annotations use generic createAnnotation
347
- await createAnnotation(
348
- rUri,
349
- motivation,
350
- { type: 'SvgSelector', value: selector.value },
351
- []
352
- );
353
- onRefetchAnnotationsRef.current?.();
354
- } else if (selector.type === 'FragmentSelector' && selector.value) {
355
- // PDF annotations use FragmentSelector
356
- await createAnnotation(
357
- rUri,
358
- motivation,
359
- {
360
- type: 'FragmentSelector',
361
- value: selector.value,
362
- ...(selector.conformsTo && { conformsTo: selector.conformsTo })
363
- },
364
- []
365
- );
366
- onRefetchAnnotationsRef.current?.();
367
- }
368
- break;
369
-
370
- case 'commenting':
371
- if (selector.type === 'TextQuoteSelector' && selector.exact) {
372
- // Text: notify parent to open Comments Panel
373
- if (onCommentCreationRequestedRef.current) {
374
- onCommentCreationRequestedRef.current({
375
- exact: selector.exact,
376
- start: selector.start || 0,
377
- end: selector.end || 0
378
- });
379
- }
380
- } else if (selector.type === 'SvgSelector' && selector.value) {
381
- // Image: create annotation, then open panel
382
- const annotation = await createAnnotation(
383
- rUri,
384
- motivation,
385
- { type: 'SvgSelector', value: selector.value },
386
- []
387
- );
388
- if (annotation && onCommentClickRef.current) {
389
- onCommentClickRef.current(annotation.id);
390
- }
391
- onRefetchAnnotationsRef.current?.();
392
- } else if (selector.type === 'FragmentSelector' && selector.value) {
393
- // PDF: create annotation, then open panel
394
- const annotation = await createAnnotation(
395
- rUri,
396
- motivation,
397
- {
398
- type: 'FragmentSelector',
399
- value: selector.value,
400
- ...(selector.conformsTo && { conformsTo: selector.conformsTo })
401
- },
402
- []
403
- );
404
- if (annotation && onCommentClickRef.current) {
405
- onCommentClickRef.current(annotation.id);
406
- }
407
- onRefetchAnnotationsRef.current?.();
408
- }
409
- break;
410
-
411
- case 'tagging':
412
- if (selector.type === 'TextQuoteSelector' && selector.exact) {
413
- // Text: notify parent to open Tags Panel
414
- if (onTagCreationRequestedRef.current) {
415
- onTagCreationRequestedRef.current({
416
- exact: selector.exact,
417
- start: selector.start || 0,
418
- end: selector.end || 0
419
- });
420
- }
421
- } else if (selector.type === 'SvgSelector' && selector.value) {
422
- // Image: create annotation, then open panel
423
- const annotation = await createAnnotation(
424
- rUri,
425
- motivation,
426
- { type: 'SvgSelector', value: selector.value },
427
- []
428
- );
429
- if (annotation && onTagClickRef.current) {
430
- onTagClickRef.current(annotation.id);
431
- }
432
- onRefetchAnnotationsRef.current?.();
433
- } else if (selector.type === 'FragmentSelector' && selector.value) {
434
- // PDF: create annotation, then open panel
435
- const annotation = await createAnnotation(
436
- rUri,
437
- motivation,
438
- {
439
- type: 'FragmentSelector',
440
- value: selector.value,
441
- ...(selector.conformsTo && { conformsTo: selector.conformsTo })
442
- },
443
- []
444
- );
445
- if (annotation && onTagClickRef.current) {
446
- onTagClickRef.current(annotation.id);
447
- }
448
- onRefetchAnnotationsRef.current?.();
449
- }
450
- break;
451
-
452
- case 'linking':
453
- // Call onReferenceCreationRequested for text, image, and PDF selections
454
- if (onReferenceCreationRequestedRef.current) {
455
- if (selector.type === 'TextQuoteSelector' && selector.exact) {
456
- const selection = {
457
- exact: selector.exact,
458
- start: selector.start || 0,
459
- end: selector.end || 0,
460
- ...(selector.prefix && { prefix: selector.prefix }),
461
- ...(selector.suffix && { suffix: selector.suffix })
462
- };
463
- onReferenceCreationRequestedRef.current(selection);
464
- } else if (selector.type === 'SvgSelector' && selector.value) {
465
- const selection = {
466
- exact: '', // Images don't have exact text
467
- start: 0,
468
- end: 0,
469
- svgSelector: selector.value
470
- };
471
- onReferenceCreationRequestedRef.current(selection);
472
- } else if (selector.type === 'FragmentSelector' && selector.value) {
473
- const selection = {
474
- exact: '', // PDFs don't have exact text
475
- start: 0,
476
- end: 0,
477
- fragmentSelector: selector.value,
478
- ...(selector.conformsTo && { conformsTo: selector.conformsTo })
479
- };
480
- onReferenceCreationRequestedRef.current(selection);
481
- }
482
- }
483
- break;
309
+ }, [annotateMode, selectedClick, focusAnnotation]);
310
+
311
+ // Annotation click coordinator - handles panel opening and scrolling
312
+ const handleAnnotationClickEvent = useCallback(({ annotationId, motivation }: {
313
+ annotationId: string;
314
+ motivation: components['schemas']['Motivation'];
315
+ }) => {
316
+ // Find the annotation metadata
317
+ const metadata = Object.values(ANNOTATORS).find(a => a.matchesAnnotation({ motivation } as Annotation));
318
+
319
+ if (!metadata?.hasSidePanel) {
320
+ // Annotation doesn't have a side panel - let handleAnnotationClick handle it
321
+ const allAnnotations = [...highlights, ...references, ...assessments, ...comments, ...tags];
322
+ const annotation = allAnnotations.find(a => a.id === annotationId);
323
+ if (annotation) {
324
+ handleAnnotationClick(annotation);
484
325
  }
485
- } catch (err) {
486
- console.error('Failed to create annotation:', err);
326
+ return;
487
327
  }
488
- }, [rUri, createAnnotation]);
489
328
 
490
- // Quick action: Delete annotation from widget
491
- const handleDeleteAnnotationWidget = useCallback(async (annotation: Annotation) => {
492
- await handleDeleteAnnotation(annotation.id);
493
- }, [handleDeleteAnnotation]);
494
-
495
- // Memoize objects to prevent infinite re-renders
496
- const annotationsCollection = useMemo(
497
- () => ({ highlights, references, assessments, comments, tags }),
498
- [highlights, references, assessments, comments, tags]
499
- );
329
+ if (selectedClick !== 'detail') {
330
+ // Only open panels in detail mode - for other modes, let handleAnnotationClick handle it
331
+ const allAnnotations = [...highlights, ...references, ...assessments, ...comments, ...tags];
332
+ const annotation = allAnnotations.find(a => a.id === annotationId);
333
+ if (annotation) {
334
+ handleAnnotationClick(annotation);
335
+ }
336
+ return;
337
+ }
500
338
 
501
- const handlersForAnnotate = useMemo(
502
- () => ({
503
- onClick: handleAnnotationClick,
504
- ...(onAnnotationHover && { onHover: onAnnotationHover }),
505
- ...(onCommentHover && { onCommentHover })
506
- }),
507
- [handleAnnotationClick, onAnnotationHover, onCommentHover]
508
- );
339
+ // All annotations open the unified annotations panel
340
+ // The panel internally switches tabs based on the motivation → tab mapping in UnifiedAnnotationsPanel
341
+ eventBus.emit('panel:open', { panel: 'annotations', scrollToAnnotationId: annotationId, motivation });
342
+ }, [highlights, references, assessments, comments, tags, handleAnnotationClick, selectedClick]);
343
+
344
+ // Event subscriptions - Combined into single useEventSubscriptions call to prevent hook ordering issues
345
+ // IMPORTANT: All event subscriptions MUST be in a single call to maintain consistent hook order between renders
346
+ useEventSubscriptions({
347
+ // View mode
348
+ 'view:mode-toggled': handleViewModeToggle,
349
+
350
+ // Annotation cache invalidation
351
+ 'annotation:added': handleAnnotationAdded,
352
+ 'annotation:removed': handleAnnotationRemoved,
353
+ 'annotation:updated': handleAnnotationUpdated,
354
+
355
+ // Toolbar state
356
+ 'toolbar:selection-changed': handleToolbarSelectionChanged,
357
+ 'toolbar:click-changed': handleToolbarClickChanged,
358
+ 'toolbar:shape-changed': handleToolbarShapeChanged,
359
+
360
+ // Annotation clicks
361
+ 'annotation:click': handleAnnotationClickEvent,
362
+ });
509
363
 
510
- const handlersForBrowse = useMemo(
511
- () => ({
512
- onClick: handleAnnotationClick,
513
- ...(onCommentHover && { onCommentHover })
514
- }),
515
- [handleAnnotationClick, onCommentHover]
516
- );
364
+ // Prepare props for child components
365
+ // Note: These objects are created inline - React's reconciliation handles re-renders efficiently
366
+ const annotationsCollection = { highlights, references, assessments, comments, tags };
517
367
 
518
- const creationHandler = useMemo(
519
- () => ({ onCreate: handleAnnotationCreate }),
520
- [handleAnnotationCreate]
521
- );
368
+ const uiState = {
369
+ selectedMotivation,
370
+ selectedClick,
371
+ selectedShape,
372
+ hoveredAnnotationId,
373
+ scrollToAnnotationId
374
+ };
522
375
 
523
- const uiState = useMemo(
524
- () => ({
525
- selectedMotivation,
526
- selectedClick,
527
- selectedShape,
528
- ...(hoveredAnnotationId !== undefined && { hoveredAnnotationId }),
529
- ...(hoveredCommentId !== undefined && { hoveredCommentId }),
530
- ...(scrollToAnnotationId !== undefined && { scrollToAnnotationId })
531
- }),
532
- [selectedMotivation, selectedClick, selectedShape, hoveredAnnotationId, hoveredCommentId, scrollToAnnotationId]
533
- );
376
+ // Define getTargetDocumentName callback OUTSIDE the conditional
377
+ // IMPORTANT: This must be defined before the return statement to avoid hook ordering violations
378
+ const getTargetDocumentName = useCallback((documentId: string) => {
379
+ const referencedResource = references.find((a: Annotation) => getBodySource(a.body) === documentId);
380
+ return referencedResource ? getExactText(getTargetSelector(referencedResource.target)) : undefined;
381
+ }, [references]);
534
382
 
535
383
  return (
536
384
  <div ref={documentViewerRef} className="semiont-resource-viewer">
@@ -541,8 +389,6 @@ export function ResourceViewer({
541
389
  mimeType={mimeType}
542
390
  resourceUri={resource['@id']}
543
391
  annotations={annotationsCollection}
544
- handlers={handlersForAnnotate}
545
- creationHandler={creationHandler}
546
392
  uiState={uiState}
547
393
  onUIStateChange={(updates) => {
548
394
  if ('selectedMotivation' in updates) setSelectedMotivation(updates.selectedMotivation!);
@@ -550,21 +396,10 @@ export function ResourceViewer({
550
396
  if ('selectedShape' in updates) setSelectedShape(updates.selectedShape!);
551
397
  }}
552
398
  enableWidgets={true}
553
- onEntityTypeClick={(entityType) => {
554
- window.location.href = `/know?entityType=${encodeURIComponent(entityType)}`;
555
- }}
556
- onUnresolvedReferenceClick={handleAnnotationClick}
399
+ getTargetDocumentName={getTargetDocumentName}
557
400
  {...(generatingReferenceId !== undefined && { generatingReferenceId })}
558
- onDeleteAnnotation={handleDeleteAnnotationWidget}
559
401
  showLineNumbers={showLineNumbers}
560
402
  annotateMode={annotateMode}
561
- onAnnotateModeToggle={onAnnotateModeToggle}
562
- {...(onAnnotationRequested && { onAnnotationRequested })}
563
- {...(onCommentCreationRequested && { onCommentCreationRequested })}
564
- {...(onTagCreationRequested && { onTagCreationRequested })}
565
- {...(onAssessmentCreationRequested && { onAssessmentCreationRequested })}
566
- {...(onReferenceCreationRequested && { onReferenceCreationRequested })}
567
- annotators={annotators}
568
403
  />
569
404
  ) : (
570
405
  <BrowseView
@@ -572,13 +407,9 @@ export function ResourceViewer({
572
407
  mimeType={mimeType}
573
408
  resourceUri={resource['@id']}
574
409
  annotations={annotationsCollection}
575
- handlers={handlersForBrowse}
576
- {...(hoveredCommentId !== undefined && { hoveredCommentId })}
410
+ hoveredCommentId={hoveredCommentId}
577
411
  selectedClick={selectedClick}
578
- onClickChange={setSelectedClick}
579
412
  annotateMode={annotateMode}
580
- onAnnotateModeToggle={onAnnotateModeToggle}
581
- annotators={annotators}
582
413
  />
583
414
  )}
584
415
 
@@ -587,7 +418,7 @@ export function ResourceViewer({
587
418
  <PopupContainer
588
419
  isOpen={showJsonLdView}
589
420
  onClose={() => setShowJsonLdView(false)}
590
- position={jsonLdModalPosition}
421
+ position={getJsonLdModalPosition()}
591
422
  wide={true}
592
423
  >
593
424
  <JsonLdView
@@ -603,7 +434,7 @@ export function ResourceViewer({
603
434
  {/* Delete Confirmation Modal */}
604
435
  {deleteConfirmation && (() => {
605
436
  const annotation = deleteConfirmation.annotation;
606
- const metadata = Object.values(annotators).find(a => a.matchesAnnotation(annotation));
437
+ const metadata = Object.values(ANNOTATORS).find(a => a.matchesAnnotation(annotation));
607
438
  const targetSelector = getTargetSelector(annotation.target);
608
439
  const selectedText = getExactText(targetSelector);
609
440
  const motivationEmoji = metadata?.iconEmoji || '📝';
@@ -641,8 +472,8 @@ export function ResourceViewer({
641
472
  {t('deleteConfirmationCancel')}
642
473
  </button>
643
474
  <button
644
- onClick={async () => {
645
- await handleDeleteAnnotation(deleteConfirmation.annotation.id);
475
+ onClick={() => {
476
+ handleDeleteAnnotation(deleteConfirmation.annotation.id);
646
477
  setDeleteConfirmation(null);
647
478
  }}
648
479
  className="semiont-button semiont-button--danger"