@semiont/react-ui 0.2.33-build.78 → 0.2.33-build.80
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/EventBusContext-7GvDyO0d.d.mts +414 -0
- package/dist/{PdfAnnotationCanvas.client-ADC4FFSE.mjs → PdfAnnotationCanvas.client-RAJRPQLU.mjs} +42 -27
- package/dist/PdfAnnotationCanvas.client-RAJRPQLU.mjs.map +1 -0
- package/dist/{ar-RNNSPLQB.mjs → ar-4ZEORRW2.mjs} +8 -4
- package/dist/ar-4ZEORRW2.mjs.map +1 -0
- package/dist/{bn-S2CDL7EC.mjs → bn-SEDE5BQJ.mjs} +8 -4
- package/dist/bn-SEDE5BQJ.mjs.map +1 -0
- package/dist/{chunk-UDX2Q35T.mjs → chunk-D7NBW4RV.mjs} +8 -4
- package/dist/chunk-D7NBW4RV.mjs.map +1 -0
- package/dist/{chunk-35LLVRFK.mjs → chunk-ZR4ZV2LY.mjs} +206 -146
- package/dist/chunk-ZR4ZV2LY.mjs.map +1 -0
- package/dist/{cs-RSV675WU.mjs → cs-7W4WF5WD.mjs} +8 -4
- package/dist/cs-7W4WF5WD.mjs.map +1 -0
- package/dist/{da-CHXNPWJC.mjs → da-75XGBCBK.mjs} +8 -4
- package/dist/da-75XGBCBK.mjs.map +1 -0
- package/dist/{de-KPEZ53D4.mjs → de-ODJVFLHM.mjs} +8 -4
- package/dist/de-ODJVFLHM.mjs.map +1 -0
- package/dist/{el-MW2BME5T.mjs → el-C4PM4WB3.mjs} +8 -4
- package/dist/el-C4PM4WB3.mjs.map +1 -0
- package/dist/{en-EVMIX24Y.mjs → en-KJCJQ4OO.mjs} +2 -2
- package/dist/{es-HQ24NYS3.mjs → es-WD33R7QL.mjs} +8 -4
- package/dist/es-WD33R7QL.mjs.map +1 -0
- package/dist/{fa-W34LRLHG.mjs → fa-2BP6V56P.mjs} +8 -4
- package/dist/fa-2BP6V56P.mjs.map +1 -0
- package/dist/{fi-3U44IGOA.mjs → fi-USRRW24J.mjs} +8 -4
- package/dist/fi-USRRW24J.mjs.map +1 -0
- package/dist/{fr-N7DKX6NN.mjs → fr-EC5S6WVF.mjs} +8 -4
- package/dist/fr-EC5S6WVF.mjs.map +1 -0
- package/dist/{he-CS4WRXN3.mjs → he-7TBVIKAA.mjs} +8 -4
- package/dist/he-7TBVIKAA.mjs.map +1 -0
- package/dist/{hi-GJDY46KA.mjs → hi-FO4VIZLA.mjs} +8 -4
- package/dist/hi-FO4VIZLA.mjs.map +1 -0
- package/dist/{id-WAEZJK2Y.mjs → id-7U7GGVWY.mjs} +8 -4
- package/dist/id-7U7GGVWY.mjs.map +1 -0
- package/dist/index.css +123 -85
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +699 -529
- package/dist/index.mjs +4291 -3491
- package/dist/index.mjs.map +1 -1
- package/dist/{it-VDNDMZPU.mjs → it-Y4OPL6I2.mjs} +8 -4
- package/dist/it-Y4OPL6I2.mjs.map +1 -0
- package/dist/{ja-5PEH56J5.mjs → ja-PK7SQL55.mjs} +8 -4
- package/dist/ja-PK7SQL55.mjs.map +1 -0
- package/dist/{ko-JYPL3WVA.mjs → ko-L25PXMYD.mjs} +8 -4
- package/dist/ko-L25PXMYD.mjs.map +1 -0
- package/dist/{ms-5PZVW76T.mjs → ms-STH777QM.mjs} +8 -4
- package/dist/ms-STH777QM.mjs.map +1 -0
- package/dist/{nl-YXES36KM.mjs → nl-Y7LECDDR.mjs} +8 -4
- package/dist/nl-Y7LECDDR.mjs.map +1 -0
- package/dist/{no-XRA2UCQD.mjs → no-KEKCEWU6.mjs} +8 -4
- package/dist/no-KEKCEWU6.mjs.map +1 -0
- package/dist/{pl-WH6LJA5G.mjs → pl-7A7OC75O.mjs} +8 -4
- package/dist/pl-7A7OC75O.mjs.map +1 -0
- package/dist/{pt-7GAG57BM.mjs → pt-35HTM7RA.mjs} +8 -4
- package/dist/pt-35HTM7RA.mjs.map +1 -0
- package/dist/{ro-BTDDRB7N.mjs → ro-VAWL5KQA.mjs} +8 -4
- package/dist/ro-VAWL5KQA.mjs.map +1 -0
- package/dist/{sv-7V5C2IT4.mjs → sv-7ZK5EQEB.mjs} +8 -4
- package/dist/sv-7ZK5EQEB.mjs.map +1 -0
- package/dist/test-utils.d.mts +18 -8
- package/dist/test-utils.mjs +36 -14
- package/dist/test-utils.mjs.map +1 -1
- package/dist/{th-LPKYLBX5.mjs → th-UDWZ4X34.mjs} +8 -4
- package/dist/th-UDWZ4X34.mjs.map +1 -0
- package/dist/{tr-DU4RQL4M.mjs → tr-4WMPK3UX.mjs} +8 -4
- package/dist/tr-4WMPK3UX.mjs.map +1 -0
- package/dist/{uk-36UHTDDI.mjs → uk-SSLASQYJ.mjs} +8 -4
- package/dist/uk-SSLASQYJ.mjs.map +1 -0
- package/dist/{vi-GDHOUZDH.mjs → vi-IF42Z5PU.mjs} +8 -4
- package/dist/vi-IF42Z5PU.mjs.map +1 -0
- package/dist/{zh-TYUID4XZ.mjs → zh-HRQTNTAI.mjs} +8 -4
- package/dist/zh-HRQTNTAI.mjs.map +1 -0
- package/package.json +8 -2
- package/src/components/CodeMirrorRenderer.tsx +66 -93
- package/src/components/DetectionProgressWidget.tsx +16 -5
- package/src/components/LiveRegion.tsx +18 -18
- package/src/components/ResizeHandle.tsx +10 -4
- package/src/components/SessionTimer.tsx +2 -2
- package/src/components/Toolbar.tsx +18 -9
- package/src/components/__tests__/SessionTimer.test.tsx +9 -9
- package/src/components/annotation/AnnotateToolbar.tsx +17 -15
- package/src/components/annotation/__tests__/AnnotateToolbar.test.tsx +165 -63
- package/src/components/annotation/annotation-entries.css +10 -0
- package/src/components/annotation-popups/JsonLdView.tsx +8 -2
- package/src/components/image-annotation/AnnotationOverlay.tsx +42 -22
- package/src/components/image-annotation/SvgDrawingCanvas.tsx +27 -30
- package/src/components/layout/__tests__/LeftSidebar.test.tsx +12 -33
- package/src/components/layout/__tests__/PageLayout.test.tsx +37 -32
- package/src/components/layout/__tests__/UnifiedHeader.test.tsx +21 -40
- package/src/components/modals/ResourceSearchModal.tsx +2 -2
- package/src/components/modals/SearchModal.tsx +1 -1
- package/src/components/navigation/CollapsibleResourceNavigation.tsx +14 -9
- package/src/components/navigation/NavigationTabs.css +36 -24
- package/src/components/navigation/ObservableLink.tsx +91 -0
- package/src/components/navigation/SimpleNavigation.tsx +20 -16
- package/src/components/navigation/SortableResourceTab.tsx +11 -5
- package/src/components/pdf-annotation/PdfAnnotationCanvas.tsx +51 -26
- package/src/components/pdf-annotation/__tests__/PdfAnnotationCanvas.test.tsx +28 -22
- package/src/components/resource/AnnotateView.tsx +64 -138
- package/src/components/resource/AnnotationHistory.tsx +12 -13
- package/src/components/resource/BrowseView.tsx +89 -177
- package/src/components/resource/HistoryEvent.tsx +16 -11
- package/src/components/resource/ResourceViewer.tsx +201 -370
- package/src/components/resource/__tests__/BrowseView.test.tsx +631 -0
- package/src/components/resource/__tests__/ResourceViewer.mode-switch.test.tsx +231 -0
- package/src/components/resource/event-formatting.ts +316 -0
- package/src/components/resource/panels/AssessmentEntry.tsx +25 -33
- package/src/components/resource/panels/AssessmentPanel.tsx +137 -31
- package/src/components/resource/panels/CollaborationPanel.tsx +20 -13
- package/src/components/resource/panels/CommentEntry.tsx +38 -32
- package/src/components/resource/panels/CommentsPanel.tsx +153 -31
- package/src/components/resource/panels/DetectSection.css +36 -1
- package/src/components/resource/panels/DetectSection.tsx +38 -10
- package/src/components/resource/panels/HighlightEntry.tsx +25 -33
- package/src/components/resource/panels/HighlightPanel.tsx +100 -25
- package/src/components/resource/panels/ReferenceEntry.tsx +61 -75
- package/src/components/resource/panels/ReferencesPanel.tsx +166 -49
- package/src/components/resource/panels/ResourceInfoPanel.tsx +47 -48
- package/src/components/resource/panels/StatisticsPanel.tsx +9 -19
- package/src/components/resource/panels/TagEntry.tsx +25 -33
- package/src/components/resource/panels/TaggingPanel.tsx +141 -25
- package/src/components/resource/panels/UnifiedAnnotationsPanel.tsx +46 -101
- package/src/components/resource/panels/__tests__/AssessmentPanel.test.tsx +566 -0
- package/src/components/resource/panels/__tests__/CommentEntry.test.tsx +86 -78
- package/src/components/resource/panels/__tests__/CommentsPanel.test.tsx +146 -141
- package/src/components/resource/panels/__tests__/DetectSection.test.tsx +480 -0
- package/src/components/resource/panels/__tests__/HighlightPanel.detectionProgress.test.tsx +362 -0
- package/src/components/resource/panels/__tests__/ReferencesPanel.test.tsx +228 -103
- package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +117 -61
- package/src/components/resource/panels/__tests__/TaggingPanel.test.tsx +586 -0
- package/src/components/settings/SettingsPanel.tsx +15 -12
- package/src/features/admin-devops/__tests__/AdminDevOpsPage.test.tsx +1 -46
- package/src/features/admin-devops/components/AdminDevOpsPage.tsx +0 -9
- package/src/features/admin-security/__tests__/AdminSecurityPage.test.tsx +0 -3
- package/src/features/admin-security/components/AdminSecurityPage.tsx +0 -9
- package/src/features/admin-users/__tests__/AdminUsersPage.test.tsx +0 -3
- package/src/features/admin-users/components/AdminUsersPage.tsx +0 -9
- package/src/features/moderate-entity-tags/__tests__/EntityTagsPage.test.tsx +0 -3
- package/src/features/moderate-entity-tags/components/EntityTagsPage.tsx +1 -9
- package/src/features/moderate-recent/__tests__/RecentDocumentsPage.test.tsx +0 -32
- package/src/features/moderate-recent/components/RecentDocumentsPage.tsx +1 -9
- package/src/features/moderate-tag-schemas/__tests__/TagSchemasPage.test.tsx +0 -32
- package/src/features/moderate-tag-schemas/components/TagSchemasPage.tsx +1 -9
- package/src/features/resource-compose/__tests__/ResourceComposePage.test.tsx +51 -54
- package/src/features/resource-compose/components/ResourceComposePage.tsx +3 -13
- package/src/features/resource-discovery/__tests__/ResourceDiscoveryPage.test.tsx +39 -45
- package/src/features/resource-discovery/components/ResourceDiscoveryPage.tsx +16 -27
- package/src/features/resource-viewer/__tests__/AnnotationDeletionIntegration.test.tsx +231 -0
- package/src/features/resource-viewer/__tests__/DetectionFlowBug.test.tsx +234 -0
- package/src/features/resource-viewer/__tests__/DetectionFlowIntegration.test.tsx +388 -0
- package/src/features/resource-viewer/__tests__/DetectionProgressDismissal.test.tsx +318 -0
- package/src/features/resource-viewer/__tests__/GenerationFlowIntegration.test.tsx +504 -0
- package/src/features/resource-viewer/__tests__/ResourceViewerPage.test.tsx +145 -91
- package/src/features/resource-viewer/__tests__/detection-progress-flow.test.tsx +322 -0
- package/src/features/resource-viewer/components/ResourceViewerPage.tsx +325 -476
- package/src/styles/motivations/motivation-assessment.css +28 -0
- package/src/styles/patterns/panel-helpers.css +26 -0
- package/translations/ar.json +7 -3
- package/translations/bn.json +7 -3
- package/translations/cs.json +7 -3
- package/translations/da.json +7 -3
- package/translations/de.json +7 -3
- package/translations/el.json +7 -3
- package/translations/en.json +7 -3
- package/translations/es.json +7 -3
- package/translations/fa.json +7 -3
- package/translations/fi.json +7 -3
- package/translations/fr.json +7 -3
- package/translations/he.json +7 -3
- package/translations/hi.json +7 -3
- package/translations/id.json +7 -3
- package/translations/it.json +7 -3
- package/translations/ja.json +7 -3
- package/translations/ko.json +7 -3
- package/translations/ms.json +7 -3
- package/translations/nl.json +7 -3
- package/translations/no.json +7 -3
- package/translations/pl.json +7 -3
- package/translations/pt.json +7 -3
- package/translations/ro.json +7 -3
- package/translations/sv.json +7 -3
- package/translations/th.json +7 -3
- package/translations/tr.json +7 -3
- package/translations/uk.json +7 -3
- package/translations/vi.json +7 -3
- package/translations/zh.json +7 -3
- package/dist/PdfAnnotationCanvas.client-ADC4FFSE.mjs.map +0 -1
- package/dist/TranslationManager-Co_5fSxl.d.mts +0 -118
- package/dist/ar-RNNSPLQB.mjs.map +0 -1
- package/dist/bn-S2CDL7EC.mjs.map +0 -1
- package/dist/chunk-35LLVRFK.mjs.map +0 -1
- package/dist/chunk-UDX2Q35T.mjs.map +0 -1
- package/dist/cs-RSV675WU.mjs.map +0 -1
- package/dist/da-CHXNPWJC.mjs.map +0 -1
- package/dist/de-KPEZ53D4.mjs.map +0 -1
- package/dist/el-MW2BME5T.mjs.map +0 -1
- package/dist/es-HQ24NYS3.mjs.map +0 -1
- package/dist/fa-W34LRLHG.mjs.map +0 -1
- package/dist/fi-3U44IGOA.mjs.map +0 -1
- package/dist/fr-N7DKX6NN.mjs.map +0 -1
- package/dist/he-CS4WRXN3.mjs.map +0 -1
- package/dist/hi-GJDY46KA.mjs.map +0 -1
- package/dist/id-WAEZJK2Y.mjs.map +0 -1
- package/dist/it-VDNDMZPU.mjs.map +0 -1
- package/dist/ja-5PEH56J5.mjs.map +0 -1
- package/dist/ko-JYPL3WVA.mjs.map +0 -1
- package/dist/ms-5PZVW76T.mjs.map +0 -1
- package/dist/nl-YXES36KM.mjs.map +0 -1
- package/dist/no-XRA2UCQD.mjs.map +0 -1
- package/dist/pl-WH6LJA5G.mjs.map +0 -1
- package/dist/pt-7GAG57BM.mjs.map +0 -1
- package/dist/ro-BTDDRB7N.mjs.map +0 -1
- package/dist/sv-7V5C2IT4.mjs.map +0 -1
- package/dist/th-LPKYLBX5.mjs.map +0 -1
- package/dist/tr-DU4RQL4M.mjs.map +0 -1
- package/dist/uk-36UHTDDI.mjs.map +0 -1
- package/dist/vi-GDHOUZDH.mjs.map +0 -1
- package/dist/zh-TYUID4XZ.mjs.map +0 -1
- /package/dist/{en-EVMIX24Y.mjs.map → en-KJCJQ4OO.mjs.map} +0 -0
|
@@ -320,6 +320,7 @@
|
|
|
320
320
|
display: flex;
|
|
321
321
|
flex-direction: column;
|
|
322
322
|
gap: 0.5rem;
|
|
323
|
+
position: relative;
|
|
323
324
|
}
|
|
324
325
|
|
|
325
326
|
.semiont-detection-progress__message {
|
|
@@ -329,13 +330,47 @@
|
|
|
329
330
|
font-size: 0.875rem;
|
|
330
331
|
color: var(--semiont-color-gray-900);
|
|
331
332
|
font-weight: 500;
|
|
332
|
-
padding: 0.75rem 1rem;
|
|
333
|
+
padding: 0.75rem 2.5rem 0.75rem 1rem; /* Extra right padding for close button */
|
|
333
334
|
border-radius: 0.5rem;
|
|
334
335
|
background: linear-gradient(135deg, rgba(59, 130, 246, 0.15), rgba(37, 99, 235, 0.15));
|
|
335
336
|
border: 2px solid rgba(59, 130, 246, 0.3);
|
|
336
337
|
animation: semiont-detection-pulse 2s ease-in-out infinite;
|
|
337
338
|
}
|
|
338
339
|
|
|
340
|
+
.semiont-detection-progress__close {
|
|
341
|
+
position: absolute;
|
|
342
|
+
top: 0.5rem;
|
|
343
|
+
right: 0.5rem;
|
|
344
|
+
width: 1.5rem;
|
|
345
|
+
height: 1.5rem;
|
|
346
|
+
border: none;
|
|
347
|
+
background-color: rgba(0, 0, 0, 0.1);
|
|
348
|
+
color: var(--semiont-color-gray-700);
|
|
349
|
+
font-size: 1.25rem;
|
|
350
|
+
line-height: 1;
|
|
351
|
+
border-radius: 0.25rem;
|
|
352
|
+
cursor: pointer;
|
|
353
|
+
display: flex;
|
|
354
|
+
align-items: center;
|
|
355
|
+
justify-content: center;
|
|
356
|
+
transition: all 0.2s ease;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
.semiont-detection-progress__close:hover {
|
|
360
|
+
background-color: rgba(0, 0, 0, 0.2);
|
|
361
|
+
color: var(--semiont-color-gray-900);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
[data-theme="dark"] .semiont-detection-progress__close {
|
|
365
|
+
background-color: rgba(255, 255, 255, 0.1);
|
|
366
|
+
color: var(--semiont-color-gray-300);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
[data-theme="dark"] .semiont-detection-progress__close:hover {
|
|
370
|
+
background-color: rgba(255, 255, 255, 0.2);
|
|
371
|
+
color: var(--semiont-color-gray-100);
|
|
372
|
+
}
|
|
373
|
+
|
|
339
374
|
[data-theme="dark"] .semiont-detection-progress__message {
|
|
340
375
|
color: var(--semiont-color-gray-100);
|
|
341
376
|
background: linear-gradient(135deg, rgba(59, 130, 246, 0.25), rgba(37, 99, 235, 0.25));
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import { useState, useEffect } from 'react';
|
|
4
4
|
import { useTranslations } from '../../../contexts/TranslationContext';
|
|
5
|
+
import { useEventBus } from '../../../contexts/EventBusContext';
|
|
6
|
+
import type { Motivation } from '@semiont/api-client';
|
|
5
7
|
import './DetectSection.css';
|
|
6
8
|
|
|
7
9
|
interface DetectSectionProps {
|
|
@@ -13,7 +15,6 @@ interface DetectSectionProps {
|
|
|
13
15
|
message?: string;
|
|
14
16
|
requestParams?: Array<{ label: string; value: string }>;
|
|
15
17
|
} | null | undefined;
|
|
16
|
-
onDetect: (instructions?: string, tone?: string, density?: number) => void | Promise<void>;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
// Color schemes are now handled via CSS data attributes
|
|
@@ -26,17 +27,20 @@ interface DetectSectionProps {
|
|
|
26
27
|
* - Optional tone selector (for comments)
|
|
27
28
|
* - Detect button with sparkle animation
|
|
28
29
|
* - Progress display during detection
|
|
30
|
+
*
|
|
31
|
+
* @emits detection:start - Start detection for annotation type. Payload: { motivation: Motivation, options: { instructions?: string, tone?: string, density?: number } }
|
|
29
32
|
*/
|
|
30
33
|
export function DetectSection({
|
|
31
34
|
annotationType,
|
|
32
35
|
isDetecting,
|
|
33
36
|
detectionProgress,
|
|
34
|
-
onDetect
|
|
35
37
|
}: DetectSectionProps) {
|
|
38
|
+
|
|
36
39
|
const panelName = annotationType === 'highlight' ? 'HighlightPanel' :
|
|
37
40
|
annotationType === 'assessment' ? 'AssessmentPanel' :
|
|
38
41
|
'CommentsPanel';
|
|
39
42
|
const t = useTranslations(panelName);
|
|
43
|
+
const eventBus = useEventBus();
|
|
40
44
|
const [instructions, setInstructions] = useState('');
|
|
41
45
|
const [tone, setTone] = useState('');
|
|
42
46
|
// Default density depends on annotation type
|
|
@@ -58,11 +62,22 @@ export function DetectSection({
|
|
|
58
62
|
}, [isExpanded, annotationType]);
|
|
59
63
|
|
|
60
64
|
const handleDetect = () => {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
// Map annotation type to motivation
|
|
66
|
+
const motivation: Motivation =
|
|
67
|
+
annotationType === 'highlight' ? 'highlighting' :
|
|
68
|
+
annotationType === 'assessment' ? 'assessing' :
|
|
69
|
+
'commenting';
|
|
70
|
+
|
|
71
|
+
// Emit detection:start event with options
|
|
72
|
+
eventBus.emit('detection:start', {
|
|
73
|
+
motivation,
|
|
74
|
+
options: {
|
|
75
|
+
instructions: instructions.trim() || undefined,
|
|
76
|
+
tone: (annotationType === 'comment' || annotationType === 'assessment') && tone ? tone as any : undefined,
|
|
77
|
+
density: (annotationType === 'comment' || annotationType === 'assessment' || annotationType === 'highlight') && useDensity ? density : undefined,
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
|
|
66
81
|
setInstructions('');
|
|
67
82
|
setTone('');
|
|
68
83
|
// Don't reset density/useDensity - persist across detections
|
|
@@ -91,7 +106,8 @@ export function DetectSection({
|
|
|
91
106
|
data-detecting={isDetecting && detectionProgress ? 'true' : 'false'}
|
|
92
107
|
data-type={annotationType}
|
|
93
108
|
>
|
|
94
|
-
{
|
|
109
|
+
{/* Show form when NOT detecting and NO progress to display */}
|
|
110
|
+
{!detectionProgress && (
|
|
95
111
|
<>
|
|
96
112
|
<div className="semiont-form-field">
|
|
97
113
|
<label className="semiont-form-field__label">
|
|
@@ -194,8 +210,8 @@ export function DetectSection({
|
|
|
194
210
|
</>
|
|
195
211
|
)}
|
|
196
212
|
|
|
197
|
-
{/* Detection Progress */}
|
|
198
|
-
{
|
|
213
|
+
{/* Detection Progress - show whenever we have progress (during or after detection) */}
|
|
214
|
+
{detectionProgress && (
|
|
199
215
|
<div className="semiont-detection-progress" data-type={annotationType}>
|
|
200
216
|
{/* Request Parameters */}
|
|
201
217
|
{detectionProgress.requestParams && detectionProgress.requestParams.length > 0 && (
|
|
@@ -214,6 +230,18 @@ export function DetectSection({
|
|
|
214
230
|
<span className="semiont-detection-progress__icon">✨</span>
|
|
215
231
|
<span>{detectionProgress.message}</span>
|
|
216
232
|
</div>
|
|
233
|
+
{/* Close button - shown after detection completes (when not actively detecting) */}
|
|
234
|
+
{!isDetecting && (
|
|
235
|
+
<button
|
|
236
|
+
onClick={() => eventBus.emit('detection:dismiss-progress', undefined)}
|
|
237
|
+
className="semiont-detection-progress__close"
|
|
238
|
+
aria-label={t('closeProgress')}
|
|
239
|
+
title={t('closeProgress')}
|
|
240
|
+
type="button"
|
|
241
|
+
>
|
|
242
|
+
×
|
|
243
|
+
</button>
|
|
244
|
+
)}
|
|
217
245
|
</div>
|
|
218
246
|
</div>
|
|
219
247
|
)}
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { forwardRef } from 'react';
|
|
4
4
|
import type { components } from '@semiont/api-client';
|
|
5
5
|
import { getAnnotationExactText } from '@semiont/api-client';
|
|
6
|
+
import { useEventBus } from '../../../contexts/EventBusContext';
|
|
6
7
|
|
|
7
8
|
type Annotation = components['schemas']['Annotation'];
|
|
8
9
|
|
|
9
10
|
interface HighlightEntryProps {
|
|
10
11
|
highlight: Annotation;
|
|
11
12
|
isFocused: boolean;
|
|
12
|
-
|
|
13
|
-
onHighlightRef: (highlightId: string, el: HTMLElement | null) => void;
|
|
14
|
-
onHighlightHover?: (highlightId: string | null) => void;
|
|
13
|
+
isHovered?: boolean;
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
function formatRelativeTime(isoString: string): string {
|
|
@@ -31,41 +30,34 @@ function formatRelativeTime(isoString: string): string {
|
|
|
31
30
|
return date.toLocaleDateString();
|
|
32
31
|
}
|
|
33
32
|
|
|
34
|
-
export
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
useEffect(() => {
|
|
45
|
-
onHighlightRef(highlight.id, highlightRef.current);
|
|
46
|
-
return () => {
|
|
47
|
-
onHighlightRef(highlight.id, null);
|
|
48
|
-
};
|
|
49
|
-
}, [highlight.id, onHighlightRef]);
|
|
50
|
-
|
|
51
|
-
// Scroll to highlight when focused
|
|
52
|
-
useEffect(() => {
|
|
53
|
-
if (isFocused && highlightRef.current) {
|
|
54
|
-
highlightRef.current.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
55
|
-
}
|
|
56
|
-
}, [isFocused]);
|
|
33
|
+
export const HighlightEntry = forwardRef<HTMLDivElement, HighlightEntryProps>(
|
|
34
|
+
function HighlightEntry(
|
|
35
|
+
{
|
|
36
|
+
highlight,
|
|
37
|
+
isFocused,
|
|
38
|
+
isHovered = false,
|
|
39
|
+
},
|
|
40
|
+
ref
|
|
41
|
+
) {
|
|
42
|
+
const eventBus = useEventBus();
|
|
57
43
|
|
|
58
44
|
const selectedText = getAnnotationExactText(highlight);
|
|
59
45
|
|
|
60
46
|
return (
|
|
61
47
|
<div
|
|
62
|
-
ref={
|
|
63
|
-
className=
|
|
48
|
+
ref={ref}
|
|
49
|
+
className={`semiont-annotation-entry${isHovered ? ' semiont-annotation-pulse' : ''}`}
|
|
64
50
|
data-type="highlight"
|
|
65
51
|
data-focused={isFocused ? 'true' : 'false'}
|
|
66
|
-
onClick={
|
|
67
|
-
|
|
68
|
-
|
|
52
|
+
onClick={() => {
|
|
53
|
+
eventBus.emit('annotation:click', { annotationId: highlight.id, motivation: highlight.motivation });
|
|
54
|
+
}}
|
|
55
|
+
onMouseEnter={() => {
|
|
56
|
+
eventBus.emit('annotation:hover', { annotationId: highlight.id });
|
|
57
|
+
}}
|
|
58
|
+
onMouseLeave={() => {
|
|
59
|
+
eventBus.emit('annotation:hover', { annotationId: null });
|
|
60
|
+
}}
|
|
69
61
|
>
|
|
70
62
|
{/* Highlighted text */}
|
|
71
63
|
{selectedText && (
|
|
@@ -80,4 +72,4 @@ export function HighlightEntry({
|
|
|
80
72
|
</div>
|
|
81
73
|
</div>
|
|
82
74
|
);
|
|
83
|
-
}
|
|
75
|
+
});
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { useEffect } from 'react';
|
|
3
|
+
import { useEffect, useState, useRef, useCallback, useMemo } from 'react';
|
|
4
4
|
import { useTranslations } from '../../../contexts/TranslationContext';
|
|
5
|
+
import { useEventBus } from '../../../contexts/EventBusContext';
|
|
6
|
+
import { useEventSubscriptions } from '../../../contexts/useEventSubscription';
|
|
5
7
|
import type { components, Selector } from '@semiont/api-client';
|
|
8
|
+
import { getTextPositionSelector, getTargetSelector } from '@semiont/api-client';
|
|
6
9
|
import { HighlightEntry } from './HighlightEntry';
|
|
7
|
-
import { useAnnotationPanel } from '../../../hooks/useAnnotationPanel';
|
|
8
10
|
import { DetectSection } from './DetectSection';
|
|
9
11
|
import { PanelHeader } from './PanelHeader';
|
|
10
12
|
import './HighlightPanel.css';
|
|
@@ -20,12 +22,6 @@ interface PendingAnnotation {
|
|
|
20
22
|
|
|
21
23
|
interface HighlightPanelProps {
|
|
22
24
|
annotations: Annotation[];
|
|
23
|
-
onAnnotationClick: (annotation: Annotation) => void;
|
|
24
|
-
focusedAnnotationId: string | null;
|
|
25
|
-
hoveredAnnotationId?: string | null;
|
|
26
|
-
onAnnotationHover?: (annotationId: string | null) => void;
|
|
27
|
-
onDetect?: (instructions?: string) => void | Promise<void>;
|
|
28
|
-
onCreate: (selector: Selector | Selector[]) => void;
|
|
29
25
|
pendingAnnotation: PendingAnnotation | null;
|
|
30
26
|
isDetecting?: boolean;
|
|
31
27
|
detectionProgress?: {
|
|
@@ -34,34 +30,115 @@ interface HighlightPanelProps {
|
|
|
34
30
|
message?: string;
|
|
35
31
|
} | null;
|
|
36
32
|
annotateMode?: boolean;
|
|
33
|
+
scrollToAnnotationId?: string | null;
|
|
34
|
+
onScrollCompleted?: () => void;
|
|
35
|
+
hoveredAnnotationId?: string | null;
|
|
37
36
|
}
|
|
38
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Panel for managing highlight annotations with auto-creation
|
|
40
|
+
*
|
|
41
|
+
* @emits annotation:create - Create new highlight annotation (auto-triggered). Payload: { motivation: 'highlighting', selector: Selector | Selector[], body: Body[] }
|
|
42
|
+
* @subscribes annotation:click - Annotation clicked. Payload: { annotationId: string }
|
|
43
|
+
*/
|
|
39
44
|
export function HighlightPanel({
|
|
40
45
|
annotations,
|
|
41
|
-
onAnnotationClick,
|
|
42
|
-
focusedAnnotationId,
|
|
43
|
-
hoveredAnnotationId,
|
|
44
|
-
onAnnotationHover,
|
|
45
|
-
onDetect,
|
|
46
|
-
onCreate,
|
|
47
46
|
pendingAnnotation,
|
|
48
47
|
isDetecting = false,
|
|
49
48
|
detectionProgress,
|
|
50
49
|
annotateMode = true,
|
|
50
|
+
scrollToAnnotationId,
|
|
51
|
+
onScrollCompleted,
|
|
52
|
+
hoveredAnnotationId,
|
|
51
53
|
}: HighlightPanelProps) {
|
|
54
|
+
|
|
52
55
|
const t = useTranslations('HighlightPanel');
|
|
56
|
+
const eventBus = useEventBus();
|
|
57
|
+
const [focusedAnnotationId, setFocusedAnnotationId] = useState<string | null>(null);
|
|
58
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
59
|
+
|
|
60
|
+
// Direct ref management
|
|
61
|
+
const entryRefs = useRef<Map<string, HTMLDivElement>>(new Map());
|
|
62
|
+
|
|
63
|
+
// Sort annotations by their position in the resource
|
|
64
|
+
const sortedAnnotations = useMemo(() => {
|
|
65
|
+
return [...annotations].sort((a, b) => {
|
|
66
|
+
const aSelector = getTextPositionSelector(getTargetSelector(a.target));
|
|
67
|
+
const bSelector = getTextPositionSelector(getTargetSelector(b.target));
|
|
68
|
+
if (!aSelector || !bSelector) return 0;
|
|
69
|
+
return aSelector.start - bSelector.start;
|
|
70
|
+
});
|
|
71
|
+
}, [annotations]);
|
|
72
|
+
|
|
73
|
+
// Ref callback for entry components
|
|
74
|
+
const setEntryRef = useCallback((id: string, element: HTMLDivElement | null) => {
|
|
75
|
+
if (element) {
|
|
76
|
+
entryRefs.current.set(id, element);
|
|
77
|
+
} else {
|
|
78
|
+
entryRefs.current.delete(id);
|
|
79
|
+
}
|
|
80
|
+
}, []);
|
|
81
|
+
|
|
82
|
+
// Handle scrollToAnnotationId (click scroll)
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
if (!scrollToAnnotationId) return;
|
|
85
|
+
const element = entryRefs.current.get(scrollToAnnotationId);
|
|
86
|
+
if (element && containerRef.current) {
|
|
87
|
+
const elementTop = element.offsetTop;
|
|
88
|
+
const containerHeight = containerRef.current.clientHeight;
|
|
89
|
+
const elementHeight = element.offsetHeight;
|
|
90
|
+
const scrollTo = elementTop - (containerHeight / 2) + (elementHeight / 2);
|
|
91
|
+
containerRef.current.scrollTo({ top: scrollTo, behavior: 'smooth' });
|
|
92
|
+
element.classList.remove('semiont-annotation-pulse');
|
|
93
|
+
void element.offsetWidth;
|
|
94
|
+
element.classList.add('semiont-annotation-pulse');
|
|
95
|
+
if (onScrollCompleted) onScrollCompleted();
|
|
96
|
+
}
|
|
97
|
+
}, [scrollToAnnotationId]);
|
|
98
|
+
|
|
99
|
+
// Handle hoveredAnnotationId (hover scroll only - pulse is handled by isHovered prop)
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
if (!hoveredAnnotationId) return;
|
|
102
|
+
const element = entryRefs.current.get(hoveredAnnotationId);
|
|
103
|
+
if (!element || !containerRef.current) return;
|
|
104
|
+
|
|
105
|
+
const container = containerRef.current;
|
|
106
|
+
const elementRect = element.getBoundingClientRect();
|
|
107
|
+
const containerRect = container.getBoundingClientRect();
|
|
108
|
+
const isVisible = elementRect.top >= containerRect.top && elementRect.bottom <= containerRect.bottom;
|
|
109
|
+
if (!isVisible) {
|
|
110
|
+
const elementTop = element.offsetTop;
|
|
111
|
+
const containerHeight = container.clientHeight;
|
|
112
|
+
const elementHeight = element.offsetHeight;
|
|
113
|
+
const scrollTo = elementTop - (containerHeight / 2) + (elementHeight / 2);
|
|
114
|
+
container.scrollTo({ top: scrollTo, behavior: 'smooth' });
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Pulse effect is handled by isHovered prop on HighlightEntry
|
|
118
|
+
}, [hoveredAnnotationId]);
|
|
119
|
+
|
|
120
|
+
// Subscribe to click events - update focused state
|
|
121
|
+
// Event handler for annotation clicks (extracted to avoid inline arrow function)
|
|
122
|
+
const handleAnnotationClick = useCallback(({ annotationId }: { annotationId: string }) => {
|
|
123
|
+
setFocusedAnnotationId(annotationId);
|
|
124
|
+
setTimeout(() => setFocusedAnnotationId(null), 3000);
|
|
125
|
+
}, []);
|
|
53
126
|
|
|
54
|
-
|
|
55
|
-
|
|
127
|
+
useEventSubscriptions({
|
|
128
|
+
'annotation:click': handleAnnotationClick,
|
|
129
|
+
});
|
|
56
130
|
|
|
57
131
|
// Highlights auto-create: when pendingAnnotation arrives with highlighting motivation,
|
|
58
|
-
// immediately
|
|
132
|
+
// immediately emit annotation:create event
|
|
59
133
|
useEffect(() => {
|
|
60
134
|
if (pendingAnnotation && pendingAnnotation.motivation === 'highlighting') {
|
|
61
|
-
|
|
135
|
+
eventBus.emit('annotation:create', {
|
|
136
|
+
motivation: 'highlighting',
|
|
137
|
+
selector: pendingAnnotation.selector,
|
|
138
|
+
body: [],
|
|
139
|
+
});
|
|
62
140
|
}
|
|
63
|
-
|
|
64
|
-
}, [pendingAnnotation]); // Only depend on pendingAnnotation, not onCreate (which is recreated on every render)
|
|
141
|
+
}, [pendingAnnotation]);
|
|
65
142
|
|
|
66
143
|
return (
|
|
67
144
|
<div className="semiont-panel">
|
|
@@ -70,12 +147,11 @@ export function HighlightPanel({
|
|
|
70
147
|
{/* Scrollable content area */}
|
|
71
148
|
<div ref={containerRef} className="semiont-panel__content">
|
|
72
149
|
{/* Detection Section - only in Annotate mode and for text resources */}
|
|
73
|
-
{annotateMode &&
|
|
150
|
+
{annotateMode && (
|
|
74
151
|
<DetectSection
|
|
75
152
|
annotationType="highlight"
|
|
76
153
|
isDetecting={isDetecting}
|
|
77
154
|
detectionProgress={detectionProgress}
|
|
78
|
-
onDetect={onDetect}
|
|
79
155
|
/>
|
|
80
156
|
)}
|
|
81
157
|
|
|
@@ -91,9 +167,8 @@ export function HighlightPanel({
|
|
|
91
167
|
key={highlight.id}
|
|
92
168
|
highlight={highlight}
|
|
93
169
|
isFocused={highlight.id === focusedAnnotationId}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
{...(onAnnotationHover && { onHighlightHover: onAnnotationHover })}
|
|
170
|
+
isHovered={highlight.id === hoveredAnnotationId}
|
|
171
|
+
ref={(el) => setEntryRef(highlight.id, el)}
|
|
97
172
|
/>
|
|
98
173
|
))
|
|
99
174
|
)}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { forwardRef } from 'react';
|
|
4
4
|
import type { RouteBuilder } from '../../../contexts/RoutingContext';
|
|
5
5
|
import { useTranslations } from '../../../contexts/TranslationContext';
|
|
6
6
|
import type { components } from '@semiont/api-client';
|
|
7
7
|
import { getAnnotationExactText, isBodyResolved, getBodySource, getFragmentSelector, getSvgSelector, getTargetSelector } from '@semiont/api-client';
|
|
8
8
|
import { getEntityTypes } from '@semiont/ontology';
|
|
9
9
|
import { getResourceIcon } from '../../../lib/resource-utils';
|
|
10
|
+
import { useEventBus } from '../../../contexts/EventBusContext';
|
|
11
|
+
import { useObservableExternalNavigation } from '../../../hooks/useObservableNavigation';
|
|
10
12
|
|
|
11
13
|
type Annotation = components['schemas']['Annotation'];
|
|
12
14
|
|
|
@@ -19,68 +21,27 @@ interface EnrichedAnnotation extends Annotation {
|
|
|
19
21
|
interface ReferenceEntryProps {
|
|
20
22
|
reference: Annotation;
|
|
21
23
|
isFocused: boolean;
|
|
22
|
-
|
|
24
|
+
isHovered?: boolean;
|
|
23
25
|
routes: RouteBuilder;
|
|
24
|
-
onReferenceRef: (referenceId: string, el: HTMLElement | null) => void;
|
|
25
|
-
onReferenceHover?: (referenceId: string | null) => void;
|
|
26
|
-
onGenerateDocument?: (referenceId: string, options: { title: string; prompt?: string }) => void;
|
|
27
|
-
onCreateDocument?: (annotationUri: string, title: string, entityTypes: string[]) => void;
|
|
28
|
-
onSearchDocuments?: (referenceId: string, searchTerm: string) => void;
|
|
29
|
-
onUpdateReference?: (referenceId: string, updates: Partial<Annotation>) => void;
|
|
30
26
|
annotateMode?: boolean;
|
|
31
27
|
isGenerating?: boolean;
|
|
32
28
|
}
|
|
33
29
|
|
|
34
|
-
export
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
isGenerating = false,
|
|
47
|
-
}: ReferenceEntryProps) {
|
|
30
|
+
export const ReferenceEntry = forwardRef<HTMLDivElement, ReferenceEntryProps>(
|
|
31
|
+
function ReferenceEntry(
|
|
32
|
+
{
|
|
33
|
+
reference,
|
|
34
|
+
isFocused,
|
|
35
|
+
isHovered = false,
|
|
36
|
+
routes,
|
|
37
|
+
annotateMode = true,
|
|
38
|
+
isGenerating = false,
|
|
39
|
+
},
|
|
40
|
+
ref
|
|
41
|
+
) {
|
|
48
42
|
const t = useTranslations('ReferencesPanel');
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
// Register ref with parent
|
|
52
|
-
useEffect(() => {
|
|
53
|
-
onReferenceRef(reference.id, referenceRef.current);
|
|
54
|
-
return () => {
|
|
55
|
-
onReferenceRef(reference.id, null);
|
|
56
|
-
};
|
|
57
|
-
}, [reference.id, onReferenceRef]);
|
|
58
|
-
|
|
59
|
-
// Scroll to reference when focused - use container.scrollTo to avoid scrolling ancestors
|
|
60
|
-
useEffect(() => {
|
|
61
|
-
if (isFocused && referenceRef.current) {
|
|
62
|
-
const element = referenceRef.current;
|
|
63
|
-
const container = element.closest('.semiont-toolbar-panels__content') as HTMLElement;
|
|
64
|
-
|
|
65
|
-
if (container) {
|
|
66
|
-
const elementRect = element.getBoundingClientRect();
|
|
67
|
-
const containerRect = container.getBoundingClientRect();
|
|
68
|
-
|
|
69
|
-
const isVisible =
|
|
70
|
-
elementRect.top >= containerRect.top &&
|
|
71
|
-
elementRect.bottom <= containerRect.bottom;
|
|
72
|
-
|
|
73
|
-
if (!isVisible) {
|
|
74
|
-
const elementTop = element.offsetTop;
|
|
75
|
-
const containerHeight = container.clientHeight;
|
|
76
|
-
const elementHeight = element.offsetHeight;
|
|
77
|
-
const scrollTo = elementTop - (containerHeight / 2) + (elementHeight / 2);
|
|
78
|
-
|
|
79
|
-
container.scrollTo({ top: scrollTo, behavior: 'smooth' });
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}, [isFocused]);
|
|
43
|
+
const eventBus = useEventBus();
|
|
44
|
+
const navigate = useObservableExternalNavigation();
|
|
84
45
|
|
|
85
46
|
const selectedText = getAnnotationExactText(reference) || '';
|
|
86
47
|
const isResolved = isBodyResolved(reference.body);
|
|
@@ -103,44 +64,69 @@ export function ReferenceEntry({
|
|
|
103
64
|
if (resolvedResourceUri) {
|
|
104
65
|
const resourceId = resolvedResourceUri.split('/resources/')[1];
|
|
105
66
|
if (resourceId) {
|
|
106
|
-
|
|
67
|
+
// Use observable navigation - emits 'navigation:external-navigate' event
|
|
68
|
+
navigate(routes.resourceDetail(resourceId), { resourceId });
|
|
107
69
|
}
|
|
108
70
|
}
|
|
109
71
|
};
|
|
110
72
|
|
|
111
73
|
const handleComposeDocument = () => {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
74
|
+
eventBus.emit('reference:create-manual', {
|
|
75
|
+
annotationUri: reference.id,
|
|
76
|
+
title: selectedText,
|
|
77
|
+
entityTypes,
|
|
78
|
+
});
|
|
115
79
|
};
|
|
116
80
|
|
|
117
81
|
const handleUnlink = () => {
|
|
118
|
-
|
|
119
|
-
|
|
82
|
+
// Unlinking removes all body items from the reference annotation
|
|
83
|
+
const sourceUri = typeof reference.target === 'object' && 'source' in reference.target
|
|
84
|
+
? reference.target.source
|
|
85
|
+
: '';
|
|
86
|
+
if (sourceUri) {
|
|
87
|
+
eventBus.emit('annotation:update-body', {
|
|
88
|
+
annotationUri: reference.id,
|
|
89
|
+
resourceId: sourceUri.split('/resources/')[1] || '',
|
|
90
|
+
operations: [{ op: 'remove', item: null }], // Remove all body items
|
|
91
|
+
});
|
|
120
92
|
}
|
|
121
93
|
};
|
|
122
94
|
|
|
123
95
|
const handleGenerate = () => {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
96
|
+
const resourceUri = typeof reference.target === 'object' && 'source' in reference.target
|
|
97
|
+
? reference.target.source
|
|
98
|
+
: '';
|
|
99
|
+
|
|
100
|
+
// Emit request to open generation modal
|
|
101
|
+
eventBus.emit('generation:modal-open', {
|
|
102
|
+
annotationUri: reference.id,
|
|
103
|
+
resourceUri,
|
|
104
|
+
defaultTitle: selectedText,
|
|
105
|
+
});
|
|
127
106
|
};
|
|
128
107
|
|
|
129
108
|
const handleSearch = () => {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
109
|
+
eventBus.emit('reference:link', {
|
|
110
|
+
annotationUri: reference.id,
|
|
111
|
+
searchTerm: selectedText,
|
|
112
|
+
});
|
|
133
113
|
};
|
|
134
114
|
|
|
135
115
|
return (
|
|
136
116
|
<div
|
|
137
|
-
ref={
|
|
138
|
-
className=
|
|
117
|
+
ref={ref}
|
|
118
|
+
className={`semiont-annotation-entry${isHovered ? ' semiont-annotation-pulse' : ''}`}
|
|
139
119
|
data-type="reference"
|
|
140
120
|
data-focused={isFocused ? 'true' : 'false'}
|
|
141
|
-
onClick={
|
|
142
|
-
|
|
143
|
-
|
|
121
|
+
onClick={() => {
|
|
122
|
+
eventBus.emit('annotation:click', { annotationId: reference.id, motivation: reference.motivation });
|
|
123
|
+
}}
|
|
124
|
+
onMouseEnter={() => {
|
|
125
|
+
eventBus.emit('annotation:hover', { annotationId: reference.id });
|
|
126
|
+
}}
|
|
127
|
+
onMouseLeave={() => {
|
|
128
|
+
eventBus.emit('annotation:hover', { annotationId: null });
|
|
129
|
+
}}
|
|
144
130
|
>
|
|
145
131
|
{/* Status indicator and text quote */}
|
|
146
132
|
<div className="semiont-annotation-entry__header">
|
|
@@ -235,4 +221,4 @@ export function ReferenceEntry({
|
|
|
235
221
|
</div>
|
|
236
222
|
</div>
|
|
237
223
|
);
|
|
238
|
-
}
|
|
224
|
+
});
|