@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
@@ -2,21 +2,22 @@
2
2
 
3
3
  import React, { useState, useRef, useEffect, useCallback, useMemo } from 'react';
4
4
  import { useTranslations } from '../../../contexts/TranslationContext';
5
- import { useEventBus } from '../../../contexts/EventBusContext';
5
+ import { useSemiont } from '../../../session/SemiontProvider';
6
+ import { useObservable } from '../../../hooks/useObservable';
6
7
  import { useEventSubscriptions } from '../../../contexts/useEventSubscription';
7
8
  import type { RouteBuilder, LinkComponentProps } from '../../../contexts/RoutingContext';
8
9
  import { AnnotateReferencesProgressWidget } from '../../AnnotateReferencesProgressWidget';
9
10
  import { ReferenceEntry } from './ReferenceEntry';
10
- import type { components, paths, Selector } from '@semiont/core';
11
- import { getTextPositionSelector, getTargetSelector } from '@semiont/api-client';
11
+ import type { components, Selector } from '@semiont/core';
12
+ import { getTextPositionSelector, getTargetSelector } from '@semiont/core';
12
13
  import { PanelHeader } from './PanelHeader';
13
14
  import './ReferencesPanel.css';
14
- import type { MarkProgress } from '@semiont/core';
15
15
 
16
- type Annotation = components['schemas']['Annotation'];
16
+ type JobProgress = components['schemas']['JobProgress'];
17
+
18
+ import type { Annotation } from '@semiont/core';
17
19
  type Motivation = components['schemas']['Motivation'];
18
- type ResponseContent<T> = T extends { responses: { 200: { content: { 'application/json': infer R } } } } ? R : never;
19
- type ReferencedBy = ResponseContent<paths['/resources/{id}/referenced-by']['get']>['referencedBy'][number];
20
+ type ReferencedBy = components['schemas']['GetReferencedByResponse']['referencedBy'][number];
20
21
 
21
22
  // Unified pending annotation type
22
23
  interface PendingAnnotation {
@@ -45,7 +46,7 @@ interface Props {
45
46
  // Generic panel props
46
47
  annotations?: Annotation[];
47
48
  isAssisting: boolean;
48
- progress: MarkProgress | null;
49
+ progress: JobProgress | null;
49
50
  annotateMode?: boolean;
50
51
  Link: React.ComponentType<LinkComponentProps>;
51
52
  routes: RouteBuilder;
@@ -86,7 +87,7 @@ export function ReferencesPanel({
86
87
  hoveredAnnotationId,
87
88
  }: Props) {
88
89
  const t = useTranslations('ReferencesPanel');
89
- const eventBus = useEventBus();
90
+ const session = useObservable(useSemiont().activeSession$);
90
91
  const [selectedEntityTypes, setSelectedEntityTypes] = useState<string[]>([]);
91
92
  const [lastAnnotationLog, setLastDetectionLog] = useState<Array<{ entityType: string; foundCount: number }> | null>(null);
92
93
  const [pendingEntityTypes, setPendingEntityTypes] = useState<string[]>([]);
@@ -202,12 +203,9 @@ export function ReferencesPanel({
202
203
  // Clear log when starting new annotation
203
204
  const handleAssist = () => {
204
205
  setLastDetectionLog(null);
205
- eventBus.get('mark:assist-request').next({
206
- motivation: 'linking',
207
- options: {
208
- entityTypes: selectedEntityTypes,
209
- includeDescriptiveReferences,
210
- },
206
+ session?.client.mark.requestAssist('linking', {
207
+ entityTypes: selectedEntityTypes,
208
+ includeDescriptiveReferences,
211
209
  });
212
210
  };
213
211
 
@@ -245,7 +243,7 @@ export function ReferencesPanel({
245
243
  const handleCreateReference = () => {
246
244
  if (pendingAnnotation) {
247
245
  const entityType = pendingEntityTypes.join(',') || undefined;
248
- eventBus.get('mark:submit').next({
246
+ session?.client.mark.submit({
249
247
  motivation: 'linking',
250
248
  selector: pendingAnnotation.selector,
251
249
  body: entityType ? [{ type: 'TextualBody', value: entityType, purpose: 'tagging' }] : [],
@@ -260,14 +258,14 @@ export function ReferencesPanel({
260
258
 
261
259
  const handleEscape = (e: KeyboardEvent) => {
262
260
  if (e.key === 'Escape') {
263
- eventBus.get('mark:cancel-pending').next(undefined);
261
+ session?.client.mark.cancelPending();
264
262
  setPendingEntityTypes([]);
265
263
  }
266
264
  };
267
265
 
268
266
  document.addEventListener('keydown', handleEscape);
269
267
  return () => document.removeEventListener('keydown', handleEscape);
270
- }, [pendingAnnotation]);
268
+ }, [pendingAnnotation, session]);
271
269
 
272
270
  return (
273
271
  <div className="semiont-panel">
@@ -312,7 +310,7 @@ export function ReferencesPanel({
312
310
  <div className="semiont-annotation-prompt__actions">
313
311
  <button
314
312
  onClick={() => {
315
- eventBus.get('mark:cancel-pending').next(undefined);
313
+ session?.client.mark.cancelPending();
316
314
  setPendingEntityTypes([]);
317
315
  }}
318
316
  className="semiont-button semiont-button--secondary"
@@ -1,8 +1,9 @@
1
1
  'use client';
2
2
 
3
3
  import { useTranslations } from '../../../contexts/TranslationContext';
4
- import { useEventBus } from '../../../contexts/EventBusContext';
5
- import { formatLocaleDisplay } from '@semiont/api-client';
4
+ import { useSemiont } from '../../../session/SemiontProvider';
5
+ import { useObservable } from '../../../hooks/useObservable';
6
+ import { formatLocaleDisplay } from '@semiont/core';
6
7
  import { resourceId as makeResourceId, type components } from '@semiont/core';
7
8
  import './ResourceInfoPanel.css';
8
9
 
@@ -47,7 +48,7 @@ export function ResourceInfoPanel({
47
48
  generator,
48
49
  }: Props) {
49
50
  const t = useTranslations('ResourceInfoPanel');
50
- const eventBus = useEventBus();
51
+ const session = useObservable(useSemiont().activeSession$);
51
52
 
52
53
  return (
53
54
  <div className="semiont-resource-info-panel">
@@ -148,7 +149,7 @@ export function ResourceInfoPanel({
148
149
  <button
149
150
  key={id}
150
151
  className="semiont-resource-info-panel__link"
151
- onClick={() => eventBus.get('browse:reference-navigate').next({ resourceId: id })}
152
+ onClick={() => session?.client.browse.navigateReference(makeResourceId(id))}
152
153
  >
153
154
  {i > 0 && ', '}{id}
154
155
  </button>
@@ -191,7 +192,7 @@ export function ResourceInfoPanel({
191
192
  {/* Clone Action */}
192
193
  <div className="semiont-resource-info-panel__action-section">
193
194
  <button
194
- onClick={() => eventBus.get('yield:clone').next(undefined)}
195
+ onClick={() => session?.client.yield.clone()}
195
196
  className="semiont-resource-button semiont-resource-button--secondary"
196
197
  >
197
198
  🔗 {t('clone')}
@@ -206,7 +207,7 @@ export function ResourceInfoPanel({
206
207
  {isArchived ? (
207
208
  <>
208
209
  <button
209
- onClick={() => eventBus.get('mark:unarchive').next({ resourceId: makeResourceId(resourceId) })}
210
+ onClick={() => session?.client.mark.unarchive(makeResourceId(resourceId))}
210
211
  className="semiont-resource-button semiont-resource-button--secondary"
211
212
  >
212
213
  📤 {t('unarchive')}
@@ -218,7 +219,7 @@ export function ResourceInfoPanel({
218
219
  ) : (
219
220
  <>
220
221
  <button
221
- onClick={() => eventBus.get('mark:archive').next({ resourceId: makeResourceId(resourceId) })}
222
+ onClick={() => session?.client.mark.archive(makeResourceId(resourceId))}
222
223
  className="semiont-resource-button semiont-resource-button--archive"
223
224
  >
224
225
  📦 {t('archive')}
@@ -1,12 +1,11 @@
1
1
  'use client';
2
2
 
3
3
  import { useTranslations } from '../../../contexts/TranslationContext';
4
- import type { components } from '@semiont/core';
5
- import { isBodyResolved } from '@semiont/api-client';
4
+ import { isBodyResolved } from '@semiont/core';
6
5
  import { getEntityTypes } from '@semiont/ontology';
7
6
  import './StatisticsPanel.css';
8
7
 
9
- type Annotation = components['schemas']['Annotation'];
8
+ import type { Annotation } from '@semiont/core';
10
9
 
11
10
  interface StatisticsPanelProps {
12
11
  highlights: Annotation[];
@@ -1,14 +1,13 @@
1
1
  'use client';
2
2
 
3
3
  import type { Ref } from 'react';
4
- import type { components } from '@semiont/core';
5
- import { getAnnotationExactText } from '@semiont/api-client';
4
+ import type { Annotation } from '@semiont/core';
5
+ import { getAnnotationExactText } from '@semiont/core';
6
6
  import { getTagCategory, getTagSchemaId } from '@semiont/ontology';
7
7
  import { getTagSchema } from '../../../lib/tag-schemas';
8
- import { useEventBus } from '../../../contexts/EventBusContext';
9
- import { useHoverEmitter } from '../../../hooks/useBeckonFlow';
10
-
11
- type Annotation = components['schemas']['Annotation'];
8
+ import { useSemiont } from '../../../session/SemiontProvider';
9
+ import { useObservable } from '../../../hooks/useObservable';
10
+ import { useHoverEmitter } from '../../../hooks/useHoverEmitter';
12
11
 
13
12
  interface TagEntryProps {
14
13
  tag: Annotation;
@@ -23,7 +22,7 @@ export function TagEntry({
23
22
  isHovered = false,
24
23
  ref,
25
24
  }: TagEntryProps) {
26
- const eventBus = useEventBus();
25
+ const session = useObservable(useSemiont().activeSession$);
27
26
  const hoverProps = useHoverEmitter(tag.id);
28
27
 
29
28
  const selectedText = getAnnotationExactText(tag);
@@ -35,7 +34,7 @@ export function TagEntry({
35
34
  <div
36
35
  ref={ref}
37
36
  onClick={() => {
38
- eventBus.get('browse:click').next({ annotationId: tag.id, motivation: tag.motivation });
37
+ session?.client.browse.click(tag.id, tag.motivation);
39
38
  }}
40
39
  {...hoverProps}
41
40
  className={`semiont-annotation-entry${isHovered ? ' semiont-annotation-pulse' : ''}`}
@@ -2,17 +2,19 @@
2
2
 
3
3
  import { useState, useEffect, useRef, useCallback, useMemo } from 'react';
4
4
  import { useTranslations } from '../../../contexts/TranslationContext';
5
- import { useEventBus } from '../../../contexts/EventBusContext';
5
+ import { useSemiont } from '../../../session/SemiontProvider';
6
+ import { useObservable } from '../../../hooks/useObservable';
6
7
  import { useEventSubscriptions } from '../../../contexts/useEventSubscription';
7
8
  import type { components, Selector } from '@semiont/core';
8
- import { getTextPositionSelector, getTargetSelector } from '@semiont/api-client';
9
+ import { getTextPositionSelector, getTargetSelector } from '@semiont/core';
9
10
  import { TagEntry } from './TagEntry';
10
11
  import { PanelHeader } from './PanelHeader';
11
12
  import { getAllTagSchemas } from '../../../lib/tag-schemas';
12
13
  import './TaggingPanel.css';
13
14
 
14
- type Annotation = components['schemas']['Annotation'];
15
+ import type { Annotation } from '@semiont/core';
15
16
  type Motivation = components['schemas']['Motivation'];
17
+ type JobProgress = components['schemas']['JobProgress'];
16
18
 
17
19
  // Unified pending annotation type
18
20
  interface PendingAnnotation {
@@ -41,15 +43,7 @@ interface TaggingPanelProps {
41
43
  annotations: Annotation[];
42
44
  annotateMode?: boolean;
43
45
  isAssisting?: boolean;
44
- progress?: {
45
- status: string;
46
- percentage?: number;
47
- currentCategory?: string;
48
- processedCategories?: number;
49
- totalCategories?: number;
50
- message?: string;
51
- requestParams?: Array<{ label: string; value: string }>;
52
- } | null;
46
+ progress?: JobProgress | null;
53
47
  pendingAnnotation: PendingAnnotation | null;
54
48
  scrollToAnnotationId?: string | null;
55
49
  onScrollCompleted?: () => void;
@@ -75,7 +69,7 @@ export function TaggingPanel({
75
69
  hoveredAnnotationId,
76
70
  }: TaggingPanelProps) {
77
71
  const t = useTranslations('TaggingPanel');
78
- const eventBus = useEventBus();
72
+ const session = useObservable(useSemiont().activeSession$);
79
73
  const [selectedSchemaId, setSelectedSchemaId] = useState<string>('legal-irac');
80
74
  const [selectedCategories, setSelectedCategories] = useState<Set<string>>(new Set());
81
75
  const [focusedAnnotationId, setFocusedAnnotationId] = useState<string | null>(null);
@@ -195,12 +189,9 @@ export function TaggingPanel({
195
189
 
196
190
  const handleAssist = () => {
197
191
  if (selectedCategories.size > 0) {
198
- eventBus.get('mark:assist-request').next({
199
- motivation: 'tagging',
200
- options: {
201
- schemaId: selectedSchemaId,
202
- categories: Array.from(selectedCategories),
203
- },
192
+ session?.client.mark.requestAssist('tagging', {
193
+ schemaId: selectedSchemaId,
194
+ categories: Array.from(selectedCategories),
204
195
  });
205
196
  setSelectedCategories(new Set()); // Reset after annotation
206
197
  }
@@ -212,13 +203,13 @@ export function TaggingPanel({
212
203
 
213
204
  const handleEscape = (e: KeyboardEvent) => {
214
205
  if (e.key === 'Escape') {
215
- eventBus.get('mark:cancel-pending').next(undefined);
206
+ session?.client.mark.cancelPending();
216
207
  }
217
208
  };
218
209
 
219
210
  document.addEventListener('keydown', handleEscape);
220
211
  return () => document.removeEventListener('keydown', handleEscape);
221
- }, [pendingAnnotation]);
212
+ }, [pendingAnnotation, session]);
222
213
 
223
214
  // Color schemes are now handled via CSS data attributes
224
215
 
@@ -274,7 +265,7 @@ export function TaggingPanel({
274
265
  className="semiont-select"
275
266
  onChange={(e) => {
276
267
  if (e.target.value && pendingAnnotation) {
277
- eventBus.get('mark:submit').next({
268
+ session?.client.mark.submit({
278
269
  motivation: 'tagging',
279
270
  selector: pendingAnnotation.selector,
280
271
  body: [
@@ -305,7 +296,7 @@ export function TaggingPanel({
305
296
  {/* Cancel button */}
306
297
  <div className="semiont-annotation-prompt__footer">
307
298
  <button
308
- onClick={() => eventBus.get('mark:cancel-pending').next(undefined)}
299
+ onClick={() => session?.client.mark.cancelPending()}
309
300
  className="semiont-button semiont-button--secondary"
310
301
  data-type="tag"
311
302
  >
@@ -2,7 +2,8 @@
2
2
 
3
3
  import React, { useState, useEffect } from 'react';
4
4
  import { useTranslations } from '../../../contexts/TranslationContext';
5
- import type { components, Selector, MarkProgress } from '@semiont/core';
5
+ import type { components, Selector } from '@semiont/core';
6
+ type JobProgress = components['schemas']['JobProgress'];
6
7
  import type { RouteBuilder, LinkComponentProps } from '../../../contexts/RoutingContext';
7
8
  import type { Annotator } from '../../../lib/annotation-registry';
8
9
  import { StatisticsPanel } from './StatisticsPanel';
@@ -13,7 +14,7 @@ import { CommentsPanel } from './CommentsPanel';
13
14
  import { TaggingPanel } from './TaggingPanel';
14
15
  import './UnifiedAnnotationsPanel.css';
15
16
 
16
- type Annotation = components['schemas']['Annotation'];
17
+ import type { Annotation } from '@semiont/core';
17
18
  type Motivation = components['schemas']['Motivation'];
18
19
  type TabKey = 'statistics' | 'reference' | 'highlight' | 'assessment' | 'comment' | 'tag';
19
20
 
@@ -47,7 +48,7 @@ interface UnifiedAnnotationsPanelProps {
47
48
 
48
49
  // Annotation assistance state (per motivation)
49
50
  assistingMotivation?: Motivation | null;
50
- progress?: MarkProgress | null;
51
+ progress?: JobProgress | null;
51
52
 
52
53
  // Unified pending annotation (for creating new annotations)
53
54
  pendingAnnotation: PendingAnnotation | null;
@@ -6,18 +6,18 @@ import { renderWithProviders } from '../../../../test-utils';
6
6
  import userEvent from '@testing-library/user-event';
7
7
  import type { components } from '@semiont/core';
8
8
 
9
- type Annotation = components['schemas']['Annotation'];
9
+ import type { Annotation } from '@semiont/core';
10
10
 
11
11
  // Mock @semiont/api-client
12
- vi.mock('@semiont/api-client', async () => {
13
- const actual = await vi.importActual('@semiont/api-client');
12
+ vi.mock('@semiont/core', async () => {
13
+ const actual = await vi.importActual('@semiont/core');
14
14
  return {
15
15
  ...actual,
16
16
  getAnnotationExactText: vi.fn(),
17
17
  };
18
18
  });
19
19
 
20
- import { getAnnotationExactText } from '@semiont/api-client';
20
+ import { getAnnotationExactText } from '@semiont/core';
21
21
  import type { MockedFunction } from 'vitest';
22
22
  import { AssessmentEntry } from '../AssessmentEntry';
23
23
 
@@ -5,10 +5,10 @@ import { render, screen, fireEvent, waitFor } from '@testing-library/react';
5
5
  import userEvent from '@testing-library/user-event';
6
6
  import '@testing-library/jest-dom';
7
7
  import { AssessmentPanel } from '../AssessmentPanel';
8
- import { EventBusProvider, useEventBus } from '../../../../contexts/EventBusContext';
9
- import type { components } from '@semiont/core';
8
+ import type { components, EventBus } from '@semiont/core';
9
+ import { createTestSemiontWrapper } from '../../../../test-utils';
10
10
 
11
- type Annotation = components['schemas']['Annotation'];
11
+ import type { Annotation } from '@semiont/core';
12
12
 
13
13
  // Composition-based event tracker
14
14
  interface TrackedEvent {
@@ -18,59 +18,27 @@ interface TrackedEvent {
18
18
 
19
19
  function createEventTracker() {
20
20
  const events: TrackedEvent[] = [];
21
-
22
- function EventTrackingWrapper({ children }: { children: React.ReactNode }) {
23
- const eventBus = useEventBus();
24
-
25
- React.useEffect(() => {
26
- const handlers: Array<() => void> = [];
27
-
28
- const trackEvent = (eventName: string) => (payload: any) => {
29
- events.push({ event: eventName, payload });
30
- };
31
-
32
- const panelEvents = ['mark:submit'] as const;
33
-
34
- panelEvents.forEach(eventName => {
35
- const handler = trackEvent(eventName);
36
- const subscription = eventBus.get(eventName).subscribe(handler);
37
- handlers.push(subscription);
38
- });
39
-
40
- return () => {
41
- handlers.forEach(sub => sub.unsubscribe());
42
- };
43
- }, [eventBus]);
44
-
45
- return <>{children}</>;
46
- }
47
-
48
21
  return {
49
- EventTrackingWrapper,
50
22
  events,
51
- clear: () => {
52
- events.length = 0;
23
+ clear: () => { events.length = 0; },
24
+ _attach(eventBus: EventBus) {
25
+ const panelEvents = ['mark:submit'] as const;
26
+ panelEvents.forEach((eventName) => {
27
+ eventBus.get(eventName).subscribe((payload: any) => {
28
+ events.push({ event: eventName, payload });
29
+ });
30
+ });
53
31
  },
54
32
  };
55
33
  }
56
34
 
57
- // Helper to render with EventBusProvider
58
35
  const renderWithEventBus = (component: React.ReactElement, tracker?: ReturnType<typeof createEventTracker>) => {
59
- if (tracker) {
60
- return render(
61
- <EventBusProvider>
62
- <tracker.EventTrackingWrapper>
63
- {component}
64
- </tracker.EventTrackingWrapper>
65
- </EventBusProvider>
66
- );
67
- }
68
-
69
- return render(
70
- <EventBusProvider>
71
- {component}
72
- </EventBusProvider>
36
+ const { SemiontWrapper, eventBus } = createTestSemiontWrapper();
37
+ if (tracker) tracker._attach(eventBus);
38
+ const Wrapper = ({ children }: { children: React.ReactNode }) => (
39
+ <SemiontWrapper>{children}</SemiontWrapper>
73
40
  );
41
+ return render(component, { wrapper: Wrapper });
74
42
  };
75
43
 
76
44
  // Mock TranslationContext
@@ -90,8 +58,8 @@ vi.mock('../../../../contexts/TranslationContext', () => ({
90
58
  }));
91
59
 
92
60
  // Mock @semiont/api-client utilities
93
- vi.mock('@semiont/api-client', async () => {
94
- const actual = await vi.importActual('@semiont/api-client');
61
+ vi.mock('@semiont/core', async () => {
62
+ const actual = await vi.importActual('@semiont/core');
95
63
  return {
96
64
  ...actual,
97
65
  getTextPositionSelector: vi.fn(),
@@ -108,8 +76,7 @@ vi.mock('../AssessmentEntry', () => ({
108
76
  ),
109
77
  }));
110
78
 
111
- // Mock AssistSection component - it will internally use the mocked useEventBus
112
- // Just render a simplified version
79
+ // Mock AssistSection component just render a simplified version.
113
80
  vi.mock('../AssistSection', () => ({
114
81
  AssistSection: ({ annotationType, isAssisting }: any) => (
115
82
  <div data-testid="detect-section">
@@ -119,8 +86,7 @@ vi.mock('../AssistSection', () => ({
119
86
  ),
120
87
  }));
121
88
 
122
- import { getTextPositionSelector, getTargetSelector } from '@semiont/api-client';
123
-
89
+ import { getTextPositionSelector, getTargetSelector } from '@semiont/core';
124
90
  const mockGetTextPositionSelector = getTextPositionSelector as MockedFunction<typeof getTextPositionSelector>;
125
91
  const mockGetTargetSelector = getTargetSelector as MockedFunction<typeof getTargetSelector>;
126
92
 
@@ -380,7 +346,7 @@ describe('AssessmentPanel Component', () => {
380
346
  expect(tracker.events.some(e =>
381
347
  e.event === 'mark:submit' &&
382
348
  e.payload?.motivation === 'assessing' &&
383
- e.payload?.body?.[0]?.value === 'My assessment'
349
+ e.payload?.body?.value === 'My assessment'
384
350
  )).toBe(true);
385
351
  });
386
352
  });
@@ -421,8 +387,7 @@ describe('AssessmentPanel Component', () => {
421
387
  expect(tracker.events.some(e =>
422
388
  e.event === 'mark:submit' &&
423
389
  e.payload?.motivation === 'assessing' &&
424
- Array.isArray(e.payload?.body) &&
425
- e.payload.body.length === 0
390
+ e.payload?.body === undefined
426
391
  )).toBe(true);
427
392
  });
428
393
  });