@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.
- package/README.md +8 -5
- package/dist/{PdfAnnotationCanvas.client-CHDCGQBR.mjs → PdfAnnotationCanvas.client-5QESNO5H.mjs} +13 -16
- package/dist/PdfAnnotationCanvas.client-5QESNO5H.mjs.map +1 -0
- package/dist/TranslationManager-9Xj3MIWQ.d.mts +16 -0
- package/dist/chunk-4NOUO3W6.mjs +7788 -0
- package/dist/chunk-4NOUO3W6.mjs.map +1 -0
- package/dist/index.d.mts +212 -1206
- package/dist/index.mjs +3332 -13712
- package/dist/index.mjs.map +1 -1
- package/dist/test-utils.d.mts +48 -21
- package/dist/test-utils.mjs +2505 -87
- package/dist/test-utils.mjs.map +1 -1
- package/package.json +2 -2
- package/src/components/AnnotateReferencesProgressWidget.tsx +21 -28
- package/src/components/CodeMirrorRenderer.tsx +12 -12
- package/src/components/LiveRegion.tsx +1 -2
- package/src/components/StatusDisplay.tsx +42 -16
- package/src/components/Toolbar.tsx +4 -4
- package/src/components/__tests__/AnnotateReferencesProgressWidget.test.tsx +34 -20
- package/src/components/__tests__/StatusDisplay.test.tsx +50 -65
- package/src/components/__tests__/Toolbar.test.tsx +4 -4
- package/src/components/annotation/AnnotateToolbar.tsx +8 -9
- package/src/components/annotation/__tests__/AnnotateToolbar.test.tsx +31 -77
- package/src/components/annotation-popups/JsonLdView.tsx +1 -2
- package/src/components/annotation-popups/__tests__/JsonLdView.test.tsx +1 -2
- package/src/components/image-annotation/AnnotationOverlay.tsx +15 -18
- package/src/components/image-annotation/SvgDrawingCanvas.tsx +12 -17
- package/src/components/modals/ConfigureGenerationStep.tsx +1 -2
- package/src/components/modals/PermissionDeniedModal.tsx +11 -11
- package/src/components/modals/ReferenceWizardModal.tsx +14 -18
- package/src/components/modals/ResourceSearchModal.tsx +12 -8
- package/src/components/modals/SearchModal.tsx +11 -6
- package/src/components/modals/SearchResultsStep.tsx +1 -3
- package/src/components/modals/SessionExpiredModal.tsx +11 -11
- package/src/components/modals/__tests__/PermissionDeniedModal.test.tsx +7 -7
- package/src/components/modals/__tests__/ResourceSearchModal.test.tsx +10 -8
- package/src/components/modals/__tests__/SearchModal.accessibility.test.tsx +6 -2
- package/src/components/modals/__tests__/SearchModal.basic.test.tsx +6 -2
- package/src/components/modals/__tests__/SearchModal.keyboard.test.tsx +6 -2
- package/src/components/modals/__tests__/SearchModal.search-wiring.test.tsx +10 -7
- package/src/components/modals/__tests__/SearchModal.visual.test.tsx +6 -2
- package/src/components/modals/__tests__/SessionExpiredModal.test.tsx +5 -5
- package/src/components/navigation/CollapsibleResourceNavigation.tsx +10 -10
- package/src/components/navigation/ObservableLink.tsx +6 -6
- package/src/components/navigation/SimpleNavigation.tsx +4 -4
- package/src/components/navigation/__tests__/ObservableLink.test.tsx +4 -4
- package/src/components/navigation/__tests__/SimpleNavigation.test.tsx +4 -4
- package/src/components/pdf-annotation/PdfAnnotationCanvas.tsx +15 -18
- package/src/components/pdf-annotation/__tests__/PdfAnnotationCanvas.test.tsx +1 -2
- package/src/components/resource/AnnotateView.tsx +8 -10
- package/src/components/resource/AnnotationHistory.tsx +9 -12
- package/src/components/resource/BrowseView.tsx +11 -8
- package/src/components/resource/ResourceViewer.tsx +22 -34
- package/src/components/resource/__tests__/AnnotationHistory.test.tsx +54 -192
- package/src/components/resource/__tests__/BrowseView.test.tsx +38 -87
- package/src/components/resource/__tests__/ResourceViewer.mode-switch.test.tsx +41 -31
- package/src/components/resource/__tests__/event-formatting.test.ts +6 -2
- package/src/components/resource/event-formatting.ts +2 -3
- package/src/components/resource/panels/AssessmentEntry.tsx +7 -8
- package/src/components/resource/panels/AssessmentPanel.tsx +21 -17
- package/src/components/resource/panels/AssistSection.tsx +15 -21
- package/src/components/resource/panels/CollaborationPanel.tsx +29 -7
- package/src/components/resource/panels/CommentEntry.tsx +7 -8
- package/src/components/resource/panels/CommentsPanel.tsx +11 -13
- package/src/components/resource/panels/HighlightEntry.tsx +7 -8
- package/src/components/resource/panels/HighlightPanel.tsx +12 -13
- package/src/components/resource/panels/ReferenceEntry.tsx +13 -15
- package/src/components/resource/panels/ReferencesPanel.tsx +17 -19
- package/src/components/resource/panels/ResourceInfoPanel.tsx +8 -7
- package/src/components/resource/panels/StatisticsPanel.tsx +2 -3
- package/src/components/resource/panels/TagEntry.tsx +7 -8
- package/src/components/resource/panels/TaggingPanel.tsx +14 -23
- package/src/components/resource/panels/UnifiedAnnotationsPanel.tsx +4 -3
- package/src/components/resource/panels/__tests__/AssessmentEntry.test.tsx +4 -4
- package/src/components/resource/panels/__tests__/AssessmentPanel.test.tsx +22 -57
- package/src/components/resource/panels/__tests__/CollaborationPanel.test.tsx +51 -20
- package/src/components/resource/panels/__tests__/CommentEntry.test.tsx +4 -4
- package/src/components/resource/panels/__tests__/CommentsPanel.test.tsx +22 -61
- package/src/components/resource/panels/__tests__/HighlightEntry.test.tsx +4 -4
- package/src/components/resource/panels/__tests__/HighlightPanel.annotationProgress.test.tsx +1 -2
- package/src/components/resource/panels/__tests__/ReferenceEntry.test.tsx +7 -8
- package/src/components/resource/panels/__tests__/ReferencesPanel.observable-flow.test.tsx +153 -0
- package/src/components/resource/panels/__tests__/ReferencesPanel.test.tsx +51 -106
- package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +28 -53
- package/src/components/resource/panels/__tests__/StatisticsPanel.test.tsx +3 -3
- package/src/components/resource/panels/__tests__/TagEntry.test.tsx +4 -4
- package/src/components/resource/panels/__tests__/TaggingPanel.test.tsx +19 -52
- package/src/components/settings/SettingsPanel.tsx +9 -9
- package/src/components/settings/__tests__/SettingsPanel.test.tsx +15 -15
- package/src/features/admin-devops/components/AdminDevOpsPage.tsx +1 -2
- package/src/features/admin-exchange/components/AdminExchangePage.tsx +1 -1
- package/src/features/admin-exchange/components/ImportCard.tsx +2 -7
- package/src/features/admin-security/components/AdminSecurityPage.tsx +1 -2
- package/src/features/admin-users/components/AdminUsersPage.tsx +1 -1
- package/src/features/moderate-entity-tags/components/EntityTagsPage.tsx +1 -2
- package/src/features/moderate-recent/components/RecentDocumentsPage.tsx +1 -2
- package/src/features/moderate-tag-schemas/components/TagSchemasPage.tsx +1 -1
- package/src/features/moderation-linked-data/components/LinkedDataPage.tsx +1 -1
- package/src/features/resource-compose/__tests__/ResourceComposePage.test.tsx +5 -3
- package/src/features/resource-compose/components/ResourceComposePage.tsx +6 -22
- package/src/features/resource-discovery/__tests__/ResourceDiscoveryPage.test.tsx +4 -3
- package/src/features/resource-discovery/components/ResourceCard.tsx +1 -2
- package/src/features/resource-discovery/components/ResourceDiscoveryPage.tsx +3 -4
- package/src/features/resource-viewer/__tests__/ResourceViewerPage.test.tsx +37 -45
- package/src/features/resource-viewer/components/ResourceViewerPage.tsx +129 -197
- package/dist/KnowledgeBaseSessionContext-BNNunwzO.d.mts +0 -175
- package/dist/PdfAnnotationCanvas.client-CHDCGQBR.mjs.map +0 -1
- package/dist/chunk-OZICDVH7.mjs +0 -62
- package/dist/chunk-OZICDVH7.mjs.map +0 -1
- package/dist/chunk-R4CCMFJH.mjs +0 -877
- package/dist/chunk-R4CCMFJH.mjs.map +0 -1
- package/dist/chunk-VN5NY4SN.mjs +0 -200
- package/dist/chunk-VN5NY4SN.mjs.map +0 -1
- package/src/components/modals/ProposeEntitiesModal.tsx +0 -179
- package/src/components/modals/__tests__/ProposeEntitiesModal.test.tsx +0 -129
- package/src/features/resource-viewer/__tests__/AnnotationCreationPending.test.tsx +0 -323
- package/src/features/resource-viewer/__tests__/AnnotationDeletionIntegration.test.tsx +0 -245
- package/src/features/resource-viewer/__tests__/AnnotationProgressDismissal.test.tsx +0 -303
- package/src/features/resource-viewer/__tests__/BindFlowIntegration.test.tsx +0 -150
- package/src/features/resource-viewer/__tests__/DetectionFlowBug.test.tsx +0 -243
- package/src/features/resource-viewer/__tests__/DetectionFlowIntegration.test.tsx +0 -383
- package/src/features/resource-viewer/__tests__/ResourceMutations.test.tsx +0 -299
- package/src/features/resource-viewer/__tests__/ToastNotifications.test.tsx +0 -186
- package/src/features/resource-viewer/__tests__/YieldFlowIntegration.test.tsx +0 -429
- 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 {
|
|
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,
|
|
11
|
-
import { getTextPositionSelector, getTargetSelector } from '@semiont/
|
|
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
|
|
16
|
+
type JobProgress = components['schemas']['JobProgress'];
|
|
17
|
+
|
|
18
|
+
import type { Annotation } from '@semiont/core';
|
|
17
19
|
type Motivation = components['schemas']['Motivation'];
|
|
18
|
-
type
|
|
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:
|
|
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
|
|
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
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
5
|
-
import {
|
|
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
|
|
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={() =>
|
|
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={() =>
|
|
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={() =>
|
|
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={() =>
|
|
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
|
|
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
|
|
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 {
|
|
5
|
-
import { getAnnotationExactText } from '@semiont/
|
|
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 {
|
|
9
|
-
import {
|
|
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
|
|
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
|
-
|
|
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 {
|
|
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/
|
|
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
|
|
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
|
|
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
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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={() =>
|
|
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
|
|
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
|
|
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?:
|
|
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
|
|
9
|
+
import type { Annotation } from '@semiont/core';
|
|
10
10
|
|
|
11
11
|
// Mock @semiont/api-client
|
|
12
|
-
vi.mock('@semiont/
|
|
13
|
-
const actual = await vi.importActual('@semiont/
|
|
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/
|
|
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 {
|
|
9
|
-
import
|
|
8
|
+
import type { components, EventBus } from '@semiont/core';
|
|
9
|
+
import { createTestSemiontWrapper } from '../../../../test-utils';
|
|
10
10
|
|
|
11
|
-
type 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
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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/
|
|
94
|
-
const actual = await vi.importActual('@semiont/
|
|
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
|
|
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/
|
|
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?.
|
|
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
|
-
|
|
425
|
-
e.payload.body.length === 0
|
|
390
|
+
e.payload?.body === undefined
|
|
426
391
|
)).toBe(true);
|
|
427
392
|
});
|
|
428
393
|
});
|