@semiont/react-ui 0.4.20 → 0.4.22

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